The QML Canvas Element

The QML Canvas Element

By Jeff Tranter

Perhaps you have been trying to do more with QML than just using the basic elements like rectangles, text and images, or maybe you have been struggling to implement a user interface that doesn't map well into these basic elements. If so, the QML Canvas may be just the solution you are looking for.  In this post, we'll look at Canvas (1), a powerful and useful QML element that is part of Qt Quick.

Introduction

The basic QML visual elements are quite low-level, consisting of things like images, rectangles and text. Often you can build your user interface by combining these basic building blocks, but sometimes you have a design that can't be easily made up from these elements. Prior to Qt version 5, if you wanted much more than colored rectangles, text or images, you needed to drop down into C++ and implement it using Qt's paint API. Starting with Qt Quick 2 in Qt 5.0, the new QML Canvas addresses this limitation by providing a QML element with full 2D drawing capability.

Rather than reinvent the wheel and design a new set of APIs for drawing, QML leverages the work of the HTML5 canvas. The good news is that, if you know the HTML5 Canvas, you already know more than ninety percent of the QML Canvas element. The bad news is that, if you are one of those people avoiding learning how to program in JavaScript, you are going to need to write some JavaScript code in order to use the Canvas element.

More Details

The Canvas QML element has been available as part of Qt Quick 2 since Qt 5.0.0. The QML elements most relevant to using the canvas are Canvas (1) and Context2D (2). The Context2D element implements the W3C Canvas 2D Context API standard (3) with some enhanced features.

The API provides drawing primitives for arcs, Bézier curves, images, ellipses, rectangles, text, lines, quadratic curves and rounded rectangles.

As always, the Qt documentation provides full reference material for the elements. It also includes some tips for porting existing HTML5 Canvas code to QML. If you are not familiar with the HTML5 Canvas, I recommend that you check a few of the many excellent tutorials or references available on the Internet.

The Qt source code also comes with some examples which can be found in your Qt install directory under examples/quick/canvas/.

A Simple Example

Let's look at a simple programming example. Save the code example shown below to a QML file and then execute it using the qmlscene program. The sample code illustrates a number of the drawing primitives.  

// Simple example of QML Canvas element

import QtQuick 2.0

Canvas {
    width: 400
    height: 400

    onPaint: {
        // Get drawing context
        var context = getContext("2d");

        // Make canvas all white
        context.beginPath();
        context.clearRect(0, 0, width, height);
        context.fill();

        // Fill inside with blue, leaving 10 pixel border
        context.beginPath();
        context.fillStyle = "blue"
        context.fillRect(10, 10, width - 20, height - 20);
        context.fill();

        // Draw a line
        context.beginPath();
        context.lineWidth = 2;
        context.moveTo(30, 30);
        context.strokeStyle = "red"
        context.lineTo(width-30, height-30);
        context.stroke();

        // Draw a circle
        context.beginPath();
        context.fillStyle = "orange"
        context.strokeStyle = "red"
        context.moveTo(width/2+60, height/2);
        context.arc(width/2, height/2, 60, 0, 2*Math.PI, true)
        context.fill();
        context.stroke();

        // Draw some text
        context.beginPath();
        context.strokeStyle = "lime green"
        context.font = "20px sans-serif";
        context.text("Hello, world!", width/2, 50);
        context.stroke();
    }
}

Drawing is done with a 2D graphics context, obtained by calling the getContext() method. Depending on the graphics primitives used, drawing involves strokes or paths. You can learn more about these concepts from reading the HTML5 canvas documentation.

A screen shot of the above example running is shown below.

A More Complex Example

A slightly more complex example is shown below. It illustrates the use of JavaScript code for programmatically drawing lines based on an algorithm, in this case the display of a number of closely spaced lines to generate a Moiré  pattern.

import QtQuick 2.0

/*
Lines are drawn from x1,y1 to x2,y2 in four "phases" as below:

From     To    Phase
x1 y1   x2 y2  Number
 0,0     w,0    1
 w,0     w,h    2
 w,h     0,h    3
 0,h     0,0    4
     done       5
*/

import QtQuick 2.0

Canvas {
    id: canvas
    width: 400
    height: 400

    onPaint: {
        var ctx = getContext("2d");
        var w = canvas.width;
        var h = canvas.height;
        var delta = 4; // Spacing between lines
        var phase = 1;
        var x1 = 0;
        var y1 = 0;
        var x2 = w;
        var y2 = 0;

        while (true) {
            ctx.moveTo(x1, y1);
            ctx.lineTo(x2, y2);
            ctx.stroke();

            switch (phase) {
            case 1:
                x1 += delta; y2 += delta;
                if (x1 >= w || y2 >= h) {
                    x1 = w; y2 = h;
                    phase = 2;
                }
                break;
            case 2:
                y1 += delta; x2 -= delta;
                if (y1 >= w || x2 <= 0) {
                    y1 = h; x2 = 0;
                    phase = 3;
                }
                break;
            case 3:
                x1 -= delta; y2 -= delta;
                if (x1 <= 0 || y2 <= 0) {
                    x1 = 0; x2 = 0;
                    phase = 4;
                }
                break;
            case 4:
                y1 -= delta; x2 += delta;
                if (y1 <= 0 || x2 >= w) {
                    y1 = 0; x2 = w;
                    phase = 5;
                }
                break;
            case 5:
                return;
            }
        }
    }
}

A screen shot of the program's output is shown below. You may find the program useful to modify and experiment with. It also illustrates the performance capabilities of Canvas and JavaScript. As written, it draws about 400 lines. You can modify it to draw more.

Summary

With Qt Quick 2 and the Canvas element, you now have the ability to draw arbitrary graphics from within QML. The canvas APIs are fast and powerful - people have implemented complete graphics editing programs (4) with the HTML5 Canvas. You still have the option of using Qt's C++ APIs for painting, if desired, for performance, feature requirements, or just personal preference.

References

  1. Qt API Documentation for Canvas element, Qt Project web site, accessed April 21 2014, qt-project.org/doc/qt-5/qml-qtquick-canvas.html
  2. Qt API Documentation for Context2D element, Qt Project web site, accessed April 21 2014, qt-project.org/doc/qt-5/qml-qtquick-context2d.html
  3. W3C Description of the Canvas Element, World Wide Web Consortium (W3C) web site, accessed April 21 2014, www.w3.org/TR/2009/WD-html5-20090825/the-canvas-element.html
  4. iPaint: MS Paint-like On-line Painting Program Implemented Using HTML5, jswidget.com web site, accessed April 21 2014, www.jswidget.com/index-ipaint.html