Обезьяна исправляет классы PySide - PullRequest
0 голосов
/ 07 ноября 2018

У меня уже есть приложение с графическим интерфейсом, написанное на QT с использованием PySide. Я хочу написать тесты для этого без изменения реализации, когда это возможно. Для этого я собираюсь исправить некоторые методы, в основном, чтобы зарегистрировать ссылки на некоторые объекты 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, но я не понимаю, почему он влияет на исправление обезьян.
Любые подсказки, почему он так себя ведет? Есть ли возможность изменить это?

Протестировано на PySide-1.2.4 с Python 3.6 для Windows и CentOS.

...