自定义pyqtSignal实现

 中孝雪瑶诗涵 发布于 2023-02-03 10:24

在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信号,发送方始终是最接近信号链的小部件.

在底部的示例中,您将看到两个对象,ObjectAObjectB.ObjectA转发信号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的一些限制:

pyqtSignal:

    ..只适用于类属性

    ..不能在已经实例化的类中使用

    ..必须预先指定您要发出的数据类型

    ..产生不支持关键字参数的信号

    ..产生实例化后无法修改的信号

因此,我主要关注的是将它与基类一起使用.

考虑以下.

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

撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有