我已经阅读了很多关于如何在Python和PyQt中将多个信号连接到同一事件处理程序的文章。例如,将多个按钮或组合框连接到同一个函数。
许多示例展示了使用QSignalMapper来完成此操作,但是当信号携带参数时(如combobox.currentIndexChanged),它并不适用。
许多人建议使用lambda来解决这个问题。我同意,这是一种干净、漂亮的解决方案,但没有人提到lambda会创建一个闭包,其中包含引用 - 因此引用的对象无法被删除。内存泄漏你好!
证明:
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的连接 - 它将被正确删除。
因此,问题是:连接多个带参数信号到单个处理程序的正确方法是什么,而不会泄漏内存?
wdg.deleteLater() wdg2 = Widget() wdg2.move(300,100) wdg2.show()
- Grigory Makeevdel wdg
,并且一旦Qt删除了C++部分,__del__
就会被调用。我在我的答案中添加了另一个调试示例,这应该更清楚地显示实际发生的情况。 - ekhumoro__del__
的信息(http://docs.python.org/2/reference/datamodel.html#object.__del__)。Qt仍然持有引用,因此Python垃圾回收器必须等待`deleteLater`事件被处理。我在调试示例中将打印语句放在事件循环之前,以明确表示只有在事件处理开始后才会调用`__del__`。因此,Qt首先删除C++部分,然后允许垃圾回收器删除Python部分。 - ekhumoro