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

PyQt5 QToolButton替换QMenu点击菜单项出现空指针及对象删除错误

错误原因
  • 你在菜单项触发的槽函数中直接重建并替换QToolButton绑定的菜单,此时被点击的原菜单仍处于Qt的事件处理流程中,你直接销毁原菜单对象,导致Qt访问已经被删除的QObject对象,触发QObject: shared QObject was deleted directly报错。
  • 首次调用make_qtoolbutton_menu方法时,self.mute_deck_1_action等Action对象还未初始化,执行disconnect操作时Qt底层收到空指针参数,触发QObject::disconnect: Unexpected nullptr parameter报错,虽然Python层的异常被try-except捕获,但Qt底层的警告依然会输出。
  • 每次重建菜单时你直接将新的QMenu赋值给self.mute_toolbutton_menu,原菜单对象的Python引用计数归零被GC回收,同时Qt也在管理该QObject的生命周期,出现双重释放的风险,进一步加剧崩溃概率。
解决方案

最优方案(推荐)

你的需求仅为更新菜单项文本,完全不需要每次重建整个菜单和Action对象,仅需初始化时一次创建好菜单、绑定信号槽,状态变更时仅更新Action文本即可,彻底规避对象生命周期问题。修改后可正常运行的main.py代码如下:

from PyQt5 import QtCore, QtGui, QtWidgets
from ui import Ui_Dialog
import sys

class Main_Program:
    def __init__(self):
        
        app = QtWidgets.QApplication(sys.argv)
        self.Dialog = QtWidgets.QDialog()
        self.ui = Ui_Dialog()
        self.ui.setupUi(self.Dialog)
        self.Dialog.show()
        
        self.deck_1 = {
            "mute":False
        }
        
        self.deck_2 = {
            "mute":False
        }
        
        self.music_clip_deck = {
            "mute":False
        }
        
        # 初始化仅执行1次:创建菜单、Action、绑定信号槽
        self.init_menu()
        # 初始化更新菜单项文本
        self.update_menu_text()
        
        sys.exit(app.exec_())
    
    def init_menu(self):
        self.mute_toolbutton_menu = QtWidgets.QMenu()
        # 创建deck1操作项
        self.mute_deck_1_action = QtWidgets.QAction(self.Dialog)
        self.mute_deck_1_action.triggered.connect(self.mute_or_unmute_deck_1)
        self.mute_toolbutton_menu.addAction(self.mute_deck_1_action)
        # 创建deck2操作项
        self.mute_deck_2_action = QtWidgets.QAction(self.Dialog)
        self.mute_deck_2_action.triggered.connect(self.mute_or_unmute_deck_2)
        self.mute_toolbutton_menu.addAction(self.mute_deck_2_action)
        # 创建music clip deck操作项
        self.mute_music_clip_deck_action = QtWidgets.QAction(self.Dialog)
        self.mute_music_clip_deck_action.triggered.connect(self.mute_or_unmute_music_clip_deck)
        self.mute_toolbutton_menu.addAction(self.mute_music_clip_deck_action)
        # 绑定菜单到工具按钮
        self.ui.general_deck_mute_toolbutton.setMenu(self.mute_toolbutton_menu)
    
    def update_menu_text(self):
        # 仅更新菜单项文本,无对象销毁创建操作
        if not self.deck_1["mute"]:
            self.mute_deck_1_action.setText("Απενεργοποίηση σίγασης deck 1")
        else:
            self.mute_deck_1_action.setText("Ενεργοποίηση σίγασης στο deck 1")
        
        if not self.deck_2["mute"]:
            self.mute_deck_2_action.setText("Απενεργοποίηση σίγασης deck 2")
        else:
            self.mute_deck_2_action.setText("Ενεργοποίηση σίγασης στο deck 2")
        
        if not self.music_clip_deck["mute"]:
            self.mute_music_clip_deck_action.setText("Απενεργοποίηση σίγασης music clip deck")
        else:
            self.mute_music_clip_deck_action.setText("Ενεργοποίηση σίγασης στο music clip deck")

    
    def mute_or_unmute_deck_1(self):
        self.deck_1["mute"] = not self.deck_1["mute"]
        self.update_menu_text()
        
    def mute_or_unmute_deck_2(self):
        self.deck_2["mute"] = not self.deck_2["mute"]
        self.update_menu_text()
        
    def mute_or_unmute_music_clip_deck(self):
        self.music_clip_deck["mute"] = not self.music_clip_deck["mute"]
        self.update_menu_text()
        
if __name__ == "__main__":
    program = Main_Program()

备用方案(仅适用于必须动态增减菜单项的场景)

如果你的业务需要动态调整菜单项数量必须重建菜单,可按以下规则修改避免报错:

  1. 重建菜单前先调用self.mute_toolbutton_menu.deleteLater(),让Qt在事件循环空闲时安全销毁旧菜单,避免正在处理事件时直接删除对象
  2. 调用disconnect前先判断对应Action是否已存在,避免空指针断开操作
  3. 不要在菜单项的触发槽函数中直接执行重建操作,可使用QtCore.QTimer.singleShot(0, self.make_qtoolbutton_menu)将重建操作延迟到当前事件处理完成后执行

内容的提问来源于stack exchange,提问作者Chris P

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 01:36:06