Précédent   Forum du club des développeurs et IT Pro > C et C++ > Bibliothèques > Qt > Qt Quick
Qt Quick Forum d'entraide sur Qt Quick, QML et Qt Declarative, les interfaces graphiques déclaratives pour Qt
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse
 
Outils de la discussion
Publicité
'
Vieux 05/12/2012, 13h13   #1
Zaxy_
Invité de passage
 
Homme
Inscription : décembre 2012
Messages : 13
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations forums :
Inscription : décembre 2012
Messages : 13
Points : 0
Points : 0
Par défaut Faisabilité : afficher un graphe depuis C++ avec QML

Bonjour

Je viens de commencer de travailler avec QML et je voudrais savoir si le cas suivant est faisable avec QML:

Je veux faire afficher un graph depuis C++ avec QML dans la facon suivante:
- chaque noeud est répresant par une image graphique - par exemple une icone grahique .png
- on peut clicker (right mouse click) sur un noeud et pouvoir faire des actions simples sur le noeud (changer son nom, afficher/cacher son apparance)
- les noeuds sont connectés avec des lignes graphique et le graph est affiché dans une facon simple à visualiser.


J'ai vu qu'il existe GridView dans QML, mais je ne sais pas si je peux peindre des lignes graphique entre les elements. Et aussi, si je peux re-arranger les noeuds dans une facon simple à visualiser.

Est-ce que vous pouvez me donner votre avis SVP si cela est faisable avec QML et si oui avec lequels elements QML on pourrait le realiser ?

Merci beaucoup d'avance !
Zaxy_ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 05/12/2012, 20h00   #2
Amnell
Rédacteur
 
Avatar de Amnell
 
Homme Louis du Verdier
Étudiant
Inscription : mars 2009
Messages : 1 600
Détails du profil
Informations personnelles :
Nom : Homme Louis du Verdier
Localisation : France, Hauts de Seine (Île de France)

Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : mars 2009
Messages : 1 600
Points : 5 048
Points : 5 048
Bonsoir,

C'est possible avec le module Qt Declarative de Qt (qui englobe QML) en réalisant du côté du C++ l'élément QML puis en l'exploitant du côté du QML. Pour les nœuds, il s'agirait d'un ListModel avec dedans les informations, affiché par un Repeater par-dessus l'élément QML du graphe. Je peux vous fournir un exemple si cette méthode vous intéresse.

Pour la classe C++ du graphe elle-même, je vous propose d'étudier un code que j'avais donné précédemment :

http://www.developpez.net/forums/d12...s/#post6744642

Bonne continuation,
Amnell.
Amnell est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 06/12/2012, 10h05   #3
Zaxy_
Invité de passage
 
Homme
Inscription : décembre 2012
Messages : 13
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations forums :
Inscription : décembre 2012
Messages : 13
Points : 0
Points : 0
Bonjour,

Oui, je suis intéressé pour cette solution. J'aimerais avoir un exemple Stp.

P.S. et surtout comment créer dynamiquement les objets QML Node a partir de C++ et les connecter avec des lignes ?

Merci
Zaxy_ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/12/2012, 00h26   #4
Amnell
Rédacteur
 
Avatar de Amnell
 
Homme Louis du Verdier
Étudiant
Inscription : mars 2009
Messages : 1 600
Détails du profil
Informations personnelles :
Nom : Homme Louis du Verdier
Localisation : France, Hauts de Seine (Île de France)

Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : mars 2009
Messages : 1 600
Points : 5 048
Points : 5 048
Bonsoir,

Rapidement :

Code :
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
    Component.onCompleted: {
        console.log("");
 
        nodeModel.append({"posX": 10, "posY": 5, "name": "Node 1", "opened": true});
        nodeModel.append({"posX": 2, "posY": 12, "name": "Node 2", "opened": false});
    }
 
    Graph {
        id: graph
        anchors.fill: parent
        // ...
 
        property int scaleX: 50
        property int scaleY: 50
 
        MouseArea {
            anchors.fill: parent
            onClicked: {
                nodeModel.append({"posX": mouseX / graph.scaleX, "posY": mouseY / graph.scaleY,
                                     "name": "Node " + (nodeModel.count + 1), "opened": true});
            }
        }
 
        Repeater {
            model: nodeModel
            delegate: Rectangle {
                x: graph.scaleX * posX
                y: graph.scaleY * posY
                width: contentText.width + 10
                height: opened ? 50 : 25
                border.width: 1
                border.color: "black"
 
                Text {
                    id: contentText
                    visible: opened
                    text: name
                    anchors.centerIn: parent
                }
 
                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        nodeModel.get(index).opened = !opened;
                    }
                }
            }
        }
    }
 
    ListModel {
        id: nodeModel
    }
