You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 05:32:32