I'm working on a QGIS plugin, where the UI is made with PyQt. I have a QListWidget and a function that fills it. I'd like to add a context menu for each item with only one option: to open another window.
I'm having trouble searching for info, since most of it works only on PyQt4 and I'm using version 5. The QListWidget that I want to add a context menu on is ds_list_widget
. Here's some of the relevant code.
FORM_CLASS, _ = uic.loadUiType(os.path.join(
os.path.dirname(__file__), 'dialog_base.ui'))
class Dialog(QDialog, FORM_CLASS):
def __init__(self, parent=None):
...
self.p_list_widget = self.findChild(QListWidget, 'projects_listWidget')
self.p_list_widget.itemClicked.connect(self.project_clicked)
self.ds_list_widget = self.findChild(QListWidget, 'datasets_listWidget')
self.ds_list_widget.itemClicked.connect(self.dataset_clicked)
...
def project_clicked(self, item):
self.fill_datasets_list(str(item.data(Qt.UserRole)))
self.settings.setValue('projectIdValue', str(item.data(Qt.UserRole)))
def fill_datasets_list(self, project_id):
self.ds_list_widget.clear()
dataset_list = self.anotherClass.fetch_dataset_list(project_id)
for dataset in dataset_list:
#Query stuff from remote
...
item = QListWidgetItem(ds_name, self.ds_list_widget)
item.setIcon(self.newIcon(ds_img))
item.setData(Qt.UserRole, ds_id)
self.ds_list_widget.addItem(item)
self.ds_list_widget.setIconSize(self.iconSize)
Since your list-widget is created by Qt Designer, it is probably easiest to install an event-filter on it and trap the context-menu event. With that in place, the rest is quite straightforward - here is a simple demo:
import sys
from PyQt5 import QtCore, QtWidgets
class Dialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__()
self.listWidget = QtWidgets.QListWidget()
self.listWidget.addItems('One Two Three'.split())
self.listWidget.installEventFilter(self)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.listWidget)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.ContextMenu and
source is self.listWidget):
menu = QtWidgets.QMenu()
menu.addAction('Open Window')
if menu.exec_(event.globalPos()):
item = source.itemAt(event.pos())
print(item.text())
return True
return super(Dialog, self).eventFilter(source, event)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Dialog()
window.setGeometry(600, 100, 300, 200)
window.show()
sys.exit(app.exec_())
PS:
You should also note that code like this:
self.p_list_widget = self.findChild(QListWidget, 'projects_listWidget')
is completely unnecessary. All the widgets from Qt Designer are automatically added as attributes to the form class using the object-name. So your code can be simplified to this:
self.projects_listWidget.itemClicked.connect(self.project_clicked)
self.datasets_listWidget.itemClicked.connect(self.dataset_clicked)
there is no need to use findChild
.
In addition to the answer above, you can also set multiple QAction()
submenu items to do multiple things. As you would a normal menu.
One way is to edit your eventFilter
so that menu.exec()
becomes a variable:
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.ContextMenu and source is self.listWidget):
menu = QtWidgets.QMenu()
open_window_1 = QAction("Open Window 1")
open_window_2 = QAction("Open Window 2")
menu.addAction(open_window_1)
menu.addAction(open_window_2)
menu_click = menu.exec(event.globalPos())
try:
item = source.itemAt(event.pos())
except Exception as e:
print(f"No item selected {e}")
if menu_click == open_window_1 :
print("Opening Window 1...")
# Your code here
if menu_click == open_window_2 :
print("Opening Window 2...")
# Your code here
# and so on... You can now add as many items as you want
return True
return super(Dialog, self).eventFilter(source, event)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With