Participer au site avec un Tip
Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

Dessiner dans un canvas

Accès rapide :
Rappel sur les liens entre QtQuick/QML et Chromium
Obtenir un contexte de tracé
Tracé un polygone fermé
Quelques autres primitives de tracé

Rappel sur les liens entre QtQuick/QML et Chromium

Pour rappel, le moteur QtQuick embarque le moteur Web Chromium (le projet au coeur du navigateur Chrome de la société Google). Chromium est un projet « Open Source » qui est développé par « The Chromium Project ». Vous pouvez consulter le site http://www.chromium.org/Home pour de plus amples informations.

Ce que cela implique pour nous développeurs QtQuick/QML, c'est que bon nombre d'APIs Web sont aussi accessibles en QML. Et notament les APIs « Canvas », les « WebWorkers » (appelés « WorkerScripts » en QtQuick/QML), XmlHttpRequest, ...

Donc si vous maitrisez déjà un peu le développement Web et l'utilisation des Canvas dans ce contexte, alors je n'ai presque plus rien à vous expliquer et vous saurez déjà produire plein de type de tracés en Qt. Si ce n'ai pas le cas, rien de grave : nous allons maintenant un peu présenter cette API.

Obtenir un contexte de tracé

La première des choses à faire pour dessiner dans un « canvas » est de produire le composant graphique dans votre fichier QML. La classe Canvas est présente dans le paquetage QtQuick : aucun autre import n'est requis pour son utilisation. Voici comment instancier un composant Canvas.

import QtQuick

Window {
    id: "window"
    width: 600
    height: 300
    visible: true
    title: qsTr("QtQuick Canvas demonstration")

    Canvas {
        id: canvas
        anchors.fill: parent
    }
}

Bien entendu, si vous lancez l'application telle quelle, aucun tracé ne sera produit. Pour passer à l'étape suivante, il vous faudra acquérir un contexte de tracé : c'est l'objet le plus important de l'API Canvas. Ce contexte de tracé devra être utilisé à chaque fois que le canvas devra se redessiner : pour ce faire nous allons abonner un signal handler sur l'événement onPaint du canvas. Voici un exemple de code.

import QtQuick

Window {
    id: "window"
    width: 600
    height: 300
    visible: true
    title: qsTr("QtQuick Canvas demonstration")

    Canvas {
        id: canvas
        anchors.fill: parent

        onPaint: function() {
            // On acquière le contexte de tracé.
            let context = canvas.getContext("2d");
        }
    }
}

Votre contexte de tracé peut être configuré sur de nombreuses caractéristiques : couleur du stylo, type de tracé (plein, hachuré...), la couleur de remplissage (en cas de formes complexes), la couleur de détourage... Voici un exemple de configuration du contexte de tracé.

import QtQuick

Window {
    id: "window"
    width: 600
    height: 300
    visible: true
    title: qsTr("QtQuick Canvas demonstration")

    Canvas {
        id: canvas
        anchors.fill: parent

        onPaint: function() {
            // On acquière le contexte de tracé.
            let context = canvas.getContext("2d");

            // On fixe l'épaisseur de tracé du contexte de tracé.
            context.lineWidth = 4
            // On fixe la couleur de détourage en noir.
            context.strokeStyle = "black"
            // On fixe la couleur de remplissage en rouge.
            context.fillStyle = "red"

            // ... Suite du code ...
        }
    }
}

Tracé un polygone fermé

Commençons par faire un premier tracé simple : nous allons dessiner un polygone fermé. Bien que plusieurs types de code permettraient d'arriver au même résultat, nous allons utiliser la notion de path (de chemin). Un path consiste en un ensemble de points définissant le polygone (dans l'exemple, un simple carré). Le path pourra être fermé ou non, mais dans notre cas nous souhaitons parler d'un polygone ce qui implique que le chemin sera fermé. Pour initier un chemin, il faut invoquer la méthode beginPath sur le contexte. Pour fermer le chemin (revenir au point de départ), il faut invoquer la méthode endPath. Voici un exemple d'utilisation.

import QtQuick

Window {
    id: "window"
    width: 600
    height: 300
    visible: true
    title: qsTr("QtQuick Canvas demonstration")

    Canvas {
        id: canvas
        anchors.fill: parent

        onPaint: function() {
            // On acquière le contexte de tracé.
            let context = canvas.getContext("2d");

            // On configure le contexte de tracé.
            context.lineWidth = 4
            context.strokeStyle = "black"
            context.fillStyle = "red"

            // On définit un chemin correspondant à un carré.
            context.beginPath()
            context.moveTo(50, 50)
            context.lineTo(150, 50)
            context.lineTo(150, 150)
            context.lineTo(50, 150)
            context.closePath()

            // On détoure (stroke) et on remplit (fill) le chemin.
            context.fill()
            context.stroke()
        }
    }
}

