Сигналы или события для отрыва QMenu - PullRequest
2 голосов
/ 09 апреля 2019

Есть ли сигнал / событие, которое я могу использовать для отрыва QMenu?

У меня есть подкласс QMenu, в котором он имеет .setTearOffEnabled(True), но я бы хотел, чтобы этот отрыв всегда был сверху, если пользователь нажимает на отрывную планку.

Я не могу использовать QtCore.Qt.WindowStaysOnTopHint, так как это приведет к тому, что мое меню уже будет в состоянии отрыва.

Например: если область моего основного инструмента больше, чем отрывной, и я нажимаю на свой основной инструмент, за ним будет отрывное окно.

Ответы [ 2 ]

1 голос
/ 10 апреля 2019

В следующем коде нажатый сигнал испускается при нажатии отрыв (пунктирные линии):

import sys
from PyQt5 import QtCore, QtWidgets


class Menu(QtWidgets.QMenu):
    clicked = QtCore.pyqtSignal()

    def mouseReleaseEvent(self, event):
        if self.isTearOffEnabled():
            tearRect = QtCore.QRect(
                0,
                0,
                self.width(),
                self.style().pixelMetric(
                    QtWidgets.QStyle.PM_MenuTearoffHeight, None, self
                ),
            )
            if tearRect.contains(event.pos()):
                self.clicked.emit()
                QtCore.QTimer.singleShot(0, self.after_clicked)
        super(Menu, self).mouseReleaseEvent(event)

    @QtCore.pyqtSlot()
    def after_clicked(self):
        tornPopup = None
        for tl in QtWidgets.QApplication.topLevelWidgets():
            if tl.metaObject().className() == "QTornOffMenu":
                tornPopup = tl
                break
        if tornPopup is not None:
            print("This is the tornPopup: ", tornPopup)
            tornPopup.setWindowFlag(QtCore.Qt.WindowStaysOnTopHint)


if __name__ == "__main__":

    app = QtWidgets.QApplication(sys.argv)
    w = QtWidgets.QMainWindow(parent=None)
    menu = Menu("Menu", tearOffEnabled=True)
    menu.clicked.connect(lambda: print("clicked"))
    w.menuBar().addMenu(menu)
    for i in range(5):
        action = QtWidgets.QAction("action{}".format(i), w)
        menu.addAction(action)
    w.show()
    sys.exit(app.exec_())
0 голосов
/ 10 апреля 2019

Когда меню обрывается, оно скрывается, и Qt заменяет его копией, созданной из внутреннего подкласса QMenu. Поэтому, чтобы установить WindowStaysOnTopHint в оторванном меню, вам сначала нужно найти способ получить ссылку на него. Один из способов сделать это - установить фильтр событий на объект приложения и наблюдать за дочерними событиями правильного типа:

class MenuWatcher(QtCore.QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        QtWidgets.qApp.installEventFilter(self)

    def eventFilter(self, source, event):
        if (event.type() == QtCore.QEvent.ChildAdded and
            event.child().metaObject().className() == 'QTornOffMenu'):
            event.child().setWindowFlag(QtCore.Qt.WindowStaysOnTopHint)
        return super().eventFilter(source, event)

Этот класс будет работать с всеми оторванными меню, созданными в приложении.

Однако, если фильтрация событий была выполнена классом исходного меню, его собственное оторванное меню можно определить, сравнив пункты меню:

class Menu(QtWidgets.QMenu):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setTearOffEnabled(True)
        QtWidgets.qApp.installEventFilter(self)

    def eventFilter(self, source, event):
        if event.type() == QtCore.QEvent.ChildAdded:
            child = event.child()
            if (child.metaObject().className() == 'QTornOffMenu' and
                all(a is b for a, b in zip(child.actions(), self.actions()))):
                child.setWindowFlag(QtCore.Qt.WindowStaysOnTopHint)
        return super().eventFilter(source, event)
...