Python 小部件调用show()后resizeEvent不工作
此代码#1(当我手动调整窗口大小时)中从未触发新的resizeEvent 新的resizeEvent在这段代码#2中很好地触发(当我手动调整窗口大小时)。我可以看到味精打印出来Python 小部件调用show()后resizeEvent不工作,python,pyqt,pyqt5,Python,Pyqt,Pyqt5,此代码#1(当我手动调整窗口大小时)中从未触发新的resizeEvent 新的resizeEvent在这段代码#2中很好地触发(当我手动调整窗口大小时)。我可以看到味精打印出来 有人知道原因吗?即使我在代码1中的widget.update()和widget.show()之后添加了widget.resizeEvent=onResize,resizeEvent代码仍然保持沉默…PyQt的创建者菲尔·汤普森在一篇文章中谈到了这一点 PyQt缓存查找Python方法的重新实现。猴子 只有在查找方法之前对
有人知道原因吗?即使我在代码1中的
widget.update()
和widget.show()
之后添加了widget.resizeEvent=onResize
,resizeEvent代码仍然保持沉默…PyQt的创建者菲尔·汤普森在一篇文章中谈到了这一点
PyQt缓存查找Python方法的重新实现。猴子
只有在查找方法之前对其进行修补,修补才会起作用
这是第一次。如果您移动
在修补后调用show()
我认为最好的解决方案是实现您自己的
QPushButton
子类,或者在show
方法之前将其指定为您的代码2。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_())
Virtual方法是主要用于子类的函数 当子类调用该方法时,它将首先查找其类方法。
如果该方法存在,将调用该方法,否则将遵循MRO(如中所示):简单地说,“遵循从类到其继承的步骤,并在找到该方法时停止” 注意:在Python中,几乎所有东西都是虚拟的,除了像“magic methods”(dunder methods)之类的东西,它是一种内置的方法,包含在double下面的分数中,比如
\uu init\uuu
)。使用像PyQt这样的绑定,事情就复杂了一点,“猴子补丁”(完全依赖于Python的虚拟特性)变得不像以前那么直观了
在您的情况下,继承遵循以下路径:
QPushButton('Test')
(调用其初始化(self,*args,**kwargs)
)时,路径将颠倒:
\uuuu init\uuuu()
一个QtWidget.QPushButton,新实例作为第一个参数,下面的“test”参数李>
\uuuu init\uuuu(self,text)
,该类获取实例后的第一个参数作为按钮文本李>
yourButton.resizeEvent
,路径都会反转:
SIP(为Qt生成python绑定而创建的工具)使用虚拟缓存,一旦找到虚拟[继承]函数,如果另一个Qt函数需要该函数,则该函数将始终在将来被调用。这意味着,一旦没有找到python重写并且方法查找成功,该方法将从那时起一直被使用,除非显式调用(在python代码中使用“
self.resizeEvent()
”)
调用show()
后,接收到一个QEvent.Resize
(它本身就是一个虚拟对象);如果未覆盖event()
,则调用基类实现,它将查找类resizeEvent
函数,并以事件作为参数调用它。因为,此时您还没有覆盖它,PyQt将退回到默认的widget resizeEvent函数,从那时起,PyQt将始终使用该函数(根据上面的列表和QPushButton实现,它将是基本的
QWidget.resizeEvent
调用)
在第二个示例中,在覆盖resizeEvent
函数后调用show()
,允许event()
函数“查找”它(从而忽略其基本实现以及继承类中定义的实现),并将使用您的实现,直到程序退出。此外,在这种情况下,该方法可以再次被覆盖,因为SIP/Qt将不再使用缓存。这是一个微妙但仍然非常重要的区别,需要记住:从现在起,只要确定以前没有调用过该方法,就可以根据需要覆盖该实例(请注意粗体字符)方法
class MyButton(QtWidgets.QPushButton):
def resizeEvent(self, event):
print("Nice to get here!")
根据经验,在官方Qt文档中看到的所有标记为virtual/protected
的内容通常都需要一个子类来正确覆盖它。您可以在定义右侧看到“[virtual protected]
”文本,该函数也在列表中
PS:对于PyQt,某些级别的monkey补丁也适用于类(这意味着您可以覆盖将由其子类自动继承的类方法),但这并不能保证,而且它们的行为通常是意外的,特别是由于Qt的跨平台性质。它主要取决于类,它的内部C++行为和继承,以及SIP是如何与原始C++对象相关的。我对什么是不起作用,什么也不起作用感到困惑,抱歉。你说它是在代码2中激发的,但是它不是在代码2中激发的。。。你最后一句话的意思是“代码1”吗?仅当实际大小发生更改时才会触发Resize事件,这通常发生在第一次
show()
调用上,但不一定发生在其他show()
调用上,并且不直接绑定到update()
(可以在调整大小后调用update(),但不能反过来调用)。在代码#1中,当您第一次sh时,不会调用您的onResize
class MyButton(QtWidgets.QPushButton):
def resizeEvent(self, event):
print("Nice to get here!")
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_())