Les propriétés scaleX et scaleY devront correspondre à l'échelle du graphe (en gros, 1 en abscisses équivaut à 50px, dans le cas présent). Cette propriété sera à définir dans la classe C++ du Graphe. Là, j'ai fait en sorte que lors qu'on clique sur une zone du graphe, ça ajoute un Node à la position du clic. De plus, le fait de cliquer sur un Node permet de l'ouvrir/le fermer, selon son état courant. Bref, voilà pour l'exemple.

Par rapport à votre question : comment créer des éléments directement depuis le C++, l'idéal serait de passer par une contextProperty() :

Code :
1
2
3
4
5
6
7
8
9
class MyNotifier
{
    Q_OBJECT
public:
    // ...
 
signals:
    void createNodeRequest(int x, int y, const QString &name);
};
La définition de la propriété :

Code :
1
2
3
MyNotifier notifier;
// ...
viewer.rootContext()->setContextProperty("notifier", &notifier);
Et dans le QML :

Code :
1
2
3
4
5
6
    Connections {
        target: notifier
        onCreateNodeRequest: {
            nodeModel.append({"posX": x, "posY": y, "name": name, "opened": true});
        }
    }
Avec cette méthode, pour créer un Node depuis le C++, il suffit donc d'émettre le signal createNodeRequest() de l'instance "notifier".

Il y a bien sûr d'autres solutions mais j'ai préféré vous présenter celle-ci.

Bonne continuation,
Amnell.
Amnell est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/12/2012, 09h55   #5
Zaxy_
Invité de passage
 
Homme
Inscription : décembre 2012
Messages : 13
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations forums :
Inscription : décembre 2012
Messages : 13
Points : 0
Points : 0
Par défaut fusion

Merci beaucoup.

Comment tu peindre des lignes qui connectent les noeud ?
Zaxy_ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/12/2012, 12h23   #6
Amnell
Rédacteur
 
Avatar de Amnell
 
Homme Louis du Verdier
Étudiant
Inscription : mars 2009
Messages : 1 600
Détails du profil
Informations personnelles :
Nom : Homme Louis du Verdier
Localisation : France, Hauts de Seine (Île de France)

Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : mars 2009
Messages : 1 600
Points : 5 048
Points : 5 048
Bonjour,

L'objectif est d'avoir ceci ?

Code :
1
2
3
4
5
6
7
8
9
 
     ______
    | Node |
    |______|    /
       |   ____/
       |__/
      /
     /
/\__/
Dans ce cas, on connaît déjà le point de la courbe :

Code :
1
2
x: graph.scaleX * posX
y: graph.scaleY * posY
D'où un petit exemple rapide qui reprend mon précédent code

Code :
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
Rectangle {
    id: graph
    width: 1024
    height: 800
 
    property int scaleX: 50
    property int scaleY: 50
 
    MouseArea {
        anchors.fill: parent
        onClicked: {
            nodeModel.append({"posX": mouseX / graph.scaleX, "posY": mouseY / graph.scaleY,
                "name": "Node " + (nodeModel.count + 1), "opened": true});
        }
    }
 
    Repeater {
        model: nodeModel
        delegate: Item {
            x: graph.scaleX * posX
            y: graph.scaleY * posY
            width: nodeArea.width
            height: nodeArea.height + 75
 
            Rectangle {
                width: 1
                height: 75
                y: -75
                color: "black"
            }
 
            Rectangle {
                id: nodeArea
                x: -Math.round(width / 2)
                y: -75
                width: contentText.width + 10
                height: opened ? 50 : 25
                border.width: 1
                border.color: "black"
 
                Text {
                    id: contentText
                    visible: opened
                    text: name
                    anchors.centerIn: parent
                }
 
                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        nodeModel.get(index).opened = !opened;
                    }
                }
            }
        }
    }
 
    ListModel {
        id: nodeModel
    }
}
Bonne continuation,
Amnell.
Amnell est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 07/12/2012, 16h51   #7
Zaxy_
Invité de passage
 
Homme
Inscription : décembre 2012
Messages : 13
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations forums :
Inscription : décembre 2012
Messages : 13
Points : 0
Points : 0
Salut,

Ton exemple est très bien, mais je ne comprends pas comment tu peux peindre une ligne qui connecte deux noeuds (dans ton cas le noeud est un rectangle avec un text) ?

Merci
Zaxy_ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/12/2012, 19h10   #8
Amnell
Rédacteur
 
Avatar de Amnell
 
Homme Louis du Verdier
Étudiant
Inscription : mars 2009
Messages : 1 600
Détails du profil
Informations personnelles :
Nom : Homme Louis du Verdier
Localisation : France, Hauts de Seine (Île de France)

Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : mars 2009
Messages : 1 600
Points : 5 048
Points : 5 048
Bonsoir,

Dans le cas présent, le nœud va être le rectangle avec une bordure qui contient un Text dedans. Pour la ligne, c'est le rectangle d'épaisseur 1px de couleur noire. Après, si on souhaite dessiner des lignes qui ne sont pas droites ou horizontales/verticales (ce qui pourrait être reproduit avec un Rectangle), soit on crée une classe côté C++ dont la fonction paint dessine une ligne bêtement avec un QPainter, soit on utilise http://qt.gitorious.org/qt-labs/qmlcanvas.

