IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Qt Quick Discussion :

Création de courbes dynamiques


Sujet :

Qt Quick

  1. #1
    Membre confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Novembre 2008
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2008
    Messages : 76
    Par défaut Création de courbes dynamiques
    Bonjour,

    je suis en train de chercher comment modéliser des courbes qui serait rafraîchies et mises à jour via un timer qui leur enverrait des nouvelles données toutes les 2 secondes.

    Je suis parti sur un PathView avec un rectangle délégué et des PathCubic mais ça ne me semble pas très évolutif...

    Un exemple de ce que j'ai pu essayer à partir de choses et d'autres trouvées sur internet :

    Code : 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
                Rectangle {
                    id: rect3;
                    //x: 25
                    anchors.fill: parent
                    width: 720;
                    height: 480;
                    color: "#00000000"
                    PathView {
                        id: paV3;
                        opacity: 0.7
                        anchors.fill: parent;
                        model: 950;
                        delegate: Rectangle {
                            id: dot3;
                            width: 1.0625; height: 1.0625;
                            color: "#00c0ff";
                            smooth: true
                        }
     
                        path: Path {
                            id: ph3;
                            startX: 16.0; startY: paV3.height/2.0;
                            pathElements: [
                                PathQuad {
                                    x: 40.0; y: paV3.height/2.0;
                                    controlX: 20.0; controlY: ph3.startY +145.0;
                                },
                                PathQuad {
                                    x: 80.0; y: paV3.height/2.0;
                                    controlX: 60.0; controlY: ph3.startY -60.0;
                                },
                                PathQuad {
                                    x: 120.0; y: paV3.height/2.0;
                                    controlX: 100.0; controlY: ph3.startY +145.0;
                                },
                                PathQuad {
                                    x: 160.0; y: paV3.height/2.0;
                                    controlX: 140.0; controlY: ph3.startY -60.0;
                                },
                                PathQuad {
                                    x: 200.0; y: paV3.height/2.0;
                                    controlX: 180.0; controlY: ph3.startY +145.0;
                                },
                                PathQuad {
                                    x: 240.0; y: paV3.height/2.0;
                                    controlX: 220.0; controlY: ph3.startY -60.0;
                                },
                                PathQuad {
                                    x: 284.0; y: paV3.height/2.0;
                                    controlX: 260.0; controlY: ph3.startY +120.0;
                                }
                            ]
                        }
                    }
                }
    Existe-t-il un moyen plus simple et évolutif pour ce faire ?

    Merci d'avance

  2. #2
    Rédacteur
    Avatar de Amnell
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2009
    Messages
    1 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 840
    Par défaut
    Bonsoir,

    Pour ma part, je préférais faire mon propre item à ajouter à QML pour gérer lesdites courbes. Par exemple, la classe réalisée dans le cadre du deuxième défi Qt organisé par Developpez.com pour afficher un viewer d'ECG (un peu comme dans les hôpitaux, la courbe avance au fur et à mesure qu'on reçoit les données).

    Dans un premier temps le header :

    Code C++ : 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
    #ifndef ECGVIEWER_H
    #define ECGVIEWER_H
     
    #include <QtDeclarative>
    #include <QtGui>
     
    #define POINTS_PER_SECOND 1000
     
    class ECGViewer : public QDeclarativeItem
    {
        Q_OBJECT
        Q_DISABLE_COPY(ECGViewer)
        Q_PROPERTY(int duration READ getDuration WRITE setDuration NOTIFY durationChanged)
        Q_PROPERTY(int yInterval READ getYInterval WRITE setYInterval NOTIFY yIntervalChanged)
        Q_PROPERTY(int pWidth READ getPWidth WRITE setPWidth NOTIFY pWidthChanged)
        Q_PROPERTY(QColor color READ getColor WRITE setColor NOTIFY colorChanged)
     
    public:
        explicit ECGViewer();
        /*! Retourne la durée de l'enregistrement. */
        int getDuration() const;
        /*! Retourne l'amplitude de l'ECG en mV. */
        int getYInterval() const;
        /*! Retourne la tailler du painter de la courbe. */
        int getPWidth() const;
        /*! Retourne la couleur de la courbe. */
        QColor getColor() const;
        /*! Définit la durée de l'enregistrement. */
        void setDuration(const int&);
        /*! Définit l'amplitude en mV. */
        void setYInterval(const int&);
        /*! Définit la taille du painter de la courbe. */
        void setPWidth(const int&);
        /*! Définit la couleur de la courbe. */
        void setColor(const QColor&);
        /*! Signal NOTIFY de la durée de l'enregistrement. */
        Q_SIGNAL void durationChanged();
        /*! Signal NOTIFY de l'amplitude en mV. */
        Q_SIGNAL void yIntervalChanged();
        /*! Signal NOTIFY de la taille du painter de la courbe. */
        Q_SIGNAL void pWidthChanged();
        /*! Signal NOTIFY de la couleur de la courbe. */
        Q_SIGNAL void colorChanged();
        Q_INVOKABLE void addPoint(const float&);
        Q_INVOKABLE void refresh();
        Q_INVOKABLE void clear();
     
    protected:
        void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*);
     
    private:
        /*! Liste des points de l'ECG. */
        QList<float> _points;
        /*! Durée de l'enregistrement. */
        int _duration;
        /*! Amplitude en mV de l'ECG. */
        int _yInterval;
        /*! Taille du painter de la courbe. */
        int _pWidth;
        /*! Couleur de la courbe. */
        QColor _color;
    };
     
    QML_DECLARE_TYPE(ECGViewer)
     
    #endif // ECGVIEWER_H

    Et le .cpp associé :

    Code C++ : 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
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    #include "ecgviewer.h"
     
    /*!
        \class ECGViewer
        \brief La classe ECGViewer correspond à une extension de QML pour la mise
        en place d'un noueau composant affichant la courbe d'un ECG.
     
        Cette classe étend QML par le biais de Qt Quick et met à disposition un
        composant ECGViewer dans le code QML, utilisable à même titre qu'un élément
        Rectangle.
    */
     
    /*!
        \property ECGViewer::duration
        \brief La durée de l'enregistrement en secondes.
    */
     
    /*!
        \property ECGViewer::yInterval
        \brief L'amplitude de l'enregistrement en mV.
    */
     
    /*!
        \property ECGViewer::pWidth
        \brief La taille du painter de la courbe en pixels.
    */
     
    /*!
        \property ECGViewer::color
        \brief La couleur de la courbe.
    */
     
    /*!
        \fn ECGViewer::ECGViewer()
        \brief Constructeur de la classe.
     
        Construit un objet ECGViewer.
    */
     
    ECGViewer::ECGViewer() : _duration(1), _yInterval(3),
        _pWidth(3), _color(QColor("#56B058"))
    {
        setFlag(ItemHasNoContents, false);
    }
     
    int ECGViewer::getDuration() const
    {
        return _duration;
    }
     
    int ECGViewer::getYInterval() const
    {
        return _yInterval;
    }
     
    void ECGViewer::setDuration(const int &duration)
    {
        _duration = duration <= 0 ? 1 : duration;
        emit durationChanged();
        update();
    }
     
    void ECGViewer::setYInterval(const int &interval)
    {
        _yInterval = interval <= 0 ? 1 : interval;
        emit yIntervalChanged();
        update();
    }
     
    int ECGViewer::getPWidth() const
    {
        return _pWidth;
    }
     
    void ECGViewer::setPWidth(const int &width)
    {
        _pWidth = width;
        emit pWidthChanged();
        update();
    }
     
    QColor ECGViewer::getColor() const
    {
        return _color;
    }
     
    void ECGViewer::setColor(const QColor &color)
    {
        _color = color;
        emit colorChanged();
        update();
    }
     
    /*!
        \fn ECGViewer::paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget*)
        \brief Dessine l'ECG du patient.
     
        Les données sont affichées selon plusieurs critères :
        - L'échelle en y, déterminée selon la hauteur du composant et l'intervalle
        défini par \a _yInterval.
        - L'espacement entre deux points, déterminé selon le nombre de points par
        seconde (une constante), la longueur du composant ainsi que la durée
        \a _duration en seconde que représente le graphe de l'ECG.
     
        La position des points à l'échelle de \a _yInterval peut être positive ou
        négative, d'où le fait que l'ordonnée 0 est positionnée à mi-hauteur, dans
        le composant.
    */
     
    void ECGViewer::paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget*)
    {
        QPen pen(_color, _pWidth);
        painter->setPen(pen);
        painter->setRenderHint(QPainter::Antialiasing);
     
        QPolygonF polygon;
        int halfHeight = height() / 2;
        float xSpacing = width() / (POINTS_PER_SECOND * _duration);
        float ySpacing = height() / _yInterval;
     
        for (int i = 0; i < _points.count(); ++i) {
            polygon.append(QPointF(i * xSpacing,
                halfHeight - (_points.at(i) * ySpacing)));
        }
     
        painter->drawPolyline(polygon);
    }
     
    /*!
        \fn ECGViewer::addPoint(const float &y)
        \brief Ajoute un point à la liste des points.
     
        Le nombre de points maximum est déterminé par le nombre de points par
        seconde multiplié par la durée en secondes affichée par le graphe. Si ce
        nombre est atteint, cela supprime les premiers points jusqu'à ce que ce
        seuil ne soit plus atteint (permet la réalisation de l'animation).
    */
     
    void ECGViewer::addPoint(const float &y)
    {
        while (_points.count() >= (POINTS_PER_SECOND * _duration))
            _points.removeFirst();
        _points.append(y);
    }
     
    /*!
        \fn ECGViewer::refresh()
        \brief Appelle la fonction update() du composant, permettant une peinture.
    */
     
    void ECGViewer::refresh()
    {
        update();
    }
     
    /*!
        \fn ECGViewer::clear()
        \brief Vide la liste des points et appelle la fonction update(), permettant
        une peinture.
    */
     
    void ECGViewer::clear()
    {
        _points.clear();
        update();
    }

    Dans le main.cpp se situe également une ligne pour pouvoir utiliser la classe du côté de QML :

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    qmlRegisterType<ECGViewer>("Utopital", 1, 0, "ECGViewer");

    Les propriétés utilisées (interval, color, etc.) sont des données relatives à la courbe. Le plus intéressant se situe dans la fonction ECGViewer::paint(). On fonctionne avec des addPoint depuis QML.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    ECGViewer {
        id: ecgViewer
        anchors.top: parent.top
        anchors.left: parent.left
        anchors.right: rightAreaTitle.left
        anchors.bottom: parent.bottom
     
        Component.onCompleted{
            yInterval = module.viewerInterval;
            duration = module.viewerDuration;
            pWidth = module.viewerPWidth;
            color = module.viewerColor;
        }
    }
    Et la fonction pour ajouter des points puis rafraichir l'affichage (attention à ne pas afficher pour chaque point, sinon je ne vous dis pas le ralentissement) :

    Code js : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    function handlePoints(data) {
        for (var i = 0; i < data.length; i++)
            ecgViewer.addPoint(data[i]);
        ecgViewer.refresh();
    }

    C'est assez "artisanal" mais cela fonctionne plutôt bien, malgré le fait que cela soit un peu lent lors de l'affichage d'un grand nombre de points (on fait à chaque fois un ré-affichage de la totalité du viewer ; cela pourrait être optimisé). Cela reste une solution concrète et moins "hacky" que d'utiliser des items pour modifier la courbe (on aura sans doute des problèmes avec les Paht[...] par rapport à la possibilité de dynamisme de la courbe).

    Vous voyez tout de même le principe : on crée une classe dérivée de QDeclarativeItem, on y implémente la fonction paint() (sans oublier setFlag(ItemHasNoContents, false) pour que la fonction paint() soit appelée) de façon à ce qu'elle dessine les points stockés dans un conteneur tel qu'une QList<T>, on procède au qmlRegisterType et l'affaire est réglée. J'ai codé plusieurs classes de ce type selon mes besoins, cela a toujours bien fonctionné. Il reste cependant possible d'utiliser des bibliothèques QML existantes (y en a-t-il ?) ou bien d'embarquer un viewer de la bibliothèque Qwt dans QML par le biais d'un QGraphicsProxyWidget.

    Espérant avoir répondu à votre question,

    Bonne continuation,
    Amnell.
    N'oubliez pas de consulter la FAQ Qt ainsi que les cours et tutoriels C++/Qt !

    Dernier article : Débuter avec les Enlightenment Foundation Libraries (EFL)
    Dernières traductions : Introduction à Qt Quick - Applications modernes avec Qt et QML
    Vous cherchez un livre sur Qt 5, Qt Quick et QML ? Créer des applications avec Qt 5 - Les essentiels

  3. #3
    Membre confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Novembre 2008
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2008
    Messages : 76
    Par défaut
    (on aura sans doute des problèmes avec les Path[...] par rapport à la possibilité de dynamisme de la courbe)
    En effet, c'est à ce soucis que j'étais confronté. Bon, après ça peut toujours se bypasser de manière hacky mais c'est plutôt moche.

    Il reste cependant possible d'utiliser des bibliothèques QML existantes (y en a-t-il ?)
    Je n'ai pas eu la chance de tomber dessus si elles existent. Je ne le pense pas.

    Il reste cependant possible d'utiliser des bibliothèques QML existantes (y en a-t-il ?) ou bien d'embarquer un viewer de la bibliothèque Qwt dans QML par le biais d'un QGraphicsProxyWidget.
    C'est une autre piste que j'avais exploré, mais intégrer du Qwt me paraît moins adapté dans mon cas.

    Votre méthode me paraît effectivement mieux et plus propre que la mienne, je vais regarder ce que ça donne une fois adaptée à ce que je fais.

    Merci de votre aide.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [PowerBuilder] Création d'objets dynamiques
    Par Béné123456789 dans le forum Powerbuilder
    Réponses: 3
    Dernier message: 12/06/2006, 00h33
  2. Réponses: 5
    Dernier message: 13/11/2005, 19h10
  3. Introspection et création de code dynamiquement ?
    Par elitost dans le forum API standards et tierces
    Réponses: 10
    Dernier message: 17/10/2005, 22h43
  4. Problème avec création de fenêtre dynamique
    Par FredericB dans le forum C++Builder
    Réponses: 3
    Dernier message: 29/09/2005, 17h21
  5. [FLASH MX2004] Création d'objet dynamiquement
    Par noarno dans le forum Flash
    Réponses: 3
    Dernier message: 15/12/2004, 11h00

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo