【问题标题】:Can we emit signals of a base class in pyside?我们可以在 pyside 中发出基类的信号吗?
【发布时间】:2013-10-16 20:09:17
【问题描述】:

是否可以从基类继承信号并在派生类中将方法连接到它们?如果是,怎么做?

使用组合工作测试用例

MyWidget 中实例化MyObject,并在小部件中对对象发出的信号作出反应。

from PySide.QtGui import QApplication, QMainWindow
from PySide.QtCore import QObject, QTimer, Signal
from PySide.QtGui import QLabel

class MyObject(QObject):
    sig = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)
        QTimer.singleShot(3000, self.alert)
        QTimer.singleShot(5000, self.exit)
    def alert(self):
        self.sig.emit()
    def exit(self):
        print('All done')
        exit(0)

class MyWidget(QLabel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.monitor = MyObject(self)
        self.monitor.sig.connect(self.update)
    def update(self):
        print(2)

app = QApplication([])
w = MyWidget()
w.show()
app.exec_()

这是一个小而有效的示例,它打开了一个最小的空窗口,由小部件实例化的self.monitor 对象在 3 和 5 秒后发出一个计时器信号。第一个提示小部件只向控制台打印一个数字,第二个信号导致应用程序退出。

继承失败的测试用例

对于继承,仅将小部件类更改为:

class MyWidget(MyObject, QLabel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.sig.connect(self.update)
    def update(self):
        print(2)

如果在控制台中运行,则不会打印任何内容,但会发生分段错误。 为什么?这可以挽救吗?

通过替换 super() 来挽救

有趣的是,如果将两个类都更改为不使用super(),则该示例再次起作用:

class MyObject(QObject):
    sig = Signal()

    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        QTimer.singleShot(3000, self.alert)
        QTimer.singleShot(5000, self.exit)
    def alert(self):
        self.sig.emit()
    def exit(self):
        print('All done')
        exit(0)

class MyWidget(MyObject, QLabel):
    def __init__(self, parent=None):
        MyObject.__init__(self, parent)
        QLabel.__init__(self, parent)
        self.sig.connect(self.update)
    def update(self):
        print(2)

Generally I prefer to use super(),但也许我需要reconsider?我特意链接了两篇关于在 Python 中使用 super() 的有争议的文章。现在会对如何在 pyside 中正确使用 super() 或解释为什么它在 pyside 中根本不起作用感兴趣。

小更新: 当使用继承和super() 但删除所有与信号相关的代码时,该示例的工作原理是它确实打开了窗口并且没有段错误。所以似乎有一些迹象表明super() 初始化和信号的组合会导致问题。

小更新2: ..当注释掉 self.sig.connect 时,窗口会启动,并且只有在 5 秒信号触发退出应用程序时才会出现段错误。

(这是 Ubuntu 13.04 系统上的 Qt 4.8.4,Pyside 1.1.2,带有 CPython 3.3.1 解释器)

【问题讨论】:

  • 它会崩溃还是干净地退出?我认为sig.connect() 就足够了,因为信号应该是类级别而不是实例级别属性。
  • @tcaswell:根据我的定义,Segfault 是崩溃并且没有干净的退出。信号总是在对象之间连接。 This doc 没有明确说明,但没有列出替代方案。而且,考虑一下,连接类的信号和槽是没有意义的,因为这意味着这些类的所有对象都是连接的。实例可以连接并相互交谈,在这方面,类只是类型定义。类型可以定义如何相互通信的协议,但它们实际上不能通信。对象可以。
  • 不清楚您是否遇到了段错误。包装层执行了很深的魔法来将 python 映射到 c++ 并返回。如果您遇到段错误,则您将 c++ 层推入错误状态。
  • 仔细看一下,我怀疑问题是你在 c++ 层得到了菱形图。尽管 python 可以应付它,pyside 对象是 c++ 对象的一个​​薄包装,并且它们的实例化变得一团糟。我对super 的理解是,它就像 c++ 中的 const 正确性一样,如果您在任何地方使用它,它会很好地工作,而如果您只是偶尔使用它会造成极大的痛苦。
  • 也就是说,我认为如果你不混入QLabel,它会起作用。

标签: python-3.x pyside signals-slots super


【解决方案1】:

Qt 不支持multiple inheritance from QObjects,同样的限制适用于 PySide 和 PyQt。尽管有时有一些方法可以解决此限制,但尝试创建具有两个或更多 QObject 基类的子类通常不是一个好主意。

对于信号的继承,使用简单的非QObject mixin 可能是最好的方法——尽管我认为这个解决方案只适用于 PySide;对于 PyQt4,信号只能在 QObject 子类上定义。

更新

后一个限制在 PyQt5 中被删除:现在可以在不继承自 QObject 的类中定义属性、信号和槽。

【讨论】:

  • 啊。这是关于 PyQt 的一个有趣的信息!我很高兴能从 pyside 获得这个小优势。
【解决方案2】:

到目前为止,我能想到的唯一解决方案——或者更确切地说是解决方法——满足标准(a)继承和(b)super() 的使用是为了防止钻石关系受到用户 tcaswell 评论的启发。

对于我的用例,必须保留任何消费者类的继承(对于自定义小部件)。另一方面,保证 - 目前 - 所有消费类都将(间接)从QObject 派生。因此,没有必要从QObject 派生MyObject,尽管这实际上创建了一个真正的抽象 MixIn 类:它不能独立使用,并且对消费类有接口要求。我不确定我是否喜欢它。

这是工作代码:

from PySide.QtGui import QApplication, QMainWindow
from PySide.QtCore import QObject, QTimer, Signal
from PySide.QtGui import QLabel

class MyObject:
    sig = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)
        QTimer.singleShot(3000, self.alert)
        QTimer.singleShot(5000, self.exit)
    def alert(self):
        self.sig.emit()
    def exit(self):
        print('All done')
        exit(0)

class MyWidget(MyObject, QLabel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.sig.connect(self.update)
    def update(self):
        print(2)


app = QApplication([])

w = MyWidget()
w.show()


app.exec_()

【讨论】:

    猜你喜欢
    • 2013-01-05
    • 2015-11-05
    • 2015-03-15
    • 2018-04-15
    • 1970-01-01
    • 2021-12-13
    • 2018-03-17
    • 2023-04-01
    • 1970-01-01
    相关资源
    最近更新 更多