Bonne continuation,
Amnell.
Amnell est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 10/12/2012, 16h29   #9
Zaxy_
Invité de passage
 
Homme
Inscription : décembre 2012
Messages : 13
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations forums :
Inscription : décembre 2012
Messages : 13
Points : 0
Points : 0
Bonjour,

J'ai arrivé de créer des eléments Nodes dans un Repeater et les place comme je veux grace aux parametres posX et posY.

Maintenat je veux peindre une ligne entre certains elements.

Du coup, j'ai implementer la methode paint() de la classe C++ Node:

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QPen pen;
    pen.setColor(Qt::blue);
    pen.setStyle(Qt::SolidLine);
    pen.setWidth(2);
    painter->setPen(pen);
    painter->setRenderHints(QPainter::Antialiasing, true);
 
    for (QList<Node*>::const_iterator itParent = _parents.constBegin();
         itParent != _parents.constEnd();
         itParent++)
    {
        QPoint startPoint(this->posX(), this->posY());
        QPoint endPoint((*itParent)->posX(), (*itParent)->posY());
 
        painter->drawLine(startPoint, endPoint);
    }
}
Le probleme est que après dans mon main.cpp quand je crée 3 Nodes pour tester et j'ai fait la connection entre deux (Node3.parent = Node2), je ne vois pas la ligne dessigné avec paint().

Est-ce que tu sais comment ca marche avec la methode paint(). Quand elle est appellée et comment on peut l'appeler exprès ? Ca marche avec un signal specifique ?

Merci d'avance
Zaxy_ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 10/12/2012, 20h29   #10
Amnell
Rédacteur
 
Avatar de Amnell
 
Homme Louis du Verdier
Étudiant
Inscription : mars 2009
Messages : 1 600
Détails du profil
Informations personnelles :
Nom : Homme Louis du Verdier
Localisation : France, Hauts de Seine (Île de France)

Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : mars 2009
Messages : 1 600
Points : 5 048
Points : 5 048
Bonsoir,

Vous avez dû oublier ceci :

Code :
setFlag(ItemHasNoContents, false);
Attention cependant, la classe C++ "Node" est une toute autre chose que l'élément QML "Node".

L'intérêt de QML reste d'avoir la logique du côté du code C++ et la partie graphique du côté du code QML. De ce fait, je vous proposerais plutôt de créer un élément "Link" qui permet de relier deux nœuds entre eux... et du côté du QML. La partie C++ permettra uniquement de coder le paint et d'ajouter quelques propriétés :

Code :
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
#ifndef LINK_H
#define LINK_H
 
#include <QtGui>
#include <QtDeclarative>
 
class Link : public QDeclarativeItem
{
    Q_OBJECT
    Q_PROPERTY(int fromX READ fromX WRITE setFromX NOTIFY fromXChanged)
    Q_PROPERTY(int fromY READ fromY WRITE setFromY NOTIFY fromYChanged)
    Q_PROPERTY(int toX READ toX WRITE setToX NOTIFY toXChanged)
    Q_PROPERTY(int toY READ toY WRITE setToY NOTIFY toYChanged)
 
public:
    explicit Link(QDeclarativeItem* const parent = NULL)
        : QDeclarativeItem(parent), _fromX(0), _fromY(0), _toX(0), _toY(0)
    {
        // Notez la présence de cette ligne pour que paint() soit appelé :
        setFlag(ItemHasNoContents, false);
    }
 
private slots:
    int fromX() const { return _fromX; }
    int fromY() const { return _fromY; }
    int toX() const { return _toX; }
    int toY() const { return _toY; }
    void setFromX(const int v) { _fromX = v; emit fromXChanged(); }
    void setFromY(const int v) { _fromY = v; emit fromYChanged(); }
    void setToX(const int v) { _toX = v; emit toXChanged(); }
    void setToY(const int v) { _toY = v; emit toYChanged(); }
 
protected:
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
        Q_UNUSED(option);
        Q_UNUSED(widget);
 
        QPen pen;
        pen.setColor(Qt::blue);
        pen.setStyle(Qt::SolidLine);
        pen.setWidth(2);
        painter->setPen(pen);
        painter->setRenderHints(QPainter::Antialiasing, true);
        painter->drawLine(QPoint(_fromX, _fromY), QPoint(_toX, _toY));
    }
 
signals:
    void fromXChanged();
    void fromYChanged();
    void toXChanged();
    void toYChanged();
 
private:
    int _fromX;
    int _fromY;
    int _toX;
    int _toY;
};
 
#endif // LINK_H
À ajouter dans le main.cpp :

Code :
qmlRegisterType<Link>("MyLib", 1, 0, "Link");
Et du coup, l'exemple QML donné plutôt avec en plus des ajouts commentés :

