使用PyQt5,我可以将某些事件处理程序绑定到我的对象,而其他事件处理程序只有在我将它们作为方法实现时才能工作changeEvent和event是后一种类型的示例。
您可以在下面的示例中看到,我可以通过编程将keyPressEvent处理程序添加到我的小部件中,但我不能对changeEvent做同样的事情。
from PyQt5 import QtGui, QtWidgets, QtCore
import types
def keyPressEvent(self, key: QtGui.QKeyEvent) -> None:
#works
print(key.isAutoRepeat())
def changeEvent(self, a0: QtCore.QEvent) -> None:
#doesn't work
print("bound change event", a0.type())
bindable = [keyPressEvent, changeEvent]
def bind_key_functions(target):
for bound in bindable:
setattr(target, bound.__name__, types.MethodType(bound, target))
class my_widget(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle("w1")
bind_key_functions(self)
class my_widget2(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle("w2")
def changeEvent(self, a0: QtCore.QEvent) -> None:
#this does work
print("derived change event", a0.type())
app = QtWidgets.QApplication([])
mw1 = my_widget()
mw1.show()
mw2 = my_widget2()
mw2.show()
app.exec_()
是什么让changeEvent与众不同?我怎么能强迫它按我的意愿行事?
使用setattr覆盖方法是一个糟糕的选择,因为它不是很优雅,如果你想监听另一个QWidget的事件,那么最好使用事件过滤器。
from PyQt5 import QtGui, QtWidgets, QtCore
class Binder(QtCore.QObject):
def __init__(self, qobject):
super().__init__(qobject)
self._qobject = qobject
self.qobject.installEventFilter(self)
@property
def qobject(self):
return self._qobject
def eventFilter(self, obj, event):
if self.qobject is obj:
print(event.type(), event)
return super().eventFilter(obj, event)
class My_Widget(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle("w1")
Binder(self)
class My_Widget2(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle("w2")
app = QtWidgets.QApplication([])
mw1 = My_Widget()
mw1.show()
mw2 = My_Widget2()
mw2.show()
app.exec_()
另一方面,它没有记录哪些方法可以分配或不分配,所以如果你想找到原因,你必须分析sip和pyqt5的源代码。很少指出的是PyQt5创建了一个方法的缓存(不知道什么时候或什么方法存储在缓存中(。