Voir le flux RSS

Jiyuu

[Actualité] Création dynamique d'une courbe en QML

Noter ce billet
par , 08/10/2015 à 09h37 (973 Affichages)
Bonjour,

Dans un précédent billet, nous avions vu comment créer un courbe en QML depuis une liste de points connus.

Je vous propose maintenant de voir comment créer une courbe dynamiquement, c'est à dire en exploitant, par exemple, une liste de coordonnées depuis une base de données ou d'un calcul.

En nous inspirant du premier billet, nous allons créer la fenêtre, le canvas et les différents éléments qui nous permettrons de créer notre courbe ou d'interagir avec celle-ci.
Voici ce que cela donnerait :


Code qml : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2
 
 
Window {
    visible: true
    width: 800; height: 500
    x: 100 ; y: 100
    title: "Create curve with QML"
 
 
    Rectangle{
        id: root
        anchors.fill: parent
 
        ColumnLayout {
            id: grid
            anchors.fill: parent
 
 
            RowLayout {
                id: control
                Layout.fillWidth: true
                implicitHeight: 25
                Button {
                    text: "Trace"
                }
 
                Button {
                    text: "Efface"
                }
 
                Slider {
                    id: slider
                    value: 0
                    maximumValue: 20
                }
 
                Item {
                    Layout.fillWidth: true
                }
 
                Label {
                    id: posiX
                }
 
                Label {
                    id: posiY
                }
 
                Label {
                    id: posiX2
                }
            }
 
            Rectangle {
                id: area
                Layout.fillHeight: true
                Layout.fillWidth: true
                color: "lightgray"
                Canvas {
                    id: canvas
                    anchors.fill: parent
                    transform: Rotation { origin.x: area.x; origin.y: area.height/2; angle: 180; axis { x: 1; y: 0; z: 0 }}
                    property int origX: 0
                    property int origY: 0
                    property int maxX: width - origX
                    property int maxY: height - origY
 
                    property alias pointCurve: pointCurve
 
                    /* c'est ici que ça commence à devenir intéressant. Comme précédemment nous allons créer au démarrage de l'application nos axes, mais nous n'irons pas plus loin.*/
 
                    onPaint: {
                        getContext("2d")
 
/* getContext("2d") est nécessaire pour indiquer dans quel type de context nous nous trouvons. 
Ici on déclare donc un Context2D (http://doc.qt.io/qt-5/qml-qtquick-context2d-members.html) qui a notamment deux méthodes qui nous intéresseront tout  particulièrement :
   - beginPath()
   - closePath()
 
Entre ces deux méthodes vous pouvez créer comme bon vous semble différents chemins et y appliquer un style unique (couleur, épaisseur du trait, ...) .
Pour appliquer d'autres styles il faudra redéclarer un nouveau Path (chemin en anglais).*/
 
                        /* Trace Axes */
                        context.beginPath()
                        context.lineWidth = 2
                        context.moveTo(origX, origY)  // permet de se déplacer sans tracer de points
                        context.lineTo(maxX, origY)    // permet de tracer une ligne
                        context.moveTo(origX, origY)
                        context.lineTo(origX, maxY)
                        context.strokeStyle = Qt.rgba(0,0,0);
                        context.stroke()
                        context.closePath()
                    }
 
                    ListModel{id: pointCurve}
 
 
                }
            }
        }
    }
}

À ce stade vous devriez avoir quelque chose qui ressemble à ceci :
Nom : 1.png
Affichages : 769
Taille : 9,2 Ko

Nous allons maintenant créer notre courbe.

Nous allons tout d'abord compléter nos composants Button et Slider afin de permettre l'interaction avec notre Canvas. Commençons par remplir notre ListModel des points à tracer :
Code qml : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
Button {
    text: "Trace"
    onClicked: {
        for (var i=0; i<=20; i++){
            pointCurve.append({"x":i*20, "y":Math.pow(i,2)})
        }
         canvas.traceCurve() // nous verrons cette fonction un peu plus bas.
    }
}

Puis la gestion de l'effacement de notre courbier :
Code qml : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
Button {
    text: "Efface"
    onClicked: {
        slider.value = 0
        canvas.clear() // Comme pour canvas.traceCurve() nous verrons bientôt cette fonction.
    }
}

Et enfin notre Slider qui nous permettra d'afficher un curseur :
Code qml : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
Slider {
    id: slider
    value: 0
    maximumValue: 20
    onValueChanged: {
        if (value !== 0)canvas.point(root.valueX*20, Math.pow(root.valueX,2))
        canvas.traceCurve()
    }
}

Et voici enfin le moment tant attendu : le traçage de la courbe.

