自定义pyqtSignal实现

时间:2014-01-13 21:14:54

标签: python qt pyqt pyqt4 pyqt5

在PyQt中,您可以使用QtCore.pyqtSignal()创建自定义信号。

我尝试用自己的Observer模式实现代替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信号,发件人始终是信号链中最接近的小部件。

在底部的示例中,您会看到两个对象ObjectAObjectBObjectA转发来自ObjectB的信号,最后由Window接收。

使用pyqtSignalsender()收到的对象为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_())

More reference

修改

道歉,我应该提供一个用例。

以下是使用pyqtSignals的一些限制:

pyqtSignal:

  1. ..仅适用于类属性
  2. ..不能在已经实例化的类中使用
  3. ..必须使用您要发出的数据类型预先指定
  4. ..生成不支持关键字参数的信号和
  5. ..生成实例化后无法修改的信号
  6. 因此,我主要关心的是将它与基类一起使用。

    请考虑以下事项。

    列表类型容器小部件的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类,我可以为两者生成基类,而不依赖于它们。

1 个答案:

答案 0 :(得分:1)

您可以使用继承自pyqtWrapperType的元类来执行此操作。在__new__内,根据需要调用pyqtSignal()并在结果类上设置属性。

相关问题