pyqt Несколько объектов совместно используют контекстное меню - PullRequest
0 голосов
/ 18 января 2020

Так что я относительно новичок в qt и довольно приправлен python. Тем не менее, у меня есть экземпляр, где я хочу, чтобы несколько виджетов (в данном случае метки) имели одно и то же пользовательское контекстное меню, но мне нужно получить доступ к информации виджета.

Когда я использую setContextMenuPolicy и customContextMenuRequested.connect на каждом ярлыке я получаю информацию только для первого ярлыка, несмотря на доступ к контекстному меню со вторым ярлыком.

Ниже приведена урезанная версия того, с чем я работаю:

from PyQt5 import QtGui
from PyQt5 import QtCore
from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu, QLabel
import sys


class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.title = "PyQt5 Context Menu"
        self.top = 200
        self.left = 500
        self.width = 200
        self.height = 100
        self.InitWindow()

    def InitWindow(self):
        self.setWindowIcon(QtGui.QIcon("icon.png"))
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.firstLabel = QLabel(self)
        self.firstLabel.setText("Meep!")
        self.firstLabel.setObjectName("firstLabel")
        self.firstLabel.setStyleSheet("background-color: rgb(252, 233, 79);")

        self.firstLabel.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.firstLabel.customContextMenuRequested.connect(self.customMenuEvent)

        self.firstLabel.setGeometry(QtCore.QRect(0,0,50,30))


        self.secondLabel = QLabel(self)
        self.secondLabel.setText("Peem!")
        self.secondLabel.setObjectName("secondLabel")
        self.secondLabel.setStyleSheet("background-color: rgb(79,233, 252);")

        self.secondLabel.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.secondLabel.customContextMenuRequested.connect(self.customMenuEvent)

        self.secondLabel.setGeometry(QtCore.QRect(80,40,50,30))

        print("FIRST:", self.firstLabel)
        print("SECOND:", self.secondLabel)

        self.show()

    def customMenuEvent(self, eventPosition):
        child = self.childAt(eventPosition)
        print(child)
        contextMenu = QMenu(self)
        getText = contextMenu.addAction("Text")
        getName = contextMenu.addAction("Name")
        quitAct = contextMenu.addAction("Quit")
        action = contextMenu.exec_(self.mapToGlobal(eventPosition))

        if action == getText:
            print(child.text())

        if action == getName:
            print(child.objectName())

        if action == quitAct:
            self.close()



App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())

1 Ответ

0 голосов
/ 18 января 2020

Из документации о сигнале customContextMenuRequested(pos):

Позиция pos - это позиция события контекстного меню, которое получает виджет

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

Вы используете QWidget.childAt(), что относительно родительской геометрии, но так как предоставленная позиция относительно дочернего виджета, вы всегда будете иметь координаты, относящиеся к верхнему левому углу родительского элемента.

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

Для простоты простым решением было бы сопоставьте координаты от "sender" (который является объектом, который сгенерировал последний сигнал, полученный приемником) его родителю:

    def customMenuEvent(self, eventPosition):
        child = self.childAt(self.sender().mapTo(self, eventPosition))
        contextMenu = QMenu(self)
        getText = contextMenu.addAction("Text")
        getName = contextMenu.addAction("Name")
        quitAct = contextMenu.addAction("Quit")

        # note that the mapToGlobal is referred to the child!
        action = contextMenu.exec_(child.mapToGlobal(eventPosition))
        # ...

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

Существуют различные подходы, позволяющие избежать этого, но ИТ в основном зависят от того, как вы собираетесь структурировать свою программу.

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

        self.firstLabel.customContextMenuRequested.connect(
            lambda pos, child=self.firstLabel: self.customMenuEvent(pos, child))
        # ...

    def customMenuEvent(self, eventPosition, child):
        # ...
...