我已经有一个使用PySide用QT编写的GUI应用程序。我想为此编写测试,而不会尽可能地更改实现。为此,我正在修补一些方法,主要是为了注册对在应用程序中实例化的某些QT对象的引用。在此期间,我偶然发现了QMenu
类的怪异行为,结果证明它是抗猴子补丁的。
下面是一个简单的代码来演示它:
from PySide.QtGui import QApplication, QMainWindow, QMenu, QDialog
class Window(QDialog):
def __index__(self, main_window):
self.setWindowTitle('Dialog')
def contextMenuEvent(self, event):
menu = self.prepare_menu()
menu.exec_(event.globalPos())
def prepare_menu(self):
menu = QMenu(self)
close_action = menu.addAction('Close')
close_action.triggered.connect(self.close)
return menu
if __name__ == '__main__':
app = QApplication(sys.argv)
main_window = QMainWindow()
main_window.show()
Window().exec_()
app.exec_()
这是一个非常简单的应用程序,仅显示两个窗口。其中之一具有上下文菜单,该菜单允许关闭窗口。
为了显示正确的行为,我将通过添加以下行来猴子修补QDialog.exec_
:
QDialog.exec_ = lambda x: print('QDialog.exec_()')
当我用该行运行代码时,第二个窗口将不会显示,而将显示QDialog.exec_()
。
现在,当我证明它可以工作时,我将行更改为如下所示:
QMenu.exec_ = lambda x: print('QMenu.exec_()')
但是令人惊讶的是,它不会影响QMenu类的行为。它的工作原理与以前完全一样。
但是这里还有另一段代码,可以正常工作:
old_prepare_menu = Window.prepare_menu
def new_prepare_menu(self):
menu = old_prepare_menu(self)
menu.exec_ = lambda x: print('menu.exec_()')
return menu
Window.prepare_menu = new_prepare_menu
现在运行该程序将在右键单击后不显示上下文菜单,而是打印menu.exec_()
。
所以基本上-QDialog
类可以打补丁,QMenu
类不能打补丁,但是QMenu
的实例也可以打补丁。
这是我注意到的另一件事:
>>> from PySide.QtGui import QMenu, QDialog
>>> QDialog.exec_
<method 'exec_' of 'PySide.QtGui.QDialog' objects>
>>> QMenu.exec_
<built-in method exec_ of Shiboken.ObjectType object at 0x5694F380>
我不明白为什么会这样。 exec_()
类的QMenu
似乎与QDialog
中的类型不同,但是我不明白为什么它会影响猴子的修补。
有什么线索为什么它会那样?有可能改变它吗?
在Windows和CentOS上使用Python 3.6在PySide-1.2.4上进行了测试。