Skip to main content
 首页 » 编程设计

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

2025年05月04日18Renyi-Fan

# 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 ++对象的关系。