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

Discussion :

Objets QGraphicsItem interactifs [Graphics View]

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Invité
    Invité(e)
    Par défaut Objets QGraphicsItem interactifs
    Bonjour,

    Je reprends une application développée en C++ avec Qt : elle affiche des graphiques composés de rectangles reliés par des flèches.

    Pour l'instant, ces graphiques sont statiques. A terme, je dois les rendre dynamiques et interactifs en permettant notamment le drag&drop des rectangles.

    Ces rectangles sont actuellement des qGraphicsItems affichés dans une qGraphicsScene. En termes d'interface, je voudrais qu'ils réagissent au survol et au clic de la souris : changement de style, cadre de sélection avec poignées de redimensionnement, etc.

    Au lieu de réinventer la roue, je voulais savoir s'il était possible d'utiliser le framework Qt à bon escient pour "rendre interactifs" mes bêtes rectangles ?

    Vos pistes et idées seront appréciées. Si ce n'est pas assez précis, faites-le moi savoir.

    Merci

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Tu peux réimplémenter, assez facilement, les fonctions (protégées virtuelles) relatives aux différents événements qui peuvent survenir pour qu'elles réagissent comme tu le souhaites, comme, par exemple, émettre un signal qui sera connecté à un slot particulier de ton IHM (ce peut, pourquoi pas, être le signal triggered() d'une QAction, hein ).

    N'oublies cependant pas que tu devras systématiquement connecter le signal émis chaque fois que tu ajoutera un élément dans ta scène et le déconnecter chaque fois que l'élément sera supprimé, pour éviter les éventuels problèmes de signal non reçu

    N'oublies pas, non plus, que ton QGraphicsItem n'est sensé qu'être "une représentation parmi d'autres" d'un élément de ton business, et qu'il est peut etre "de bon ton" de faire en sorte de faire passer cet élément de business dans un model sous la forme d'un modelindex, de manière à ce que ton application puisse justement, tirer parti de ce que tu fera au niveau de ta vue "graphique" (par opposition ici aux autres vues que tu pourrais avoir en simultané de ton business )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Comme je me rend compte que j'ai été un peu évasif ce matin, je vais tenter d'exprimer un peu plus clairement toutes les idées que j'ai soulevé

    Même si tu n'offre qu'une vue "graphique" (dans le sens de "basée sur des QGraphicsItem et leur pendant), les rectangles vont représenter des objets "métier" et les flèches vont représenter les relations qui existent entre ces différents objets métiers.

    Par exemple, les rectangles vont représenter des personnes quand les flèches représenteront un lien de parenté ("est l'enfant de" ou "est le parent de")

    Si chaque personne sera, effectivement, représentée à une position donnée dans ta scène, il n'y a strictement rien qui t'interdit d'envisager de représenter ces relations sous une forme différente, par exemple, sous une forme tabulaire ou sous la forme d'un arbre, et la notion de "position" devient donc parfaitement inutile

    De même, alors que la relation de parenté peut parfaitement passer par plusieurs point bien définis pour aller du parent à son enfant (pour éviter d'avoir des flèches qui se croisent dans tous les sens) dans ta scène, l'affichage sous forme tabulaire ou sous la forme d'un arbre n'a pas forcément besoin d'afficher les postions par lesquels passe la relation : si on fait, simplement, afficher le nom de l'enfant, l'utilisateur sera largement en mesure de faire les rapprochements

    Tu travailleras donc avec les mêmes données, mais tu les afficheras, tout simplement, de manière différente, et il y aura même certaines informations (celles de positon dans la scène) qui ne sont, a priori, pas indispensables, et qui pourraient donc parfaitement être "supprimée" du modèle

    Au niveau de tes données "métier", tu pourrais très bien avoir, d'un coté, une liste de personne et de l'autre une liste de relations.

    Comme il s'agit d'afficher les personnes et les relation, tu aurais, en plus, une liste de position pour les personnes et une liste de "point de passage" pour les différentes relations.

    Cela pourrait parfaitement prendre une forme proche de
    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
    class Personne
    {
        /* les informations relatives à la personne, dont un identifiant unique
          * MAIS PAS LA POSITION 
          */
    };
    struct IsParentOf
    {
        RelationID id_relation;
        ID id_parent;
        ID id_enfant;
    };
    /* regroupe l'ensemble des liens de parenté qui existent, contenant, tu
     * l'aura deviné, un ensemble de IsParentOf ;)
     */
    class ListOfParents
    {
        /* tout ce qui va bien ;) */
    };
    /* met chaque personne en relation avec la position de son coin supérieur
     * gauche
     */
    struct PersonnePosition
    {
        ID personne_id;
        QPos topLeft;
    };
    /* la liste des position de chaque personne, contient, tu t'en doutes,
     * un ensemble de PersonPosition
     */
    class PersonnePositionList
    {
        /* tout ce qui va bien */
        private:
            std::vector<PersonnePosition> personnesPositions_;
    }
    /* regroupe les "points de passage" d'une relation. Le premier point est
     * d'office le début, le dernier est d'office la fin (l'endroit ou la flèche sera
     * tracée
     */
    class RelationPassagePosition
    {
        /* tout ce qui va bien */
        private:
            RelationID id_relation;
            std::vector<QPos> passage_;
    };
    /* regroupe toutes les RelationPassagePosition */
    class RelationPassageList
    {
        /* ce qui va bien */
        private:
            std::vector<RelationPassagePosition> relationPassages_;
    };
    (dois-je préciser que ce code a été écrit "selon l'inspiration du moment" et ne réflète aucunement une conception très réfléchie )

    Toutes les listes seront, bien évidemment, disponibles directement au niveau de l'application, mais elles auront l'énorme avantage de permettre d'avoir une relation 1-1 entre les différents objets, même si un enfant a a priori deux parents et qu'un parent peut avoir plusieurs enfants.

    Mais grace aux identifiants unique que l'on aura placé dans les différentes structures, il sera finalement très facile de retrouver "n'importe quoi" .

    De plus, il te sera très facile de créer tous les modèles que tu peux vouloir envisager, "simplement" en "allant pêcher" les informations dont tu as besoin

    Il te sera donc "assez facile" de générer un modèle qui les reprend toutes et qui te servira pour l'affichage dans ta scène.

    Mais il te sera "tout aussi facile" de générer un modèle qui ne reprend que les personnes (sans notion de lien de parenté) ou un autre qui reprend les personnes et les liens de parenté, dans un sens, ou dans l'autre, et de t'en servir pour fournir une vue plus ou moins tabulaire, voire, pour remplir un combobox ou pour sérialiser le tout

    Et l'énorme avantage est que le modèle qui reprend toutes les informations peut parfaitement être utilisé pour les représenter sous une forme tabulaire ou autre :
    Tu es tout à fait en mesure d'envisager autant de représentation d'un même modèle que tu le voudras, la seule limite sera celle... de ton imagination

    En revenant maintenant sur le problème spécifique aux graphicsItems, il faut être conscient que, finalement, chaque objet graphique sera corrélé à un élément du modèle.

    Tu peux donc "assez facilement" récupérer l'index correspondant à un objet graphique lorsque tu vas cliquer dessus, par exemple.

    De même, il est possible, en se basant sur les événement mousePressEvent ( QGraphicsSceneMouseEvent * mouseEvent ) et mouseReleaseEvent ( QGraphicsSceneMouseEvent * mouseEvent ) de ta scène, il est tout à fait possible d'obtenir un rectangle qui relie les deux postions au moment de ces événements.

    Tu peux donc parfaitement te baser sur le modèle généré pour retrouver l'ensemble des éléments qui se trouvent dans le dit rectangle

    D'une part comme de l'autre, il est donc tout à fait possible de placer ces indexes dans un QItemSelectionModel afin de gérer la sélection.

    L'énorme avantage est que si tu as plusieurs vues basées sur le même modèle, la sélection d'un objet dans l'une des vues sera automatiquement reportée dans l'autre

    Mais, pour arriver à ce genre de résultat, il est important de faire en sorte que le comportement de sélection (hors gestion pure et simple des événements de la souris) puisse être connecté directement à ce qui contient le modèle, à savoir... l'application

    Pour ce faire, tu créeras sans doute plusieurs classes dérivées de QGraphicItem dans lesquelles tu déclareras quelques signaux "personnalisés" et, tu feras en sorte de connecter ces signaux (à la scène, qui se chargera de les transférer à la vue qui les enverra à l'application ) au moment de les ajouter dans ta scène et de les déconnecter au moment où tu retire l'élément de la scène
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  4. #4
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Merci pour tes réponses et tes conseils sur l'architecture. A ce propos, l'application sur laquelle je travaille est déjà structurée de cette manière : classes du modèle distinctes de leurs pendants graphiques. Avec la scène contenant les listes (vecteurs) de mes rectangles et de mes relations.

    Ce qui m'intéresse plutôt, c'est l'implémentation, quelles classes utiliser, comment, etc. Une question me vient à la lecture de ton deuxième message : comment gérer les signaux et les slots sur des QGraphicsItems, vu qu'ils n'héritent pas de QObject ?

    Les QItemSelectionModel servent apparemment à gérer la sélection des représentations graphiques des objets d'un modèle. Mais comment gérer cela graphiquement ? Existe-t-il une classe à dériver qui ajoutent à des "rectangles" (sans préjuger de leur nature dans le code) la capacité d'être réactifs à la sélection, au glisser/déposer, etc. Bon, peut-être que la réponse est tout simplement non et qu'il faut s'arranger pour hériter d'une manière ou d'une autre de QObject et tout réinventer soi-même ?

  5. #5
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    En fait, tous les QGraphicsItems peuvent accéder à tout plein d'objet dérivés de QObject et sur lesquels tu peux te baser.

    Parmis ceux ci, on peut compter :
    • La QGraphicsScene avec la fonction scene()
    • un QGraphicWidget avec la fonction parentWidget()
    • un QGraphicObject avec la fonction parentObject()
    (note que QGraphicWidget hérite de QGraphicObject )
    Tu peux donc, assez facilement, redéfinir les événements auxquels réagit ton QGraphicsItem de sorte à apliquer la souris puis à invoquer un slot particulier de la scène

    Tu pourrais, par exemple, implémenter ton rectangle sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Rectangle : public QGraphicsItem
    {
        protected:
            void mousePressEvent ( QGraphicsSceneMouseEvent * event ) 
             {
                  /* appel du comportement "par défaut */
                  QGraphicsItem::mousePressEvent(event);
                   /* ... */
                    dynamic_cast<MyPersonnalScene*>(scene())->LeSlotQuiVaBien();
             }
    }
    et LeSlotQuiVaBien pourra, bien sur, renvoyer tout cela vers ... tout ce que tu veux
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

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

Discussions similaires

  1. [2D/3D] Affichage Objet heritant de QObject et QGraphicsITem
    Par mrdus dans le forum Qt
    Réponses: 9
    Dernier message: 01/06/2010, 12h37
  2. Déplacement interactif d'un objet
    Par incha221 dans le forum PyQt
    Réponses: 4
    Dernier message: 28/06/2008, 10h37
  3. Importer des objets de 3dsMax
    Par Anonymous dans le forum OpenGL
    Réponses: 3
    Dernier message: 06/05/2002, 13h53
  4. Peux t'on créer une copie locale de l'objet partagé?
    Par Anonymous dans le forum CORBA
    Réponses: 8
    Dernier message: 16/04/2002, 16h20
  5. [Kylix] Erreur objet
    Par Anonymous dans le forum EDI
    Réponses: 1
    Dernier message: 22/03/2002, 09h41

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