Python pyqt多对象共享上下文菜单
所以我对qt比较陌生,对python也相当熟悉。 但是,我有一个实例,我希望多个小部件(在本例中是标签)共享同一个自定义上下文菜单,但我需要访问小部件的信息 当我在每个标签上使用Python pyqt多对象共享上下文菜单,python,pyqt5,contextmenu,Python,Pyqt5,Contextmenu,所以我对qt比较陌生,对python也相当熟悉。 但是,我有一个实例,我希望多个小部件(在本例中是标签)共享同一个自定义上下文菜单,但我需要访问小部件的信息 当我在每个标签上使用setContextMenuPolicy和customContextMenuRequested.connect时,我只获取第一个标签的信息,尽管使用第二个标签访问上下文菜单 下面是我正在处理的内容的精简版本: from PyQt5 import QtGui from PyQt5 import QtCore from Py
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())
从有关信号的文档中: position pos是小部件接收的上下文菜单事件的位置 这意味着您将始终收到相对于触发信号的小部件的鼠标位置 您使用的是相对于父几何体的
QWidget.childAt()
,但由于提供的位置是相对于子小部件的,因此最终的坐标总是相对于父几何体的左上角
如果您尝试将第一个小部件的几何图形设置在非左上角的位置,这一点就会变得很清楚:即使您右键单击第一个小部件,您也会看到菜单不会显示在单击的位置。如果仔细观察,还将看到菜单正好显示在您单击的位置,该位置基于父菜单左上角的坐标
为了简单起见,一个简单的解决方案是将坐标从“”映射到其父对象(这是发出接收器已接收到的最后一个信号的对象):
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))
# ...
但是请注意,这可能会导致一些不一致,特别是在处理多线程时(理论上,另一个线程可能会在右键单击事件和接收器实际接收到该事件之间触发另一个信号)
有各种不同的方法可以避免这种情况,但这主要取决于您将如何构建程序
例如,您可以使用lambda添加一个参数,该参数有助于标识发送事件的源:
self.firstLabel.customContextMenuRequested.connect(
lambda pos, child=self.firstLabel: self.customMenuEvent(pos, child))
# ...
def customMenuEvent(self, eventPosition, child):
# ...
提示:如果您没有安装qt5 designer,我建议您这样做。它可以让您在几分钟内构建一个简单的GUI,并将所有内容放在适当的位置和上下文中。qt的另一种叉子是PySide。谢谢,我确实拥有并使用了Qt5设计器,但我把它放在一起作为一个概念证明(不合适)。非常感谢!多线程目前对我来说不是问题,所以第一个示例非常有效。我还有很长的路要走!不客气!但是,请记住,
self.sender()
应始终谨慎且有意识地使用(关于这一点),第二种方法虽然看起来有点复杂,但更为可取且更为连贯,因为子参数始终是明确的。