【问题标题】:QToolButton pops up menu only when pressed and HOLDQToolButton 仅在按下并按住时才会弹出菜单
【发布时间】:2026-01-26 09:05:02
【问题描述】:

我正在尝试实现一个“打开”按钮,这样如果用户按住它,就会弹出一个供用户选择的应用程序菜单;但是一旦用户释放鼠标,菜单就会消失。如果用户在未选择菜单上的应用程序的情况下释放鼠标,则应使用默认应用程序打开文件。我将此按钮实现为QToolButton,并按如下方式连接信号:

self.ui_open_btn.pressed.connect(self._onOpenBtnPressed)
self.ui_open_btn.triggered.connect(self._onOpenBtnTriggered)

def _onOpenBtnPressed(self):
    self.ui_open_btn.showMenu()

def _onOpenBtnTriggered(self, action):
    application_name = action.text()
    # code to launch the application

现在,当用户按下按钮时,菜单会弹出。但是,当用户释放按钮时,菜单仍然存在,并且通过单击它来触发菜单上的操作。我在DelayedPopupInstantPopup 模式下都试过了。只要菜单已设置为self.ui_open_btn,我就无法再捕捉到任何released 信号。用户释放鼠标时如何隐藏菜单?如何通过释放鼠标来触发菜单上的动作?

---添加---

我发现使用QToolButton 的另一个问题:按下按钮时菜单总是弹出。相反,我想捕捉pressed 信号,做一些检查以确定菜单是否应该弹出。所以我改变了编写自定义工具按钮的方法,将QPushButtonQMenu 子类化。请在下面的答案中查看我的代码。

谢谢。

【问题讨论】:

  • 您是否尝试使用released 信号隐藏菜单?
  • @mguijarr:是的,我试过self.ui_open_btn.menu().close()self.ui_open_btn.menu().hide()。都没有奏效。我认为问题在于只要菜单设置为self.ui_open_btn,我就无法再捕捉到任何released 信号。

标签: qt pyqt mouseevent qpushbutton qmenu


【解决方案1】:

创建一个自定义 QToolButton,它将过滤来自菜单的事件,并对 菜单接收到鼠标释放事件:

class MyToolButton(QtGui.QToolButton):
    def __init__(self, *args):
        QtGui.QToolButton.__init__(self, *args)
    def eventFilter(self, menu, event):
        if event.type() == QtCore.QEvent.MouseButtonRelease:
            if self.underMouse():
                menu.close()
                # and now do default action
                print "doing default action"
                return True
        return False

设置菜单后安装事件过滤器:

self.ui_open_btn.menu().installEventFilter(self.ui_open_btn)

【讨论】:

  • 我刚试过。它不起作用。 eventFilter() 根本没有被调用。
  • 您创建了一个 MyToolButton,对吧?不是 QToolButton。您还必须安装事件过滤器。请显示一些代码。
  • 是的。我做到了:self.open_btn = MyToolButton(self)' and 'self.open_btn.installEventFilter(self.openBtn.menu())MyToolButton 与您发布的完全一样。
  • 我也想知道为什么我们使用eventFilter()而不是mouseReleaseEvent()
  • 只是为了确保:在关联菜单后调用 installEventFilter?我的意思是, self.open_btn.menu() 返回一些东西,对吧?我可以告诉你,我设法让它与答案的代码一起正常工作,所以你也可以:)
【解决方案2】:

我设法通过子类化QPushButtonQMennu 来实现我想要的:

class MyMenu(QtGui.QMenu):
    """ Custom menu which will close when mouse is released. ""'"
    def mouseReleaseEvent(self, event):
        action = self.actionAt(event.pos())
        self.triggered.emit(action)
        self.close()

class MyButton(QtGui.QPushButton):
    triggered = QtCore.pyqtSignal("QAction")

    def __init__(self, menu=None, parent=None):
        super(MyButton, self).__init__(parent)
        self.setMenu(menu)

    def menu(self):
        return self._menu

    def setMenu(self, menu):
        self._menu = menu if menu else MyMenu(self)
        self._menu.triggered.connect(self.triggered.emit)

在包含此按钮的QDialog 中,我执行以下操作:

menu = MyMenu(self)
# insert here code to add actions to menu
self.open_btn = MyButton(parent=self, menu=menu)
self.open_btn.pressed.connect(self._onOpenBtnPressed)
self.open_btn.triggered.connect(self._onOpenBtnTriggered)

def _onOpenBtnPressed(self):
    # insert here code to check whether we should pop up the menu
    pos = self.mapToGlobal(self.open_btn.pos())
    pos.setY(pos.y() + self.open_btn.height())
    self.open_btn.menu().move(pos)
    self.open_btn.menu().show()

def _onOpenBtnTriggered(self, action):
    if action:
        application_name = str(action.text())
        # insert here code to launch this application
    else:
        # insert here code to launch the default application
    self.close() # close this dialog

【讨论】:

    最近更新 更多