Right-click context menus with Qt

—richardwb on Thursday, July 23, 2009 @ 20:47

Getting right-clicks to popup a context menu is pretty straightforward in Qt. There are just a couple of things to watch out for…

First, there are several ways to tell Qt you want a context menu. One approach is to subclass the widget and override the QWidget::contextMenuEvent() event handler. However, I think the easiest approach is to call setContextMenuPolicy(Qt::CustomContextMenu) on the widget you want, and then connect the customContextMenuRequested() signal to the appropriate slot:

// myWidget is any QWidget-derived class
myWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(myWidget, SIGNAL(customContextMenuRequested(const QPoint&)),
    this, SLOT(ShowContextMenu(const QPoint&)));

Next, note that the const QPoint& pos parameter in the customContextMenuRequested() signal is normally in widget coordinates. However, also note that classes which inherit from QAbstractScrollArea1 instead use the coordinates of their viewport(). Either way, you will need to map these coordinates to global coordinates using mapToGlobal().

Finally, simply either popup() or exec() your QMenu. Remember that popup() is non-blocking, so if you wish to use that, make sure your QMenu is created on the heap (or some other way of guaranteeing that the QMenu’s lifetime outlasts the scope of the function)!

void MyClass::ShowContextMenu(const QPoint& pos) // this is a slot
{
    // for most widgets
    QPoint globalPos = myWidget->mapToGlobal(pos);
    // for QAbstractScrollArea and derived classes you would use:
    // QPoint globalPos = myWidget->viewport()->mapToGlobal(pos); 

    QMenu myMenu;
    myMenu.addAction("Menu Item 1");
    // ...

    QAction* selectedItem = myMenu.exec(globalPos);
    if (selectedItem)
    {
        // something was chosen, do stuff
    }
    else
    {
        // nothing was chosen
    }
}

  1. Examples include QTreeWidget, QTreeView, QListWidget, QListView, QTableWidget, QTableView, QTextEdit, QPlainTextEdit, and QMdiArea

Comments

  1. pif
    Friday, January 29, 2010 @ 08:22

    Thanks for this! Really useful.

  2. webjet
    Friday, March 5, 2010 @ 12:59

    Easy to follow, very informative thanks!

  3. Kaener
    Sunday, October 3, 2010 @ 20:58

    Wow! You're realy helped me. Thanks. ;)

  4. gary
    Monday, December 6, 2010 @ 06:56

    genius, been trying to figure, how to get a right click all day, thanks so much :))

  5. Belgian
    Wednesday, February 2, 2011 @ 06:35

    Nice, thank you!

  6. Stefan
    Thursday, March 10, 2011 @ 01:23

    Really useful ! Thanks to yo uI've been able to complete this in Ruby :

    class TaskView < Qt::GraphicsView
      slots 'showContextMenu(const QPoint)'
      
      def initialize(scene)
        super(scene)
        connect( self, SIGNAL('customContextMenuRequested(const QPoint)'), self, SLOT('showContextMenu(const QPoint)')) 
        setContextMenuPolicy(Qt::CustomContextMenu)
      end
      
      def showContextMenu(pos)
        fileMenu = Qt::Menu.new(Qt::MainWindow.tr("File"))
        quitAction = fileMenu.addAction(Qt::MainWindow.tr("E&xit"))
        fileMenu.exec( self.mapToGlobal(pos))
      end
    end
    

    thx ! stF

  7. toto
    Tuesday, April 5, 2011 @ 18:43

    Thank you very much. This is what I am search.

  8. john
    Friday, July 15, 2011 @ 16:56

    Hi , thanks for this. A question though.. If we add many menus how to know which one was clicked???

  9. Stephen Hoskins
    Saturday, October 1, 2011 @ 13:38

    Hey There! I totally plagiarized your code here and took credit for it myself over here at this company! (Just kidding.)

    But seriously, I appreciate it. Sometimes it is SO hard to find a SIMPLE example/explanation for how to do things in Qt. I usually try looking in my /lib/qt4/examples directory or QtDesigner.

    One thing I'd like to add though. Your comment, "Remember that popup() is non-blocking, so if you wish to use that, make sure your QMenu is created on the heap!" made me raise an eyebrow.

    You can have something running on a stack in a separate thread in order to prevent something from blocking. In fact, all functionality that is ever executed occurs on a call stack. So getting your memory from the heap shouldn't make a difference. You can just declare your QMenu as a member variable of a class as a non-pointer and everything will work just fine. That's what I did and I haven't had any problems. In fact, in your own example you got your QMenu off the stack. 0_o

    Thanks again for the post! It is greatly appreciated!

  10. anotherOne
    Saturday, December 3, 2011 @ 03:38

    Thank you for the simple comprehensive example. I really appreciate it.

  11. Krishna Prasad
    Thursday, December 22, 2011 @ 19:34

    Thanks. Simple but elegant example and I copied as it is!

  12. richardwb
    Wednesday, December 28, 2011 @ 17:13

    Hi Stephen, I mentioned that QMenu should be created on the heap if used in conjunction with popup() because if QMenu falls out of scope and gets destructed while the QMenu is still being used you will likely crash.

    In the example I used exec() instead, which is a blocking call. Since it blocks until the user either selects something or dismisses the menu, there won't be any problem, QMenu will be "done with" before it gets destroyed.

    However, you are correct in saying that you could have the QMenu as a member variable of a class. It's not that QMenu necessarily needs to be created on the heap, but more that it must not be destroyed while the QMenu is being used. Qt does make it simpler to just assign the QMenu a parent and create things on the heap so lifetime isn't a big problem. I'll clarify my sentence above a bit.

    Thanks for the feedback!

  13. richardwb
    Wednesday, December 28, 2011 @ 17:23

    Hi john, Normally you wouldn't care too much about which menu is being clicked, but more about the action which is being executed. The menu is just a container of actions. Typically there will only be one active menu at a time in an application.

    If you do have multiple active menus at a time, one approach would be to use the triggered() signal for that action (QMenu::addAction() returns a QAction*)and connect it to a slot.

  14. Jerry
    Sunday, January 15, 2012 @ 03:36

    Thanks a lot! very good!

Add a comment
(?)

BBCode