我有一个PyQt5应用程序,该应用程序具有可选功能(例如一个按钮),仅在明确告知该应用程序“打开此选项”时才会显示。调用此按钮后,应用程序将进行一些计算,然后发出带有结果的信号。
我不需要程序显式传递构造函数选项,而是希望程序在有人连接到信号时显示功能。
工作示例:
class ExampleApp(QWidget):
do_something = pyqtSignal(str)
def __init__(self, something_enabled: bool = False):
super().__init__()
self.button = QPushButton("Do something")
self.button.pressed.connect(self.do_work)
self.button.setVisible(something_enabled)
self.setLayout(QVBoxLayout())
self.layout().addWidget(self.button)
self.show()
def do_work(self):
self.do_something.emit("Something!")
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = ExampleApp(something_enabled=True)
window.do_something.connect(lambda result: print(result))
app.exec()
我在文档中找不到任何可用的选项,也无法找到此功能的实现。在我自己的代码中,我尝试直接更改connect函数,但是得到了只读的AttributeError。
包装器功能:
self.original_connect = self.do_something.connect
def new_connect(slot, type=None, no_receiver_check=False):
print("New connection!")
self.original_connect(slot, type, no_receiver_check)
self.do_something.connect = new_connect
AttributeError :
AttributeError: 'PyQt5.QtCore.pyqtBoundSignal' object attribute 'connect' is read-only
我还创建了一个有效的包装器类,但是我对未知的后果保持警惕。
包装类:
class SignalWrapper(QObject):
new_connection = pyqtSignal(object)
def __init__(self, signal_to_wrap):
super().__init__()
self.wrapped_signal = signal_to_wrap
self.signal = self.wrapped_signal.signal
def connect(self, slot, type=None, no_receiver_check=False):
self.new_connection.emit(slot)
return self.wrapped_signal.connect(slot)
def disconnect(self, slot=None): # real signature unknown; restored from __doc__
return self.wrapped_signal.disconnect(slot)
def emit(self, *args): # real signature unknown; restored from __doc__
return self.wrapped_signal.emit(*args)
def __call__(self, *args, **kwargs): # real signature unknown
return self.wrapped_signal.__call__(*args, **kwargs)
def __getitem__(self, *args, **kwargs): # real signature unknown
return self.wrapped_signal.__getitem__(*args, **kwargs)
def __repr__(self, *args, **kwargs): # real signature unknown
return self.wrapped_signal.__repr__(*args, **kwargs)
ExampleApp的修改部分:
class ExampleApp(QWidget):
do_something = pyqtSignal(str)
def __init__(self):
super().__init__()
self.do_something = SignalWrapper(self.do_something)
我想知道在PyQt中是否有一种优雅的方法来拦截信号连接。
答案 0 :(得分:2)
您不应更改内置函数的默认行为。由于存在隐藏的逻辑(为什么信号会使按钮可见?),这将使您的代码难以理解和维护。
但是,您可以将连接包装在一个方法中:
class ExampleApp(QWidget):
def turnOnOption(self, callback):
"""
Make button visible. Callback will be called when the work is done.
"""
self.do_something.connect(callback)
self.button.setVisible(True)
window = ExampleApp()
#window.do_something.connect(lambda result: print(result))
window.turnOnOption(lambda result: print(result))
对于其他开发人员来说是明确的,您的代码看起来不像是黑魔法……
答案 1 :(得分:1)
QObject
类具有虚拟的connectNotify和disconnectNotify方法,可以忽略这些方法以提供不同的行为。默认实现不执行任何操作。还有一种receivers方法可以给出信号的当前连接计数。
下面的演示使用这些方法来创建可以连接到任何QObject
子类的通用连接监视程序类。它监视一组信号并在检测到初始连接或最终断开连接时发出connected
信号:
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class ConnectionWatcher(QObject):
connected = pyqtSignal(str, bool)
def __init__(self, target, *signals):
if not target.findChild(ConnectionWatcher):
super().__init__(target)
self._signals = set(signals)
target.connectNotify = lambda s: self._notified(s, True)
target.disconnectNotify = lambda s: self._notified(s, False)
else:
raise RuntimeError('target already has a connection watcher')
def _notified(self, signal, connecting):
name = str(signal.name(), 'utf-8')
if name in self._signals:
count = self.parent().receivers(getattr(self.parent(), name))
if connecting and count == 1:
self.connected.emit(name, True)
elif not connecting and count == 0:
self.connected.emit(name, False)
class ExampleApp(QWidget):
do_something = pyqtSignal(str)
def __init__(self, something_enabled: bool = False):
super().__init__()
self.button = QPushButton("Do something")
watcher = ConnectionWatcher(self.button, 'pressed')
watcher.connected.connect(self.handleConnection)
c1 = self.button.pressed.connect(self.do_work)
c2 = self.button.pressed.connect(self.do_work)
self.button.pressed.disconnect(c1)
self.button.pressed.disconnect(c2)
self.setLayout(QVBoxLayout())
self.layout().addWidget(self.button)
self.show()
def handleConnection(self, name, connecting):
print('connecting:', name, connecting)
def do_work(self):
self.do_something.emit("Something!")
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = ExampleApp(something_enabled=True)
window.do_something.connect(lambda result: print(result))
app.exec()