Code :
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
import QtQuick 1.1
import MyLib 1.0
 
Rectangle {
    id: graph
    width: 1024
    height: 800
 
    property int scaleX: 50
    property int scaleY: 50
 
    // Ajout d'une propriété selectedItem pour relier l'élément
    // à un autre élément avec un Link : on a besoin de savoir quel
    // est l'index dans le modèle de l'item sélectionné.
    property int selectedItem: -1;
 
    MouseArea {
        anchors.fill: parent
        onClicked: {
            if (graph.selectedItem != -1)
            {
                // Il y a un item sélectionné et on a cliqué ailleurs.
                // On retire la sélection :
                graph.selectedItem = -1;
            }
            else
            {
                // Vu qu'il n'y a pas d'item sélectionné,
                // on peut ajouter un Node au graph :
                nodeModel.append({"posX": mouseX / graph.scaleX,
                    "posY": mouseY / graph.scaleY,
                    "name": "Node " + (nodeModel.count + 1)});
            }
        }
    }
 
    // Le Repeater qui va afficher tous les liens entre les Nodes :
    Repeater {
        model: linkModel
        // Le delegate sera notre item "Link", défini du coté du C++
        delegate: Link {
            fromX: nodeModel.get(nodeFrom).posX * graph.scaleX
            fromY: nodeModel.get(nodeFrom).posY * graph.scaleY
            toX: nodeModel.get(nodeTo).posX * graph.scaleX
            toY: nodeModel.get(nodeTo).posY * graph.scaleY
            anchors.fill: parent
        }
    }
 
    Repeater {
        model: nodeModel
        delegate: Item {
            x: graph.scaleX * posX
            y: graph.scaleY * posY
            width: nodeArea.width
            height: nodeArea.height + 75
 
            Rectangle {
                width: 1
                height: 75
                y: -75
                color: "black"
            }
 
            Rectangle {
                id: nodeArea
                x: -Math.round(width / 2)
                y: -75
                width: contentText.width + 10
                height: 50
 
                // La couleur change selon le fait que le noeud soit sélectionné ou non :
                color: index == graph.selectedItem ? "lightgrey" : "white"
 
                border.width: 1
                border.color: "black"
 
                Text {
                    id: contentText
                    anchors.centerIn: parent
                    text: name
                }
 
                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        if (graph.selectedItem == -1)
                        {
                            // Lors d'un clic sur le Node, on change l'élément
                            // actuellement sélectionné s'il n'y a pas de sélection :
                            graph.selectedItem = index;
                        }
                        else
                        {
                            // Sinon, on ajoute un lien entre ce Node et le Node sélectionné :
                            linkModel.append({"nodeFrom": graph.selectedItem, "nodeTo": index});
                            // On efface alors la sélection :
                            graph.selectedItem = -1;
                        }
                    }
                }
            }
        }
    }
 
    ListModel {
        id: nodeModel
    }
 
    // Ajout d'un modèle pour les
    // liens entre les Nodes :
    ListModel {
        id: linkModel
    }
}
Je n'ai pas trop le temps d'expliquer plus en détail tout ça ce soir, donc si vous avez des questions, n'hésitez pas à les poser, j'y répondrai demain.

Bonne soirée,
Amnell.
Amnell est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 12/12/2012, 10h20   #11
Zaxy_
Invité de passage
 
Homme
Inscription : décembre 2012
Messages : 13
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations forums :
Inscription : décembre 2012
Messages : 13
Points : 0
Points : 0
Bonjour,

Pour l'instant je vais laisser la creation des liens (lignes) dans la methode paint() de Node.cpp

J'ai bien mis setFlag(xxxxxx, false) dans le constructeur de Node.

Mais je ne vois pas des lignes dessinées.

J'ai tracé le code, je passe bien par la methode paint(), mais par contre je passe plusieurs fois, pas seulement 3 fois (j'ai trois objets Node).
Et chaque fois le pointer this pointe vers un objet vide de type Node et pas vers le vrai object Node, que j'ai créé avant.

Je ne comprends pas pourqoui on appelle paint avec un this qui pointe vers un objet vide.

Tu as une idée ?

Merci d'avance
Zaxy_ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/12/2012, 15h16   #12
Zaxy_
Invité de passage
 
Homme
Inscription : décembre 2012
Messages : 13
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations forums :
Inscription : décembre 2012
Messages : 13
Points : 0
Points : 0
Bonjour,

J'ai trouvé le probleme.

En fait, QML appelle paint() sur une instance de Node.cpp, qui est vide.
Apres QML copie les properties de C++ qui sont declaré comme roles ou Q_Properties.

Donc, j'ai crée une role en plus -> parentNodes /QList<Node*> dans le Node.cpp/ qui contient tous les parents d-un node.

Donc, je veux desinner une ligne pour chaque membre dans parentNodes dans la fonctionne onCompleted, alors il faut faire dans Node.qml

