Saturday, March 30, 2013

QGraphicsWidget

Usually it's easy to get things working with Qt (http://qt-project.org), but recently I encoutered an issue when trying to implement simple component derived from QGraphicsWidget. My initial idea was to use QGraphicsItem, so I made this little class:
class TestItem : public QGraphicsItem
{
public:
        TestItem(QGraphicsItem *parent=0) : QGraphicsItem(parent) {}
        void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
        virtual QRectF boundingRect () const;

protected:
        virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
        virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
};

void TestItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
        qDebug() << __PRETTY_FUNCTION__ << "press";
}

void TestItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
        qDebug() << __PRETTY_FUNCTION__ <<  "release";
}

void TestItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
        Q_UNUSED(option)
        Q_UNUSED(widget)
        painter->fillRect(boundingRect(), QColor(255,0,0,100));
}

QRectF TestItem::boundingRect () const
{
        return QRectF(-100, -40, 100, 40);
}
Everything was working like expected, but in order to use a QGraphicsLayout, I wanted to derive that class from QGraphicsWidget. The naive way was to make minimal changes:
class TestWid : public QGraphicsWidget
{
        Q_OBJECT
public:
        TestWid(QGraphicsItem *parent=0) : QGraphicsWidget(parent) { }
        void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
        virtual QRectF boundingRect () const;

protected:
        virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
        virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
};

void TestWid::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
        qDebug() << __PRETTY_FUNCTION__ <<  "press";
}

void TestWid::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
        qDebug() << __PRETTY_FUNCTION__ <<  "release";
}

void TestWid::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
        Q_UNUSED(option)
        Q_UNUSED(widget)
        painter->fillRect(boundingRect(), QColor(0,0,255,100));
}

QRectF TestWid::boundingRect () const
{
        return QRectF(-100, -40, 100, 40);
}

Pretty straightforward, isn't it? It showed and painted things like expected, but I didn't get any mouse events. Wait what?

I spent hours just trying out things and googling this problem. I knew I had this very same issue earlier but didn't remember how I solved it. Until I figured out a very crucial thing, in case of QGraphicsWidget you must NOT implement boundingRect(). Instead use setGeometry for the object.

So the needed changes was to remote the boundingRect() method, and to call setGeometry in TestWid constructor:

setGeometry(QRectF(-100, -40, 100, 40));

After these very tiny little changes I finally got everthing working. That all thing made me really frustrated. Solving this issue didn't cause good feeling, I was just feeling stupid. Sometimes programming is a great waste of time.