- Qt 6.12
- Qt Canvas Painter
- Qt Canvas Painter Examples and Tutorials
- Qt Canvas Painter - Hello Widget Example
- Qt Canvas Painter | Commercial or GPLv3

# Qt Canvas Painter - Hello Widget Example

Demonstrates the use of QCanvasPainter and QCanvasPainterWidget.

The example implements a QCanvasPainterWidget subclass. One or more instances of this widget can then be added into a QMdiArea inside the QMainWindow.

QCanvasPainterWidget itself derives from QRhiWidget, and is always using accelerated 3D rendering via QRhi.

Subclasses of QCanvasPainterWidget will at minimum want to implement paint(). This example also uses an image, loaded from a PNG file.

     class CanvasWidget : public QCanvasPainterWidget
     {
     public:
         CanvasWidget();
         void initializeResources(QCanvasPainter *p) override;
         void paint(QCanvasPainter *p) override;
         void graphicsResourcesInvalidated() override;

     private:
         QCanvasImage m_image;
     };

The paint() function can start drawing using the provider QCanvasPainter right away:

     void CanvasWidget::paint(QCanvasPainter *p)
     {
         const float size = std::min(width(), height());
         const float centerX = width() / 2;
         const float centerY = height() / 2;

         // Paint the background circle
         QCanvasRadialGradient gradient1(centerX, centerY - size * 0.1f, 0, size * 0.6f);
         gradient1.setStartColor(QColor(0x909090));
         gradient1.setEndColor(QColor(0x404040));
         p->beginPath();
         p->circle(QPointF(centerX, centerY), size * 0.46f);
         p->setFillStyle(gradient1);
         p->fill();
         p->setStrokeStyle(QColor(0x202020));
         p->setLineWidth(size * 0.02f);
         p->stroke();

See QCanvasPainter, QCanvasBrush, QCanvasRadialGradient, QCanvasImagePattern, QCanvasImage, and QFont for more information on the features used by this example.

The image is used as a pattern, for filling the heart shape:

         // Paint heart
         QCanvasImagePattern pattern(m_image, centerX, centerY, size * 0.08f, size * 0.05f);
         p->setFillStyle(pattern);

When resources like QCanvasImage and QCanvasOffscreenCanvas are involved, these are managed in initializeResources() and graphicsResourcesInvalidated():

     void CanvasWidget::initializeResources(QCanvasPainter *p)
     {
         Q_ASSERT(m_image.isNull());
         const auto flags = QCanvasPainter::ImageFlag::Repeat | QCanvasPainter::ImageFlag::GenerateMipmaps;
         m_image = p->addImage(QImage(u":/qt-translucent.png"_s), flags);
     }

     void CanvasWidget::graphicsResourcesInvalidated()
     {
         m_image = {};
     }

initializeResources() is merely a convenience. Instead of implementing it, one could also write the following in paint():

     if (m_image.isNull())
         m_image = p->addImage(QImage(":/qt-translucent.png"), QCanvasPainter::ImageFlag::Repeat);

This example does not reparent widgets between windows, so graphics resources are not going to be lost. It is nonetheless a good pattern to assign a default, empty object to all QCanvasImage and QCanvasOffscreenCanvas variables in graphicsResourcesInvalidated().

The main() function creates a QMainWindow and QMdiArea. Multiple instances of the CanvasWidget class can be added as sub-windows. Due to hasSharedPainter() defaulting to true, and due to being placed within the same top-level widget, all the painter widgets will share the same QCanvasPainter and the associated rendering infrastructure, instead of creating dedicated ones.

Example project @ code.qt.io