Component.onCompleted: { for each in parentNodes, draw line posX, posY to iteratorEachInParentNodes.posX, posY}

Mais je ne connais pas le syntax.

Tu peux m'aider un peu stp ?

Merci d-avance

[edit]

Peut etre qqchose comme:

Code :
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
 
Node {
    id: nodeDelegate
    x: posX
    y: posY
    visible: isDisplayed
 
    Image{
        id : nodeIcon
        source: iconFilePath
    }
 
    Text {
       anchors.top: nodeIcon.bottom
       text: nodeID
    }
 
    MouseArea {
       anchors.fill: parent
    }
 
    Component.onCompleted: {
        for (var i=0; i < parentNodes.count(); i++)
        {
            Path {
                startX: posX; startY: posY
                PathLine{x: parentNodes[i].posX ; y: parentNodes[i].posY}
            }
        }
    }
}


Mais cela ne se compile pas. Je ne connais pas le syntax exact ...

Tu as une idée ?
Zaxy_ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/12/2012, 22h34   #13
Amnell
Rédacteur
 
Avatar de Amnell
 
Homme Louis du Verdier
Étudiant
Inscription : mars 2009
Messages : 1 600
Détails du profil
Informations personnelles :
Nom : Homme Louis du Verdier
Localisation : France, Hauts de Seine (Île de France)

Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : mars 2009
Messages : 1 600
Points : 5 048
Points : 5 048
Bonsoir,

Le fait que la méthode paint() soit appelée plusieurs fois signifie qu'il y a eu plusieurs demandes de peinture (du genre, un élément situé au-dessus a demandé à être repeint, ce qui a engendré une repeinture de l'élément, ou bien vous avez masqué la fenêtre et l'avez réaffiché, etc.). D'ailleurs, je m'aperçois que j'ai oublié de rajouter les demandes de repeinture dans les fonction set[...]X/Y() de la classe de l'exemple que j'ai donné dans mon précédent post.

La fonction onCompleted est appelée une seule fois, au moment où l'élément a fini d'être chargé. De ce fait, ce n'est pas nécessairement ce handler qui vous intéresse.

Pourrais-je voir le code source de l'élément Node (celui qui a dû recevoir un qmlRegisterType et qui contient la méthode paint() - à moins que ce ne soit Node.qml qui crée l'élément, auquel cas j'aimerais voir la classe avec le paint()) ?

Bonne continuation,
Amnell.
Amnell est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 13/12/2012, 13h47   #14
Zaxy_
Invité de passage
 
Homme
Inscription : décembre 2012
Messages : 13
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations forums :
Inscription : décembre 2012
Messages : 13
Points : 0
Points : 0
Salut,

Voici le code source:

main.qml
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
 
import QtQuick 1.0
 
Rectangle {
    width:  500
    height: 500
 
    Repeater {
        anchors.fill: parent
        model: configModel
        delegate: Node{}
    }
}

Code :
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
 
import QtQuick 1.0
import NodeLib 1.0
 
Node {
    id: nodeDelegate
    x: posX
    y: posY
    visible: isDisplayed
 
    Image{
        id : nodeIcon
        source: iconFilePath
    }
 
    Text {
       anchors.top: nodeIcon.bottom
       text: nodeID
    }
 
    MouseArea {
       anchors.fill: parent
    }
// code pour onCOmpleted in pseudocode. Syntax pas sur !!!
    Component.onCompleted: {
        for (var i=0; i < parentNodes.count; i++)
        {
            Path: [
               // startX: posX,
               // startY: posY,
                PathLine {
                    x: parentNodes.get(i).posX
                    y: parentNodes[i].posY
                }
            ]
        }
    }
}
main.cpp
Code :
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
 
Q_DECL_EXPORT int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> app(createApplication(argc, argv));
 
    QDeclarativeView    declarativeView;
    qmlRegisterType<Node>("NodeLib", 1, 0, "Node");
 
    ConfigurationModel* p_configModel = ConfigurationModel::GetConfigModelInstance();
 
    //p_configModel->addNodeInConfigurationModel(new Node("PanelXXXX", PLUG_IN, EVALUATION_BLOCK, "./ui-images/cutetube.png"));
 
    //Node *p_nodeY = new Node("PanelYYYY", PLUG_IN, EVALUATION_BLOCK, "./ui-images/cutetube.png");
    Node *p_nodeY = new Node();
    p_nodeY->setNodeID("PanelYYYY");
    p_nodeY->setIconFilePath("./ui-images/cutetube.png");
    p_configModel->addNodeInConfigurationModel(p_nodeY);
 
    // Node *p_nodeZ = new Node("PanelZZZZ", PLUG_IN, EVALUATION_BLOCK, "./ui-images/cutetube.png");
    Node *p_nodeZ = new Node();
    p_nodeZ->setNodeID("NODE ZZZZ");
    p_nodeZ->setIconFilePath("./ui-images/cutetube.png");
 
 
    p_nodeY->setPosX(100);
    p_nodeY->setPosY(100);
    p_nodeY->setIsDisplayed(true);
 
    p_nodeZ->setPosX(200);
    p_nodeZ->setPosY(200);
 
    p_nodeZ->connectTo(p_nodeY);
 
    p_configModel->addNodeInConfigurationModel(p_nodeZ);
 
    // if the configuration has been changed, you need to call setContextProperty() again to update the QML view
 
    declarativeView.rootContext()->setContextProperty("configModel", p_configModel);
 
    declarativeView.setSource(QUrl::fromLocalFile("qml/ConfigurationView/main.qml"));
    declarativeView.show();
 
    return app->exec();
}

