2

我一直在为我的基于 PyQt 的项目开发一些调试/测试基础设施。我用特殊的调试属性标记了大多数小部件,即使小部件是在 C++ 端创建的。

但是,多次访问 a 中的同一个按钮QFileDialog似乎会产生的sip 包装器实例。也就是说,如果我多次访问“取消”按钮,PyQt 不会每次都返回相同的 Python 对象。

这是可用于重现该问题的最小测试程序:

from PyQt4.QtCore import QTimer
from PyQt4.QtGui import QApplication, QFileDialog, QDialogButtonBox

def print_debug_stuff():
    # Find the dialog from among the top-level widgets
    dlg = filter( lambda w: isinstance(w, QFileDialog), QApplication.topLevelWidgets() )[0]

    # Find the button box in the dialog, then find the cancel button
    buttonBox = dlg.findChild(QDialogButtonBox, "buttonBox")
    cancelButton = buttonBox.children()[2]

    # Does it have our debug attribute? Can we add it?
    print "Tagging button: '{}' ({})".format( cancelButton.text(), cancelButton )
    print "...had my_debug_tag?", hasattr(cancelButton, "my_debug_tag")

    # Add the debug attribute
    buttonBox.children()[2].my_debug_tag = "hello"
    print "...has my_debug_tag?", hasattr(cancelButton, "my_debug_tag")

app = QApplication([])

# Repeatedly print debug info until the program quits
timer = QTimer(timeout=print_debug_stuff)
timer.setInterval( 100.0 )
timer.start()

# Open a (non-native!) file save dialog
QFileDialog.getSaveFileName(options=QFileDialog.DontUseNativeDialog)

希望这可以编程输出如下内容:

Tagging button: 'Cancel' (<PyQt4.QtGui.QPushButton object at 0x1013c8cb0>)
...had my_debug_tag? False
...has my_debug_tag? True
Tagging button: 'Cancel' (<PyQt4.QtGui.QPushButton object at 0x1013c8cb0>)
...had my_debug_tag? True
...has my_debug_tag? True
Tagging button: 'Cancel' (<PyQt4.QtGui.QPushButton object at 0x1013c8cb0>)
...had my_debug_tag? True
...has my_debug_tag? True

...但是,这就是我实际看到的。注意cancelButton对象每次的地址都不一样,而且我添加的调试属性丢失了:

Tagging button: 'Cancel' (<PyQt4.QtGui.QPushButton object at 0x1013c8cb0>)
...had my_debug_tag? False
...has my_debug_tag? True
Tagging button: 'Cancel' (<PyQt4.QtGui.QPushButton object at 0x1013c8ef0>)
...had my_debug_tag? False
...has my_debug_tag? True
Tagging button: 'Cancel' (<PyQt4.QtGui.QPushButton object at 0x1013c8e60>)
...had my_debug_tag? False
...has my_debug_tag? True
Tagging button: 'Cancel' (<PyQt4.QtGui.QPushButton object at 0x1013c8dd0>)
...had my_debug_tag? False
...has my_debug_tag? True

所以看起来 PyQt 正在“丢失”它之前创建的 Python 对象,然后为每次重复访问创建一个“新”对象。这是预期的吗?

顺便说一句,此测试程序在 Mac OS X (10.7) 和 Linux (Ubuntu 12) 上的行为相同。我正在使用 PyQt 4.8.5。

4

1 回答 1

2

The button you are attempting to reference is an internal Qt object which does not have a corresponding python equivalent. So naturally PyQt will have to create a new python wrapper for it every time you access it. And if you do print dlg, you will see that a new wrapper is created for that, too.

Given this, it should be obvious that tagging the python wrapper is not going to work, because the wrapper will be garbage-collected immediately after it goes out of scope.

To work around this, you need to tag the internal Qt object itself. One simple way to do this, would be to use properties:

    prop = cancelButton.property("my_debug_tag")
    print "...had my_debug_tag?", p.isValid()

    cancelButton.setProperty("my_debug_tag", "hello")

    prop = cancelButton.property("my_debug_tag")
    print "...has my_debug_tag?", p.isValid()

Obviously, this technique will only work for Qt classes that inherit QObject.

于 2014-01-19T22:21:27.593 回答