【问题标题】:Widget's "destroyed" signal is not fired (PyQT)小部件的“销毁”信号未被触发(PyQT)
【发布时间】:2013-05-26 10:17:58
【问题描述】:

我有一个小部件,它在销毁后必须进行一些手动清理(停止一些线程)。但是由于某种原因,小部件的“破坏”信号没有触发。我做了这个小例子来演示这个问题。

import sys
from PyQt4 import QtGui

class MyWidget(QtGui.QWidget):
    def __init__(self, parent):
        super(MyWidget, self).__init__(parent)

        def doSomeDestruction():
            print('Hello World!')

        self.destroyed.connect(doSomeDestruction)


class MyWindow(QtGui.QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()

        self.widget = MyWidget(self)

app = QtGui.QApplication(sys.argv)
window = MyWindow()
window.show()
ret = app.exec_()
sys.exit(ret)

我希望它打印“Hello World!”当主窗口关闭时。但是,它不会打印任何内容。

【问题讨论】:

    标签: python python-3.x pyqt pyqt4


    【解决方案1】:

    经过几次尝试,我发现它如果你在课堂外声明doSomeDestruction 会起作用。(见底部)
    但我不知道为什么. 正如this 回答中所写,这是因为At the point destroyed() is emitted, the widget isn't a QWidget anymore, just a QObject (as destroyed() is emitted from ~QObject)
    这意味着当你的函数被调用时,如果你在类中编写它,它已经被删除了。 (也可以在 qt-interest 邮件列表中查看 hereOk , I am sorry for stupid question. The signal is emitted, but the slot is not called for the obvious reason, that is because object is already deleted.

    编辑:我发现了两种让它真正起作用的方法:

    1. ret = app.exec_() 之后添加一个简单的del window
    2. 在主窗口(不是小部件)中设置WA_DeleteOnClose 属性:
      在程序顶部:

      from PyQt4 import QtCore
      

      更改后的__init__ 函数:

      class MyWindow(QtGui.QMainWindow):
          def __init__(self):
              super(MyWindow, self).__init__()
              self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
              self.widget = MyWidget(self)
      

    【讨论】:

    • 你能和我分享一下打印Hello World的代码吗?我尝试根据您的说明编辑代码,但仍然无法正常工作。 pastebin.com/ZCgteHu4
    • 这很奇怪。我的代码中唯一的区别是,doSomeDestruction 是在两个类之间定义的。但现在我发现它也只是有时有效。也是gc.collect,但没有效果。我希望我们/有人能找到一种方法让它发挥作用。
    • 查看我的更新答案。我现在应该工作了。 (不管doSomeDestruction是在哪里定义的)
    • 实际上似乎 self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True) 是您需要添加的所有内容,但谢谢:)
    【解决方案2】:

    在发出destroyed 时,python 类实例(或至少 pyqtqt 链接)不存在。您可以通过在类上将destroyed 处理程序设置为staticmethod 来解决此问题。这样,当destroyed 信号发出时,python 方法仍然存在。

    class MyWidget(QWidget):
    
        def __init__(self, parent):
            super(MyWidget, self).__init__(parent)
            self.destroyed.connect(MyWidget._on_destroyed)
    
        @staticmethod
        def _on_destroyed():
            # Do stuff here
            pass
    

    如果您需要特定于类实例的信息,您可以使用functools.partial 和实例__dict__ 将该信息传递给销毁方法。

    from functools import partial
    
    class MyWidget(QWidget):
    
        def __init__(self, parent, arg1, arg2):
            super(MyWidget, self).__init__(parent)
            self.arg1 = arg1
            self.arg2 = arg2
            self.destroyed.connect(partial(MyWidget._on_destroyed, self.__dict__))
    
        @staticmethod
        def _on_destroyed(d):
            print d['arg1']
    

    【讨论】:

    • 为什么不只是:self.destroyed.connect(lambda: print(self.__dict__))?或者使用闭包,如 OPs 原始示例中所示。 (该示例不起作用的唯一原因是小部件从未被明确删除)。
    • @ekhumoro 您通常会执行比单次打印更多的操作。 lambda 仅支持单个语句。通常,您将与一些其他库和对象进行交互,当该对象被销毁时需要清理这些库和对象。有趣的是,我什至没有注意到原始示例使用的是闭包而不是类实例方法。我认为关闭也可以。
    • 我想我应该问一下:使用staticmethod 的具体优势是什么?为什么不直接使用 any 普通函数(不一定是闭包),而不是实例方法?
    • @ekhumoro 通常,大多数信号处理程序都被定义为实例方法,但在这种情况下,这是行不通的,因为方法附加到的实例将在它被调用时被删除。与处理程序相关的大部分业务逻辑都将在类上,因此将其设为静态方法或类方法而不只是一些随机函数是有意义的。
    • 当然可以:只需使用lambda,就像我最初建议的那样。也就是说,你可以这样做:self.destroyed.connect(lambda: self.method())。闭包将使self 及其所有属性和方法保持活动状态(尽管在删除对象时您必须小心访问哪些属性和方法)。当然,如果destroyed直接连接到实例方法,就无法正常工作,因为删除该方法会自动断开连接。
    猜你喜欢
    • 2015-10-23
    • 1970-01-01
    • 1970-01-01
    • 2018-07-05
    • 1970-01-01
    • 1970-01-01
    • 2011-07-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多