Node.h
Code :
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
 
#ifndef NODE_H
#define NODE_H
 
#include <QString>
#include <QList>
#include <QDeclarativeItem>
 
#include <QPainter>
#include <QPen>
#include <QPoint>
#include <QStyleOptionGraphicsItem>
 
#include "GlobalDeclarations.h"
 
// Inherit from QDeclarativeItem in order to override paint() method and to display links to parent nodes
class Node : public QDeclarativeItem
{
    Q_OBJECT
    // Q_PROPERTY(QString iconFilePath  READ getIconFilePath WRITE setIconFilePath NOTIFY iconFilePathChanged)
    // Q_PROPERTY(QString nodeID  READ getNodeID WRITE setNodeID NOTIFY nodeIDchanged)
 
 
public:
    // Constructors
    Node();
    Node(QString nodeID, NodeType nodeType, PlugInType plugInType, QString iconFilePath);
 
    // Destructor
    ~Node();
 
    QString nodeID() const;
    Q_INVOKABLE bool setNodeID(const QString &nodeID);
 
    QString  iconFilePath() const;
    Q_INVOKABLE bool setIconFilePath(const QString &iconFilePath);
 
    Q_INVOKABLE int posX() const;
    Q_INVOKABLE bool setPosX(int posX);
 
    Q_INVOKABLE int posY() const;
    Q_INVOKABLE bool setPosY(int posY);
 
    bool isDisplayed() const;
    Q_INVOKABLE bool setIsDisplayed(bool value);
 
    const QList<Node*>& parentNodes() const;
    QList<Node*>& accessParents();
    Q_INVOKABLE bool addParent(Node* p_parentNode);
 
    void hide();
    void show();
 
    void connectTo(Node* p_toNode);
    void disconnectFrom(Node* p_toNode);
    void removeNode();
 
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
   // Q_INVOKABLE void update();
 
 
signals:
    void nodeIDchanged(QString data);
    void iconFilePathChanged(QString data);
    void posXChanged(int data);
    void posYChanged(int data);
    void isDisplayedChanged(bool data);
    void parentNodesChanged(QList<Node*>& data);
    void dataChanged();
 
 
protected:
 
    void addChild(Node* p_childNode);
    const QList<Node*>& getChildren() const;
    QList<Node*>& accessChildren();
 
 
 
private:
    QString         _nodeID;
    NodeType        _nodeType;
    PlugInType      _plugInType;
    QString         _iconFilePath;
    int             _posX;
    int             _posY;
    bool            _isDisplayed;
 
    QList<Node*>    _parents;
    QList<Node*>    _children;
};
 
#endif // NODE_H
Code :
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
 
#include <Node.h>
 
 
// Constructors
Node::Node() : _nodeID(""), _nodeType(NOT_DEFINED), _plugInType(NOT_DEFINEDD), _iconFilePath(""), _posX(0), _posY(0), _isDisplayed(true)
{
    setFlag(ItemHasNoContents, false);
    update();
}
 
 
 
Node::Node(QString nodeID, NodeType nodeType, PlugInType plugInType, QString iconFilePath) :
    QDeclarativeItem (), _nodeID(nodeID), _nodeType(nodeType), _plugInType(plugInType), _iconFilePath(iconFilePath), _posX(1), _posY(1), _isDisplayed(true)
{
    emit nodeIDchanged(_nodeID);
    emit iconFilePathChanged(_iconFilePath);
    emit posXChanged(_posX);
    emit posYChanged(_posY);
    emit isDisplayedChanged(_isDisplayed);
    emit dataChanged();
 
    setFlag(ItemHasNoContents, false);
}
 
 
/*
// copy constructor
Node::Node(Node &sourceNode)
{
    this->_nodeID       = sourceNode.nodeID();
    this->_iconFilePath = sourceNode.iconFilePath();
    this->_parents      = sourceNode.accessParents();
 
    this->update();
}
*/
 
 
// Destructor
Node::~Node()
{
}
 
 
 