Voici le résultat produit par cet exemple de code.

On trace un chemin (path) correspondant à un carré.

Vous pouvez aussi configurer votre contexte de tracé pour utiliser des dégradés de couleur. L'exemple suivant change la couleur de remplissage du carré par un dégradé.

import QtQuick

Window {
    id: "window"
    width: 600
    height: 300
    visible: true
    title: qsTr("QtQuick Canvas demonstration")

    Canvas {
        id: canvas
        anchors.fill: parent

        onPaint: function() {
            // On acquière le contexte de tracé.
            let context = canvas.getContext("2d");

            // On configure le contexte de tracé.
            context.lineWidth = 4
            context.strokeStyle = "black"
            let fillGradient = context.createLinearGradient(100, 50, 100, 150);
            fillGradient.addColorStop(0, "red");
            fillGradient.addColorStop(0.33, "yellow");
            fillGradient.addColorStop(1.0, "green");
            context.fillStyle = fillGradient    // context.fillStyle = "red"

            // On définit un chemin correspondant à un carré.
            context.beginPath()
            context.moveTo(50, 50)
            context.lineTo(150, 50)
            context.lineTo(150, 150)
            context.lineTo(50, 150)
            context.closePath()

            // On détoure (stroke) et on remplit (fill) le chemin.
            context.fill()
            context.stroke()
        }
    }
}
les coordonnées utilisées par la méthode createLinearGradient correspondent au sens du dégradé. Ici du haut (100, 50) vers le bas (100, 150). Mais rien ne vous empêche de réaliser un dégradé de couleurs en diagonal.

Et voici le résultat produit par cet exemple de code.

Exemple d'utilisation d'un dégradé.

Quelques autres primitives de tracé

Il existe de très nombreuses primitives de tracé proposées par un canvas. En voici quelques-unes.

Voici un exemple un peu plus riche que l'exemple précédent. On y trace le smiley suivant : oui, je l'ai fait un peu loucher ;-)

On trace un smiley qui louche

Et voici le code QML correspondant.

import QtQuick

Window {
    id: "window"
    width: 200
    height: 200
    visible: true
    title: qsTr("QtQuick Canvas demonstration")

    Canvas {
        id: canvas
        anchors.fill: parent
        antialiasing: true

        onPaint: function() {
            let ctx = canvas.getContext("2d");

            // On trace la face rose de notre smiley.
            context.strokeStyle = "#000000";
            context.fillStyle = "#FF00FF";
            context.beginPath();
            context.arc(100, 100, 50, 0, Math.PI*2, true);
            context.closePath();
            context.stroke();
            context.fill();

            // On dessine l'oeil gauche positionné en 80, 80.
            context.strokeStyle = "#000000";
            context.fillStyle = "#FFFFFF";
            context.beginPath();
            context.arc(80, 80, 8, 0, Math.PI*2, true);
            context.closePath();
            context.stroke();
            context.fill();

            context.fillStyle = "#009966";
            context.beginPath();
            context.arc(83, 80, 5, 0, Math.PI*2, true);
            context.closePath();
            context.fill();

            // On dessine l'oeil droit positionné en 120, 80.
            context.strokeStyle = "#000000";
            context.fillStyle = "#FFFFFF";
            context.beginPath();
            context.arc(120, 80, 8, 0, Math.PI*2, true);
            context.closePath();
            context.stroke();
            context.fill();

            context.fillStyle = "#009966";
            context.beginPath();
            context.arc(117, 80, 5, 0, Math.PI*2, true);
            context.closePath();
            context.fill();

            // On trace le nez, via un path.
            context.fillStyle = "#000000";
            context.beginPath();
            context.moveTo(93, 100);
            context.lineTo(100, 93);
            context.lineTo(107, 100);
            context.lineTo(100, 107);
            context.closePath();
            context.fill();

            // On trace la bouche via deux courbes quadratiques.
            context.strokeStyle = "#000000";
            context.beginPath();
            context.moveTo(70, 110);
            context.quadraticCurveTo(100, 150, 130, 110);
            context.quadraticCurveTo(100, 150, 70, 110);
            context.closePath();
            context.stroke();
        }
    }
}