在 PyQt 中,您可以使用它QtCore.pyqtSignal()
来创建自定义信号。
我尝试自己实现观察者模式来代替pyqtSignal
它的一些限制(例如,没有动态创建)。
它在大多数情况下都有效,至少有一个区别。
这是我到目前为止的实现
class Signal:
def __init__(self):
self.__subscribers = []
def emit(self, *args, **kwargs):
for subs in self.__subscribers:
subs(*args, **kwargs)
def connect(self, func):
self.__subscribers.append(func)
def disconnect(self, func):
try:
self.__subscribers.remove(func)
except ValueError:
print('Warning: function %s not removed from signal %s'%(func,self))
注意到的一件事是QObject.sender()
工作方式的不同。
我通常不使用sender()
,但如果它的工作方式不同,那么其他事情也可能如此。
对于常规pyqtSignal
信号,发送者始终是信号链中最近的小部件。
在底部的示例中,您将看到两个对象,ObjectA
并且ObjectB
. ObjectA
转发来自 Window 的信号ObjectB
并最终被 Window 接收。
与pyqtSignal
,接收的对象sender()
是ObjectA
,是转发信号的对象ObjectB
。
对于上面的 Signal 类,接收到的ObjectB
对象是链中的第一个对象。
为什么是这样?
完整示例
# Using PyQt5 here although the same occurs with PyQt4
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Window(QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
object_a = ObjectA(self)
object_a.signal.connect(self.listen)
layout = QBoxLayout(QBoxLayout.TopToBottom, self)
layout.addWidget(object_a)
def listen(self):
print(self.sender().__class__.__name__)
class ObjectA(QWidget):
signal = Signal()
# signal = pyqtSignal()
def __init__(self, parent=None):
super(ObjectA, self).__init__(parent)
object_b = ObjectB()
object_b.signal.connect(self.signal.emit)
layout = QBoxLayout(QBoxLayout.TopToBottom, self)
layout.addWidget(object_b)
class ObjectB(QPushButton):
signal = Signal()
# signal = pyqtSignal()
def __init__(self, parent=None):
super(ObjectB, self).__init__('Push me', parent)
self.pressed.connect(self.signal.emit)
if __name__ == '__main__':
import sys
app = QApplication([])
win = Window()
win.show()
sys.exit(app.exec_())
编辑:
抱歉,我应该提供一个用例。
以下是使用 pyqtSignals 的一些限制:
pyqt信号:
- ..仅适用于类属性
- ..不能在已经实例化的类中使用
- ..必须预先指定您希望发出的数据类型
- ..产生不支持关键字参数的信号和
- ..产生实例化后无法修改的信号
因此,我主要关心的是与基类一起使用它。
考虑以下。
列表类型容器小部件的 6 个不同小部件共享相同的界面,但外观和行为略有不同。基类提供基本变量和方法以及信号。
使用 pyqtSignal,您必须首先从至少 QObject 或 QWidget 继承您的基类。
问题是这些都不能用作混合或多重继承,例如,如果其中一个小部件也继承自 QPushButton。
class PinkListItem(QPushButton, Baseclass)
使用上面的 Signal 类,您可以改为创建没有任何先前继承的类(或只是对象)的基类,然后将它们用作任何派生子类的混合。
小心不要提出关于多重继承或混合是否好的问题,或者其他实现相同目标的方法。我也很想得到你的反馈,但也许这不是地方。
我会对向 Signal 类添加位以使其类似于 pyqtSignal 产生的工作更感兴趣。
编辑2:
刚刚注意到一个反对票,所以这里有更多用例。
发出时的关键字参数。
signal.emit(5)
可以改为写成
signal.emit(velocity=5)
与 Builder 或任何类型的依赖注入一起使用
def create(type):
w = MyWidget()
w.my_signal = Signal()
return w
松耦合
我同时使用 PyQt4 和 PyQt5。使用上面的 Signal 类,我可以为两者生成基类,而无需依赖其中任何一个。