Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QGraphicsObject: ContextMenu does not show up



  • I have a class derived from QGraphicsObject like this:

    class Room : public QGraphicsObject
    {
        Q_OBJECT
    public:
    
    //...
    public:
        QRectF boundingRect() const override;
    
        void paint(QPainter *painter,
                   const QStyleOptionGraphicsItem *option,
                   QWidget *widget) override;
    
    protected:
        void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override;
    
    //....
    }
    

    painting ect works already. Just the contextMenu does not show up. I implemented it like this:

    void Room::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
    {
        QAction guessWumpusAction{tr("&Has Wumpus")};
        guessWumpusAction.setCheckable(true);
        connect(&guessWumpusAction, &QAction::triggered, this, &Room::guessWumpus);
    
        QAction guessBatAction{tr("&Has Bat")};
        if (mHasBat) {
            guessBatAction.setChecked(true);
            guessBatAction.setEnabled(false);
        }
        else {
            guessBatAction.setCheckable(true);
        }
        connect(&guessBatAction, &QAction::triggered, this, &Room::guessBat);
    
        QAction guessPitAction{tr("&Has Pit")};
        guessPitAction.setCheckable(true);
        connect(&guessPitAction, &QAction::triggered, this, &Room::guessPit);
    
        QMenu menu;
        menu.addAction(&guessWumpusAction);
        menu.addAction(&guessBatAction);
        menu.addAction(&guessPitAction);
        menu.popup(event->screenPos());
    
        qDebug() << event->screenPos();
    }
    

    I can see that the event is called on mouse right click on the widget as expected. However the context Menu does not pop up. Whats wrong here?

    For test i call the class like this:

    #include <QApplication>
    
    #include "room.h"
    #include <QGraphicsView>
    #include <QGraphicsScene>
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
    
        QGraphicsScene scene;
        QGraphicsView view;
        view.setScene(&scene);
    
        auto room = new Room;
        room->setVisible(true);
        scene.addItem(room);
    
        view.show();
        return QApplication::exec();
    }
    


  • @sandro4912
    Two guesses:
    Are your co-ordinates right in menu.popup(event->screenPos()) (no mapping needed, I don't know)?
    Your QMenu menu goes out of scope immediately after popup() (not exec())? You would need outside storage if you want to use popup(); test it with exec() instead? (I think it's this regardless of first point.)



  • I added exec and now it shows up. I could swear i saw an example with only popup. However now that it shows up it is only is triggered when I right click in the first 1/4 of the widget.(like if the widget has half the height and width.

    I define boundingRect like this:

    QRectF Room::boundingRect() const
    {
        return QRectF{ QPointF{0, 0}, roomImage().size()};
    }
    

    were roomImage is a ressource:

    QImage Room::roomImage() const
    {
        return QImage{":/ressources/room.png"};
    }
    

    to paint i do this:

    void Room::paint(
            QPainter *painter,
            const QStyleOptionGraphicsItem *option,
            QWidget *widget)
    {
        Q_UNUSED(option)
        Q_UNUSED(widget)
    
        drawRoom(painter);
    }
    
    void Room::drawRoom(QPainter *painter)
    {
        painter->drawImage(boundingRect(), roomImage());
    }
    


  • @sandro4912
    popup() can be used, but not with a stack variable QMenu like you had. Usually you'd new it.

    For the area now: does ":/ressources/room.png" match how you spell resources on disk? Or you need to look at roomImage().size().



  • if i create menu dynamically who deallocates it later? I can't assign the Room to it as a parent since is not derrived from QWidget.

    Also i tryed:

    void Room::drawRoom(QPainter *painter)
    {
        qDebug() << boundingRect();
        qDebug() << roomImage().size();
    
        painter->drawImage(boundingRect(), roomImage());
    }
    

    Which outputs:

    QRectF(0,0 100x100)
    QSize(100, 100)
    

    So it should theoretically pop the menu in the whole image but it doesn't?


  • Lifetime Qt Champion

    Hi,

    Make it a member variable and delete it in your class destructor.



  • @sandro4912
    For dynamically created QMenu, then as @SGaist says.

    But are you sure you need QMenu:popUp() and not just QMenu::exec() for your use case? It depends how you want it to work. If the simple exec() suffices you should be able to just use a stack (local) variable QMenu menu. You don't need the menu after it has returned the action the user clicked.

    For your coordinates/click area, I don't think we're going to know. Is your image indeed 100x100 size, or is that maybe some default? Why don't you play with a bigger/smaller image and see what the pattern is?



  • there is nothing wrong with the image but to sort it out i did this:

    void Room::paint(
            QPainter *painter,
            const QStyleOptionGraphicsItem *option,
            QWidget *widget)
    {
        QRectF rect{boundingRect()};
        painter->setPen(Qt::black);
        painter->drawRect(rect);
    }
    
    QRectF Room::boundingRect() const
    {
        return QRectF{ 0, 0, 200, 200};
    }
    

    Still the Menu is not popped up on the whole rect. It only appeas like on 1/3 of the size of QRectF{ 0, 0, 200, 200}; so is not always half. Does contextMenuEvent maybe check the size from somewhere else but not from the boundingRect ? Or is this simply a bug in QT?

    I also compiled the code an a second OS to sort out that its not annother bug with KDE/Neon. But on Windows 10 same behaviour.

    Also I stayed with the local Menu:

    void Room::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
    {
        qDebug() << boundingRect();
        qDebug() << roomImage().size();
    
        QMenu menu;
        menu.addAction(mGuessWumpusAction);
        menu.addAction(mGuessBatAction);
        menu.addAction(mGuessPitAction);
        menu.exec(event->screenPos());
    }
    

    Any more ideas??



  • let me know if you need more information to find a solution for this issue. Maybe is even a qt bug.


  • Lifetime Qt Champion

    @sandro4912 said in QGraphicsObject: ContextMenu does not show up:

    Still the Menu is not popped up on the whole rect. It only appeas like on 1/3 of the size of QRectF{ 0, 0, 200, 200}; so is not always half. Does contextMenuEvent maybe check the size from somewhere else but not from the boundingRect ?

    I don't understand what you say here - does the popup / context menu event only works on 1/3 of your widget? Is the popup not as big as you expect?



  • the popup itself looks perfect.

    The problem is it does not get triggered in the whole 200x200 area of the widget. It only looks like it gets shown in a smaller rectangle from the top left point.

    so in 200x200 maybe only in 80x80 from top left point it gets created.

    I would expect in the whole rectangle the context menu gets triggere.

    maybe i should make a minimal running example so it can be verifyed.


  • Lifetime Qt Champion

    Ok, now it's clear to me. There are two examples (diagramscene, boxes) which work fine for me. Maybe you can try them out.



  • I made a minimal program and it works:

    #ifndef RECTANGLE_H
    #define RECTANGLE_H
    
    #include <QGraphicsObject>
    
    class Rectangle : public QGraphicsObject
    {
        Q_OBJECT
    public:
        explicit Rectangle(QGraphicsObject *parent = nullptr);
    
        QRectF boundingRect() const override;
    
        void paint(QPainter *painter,
                   const QStyleOptionGraphicsItem *option,
                   QWidget *widget) override;
    
    protected:
        void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override;
    };
    
    #endif // RECTANGLE_H
    
    #include "rectangle.h"
    
    #include <QGraphicsSceneContextMenuEvent>
    #include <QPainter>
    #include <QMenu>
    
    Rectangle::Rectangle(QGraphicsObject *parent)
        : QGraphicsObject(parent)
    {
    }
    
    QRectF Rectangle::boundingRect() const
    {
        return QRectF{ 0, 0, 100, 100};
    }
    
    void Rectangle::paint(
            QPainter *painter,
            const QStyleOptionGraphicsItem *option,
            QWidget *widget)
    {
        Q_UNUSED(option)
        Q_UNUSED(widget)
    
        QRectF rect{boundingRect()};
        painter->setPen(Qt::black);
        painter->drawRect(rect);
    }
    
    void Rectangle::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
    {
        QMenu menu;
        menu.addAction("Action 1");
        menu.addAction("Action 2");
        menu.exec(event->screenPos());
    }
    
    #include "rectangle.h"
    
    #include <QApplication>
    #include <QGraphicsScene>
    #include <QGraphicsView>
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
    
        QGraphicsScene scene;
    
        Rectangle rectangle;
        scene.addItem(&rectangle);
    
        QGraphicsView view;
        view.setScene(&scene);
    
        view.show();
        return QApplication::exec();
    }
    

    Everything fine here the popup shows up in all the widget. The reason it does not work in my programm is. At some point i changed the class from QGraphicsObject to QGraphicsWidget. If we modify the minimal programm to have QGraphicsWidget as a base for Rectangle we get the Issue again:

    #ifndef RECTANGLE_H
    #define RECTANGLE_H
    
    #include <QGraphicsWidget>
    
    class Rectangle : public QGraphicsWidget
    {
        Q_OBJECT
    public:
        explicit Rectangle(QGraphicsWidget *parent = nullptr);
    
        QRectF boundingRect() const override;
    
        void paint(QPainter *painter,
                   const QStyleOptionGraphicsItem *option,
                   QWidget *widget) override;
    
    protected:
        void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override;
    };
    
    #endif // RECTANGLE_H
    
    #include "rectangle.h"
    
    #include <QGraphicsSceneContextMenuEvent>
    #include <QPainter>
    #include <QMenu>
    
    Rectangle::Rectangle(QGraphicsWidget *parent)
        : QGraphicsWidget(parent)
    {
    }
    
    QRectF Rectangle::boundingRect() const
    {
        return QRectF{ 0, 0, 100, 100};
    }
    
    void Rectangle::paint(
            QPainter *painter,
            const QStyleOptionGraphicsItem *option,
            QWidget *widget)
    {
        Q_UNUSED(option)
        Q_UNUSED(widget)
    
        QRectF rect{boundingRect()};
        painter->setPen(Qt::black);
        painter->drawRect(rect);
    }
    
    void Rectangle::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
    {
        QMenu menu;
        menu.addAction("Action 1");
        menu.addAction("Action 2");
        menu.exec(event->screenPos());
    }
    
    #include "rectangle.h"
    
    #include <QApplication>
    #include <QGraphicsScene>
    #include <QGraphicsView>
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
    
        QGraphicsScene scene;
    
        Rectangle rectangle;
        scene.addItem(&rectangle);
    
        QGraphicsView view;
        view.setScene(&scene);
    
        view.show();
        return QApplication::exec();
    }
    

    So why is the behaviour different with QGraphicWidget? How can it get fixed there?


  • Lifetime Qt Champion

    You forgot to set a proper size of the QGraphicsWidget: https://doc.qt.io/qt-5/qgraphicswidget.html#size-prop - since it's a widget you have to treat it like a widget which also means you have to set a proper size since there is no layout from where the size could be calculated from.



  • I solved the issue by resize in the constructor:

        resize(boundingRect().size());
    

    That should be it or any other things to concern?


  • Lifetime Qt Champion

    @sandro4912 said in QGraphicsObject: ContextMenu does not show up:

    That should be it or any other things to concern?

    That's fine.


Log in to reply