出现上下文菜单时如何使小部件接收鼠标释放事件

how to make widget receive mouse release event when context menu appears

在 Ubuntu20.04 上,当出现上下文菜单时,我无法让小部件接收鼠标释放事件,而 Windows 可以接收。我的pyqt版本是5.15.2.

我考虑过手动发送鼠标释放事件,但我不知道出现上下文菜单时哪些系统会收到鼠标释放事件,这样做可能会导致重复释放事件。有没有更好的解决方案?

# coding:utf-8
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMenu, QLabel


class Menu(QMenu):

    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self.addAction('action 1')
        self.addAction('action 2')

    def showEvent(self, e):
        print('show context menu')
        super().showEvent(e)


class Widget(QLabel):

    def mousePressEvent(self, e):
        print('mouse press, button:', e.button())
        super().mousePressEvent(e)

    def mouseReleaseEvent(self, e):
        print('mouse release, button:', e.button())
        super().mouseReleaseEvent(e)


class Demo(QWidget):

    def __init__(self):
        super().__init__()
        self.widget = Widget('Click Me', self)
        self.widget.move(175, 180)
        self.resize(400, 400)

    def contextMenuEvent(self, e):
        menu = Menu(self)
        menu.exec(e.globalPos())


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Demo()
    w.show()
    app.exec_()

一般规则是在 Windows 上释放鼠标右键并在 Linux 上按下鼠标右键时触发上下文菜单事件。

为了避免不一致并具有忽略 OS 并始终执行相同操作的通用行为,您可以设置一个内部标志并在需要时检查它:因为上下文菜单事件可能优先,通过 鼠标按下和上下文菜单调用函数。

记住:

  • 如果您不想处理上下文菜单事件并希望它传播到父级(如您的情况),则必须 忽略该事件;如果您覆盖 contextMenuEvent ,则必须使用 event.ignore() 或通过调用基本实现显式完成,假设它 接受事件;
  • 如果文本交互标志支持 mouse/keyboard 导航(以允许剪贴板菜单),QLabel 可能会处理该事件;
  • 快捷菜单事件也可以由键盘上的menu-key触发,所以你要保证它的reason()Mouse;如果执行“unpress”部分的函数总是检查上面的标志(它应该),这实际上不是必需的,但检查完整性和一致性的原因仍然是一个好习惯;
class Widget(QLabel):
    _isPressed = False
    def mousePressEvent(self, e):
        print('mouse press, button:', e.button())
        super().mousePressEvent(e)
        self._isPressed = True

    def mouseReleaseEvent(self, e):
        super().mouseReleaseEvent(e)
        self.unpress()

    def contextMenuEvent(self, e):
        super().contextMenuEvent(e)
        # or, alternatively:
        e.ignore()

        if e.reason() == e.Mouse:
            self.unpress()

    def unpress(self):
        if not self._isPressed:
            return
        self._isPressed = True
        # do whatever you need