QString Node::nodeID() const
{
    return _nodeID;
}
 
 
bool Node::setNodeID(const QString &nodeID)
{
    _nodeID = nodeID;
    emit nodeIDchanged(_nodeID);
    emit dataChanged();
 
    return true;
}
 
 
QString  Node::iconFilePath() const
{
    return _iconFilePath;
}
 
 
bool Node::setIconFilePath(const QString &iconFilePath)
{
    _iconFilePath = iconFilePath;
    emit iconFilePathChanged(_iconFilePath);
    emit dataChanged();
 
    return true;
}
 
 
int Node::posX() const
{
    return _posX;
}
 
 
bool Node::setPosX(int posX)
{
    _posX = posX;
    emit posXChanged(_posX);
    emit dataChanged();
 
    return true;
}
 
 
int Node::posY() const
{
    return _posY;
}
 
 
bool Node::setPosY(int posY)
{
    _posY = posY;
    emit posYChanged(_posY);
    emit dataChanged();
 
    return true;
}
 
 
bool Node::isDisplayed() const
{
    return _isDisplayed;
}
 
 
bool Node::setIsDisplayed(bool value)
{
    _isDisplayed = value;
    emit isDisplayedChanged(_isDisplayed);
    emit dataChanged();
 
    return true;
}
 
 
void Node::hide()
{
    this->setIsDisplayed(false);
}
 
 
void Node::show()
{
    this->setIsDisplayed(true);
}
 
 
void Node::connectTo(Node* p_toNode)
{
    this->_parents.append(p_toNode);
 
    //update();
}
 
 
void Node::disconnectFrom(Node* p_toNode)
{
}
 
 
void Node::removeNode()
{
}
 
 
const QList<Node*>& Node::parentNodes() const
{
    return _parents;
}
 
 
QList<Node*>& Node::accessParents()
{
    return _parents;
}
 
 
bool Node::addParent(Node* p_parentNode)
{
    this->_parents.append(p_parentNode);
 
    emit parentNodesChanged(_parents);
    emit dataChanged();
 
    return true;
}
 
 
 
void Node::addChild(Node* p_childNode)
{
    _children.append(p_childNode);
}
 
 
const QList<Node*>& Node::getChildren() const
{
    return _children;
}
 
 
QList<Node*>& Node::accessChildren()
{
    return _children;
}
 
 
void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option)
    Q_UNUSED(widget)
 
    QPen pen;
    pen.setColor(Qt::blue);
    pen.setStyle(Qt::SolidLine);
    pen.setWidth(6);
    painter->setPen(pen);
    painter->setRenderHints(QPainter::Antialiasing, true);
 
    /*
    QPoint startPoint(this->posX(), this->posY());
    QPoint endPoint(-50, -50);
 
    painter->drawLine(startPoint, endPoint);
    */
 
    for (QList<Node*>::const_iterator itParent = this->_parents.begin();
         itParent != this->_parents.end();
         ++itParent)
    {
        int debug = this->posX();
               debug = this->posY();
               debug = (*itParent)->posX();
               debug = (*itParent)->posY();
        QPoint startPoint(this->posX(), this->posY());
        QPoint endPoint((*itParent)->posX(), (*itParent)->posY());
 
        painter->drawLine(startPoint, endPoint);
    }
}
 
/*
void Node::update()
{
    // QDeclarativeItem::update();
    QPainter* p_painter = new QPainter();
    QStyleOptionGraphicsItem* p_option = new QStyleOptionGraphicsItem();
    paint(p_painter, p_option);
 
    //delete p_painter;
    //delete p_option;
}
*/

Je suis blocké et je ne peux avancer a cause de ce problème avec dessin de lignes entre les noeuds.
Zaxy_ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 14/12/2012, 07h14   #15
Amnell
Rédacteur
 
Avatar de Amnell
 
Homme Louis du Verdier
Étudiant
Inscription : mars 2009
Messages : 1 600
Détails du profil
Informations personnelles :
Nom : Homme Louis du Verdier
Localisation : France, Hauts de Seine (Île de France)

Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : mars 2009
Messages : 1 600
Points : 5 048
Points : 5 048
Bonjour,

Dans votre classe Node, plutôt que de passer par des fonction Q_INVOKABLE, vous devriez passer par des propriétés, ce qui vous permettrait d'opérer dynamiquement plutôt que d'avoir à appeler vous-même les fonctions lors de changements.

Là, l'intérêt semble de relier les Nodes entre eux. En admettant que les méthodes connectTo() et disconnectTo() - en ajoutant update() pour avoir droit à un appel à paint() - prenant en paramètre un Node* soient Q_INVOKABLE, on pourrait envisage un Component.onCompleted du main.qml :

Code :
1
2
3
4
5
6
7
8
9
10
Repeater {
    anchors.fill: parent
    model: configModel
    delegate: Node{}
}
 
Component.onCompleted: {
    for (var i = 0; i < configModel.count - 1; ++i)
        configModel.get(i).connectTo(configModel.get(i + 1));
}
Vous pouvez également ajouter des MouseArea pour faire comme dans mon exemple (relier les éléments en cliquant) et faire un :