Pour cela nous allons implémenter la fonction suivante :
Code qml : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function traceCurve() {
    if (context){
        context.beginPath()                           // comme précédemment nous créons un nouveau Path
        context.lineWidth = 1
        for (var i = 0; i < pointCurve.count; i++) {  // Pour chaque élément de notre ListModel, nous créons une nouvelle ligne
           var posiX = origX + pointCurve.get(i).x
           var posiY = origY + pointCurve.get(i).y
           context.lineTo(posiX, posiY)
       }
       context.moveTo(origX, origY)
       context.strokeStyle = Qt.rgba(0,0,1);
       context.stroke()                               // Nous appliquons le style voulu
       context.closePath()                            // Nous fermons notre chemin
    }
 
requestPaint()                                        // et nous lançons la requête de rendu


Le principe pour tracer le curseur (fonction point () utilisé dans le Slider) est sensiblement le même.
Pour effacer le contenu de notre Canvas il suffit d'effacer le contenu de notre ListModel, faire un reset du context et redonner l'ordre de tracer les nouveaux éléments : c'est à dire rien du tout.
Code qml : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
function clear(){
    if (context){
        pointCurve.clear()
        context.reset()
        requestPaint()
    }
}


Voici ce que donne le code complet :

Code qml : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2
 
 
Window {
    visible: true
    width: 800; height: 500
    x: 100 ; y: 100
    title: "Create curve with QML"
 
 
    Rectangle{
        id: root
        anchors.fill: parent
 
        property alias valueX: slider.value
 
        ColumnLayout {
            id: grid
            anchors.fill: parent
 
 
            RowLayout {
                id: control
                Layout.fillWidth: true
                implicitHeight: 25
                Button {
                    text: "Trace"
                    onClicked: {
                        for (var i=0; i<=20; i++){
                            pointCurve.append({"x":i*20, "y":Math.pow(i,2)})
                        }
                        canvas.traceCurve()
                    }
                }
                Button {
                    text: "Efface"
                    onClicked: {
                        slider.value = 0
                        canvas.clear()
                    }
                }
                Slider {
                    id: slider
                    value: 0
                    maximumValue: 20
                    onValueChanged: {
                        if (value !== 0)canvas.point(root.valueX*20, Math.pow(root.valueX,2))
                        canvas.traceCurve()
                    }
                }
                Item {
                    Layout.fillWidth: true
                }
 
                Label {
                    id: posiX
                    text: "x : " + (root.valueX*20).toFixed(2)
                }
                Label {
                    id: posiY
                    text: "y : " + Math.pow(root.valueX,2).toFixed(2)
                }
                Label {
                    id: posiX2
                }
            }
 
            Rectangle {
                id: area
                Layout.fillHeight: true
                Layout.fillWidth: true
                color: "lightgray"
                Canvas {
                    id: canvas
                    anchors.fill: parent
                    transform: Rotation { origin.x: area.x; origin.y: area.height/2; angle: 180; axis { x: 1; y: 0; z: 0 }}
                    property int origX: 0
                    property int origY: 0
                    property int maxX: width - origX
                    property int maxY: height - origY
 
                    property alias pointCurve: pointCurve
 
 
 
                    function clear(){
                        if (context){
                            pointCurve.clear()
                            context.reset()
                            requestPaint()
                        }
                    }
 
                    function point(q, h){
                        context.reset()
                        context.beginPath()
                        context.lineWidth = 1
                        context.moveTo(origX, h+origY)
                        context.lineTo(maxX, h+origY)
                        context.moveTo(q+origX, origY)
                        context.lineTo(q+origX, maxY)
                        context.strokeStyle = Qt.rgba(1,0,0);
                        context.stroke()
                        context.closePath()
                        requestPaint()
                    }
 
                    function traceCurve() {
                        if (context){
                            context.beginPath()
                            context.lineWidth = 1
                            for (var i = 0; i < pointCurve.count; i++) {
                                var posiX = origX + pointCurve.get(i).x
                                var posiY = origY + pointCurve.get(i).y
                                context.lineTo(posiX, posiY)
                            }
                            context.moveTo(origX, origY)
                            context.strokeStyle = Qt.rgba(0,0,1);
                            context.stroke()
                            context.closePath()
                        }
 
                        requestPaint()
                    }
 
                    onPaint: {
                        getContext("2d")
 
                        /* Trace Axes */
                        context.beginPath()
                        context.lineWidth = 2
                        context.moveTo(origX, origY)
                        context.lineTo(maxX, origY)
                        context.moveTo(origX, origY)
                        context.lineTo(origX, maxY)
                        context.strokeStyle = Qt.rgba(0,0,0);
                        context.stroke()
                        context.closePath()
                    }
 
                    ListModel{id: pointCurve}
                }
            }
        }
    }
}
Vous avez maintenant en main de quoi réaliser de belles courbes dynamiquement, alimentées par vos calculs les plus fous ou votre super base de données

Envoyer le billet « Création dynamique d'une courbe en QML » dans le blog Viadeo Envoyer le billet « Création dynamique d'une courbe en QML » dans le blog Twitter Envoyer le billet « Création dynamique d'une courbe en QML » dans le blog Google Envoyer le billet « Création dynamique d'une courbe en QML » dans le blog Facebook Envoyer le billet « Création dynamique d'une courbe en QML » dans le blog Digg Envoyer le billet « Création dynamique d'une courbe en QML » dans le blog Delicious Envoyer le billet « Création dynamique d'une courbe en QML » dans le blog MySpace Envoyer le billet « Création dynamique d'une courbe en QML » dans le blog Yahoo

Tags: curve, dynamique, qml
Catégories
PyQt , Qt Quick - QML

Commentaires