pyqt:将多个信号连接到pyqt中的同一功能的正确方法(不适用QSignalMapper) [英] pyqt: A correct way to connect multiple signals to the same function in pyqt (QSignalMapper not applicable)

查看:225
本文介绍了pyqt:将多个信号连接到pyqt中的同一功能的正确方法(不适用QSignalMapper)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  1. 我已经准备好很多关于如何将多个信号连接到python和pyqt中的同一事件处理程序的文章.例如,将多个按钮或组合框连接到同一功能.

  1. I've ready many posts on how to connect multiple signals to the same event handler in python and pyqt. For example, connecting several buttons or comboboxes to the same function.

许多示例显示了如何使用QSignalMapper进行此操作,但不适用于信号带有参数的情况,例如combobox.currentIndexChanged

Many examples show how to do this with QSignalMapper, but it is not applicable when the signal carries a parameter, as with combobox.currentIndexChanged

许多人建议可以用lambda制成.我同意这是一个干净漂亮的解决方案,但是没有人提及lambda创建一个闭包,该闭包包含一个引用-因此无法删除被引用的对象.你好,内存泄漏!

Many people suggest it can be made with lambda. It is a clean and pretty solution, I agree, but nobody mentions that lambda creates a closure, which holds a reference - thus the referenced object can not be deleted. Hello memory leak!

证明:

from PyQt4 import QtGui, QtCore

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

        # create and set the layout
        lay_main = QtGui.QHBoxLayout()
        self.setLayout(lay_main)

        # create two comboboxes and connect them to a single handler with lambda

        combobox = QtGui.QComboBox()
        combobox.addItems('Nol Adyn Dwa Tri'.split())
        combobox.currentIndexChanged.connect(lambda ind: self.on_selected('1', ind))
        lay_main.addWidget(combobox)

        combobox = QtGui.QComboBox()
        combobox.addItems('Nol Adyn Dwa Tri'.split())
        combobox.currentIndexChanged.connect(lambda ind: self.on_selected('2', ind))
        lay_main.addWidget(combobox)

    # let the handler show which combobox was selected with which value
    def on_selected(self, cb, index):
        print '! combobox ', cb, ' index ', index

    def __del__(self):
        print 'deleted'

if __name__ == '__main__':

    import sys
    app = QtGui.QApplication(sys.argv)

    wdg = Widget()
    wdg.show()

    wdg = None

    sys.exit(app.exec_())

尽管我们清除了引用,但未删除该小部件.删除与lambda的连接-它会被正确删除.

The widget is NOT deleted though we clear the reference. Remove the connection to lambda - it gets deleted properly.

所以,问题是:在不泄漏内存的情况下,将具有参数的多个信号连接到单个处理程序的正确方法是什么?

So, the question is: which is the proper way to connect several signals with parameters to a single handler without leaking memory?

推荐答案

不能完全删除对象,因为信号连接在闭包中保留了引用,这是不正确的. Qt在删除对象时会自动删除所有信号连接,这反过来又会删除对python端lambda的引用.

It is simply untrue that an object cannot be deleted because a signal connection holds a reference in a closure. Qt will automatically remove all signal connections when it deletes an object, which will in turn remove the reference to the lambda on the python side.

但这意味着您不能总是依赖Python 来删除对象.每个PyQt对象都有两部分:Qt C ++部分和Python包装器部分.这两个部分都必须删除-有时以特定顺序删除(取决于Qt或Python当前是否拥有该对象的所有权).除此之外,还有Python垃圾收集器的各种变化因素(尤其是在解释器关闭的短时间内).

But this implies that you cannot always rely on Python alone to delete objects. There are two parts to every PyQt object: the Qt C++ part, and the Python wrapper part. Both parts must be deleted - and sometimes in a specific order (depending on whether Qt or Python currently has ownership of the object). In addition to that, there's also the vagaries of the Python garbage-collector to factor in (especially during the short period when the interpreter is shutting down).

无论如何,在您的特定示例中,简单的解决方法是简单地做到:

Anyway, in your specific example, the easy fix is to simply do:

    # wdg = None
    wdg.deleteLater()

这计划删除对象,因此需要运行的事件循环才能生效.在您的示例中,这还将自动退出应用程序(因为该对象是最后关闭的窗口).

This schedules the object for deletion, so a running event-loop is required for it have any effect. In your example, this will also automatically quit the application (because the object is the last window closed).

要更清楚地了解正在发生的事情,您还可以尝试以下操作:

To more clearly see what's happening, you can also try this:

    #wdg = None
    wdg.deleteLater()

    app.exec_()

    # Python part is still alive here...
    print(wdg)
    # but the Qt part has already gone
    print(wdg.objectName())

输出:

<__main__.Widget object at 0x7fa953688510>
Traceback (most recent call last):
  File "test.py", line 45, in <module>
    print(wdg.objectName())
RuntimeError: wrapped C/C++ object of type Widget has been deleted
deleted

编辑:

这是另一个调试示例,希望可以使其更加清晰:

Here's another debugging example that hopefully makes it even clearer:

    wdg = Widget()
    wdg.show()

    wdg.deleteLater()
    print 'wdg.deleteLater called'

    del wdg
    print 'del widget executed'

    wd2 = Widget()
    wd2.show()

    print 'starting event-loop'
    app.exec_()

输出:

$ python2 test.py
wdg.deleteLater called
del widget executed
starting event-loop
deleted

这篇关于pyqt:将多个信号连接到pyqt中的同一功能的正确方法(不适用QSignalMapper)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