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

PyQt5实现QToolBar中菜单按钮点击任意区域直接弹出菜单

解决方案:让工具栏菜单按钮点击任意区域弹出菜单

核心需求是避免每次手动修改QToolButton的popupMode,以下是两种优雅的实现方式:

方法一:子类化QToolBar(推荐,多场景统一处理)

自定义工具栏类,重写addAction方法,自动识别菜单动作并设置弹窗模式,一次定义即可全局生效:

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


class AutoPopupToolBar(QToolBar):
    def addAction(self, action):
        super().addAction(action)
        # 自动识别菜单动作并设置弹窗模式
        if isinstance(action.parent(), QMenu):
            btn = self.widgetForAction(action)
            if isinstance(btn, QToolButton):
                btn.setPopupMode(QToolButton.InstantPopup)


class ColorMenu(QMenu):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setTitle('Text color')

        self.group = QActionGroup(self)

        iconSize = self.style().pixelMetric(QStyle.PM_LargeIconSize)

        pm = QPixmap(iconSize, iconSize)
        pm.fill(self.palette().text().color())
        self.defaultAction = self.addAction(QIcon(pm), 'Default color')
        self.defaultAction.setCheckable(True)
        self.group.addAction(self.defaultAction)

        self.addSeparator()

        self.customColorAction = self.addAction('Custom color')
        self.customColorAction.setVisible(False)
        self.customColorAction.setCheckable(True)
        self.group.addAction(self.customColorAction)

        self.addSeparator()

        self.baseColorActions = []
        colors = {}
        # 获取有效全局颜色
        for key, value in Qt.__dict__.items():
            if (
                isinstance(value, Qt.GlobalColor)
                and 1 < value < 19
            ):
                # 优化颜色名称可读性
                if key.startswith('light'):
                    key = 'light {}'.format(key[5:].lower())
                elif key.startswith('dark'):
                    key = 'dark {}'.format(key[4:].lower())
                colors[value] = key.capitalize()

        # 按逻辑顺序添加预设颜色
        for i in (2, 4, 5, 6, 3, 7, 13, 8, 14, 9, 15, 10, 16, 11, 17, 12, 18):
            color = QColor(Qt.GlobalColor(i))
            pm = QPixmap(iconSize, iconSize)
            pm.fill(color)
            action = self.addAction(QIcon(pm), colors[i])
            action.setData(color)
            action.setCheckable(True)
            self.group.addAction(action)
            self.baseColorActions.append(action)

        self.setColor(None)

    def setColor(self, color):
        if isinstance(color, QBrush) and color.style():
            color = color.color()
        elif isinstance(color, (Qt.GlobalColor, int)):
            color = QColor(color)
        if isinstance(color, QColor) and color.isValid():
            for action in self.baseColorActions:
                if action.data() == color:
                    self.setIcon(action.icon())
                    action.setChecked(True)
                    self.customColorAction.setVisible(False)
                    break
            else:
                iconSize = self.style().pixelMetric(QStyle.PM_LargeIconSize)
                pm = QPixmap(iconSize, iconSize)
                pm.fill(color)
                icon = QIcon(pm)
                self.setIcon(icon)

                self.customColorAction.setIcon(icon)
                self.customColorAction.setData(color)
                self.customColorAction.setVisible(True)
                self.customColorAction.setChecked(True)
            return

        self.setIcon(self.defaultAction.icon())
        self.defaultAction.setChecked(True)
        self.customColorAction.setVisible(False)


class Editor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.editor = QTextEdit()
        self.setCentralWidget(self.editor)

        self.formatMenu = self.menuBar().addMenu('Format')
        self.colorMenu = ColorMenu(self)
        self.formatMenu.addMenu(self.colorMenu)

        # 使用自定义工具栏
        self.toolbar = AutoPopupToolBar('Format')
        self.addToolBar(Qt.TopToolBarArea, self.toolbar)
        self.toolbar.addAction(self.colorMenu.menuAction())

        self.editor.currentCharFormatChanged.connect(self.updateColorMenu)
        self.colorMenu.triggered.connect(self.setTextColor)

    def setTextColor(self, action):
        if action.data():
            self.editor.setTextColor(action.data())
        else:
            tc = self.editor.textCursor()
            fmt = tc.charFormat()
            fmt.clearForeground()
            tc.setCharFormat(fmt)

    def updateColorMenu(self, fmt):
        self.colorMenu.setColor(fmt.foreground())


app = QApplication([])
editor = Editor()
editor.show()
app.exec()

方法二:通用工具函数(适合零散添加场景)

如果不需要全局自定义工具栏,可编写工具函数单独处理菜单添加逻辑:

def add_menu_to_toolbar(toolbar: QToolBar, menu: QMenu) -> QAction:
    action = toolbar.addAction(menu.menuAction())
    btn = toolbar.widgetForAction(action)
    if isinstance(btn, QToolButton):
        btn.setPopupMode(QToolButton.InstantPopup)
    return action

使用示例:

# 替代原有的toolbar.addAction(self.colorMenu.menuAction())
add_menu_to_toolbar(self.toolbar, self.colorMenu)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 09:31:06