# 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/