如何在Python中追踪PyQt5对象的所有属性访问?重写__getattribute__未生效的问题咨询
如何在Python中追踪PyQt5对象的所有属性访问?重写__getattribute__未生效的问题咨询
你遇到的这个问题其实是PyQt的底层绑定机制在搞鬼——PyQt是Qt C库的Python封装,很多方法调用是由Qt的C框架直接触发的,根本不会走Python的__getattribute__属性查找流程,所以你的打印语句才没被触发。
至于你看到sizeHint called的输出,是因为你在Python里重写了这个方法,当Qt的C++端调用它时,会直接调用你定义的Python版本,但这个调用过程并没有经过Python的属性查找,所以__getattribute__完全没参与进来。
下面给你几个可行的解决方案,帮你追踪到所有PyQt对象的方法调用:
方案一:用sys.settrace追踪所有Python函数调用
sys.settrace可以设置一个全局追踪函数,只要是Python函数被调用(包括Qt C++端触发的Python方法),都会触发它。这个方法能覆盖绝大多数场景,是最全面的追踪方式:
import sys from PyQt5.QtCore import QSize from PyQt5.QtWidgets import QWidget, QMainWindow, QApplication def trace_calls(frame, event, arg): if event == 'call': func_name = frame.f_code.co_name # 过滤掉一些系统内置的特殊函数,只追踪业务相关方法 if not func_name.startswith('__') or func_name in ('__init__', '__getattribute__'): print(f"正在调用函数: {func_name}") return trace_calls class TestWidget(QWidget): def __init__(self): super().__init__() def sizeHint(self): print('sizeHint called') return QSize(400, 400) class MyApplication(QMainWindow): def __init__(self): super().__init__() w = TestWidget() self.setCentralWidget(w) self.show() # 启动追踪 sys.settrace(trace_calls) app = QApplication(sys.argv) window = MyApplication() sys.exit(app.exec_())
方案二:动态包装所有继承的Qt方法
利用Qt的元对象系统,我们可以获取到TestWidget继承的所有Qt方法,然后动态把它们包装成带打印逻辑的函数,这样每次调用都会触发日志:
import sys from types import MethodType from PyQt5.QtCore import QSize, QMetaMethod from PyQt5.QtWidgets import QWidget, QMainWindow, QApplication class TestWidget(QWidget): def __init__(self): super().__init__() # 通过元对象获取所有继承的方法 meta_obj = self.metaObject() for i in range(meta_obj.methodCount()): method = meta_obj.method(i) method_name = method.name().data().decode() # 跳过自定义方法和特殊方法,避免重复包装 if method_name in dir(self) and method_name not in ['__init__', 'sizeHint']: original_method = getattr(self, method_name) # 定义包装函数 def wrapped_method(self, *args, original=original_method, name=method_name): print(f"方法被调用: {name}") return original(*args) # 替换原方法 setattr(self, method_name, MethodType(wrapped_method, self)) def sizeHint(self): print('sizeHint called') return QSize(400, 400) class MyApplication(QMainWindow): def __init__(self): super().__init__() w = TestWidget() self.setCentralWidget(w) self.show() app = QApplication(sys.argv) window = MyApplication() sys.exit(app.exec_())
注意:有些Qt核心方法是纯C++实现的,无法在Python层面包装,可能会出现报错,你可以根据实际情况调整过滤规则。
方案三:用__getattr__捕获未定义的属性访问
如果你的目标只是追踪那些没有在Python类中定义的Qt方法,可以用__getattr__——它只会在属性找不到的时候被调用,局限性比较大,但实现简单:
import sys from PyQt5.QtCore import QSize from PyQt5.QtWidgets import QWidget, QMainWindow, QApplication class TestWidget(QWidget): def __init__(self): super().__init__() def __getattr__(self, name): print(f"__getattr__ 被调用,访问的属性是: {name}") return super().__getattr__(name) def sizeHint(self): print('sizeHint called') return QSize(400, 400) class MyApplication(QMainWindow): def __init__(self): super().__init__() w = TestWidget() self.setCentralWidget(w) self.show() app = QApplication(sys.argv) window = MyApplication() sys.exit(app.exec_())
这个方法只能捕获那些没有被Python类覆盖的Qt方法,已经存在的属性不会触发它。
备注:内容来源于stack exchange,提问作者Louis
相关产品推荐
相关产品推荐