Code :
configModel.get(selectedNode).connectTo(configModel.get(index));
Dans le onClicked de la MouseArea du Node.

Bonne continuation,
Amnell.
Amnell est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 14/12/2012, 10h19   #16
Zaxy_
Invité de passage
 
Homme
Inscription : décembre 2012
Messages : 13
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations forums :
Inscription : décembre 2012
Messages : 13
Points : 0
Points : 0
Bonjour,

C-est bien cette proposition.

Par contre dans ton exemple, tu fais connection entre le node courrant et tous ces précedant noeud.

Moi, je veux qu'il y a la possibilité de faire connection entre le noeud courrant et certains des noeuds (qui sont marqués dans sa liste _parents).

Ca je ne sais pas comment faire, sinon exemple est bon.

Merci beaucoup
Zaxy_ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 14/12/2012, 11h20   #17
Zaxy_
Invité de passage
 
Homme
Inscription : décembre 2012
Messages : 13
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations forums :
Inscription : décembre 2012
Messages : 13
Points : 0
Points : 0
Bonjour,

J'ai essayé de faire main.qml comme tu as proposé:

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
Rectangle {
    width:  500
    height: 500
 
    Repeater {
        anchors.fill: parent
        model: configModel
        delegate: Node {}
    }
 
    Component.onCompleted: {
        for (var i = 0; i < configModel.count - 1; ++i)
            configModel.get(i).connectTo(configModel.get(i + 1));
    }
}
Après , j'ai tracé le code, mais on ne rentre pas dans le for loop !
Ca veut dire, que configModel.count est 0 au moment d'appeler onCompleted. C'est bizarre quand meme ...

Sinon, je trouve l'idée comme bonne.

Juste comme j'ai ecrit dans mon message précédant, je veux connecter dynamiquement un noeud donné à un certain nombre de noeud spécifiés

Par exemple, une configuration de 3 noeud:
- noeud 1 - non connecté
- noeud 2 - non connecté
- noeud 3 - connecté à noeud 1
Zaxy_ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 14/12/2012, 12h16   #18
Amnell
Rédacteur
 
Avatar de Amnell
 
Homme Louis du Verdier
Étudiant
Inscription : mars 2009
Messages : 1 600
Détails du profil
Informations personnelles :
Nom : Homme Louis du Verdier
Localisation : France, Hauts de Seine (Île de France)

Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : mars 2009
Messages : 1 600
Points : 5 048
Points : 5 048
Bonjour,

Le problème vient probablement du fait que le modèle soit vide ou bien que sa fonction count() retourne 0, n'ayant pas été réimplémentée dans le C++. Par contre, je vous conseillerais plus de gérer vos Nodes du côté du QML, cela éviterait de mélanger le model et la view comme cela.

Bonne continuation,
Amnell.
Amnell est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 14/12/2012, 12h38   #19
Zaxy_
Invité de passage
 
Homme
Inscription : décembre 2012
Messages : 13
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations forums :
Inscription : décembre 2012
Messages : 13
Points : 0
Points : 0
Oui, mon idée était de faire le dessin des connections par la fonction paint(), mais le probleme est que cette fonction est appellé dans QML a partir d'une instance vide de la classe Node.

QML crée des instances de Node a partir de Node() et ils sont vides. Apres plus tard QML copie des properties (roles) a partir du model.

Donc, quand paint() est appellé, la liste _parents est vide et je ne peux pas dessiner les liens.

Tu as une idée ?
Zaxy_ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 14/12/2012, 16h32   #20
Amnell
Rédacteur
 
Avatar de Amnell
 
Homme Louis du Verdier
Étudiant
Inscription : mars 2009
Messages : 1 600
Détails du profil
Informations personnelles :
Nom : Homme Louis du Verdier
Localisation : France, Hauts de Seine (Île de France)

Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : mars 2009
Messages : 1 600
Points : 5 048
Points : 5 048
Bonjour,

L'idéal serait d'avoir un modèle contenant les informations - non les Nodes eux-mêmes - permettant de créer les Nodes et de les relier ultérieurement entre eux par la suite.

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ListModel {
    id: nodeModel
}
 
// Les Nodes sont créés ici, pas dans le C++ :
Repeater {
    model: nodeModel
    delegate: Node {
        nodeName: name
        nodeType: type
        // ...
    }
}
 
Component.onCompleted: {
    // On crée quelques Nodes en modifiant le nodeModel :
    nodeModel.append({"name": "Node 1", "type": 0, /*...*/});
    nodeModel.append({"name": "Node 2", "type": 4, /*...*/});
 
    // On relie tout le monde :
    for (var i = 0; i < configModel.count - 1; ++i)
        configModel.get(i).connectTo(configModel.get(i + 1));
}
Pour cela, vous devrez ajouter les Q_PROPERTY dans votre classe Node.

Bonne continuation,
Amnell.
Amnell est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 14h36.


 
 
 
 
Partenaires

Hébergement Web