How can I show context menu (right-click) only for a specific View?
-
Hi,
I instantiate my list View (QListView) in my QMainWindow and I want to show the context menu only for that List.
Below is signal-slot I have to call to get the custom menu, but because the slot object is "this" (i.e. Main Window), the menu shows wherever I click in the window...connect(myView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
Does it mean I have to subclass the View (e.g. myViewClass: public QListView) and then instantiate it, so that I can do:
connect(myView, SIGNAL(customContextMenuRequested(QPoint)), myViewClass, SLOT(showContextMenu(QPoint)));
?
-
Hi,
QMainWindow has its own handling of contextual menu event so you might be seeing this. Are you sure it's showing only your custom menu ?
-
@SGaist yes, it's showing my custom context menu all over the Main Window.. how can I make it only valid for the QListView?
What's more, how can I show context menu only for certain rows in QListView?Even when I set QMenu's parent in:
QMenu myRightClickMenu(myView);
it doesn't work.
-
Example:
void MyMainWindowClass::createDockWindows() { QDockWidget* dock = new QDockWidget(tr("My View Dock"), this); dock->setObjectName("My List"); MyCustomListModel* myModel = new MyCustomListModel(); myView = new QListView; myView->setModel(myModel ); /* Custom right-click menu */ myView->setContextMenuPolicy(Qt::CustomContextMenu); connect(myView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); dock->setWidget(myView); } void MyMainWindowClass::showContextMenu(const QPoint& pos) { /* Handle global position */ QPoint globalPos = deviceView->mapToGlobal(pos); /* Create menu and insert some actions */ QMenu myRightClickMenu(myView); QAction* action1= rightClickMenu.addAction("Some Action"); selectedMenuItem = rightClickMenu.exec(globalPos); if (!selectedMenuItem) { return; } if (selectedMenuItem == action1) { //do something } }
-
-
Could you give more detail about your sentence: The menu shows wherever I click in the window? The QListView occupies the entire QDockWidget even that empty space under the items is part of the QListView.
-
With your implementation you should only call it once since if you do it several times then
myView
will refer only to the last QListView. -
The position of the contextMenuEvent is with respect to the viewport of the QListView so you must change to:
QPoint globalPos = deviceView->viewport()->mapToGlobal(pos);
-
Also use the new-syntax connection:
connect(myView, &QWidget::customContextMenuRequested, this, &MyMainWindowClass::showContextMenu);
-
-
I tried to replicate with QListWidget to simplify things and the context menu still shows up when you right-click anywhere in the main window instead of showing up only for the List Widget. There's not much to it but here is an example that is hopefully reproducible for you (QListWidget* myList is a private member of MyClass)
Example:
/*Constructor*/ MyClass::MyClass(QWidget *parent, Qt::WindowFlags flags) : QMainWindow(parent, flags) { QDockWidget* dock = new QDockWidget(tr("My View Dock"), this); myList= new QListWidget(dock); myList->setContextMenuPolicy(Qt::CustomContextMenu); connect(myList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); dock->setWidget(myList); dock->setAllowedAreas(Qt::AllDockWidgetAreas); addDockWidget(Qt::LeftDockWidgetArea, dock); } void MyClass::showContextMenu(const QPoint& pos) { /* Handle global position */ QPoint globalPos = myList->viewport()->mapToGlobal(pos); /* Create menu and insert some actions */ QMenu rightClickMenu(myList); QAction* Action1 = rightClickMenu.addAction("Action1"); QAction* Action2= rightClickMenu.addAction("Action2"); selectedMenuItem = rightClickMenu.exec(globalPos); if (!selectedMenuItem) { return; } if (selectedMenuItem == Action1) { //do something } if (selectedMenuItem == Action2) { //do something else } }
-
That's why @eyllanesc asked you for a complete minimal compilable example.
With the small pieces you give here we can't do that as we have to guess the rest of the structure.
-
@eyllanesc and @SGaist Okay, so below is a reproducible example for you. Please, run it and right-click on the list inside the QListWidget - no context pops up. But when you click anywhere else, even the toolbar, the context menu shows up... what am I doing wrong? I'd like the context menu to show only for the given item in the QList (be it QListView or as it's here QListWidget for simplification purposes). I have to use Qt4 in my original code, hence my usage of lambda here is maybe wrong, as I tried to squeeze everything in main().
#include <QApplication> #include <QDebug> #include <QDockWidget> #include <QMenuBar> #include <QToolBar> #include <QMenu> #include <QAction> int main(int argc, char* argv[]) { QApplication app(argc, argv); QMainWindow myMainWindow; QMenuBar* menuBar = new QMenuBar(); QToolBar* toolBar = new QToolBar(); toolBar->setObjectName("ToolBar"); QMenu* fileMenu = new QMenu("&File", menuBar); menuBar->addMenu(fileMenu); myMainWindow.setMenuBar(menuBar); myMainWindow.addToolBar(Qt::TopToolBarArea, toolBar); QDockWidget* dock = new QDockWidget("My View Dock"); QListWidget* myList = new QListWidget(dock); myList->addItem("Item 1"); myList->addItem("Item 2"); myList->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(myList, &QListWidget::customContextMenuRequested, [myList](const QPoint& pos) { qDebug() << myList->indexAt(pos); QPoint globalPos = myList->viewport()->mapToGlobal(pos); /* Create menu and insert some actions */ QMenu rightClickMenu(myList); rightClickMenu.addAction("context1"); rightClickMenu.addAction("context2"); }); dock->setWidget(myList); dock->setAllowedAreas(Qt::AllDockWidgetAreas); myMainWindow.addDockWidget(Qt::LeftDockWidgetArea, dock); myMainWindow.show(); return app.exec(); }
-
Hello!
You must call
rightClickMenu.exec(myList->viewport()->mapToGlobal(pos));
at end of your lambda to display your context menu. I have improved your example code, feel free to try out.Code:
#include <QApplication> #include <QStandardItemModel> #include <QListView> #include <QDebug> #include <QDockWidget> #include <QMenuBar> #include <QToolBar> #include <QMenu> #include <QAction> #include <QMainWindow> #include <QListWidget> int main(int argc, char* argv[]) { QApplication app(argc, argv); QMainWindow myMainWindow; QMenuBar* menuBar = new QMenuBar(); QToolBar* toolBar = new QToolBar(); toolBar->setObjectName("ToolBar"); QMenu* fileMenu = new QMenu("&File", menuBar); menuBar->addMenu(fileMenu); myMainWindow.setMenuBar(menuBar); myMainWindow.addToolBar(Qt::TopToolBarArea, toolBar); QDockWidget *dock = new QDockWidget("My View Dock"); QListWidget *myList = new QListWidget(dock); myList->addItem("Item 1"); myList->addItem("Item 2"); myList->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(myList, &QListWidget::customContextMenuRequested, [myList](const QPoint& pos) { qDebug() << myList->indexAt(pos); /* Create menu and insert some actions */ QMenu rightClickMenu(myList); rightClickMenu.addAction("context1"); rightClickMenu.addAction("context2"); rightClickMenu.exec(myList->viewport()->mapToGlobal(pos)); // displays the context menu }); dock->setWidget(myList); dock->setAllowedAreas(Qt::AllDockWidgetAreas); myMainWindow.addDockWidget(Qt::LeftDockWidgetArea, dock); myMainWindow.show(); return app.exec(); }
Screenshot:
Happy coding!
-
@Cobra91151 Fantastic, thanks, that's helped! The context menu does still appear for the ToolBar and MenuBar, though - how can I prevent that? Plus, I don't want the context menu at a blank row in the list - I want it to show up only when I right-click a specific row - is that possible?
-
Yes, it is possible. I will do it, wait a bit.
-
So, I disabled the context menu by adding:
setContextMenuPolicy(Qt::PreventContextMenu);
tomenuBar
,toolBar
anddock
objects. Now, the context menu works only for your list. Check out my code below.Code:
#include <QApplication> #include <QStandardItemModel> #include <QListView> #include <QDebug> #include <QDockWidget> #include <QMenuBar> #include <QToolBar> #include <QMenu> #include <QAction> #include <QMainWindow> #include <QListWidget> int main(int argc, char* argv[]) { QApplication app(argc, argv); QMainWindow myMainWindow; QMenuBar *menuBar = new QMenuBar(); menuBar->setContextMenuPolicy(Qt::PreventContextMenu); QToolBar *toolBar = new QToolBar(); toolBar->setObjectName("ToolBar"); QMenu *fileMenu = new QMenu("&File", menuBar); menuBar->addMenu(fileMenu); myMainWindow.setMenuBar(menuBar); myMainWindow.addToolBar(Qt::TopToolBarArea, toolBar); toolBar->setContextMenuPolicy(Qt::PreventContextMenu); QDockWidget *dock = new QDockWidget("My View Dock"); dock->setContextMenuPolicy(Qt::PreventContextMenu); QListWidget *myList = new QListWidget(dock); myList->addItem("Item 1"); myList->addItem("Item 2"); myList->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(myList, &QListWidget::customContextMenuRequested, [myList](const QPoint& pos) { QModelIndex index = myList->indexAt(pos); if (index.isValid()) { /* Create menu and insert some actions */ QMenu rightClickMenu(myList); rightClickMenu.addAction("context1"); rightClickMenu.addAction("context2"); rightClickMenu.exec(myList->viewport()->mapToGlobal(pos)); } /* Second example, you can use the itemAt method if (myList->itemAt(pos) != nullptr) { //Create menu and insert some actions QMenu rightClickMenu(myList); rightClickMenu.addAction("context1"); rightClickMenu.addAction("context2"); rightClickMenu.exec(myList->viewport()->mapToGlobal(pos)); } */ }); dock->setWidget(myList); dock->setAllowedAreas(Qt::AllDockWidgetAreas); myMainWindow.addDockWidget(Qt::LeftDockWidgetArea, dock); myMainWindow.show(); return app.exec(); }
Screenshot:
There are two ways how you can display the context menu for your list items.
- You can check if the
QModelIndex
is valid. You can get the index from your list:myList->indexAt(pos)
- You can get the actual item by using
myList->itemAt(pos)
method and check fornullptr
.
- You can check if the
-
@Cobra91151 That's brilliant! Thanks very much!