python - 小部件调用show()后,resizeEvent不起作用

标签 python pyqt pyqt5

# code#1
import sys
from PyQt5.QtWidgets import QApplication, QPushButton

def onResize(event):
    print("Nice to get here!")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    widget = QPushButton('Test')
    widget.resize(640, 480)
    widget.show()
    widget.resizeEvent = onResize
    sys.exit(app.exec_())


在此代码#1中永远不会触发新的resizeEvent(当我手动调整窗口大小时)。

# code#2
import sys
from PyQt5.QtWidgets import QApplication, QPushButton

def onResize(event):
    print("Nice to get here!")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    widget = QPushButton('Test')
    widget.resize(640, 480)
    widget.resizeEvent = onResize
    widget.show()
    sys.exit(app.exec_())


在代码2中很好地触发了新的resizeEvent(当我手动调整窗口大小时)。而且我可以看到味精打印出来。

有人知道原因吗?即使我在代码#1的widget.update()之后添加widget.show()widget.resizeEvent = onResize,resizeEvent代码也保持沉默...

最佳答案

resizeEvent是一种“虚拟保护”方法,并且这样的方法不应被“覆盖”。

为了以安全的方式实现它们,您最好使用子类化:

class MyButton(QtWidgets.QPushButton):
    def resizeEvent(self, event):
        print("Nice to get here!")




虚方法是大多数用于子类的函数。

当子类调用该方法时,它将首先在其类方法中查找。
如果存在该方法,则将调用该方法,否则将遵循MRO(如Method Resolution Order中所述):简单地说,“遵循从类返回其继承的步骤,并在找到该方法时停止”。

注意:在Python中,除了“魔术方法”(“ dunder方法”,用双下划线括起来的内置方法,如__init__)外,几乎所有东西都是虚拟的。使用像PyQt这样的绑定,事情变得更加复杂,“猴子补丁”(完全依赖于Python的虚拟特性)变得比以前更加不直观。

在您的情况下,继承遵循以下路径:


[python对象]
Qt对象
QtWidget.QWidget
QtWidget.QAbstractButton
QtWidget.QPushButton


因此,当您创建QPushButton('Test')(调用其__init__(self, *args, **kwargs))时,路径将被反转:


__init__()一个QtWidget.QPushButton,其中新实例作为第一个参数,后跟“ test”参数;
QPushButton调用继承的QAbstractButton类__init__(self, text),该类获取实例之后的第一个参数作为按钮文本;
QAbstractButton将调用其“某些”方法或QWidget的方法以进行大小提示,字体管理等。
等等...


以相同的方式,每当调用yourButton.resizeEvent时,路径将被反转:


寻找QtWidget.QPushButton.resizeEvent
寻找QtWidget.QAbstractButton.resizeEvent
...




SIP(创建用于为Qt生成python绑定的工具)对虚拟机使用缓存,一旦找到虚拟[继承的]函数,将来会在另一个Qt函数需要时调用该函数。这意味着,一旦未找到python覆盖并且方法查找成功,则从那时起将始终使用该方法,除非明确调用(在python代码中使用“ self.resizeEvent()”)。

调用show()之后,QEvent.Resize(它是一个虚拟的本身)会收到一个QWidget.event()。如果event()没有被覆盖,则将调用基类实现,该实现将查找类resizeEvent函数,并以事件作为参数进行调用。
由于此时您尚未覆盖它,因此PyQt将回退到默认的小部件resizeEvent函数,从那时起,它将始终使用该函数(根据上面的列表和QPushButton实现,它将是基本的< cc>通话)。

在您的第二个示例中,在重写QWidget.resizeEvent函数之后调用show(),允许resizeEvent函数“查找”它(从而忽略其基本实现以及继承的类中定义的那些实现),并将使用您的直到程序退出。
同样,在这种情况下,由于SIP / Qt将不再使用缓存,因此该方法可以再次被覆盖。请记住,这是一个微妙的但仍然非常重要的区别:从这一刻起,您可以确定要被覆盖的实例(注意粗体字符)方法,只要您确定它从未被调用过即可。

def onResize1(event):
    print("Nice to get here!")

def onResize2(event):
    print("Can you see me?")

def changeResizeEvent(widget, func):
    widget.resizeEvent = func

if __name__ == "__main__":
    app = QApplication(sys.argv)
    widget = QPushButton('Test')
    widget.resize(640, 480)
    # change the resize event function *before* it might be called
    changeResizeEvent(widget, onResize1)
    widget.show()
    # change the function again after clicking
    widget.clicked.connect(lambda: changeResizeEvent(widget, onResize2))
    sys.exit(app.exec_())


根据经验,在Qt官方文档中看到的所有标记为event()的内容通常都需要一个子类才能正确覆盖它。您可以在virtual/protected定义的右侧看到“ [virtual protected]”文本,并且该功能也在Protected Function列表中。

PS:使用PyQt,某些级别的猴子修补程序也可以与类一起使用(这意味着您可以覆盖将由其子类自动继承的类方法),但这不能保证并且它们的行为通常是意外的,特别是由于跨平台的性质Qt。它主要取决于类,其内部C ++行为和继承以及SIP与原始C ++对象的关系。

关于python - 小部件调用show()后,resizeEvent不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58995898/

相关文章:

python - 我应该如何在 PyQt5 的表格小部件中连接 CheckBox clicked 信号?

qt - PyQt5 - 显示来自不同类的 QDialog

python - 如何在 pyqt 中对齐右下角的按钮?

python 3 不确定行为

python - QTableWidget 如何从代码和/或单击中选择一个单元格进行编辑

python - 在 PyQt 中将图像添加到 QTableWidget

python - 如何计算QPolygon面积

python - 存储由数据帧组成的字典的最有效方法

用于操作矩阵和向量叉积的 Python 程序

python - 使用 Django 存储整数数组