PySide6继承事件连接时自定义Slot装饰器失效问题排查
PySide6自定义槽装饰器抛出AttributeError问题解决
我在PySide2项目里有一段可正常运行的代码,通过自定义装饰器@_my_slot给槽函数添加执行后恢复父窗口焦点的功能,代码如下:
def _my_slot(func): def wrapper(*args, **kwargs): func(*args, **kwargs) obj = args[0] obj.activateWindow() obj.raise_() obj.setFocus() return Slot()(wrapper)
迁移到PySide6后这段代码无法运行,抛出错误:
AttributeError: 'PySide6.QtCore.MetaFunction' object has no attribute 'im_func'
以下是Python 3.11、PySide6 6.6.0、Windows环境下的最小复现示例:
import sys from PySide6.QtWidgets import QApplication from PySide6.QtWidgets import QMainWindow from PySide6.QtCore import Slot, Signal, QPoint def _my_slot(func): """ 给槽函数添加焦点和窗口激活功能的装饰器 """ def wrapper(*args, **kwargs): func(*args, **kwargs) obj = args[0] obj.activateWindow() obj.raise_() obj.setFocus() return Slot()(wrapper) class MainWindow(QMainWindow): total_number = Signal(int) def __init__(self, parent=None): super(MainWindow, self).__init__(parent) # 注释掉下面这行代码就正常运行 self.customContextMenuRequested.connect(self.onContextMenu) self.total_number.connect(self.update_total_number) @Slot() def onContextMenu(self, point: QPoint): pass @_my_slot def update_total_number(self, total_number: int): self.total_number.emit(total_number) if __name__ == "__main__": app = QApplication() window = MainWindow() sys.exit(app.exec())
问题原因
PySide6对Slot装饰器的内部实现做了调整,直接对wrapper函数调用Slot()后返回的MetaFunction对象,无法被Qt信号连接机制正确识别。当类中同时存在其他用标准@Slot装饰的函数时,元对象系统的处理逻辑会触发该错误。
修复方案
基础版修复(无参数类型指定)
调整装饰器结构,将Slot装饰器直接应用在wrapper上,并复制原函数的元信息,确保Qt能正确识别函数签名:
def _my_slot(func): """ 给槽函数添加焦点和窗口激活功能的装饰器 """ @Slot() def wrapper(*args, **kwargs): func(*args, **kwargs) obj = args[0] obj.activateWindow() obj.raise_() obj.setFocus() # 复制原函数元信息,保证Qt元对象系统识别正常 wrapper.__name__ = func.__name__ wrapper.__doc__ = func.__doc__ wrapper.__annotations__ = func.__annotations__ return wrapper
进阶版修复(支持参数类型指定)
如果槽函数需要指定参数类型(如示例中的int),可以让自定义装饰器接受Slot的参数并传递:
def _my_slot(*slot_args, **slot_kwargs): def decorator(func): @Slot(*slot_args, **slot_kwargs) def wrapper(*args, **kwargs): func(*args, **kwargs) obj = args[0] obj.activateWindow() obj.raise_() obj.setFocus() wrapper.__name__ = func.__name__ wrapper.__doc__ = func.__doc__ wrapper.__annotations__ = func.__annotations__ return wrapper return decorator
对应槽函数使用方式修改为:
@_my_slot(int) def update_total_number(self, total_number: int): self.total_number.emit(total_number)
修改后,Qt元对象系统能正确识别槽函数的签名和类型,既保留了焦点恢复功能,也不会触发原错误。
内容的提问来源于stack exchange,提问作者Mystic Odin
相关产品推荐
相关产品推荐

