猴子修补PySide类

时间:2018-11-07 19:58:38

标签: python-3.x qt pyside monkeypatching

我已经有一个使用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上进行了测试。

0 个答案:

没有答案