c++qtqgraphicssceneqgraphicspixmapitem

Issues with QGraphicsPixmapItem handling Mouse Events in QGraphicsScene


I created an object:

class Button : public QObject, public QGraphicsPixmapItem
{
    Q_OBJECT
public:
    Button();
    virtual QRectF boundingRect() const override;
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;

    // QGraphicsItem interface
protected:
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
signals:
    void clicked();
private:
    bool m_pressed;
    QPixmap m_buttonPixmap, m_pressedButtonPixmap;
};

next I implemented a few functions:

Button::Button()
    : m_pressed(false)
{
    m_buttonPixmap = PixmapManager::Instance()->getPixmap(PixmapManager::Button);
    m_pressedButtonPixmap = PixmapManager::Instance()->getPixmap(PixmapManager::PressedButton);
    setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
}

QRectF Button::boundingRect() const
{
    return QRectF(-m_buttonPixmap.width() / 2.0f,
                  -m_buttonPixmap.height() / 2.0f,
                  m_buttonPixmap.width(),
                  m_buttonPixmap.height());

}

void Button::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    if (m_pressed) {
        //painter->drawPixmap(0,0, m_pressedButtonPixmap);

    }
    else {
        painter->setBrush(Qt::red);
        painter->drawRect(boundingRect());
        //painter->drawPixmap(0,0, m_buttonPixmap);
    }
}

void Button::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "pressed ";
    m_pressed = true;
    update();
    QGraphicsPixmapItem::mousePressEvent(event);
}

void Button::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "released ";
    m_pressed = false;
    update();
    QGraphicsPixmapItem::mouseReleaseEvent(event);
}

I also created a QGraphicsScene with Button item:

MenuScene::MenuScene(QObject *parent)
    : QGraphicsScene(parent)
{
    setSceneRect(0,0, SCREEN::PHYSICAL_SIZE.width(), SCREEN::PHYSICAL_SIZE.height());
    addItem(&m_startButton);
}

void MenuScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "Scene pressed";
    QGraphicsScene::mousePressEvent(event);
}

void MenuScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "Scene released";
    QGraphicsScene::mouseReleaseEvent(event);
}

If I click on a button I got only information from MenuScene like "Scene pressed"/"Scene released"

In main.cpp I used:

    MenuScene* ms = new MenuScene();
    QGraphicsView gv;
    gc.show();

I tried use: m_startButton.setAcceptedMouseButtons(Qt::AllButtons); m_startButton.setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable); but without result.


Solution

  • Mouse collision detection always uses the view's items() function with the Qt::IntersectsItemShape flag to check what items may be affected by the mouse event, meaning that the result is always based on the item's shape().

    While the default behavior of QGraphicsItem is to return a QPainterPath based on the boundingRect(), this is not the case of all subclasses, including QGraphicsPixmapItem, which constructs the path based on its pixmap (and possibly considering its mask).

    Since you're never calling setPixmap() on the item, the resulting path from shape() is empty, so it will always be transparent to mouse events (and to other items collision detection, for what's worth).

    The simplest solution, then, is to call setPixmap(m_buttonPixmap) in the constructor, and then call again setPixmap() with either pixmaps in the related mouse event handler.

    Alternatively, you could override shape(), create a QPainterPath, use addRect(boundingRect()), and return it (you should also consider caching both the bounding rect and the path, to avoid unnecessary overhead).

    Note, though, that you're practically not taking advantage of any the QGraphicsPixmapItem features: not only you're not using its offset, transformation mode or masking capabilities, but you also end up by drawing the pixmaps on your own.
    Considering this, it really makes little point to subclass from it to begin with, and you could probably just inherit from QGraphicsObject, which already inherits from both QObject and QGraphicsItem.