9.10.  Paint Events, Drawing Images

[ fromfile: paintevents.xml id: paintevents ]

A widget should perform the appropriate painting inside its paintEvent() method. This is the only place where a QWidget can create a QPainter(this).

As you may have observed, a QPaintEvent can be sent to a QWidget due to a number of reasons:

  1. Widget is hidden and then exposed.

  2. Widget is resized, or re-laid out.

  3. update() or repaint() is called.

Example 9.19, defines a custom QWidget that overrides paintEvent().

Example 9.19. src/widgets/life/lifewidget.h

[ . . . . ]
class LifeWidget : public QWidget
{
    Q_OBJECT
public:
    explicit LifeWidget(QWidget* parent = 0);
    ~LifeWidget();
    QSize sizeHint() const;
    void paintEvent(QPaintEvent* evt);      1
public slots:
    void setImage(const QImage& image);
private:
    QImage m_image;
    QSize m_size;
};
[ . . . . ]

1

Custom paint event

<include src="src/widgets/life/lifewidget.h" href="src/widgets/life/lifewidget.h" id="lifewidget-h" allfiles="1" mode="cpp"/>


Steps to get a QPainter for a QWidget:

  1. Create a QPainter(this).

  2. Use QPainter API to draw on QWidget.

Example 9.20 shows a paintEvent() that takes an off-screen QImage and paints it directly onto the QWidget.

Example 9.20. src/widgets/life/lifewidget.cpp

[ . . . . ]

void LifeWidget::paintEvent(QPaintEvent* evt) {
    QPainter painter(this);         1
    if (!m_image.isNull())
        painter.drawImage(QPoint(0,0), m_image);
}

1

First line of most paintEvents.

<include segid="paintevent" mode="cpp" href="src/widgets/life/lifewidget.cpp" id="lifewidget-paint" src="src/widgets/life/lifewidget.cpp"/>


This program paints successive generations of population maps based on the rules described in Conway's Game of Life. Figure 9.27 shows a snapshot of one generation. In Section 17.2.3 we revisit and parallelize this game.

Figure 9.27.  Conway's Game of Life

Conway's Game of Life

Normally, paintEvent() is not called directly, but you can schedule it to be called, synchronously or asynchronously. repaint() does not return until paintEvent() has been called. update() returns immediately after a QPaintEvent is placed on the event queue. Example 9.21 scales the image to the correct size and saves it, before calling update(), ensuring that LifeWidget shows the new image sometime soon.

Example 9.21. src/widgets/life/lifewidget.cpp

[ . . . . ]

void LifeWidget::setImage(const QImage& image) {
    m_size = image.size();
    m_image = image.scaled(size());
    update();                       1
}

1

Asynchronous - return immediately.

<include segid="setImage" mode="cpp" href="src/widgets/life/lifewidget.cpp" id="lifewidget-repaint" src="src/widgets/life/lifewidget.cpp"/>