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
相关产品推荐
相关产品推荐

