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 :

Glisser-déposer entre deux QTreeView

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Par défaut Glisser-déposer entre deux QTreeView
    Bonsoir,

    je cherche à réaliser un glisser-déposer d'une QTreeView à une autre QTreeView. J'ai suivi un tutoriel pour apprendre à gérer le drag & drop dans une même liste (ou internal move) mais je ne sais pas vraiment comment m'y prendre lorsqu'il s'agit de :

    - Cliquer sur une ligne du premier QTreeView (standardItem qui a été créé avec un QString en paramètre et qui a ensuite été ajouté au model via un appendRow())

    - Maintenir le clic et déplacer la souris sur le deuxième QTreeView

    - Relâcher la souris et déposer l'item avec le même nom dans ce dernier

    Aussi, l'élément n'est pas vraiment glissé-déposé puisque, de préférence, j'aimerais qu'il se copie et qu'il soit donc toujours présent dans le QTreeView initial.

    Pourriez-vous me donner des pistes ? Je reconnais qu'il y a beaucoup de posts sur la technique de drag&drop mais il y en a tellement que je m'y perds :s

    Merci beaucoup !

  2. #2
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Par défaut
    Bonjour,

    je me permets de répondre puisque j'ai bien avancé sur le sujet et que mes questions ne sont plus les mêmes.

    Pour réaliser un "drag & drop" d'un QTreeView à un autre, je me suis inspiré du code ci-joint issu d'un exemple Qt.

    Cependant, dans ce code, je ne comprends pas bien comment est géré le LIEN entre la QStrinList membre du QStringItemModel et donc ce dernier.

    On dirait qu'il apparaît implicitement dans la méthode setData() :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
        bool setData(const QModelIndex &index,
                     const QVariant &value,
                     int role = Qt::EditRole )
        {
            Q_UNUSED( role );
     
            if (index.isValid()) {
                _list.replace(index.row(), value.toString());
                emit dataChanged(index, index);
                return true;
            }
            return false;
        }
    En fait, je pense que le concept de index de model est encore flou pour moi. Dans setData(), il semblerait que l'on remplace déjà à l'index donné la valeur par celle en entrée. Donc la liste est bien changée.

    Et j'imagine que c'est le signal dataChanged(index, index) qui permet d'afficher la nouvelle liste du model dans la vue ? Si c'est le cas, je ne comprends pas comment le signal procède pour faire le lien entre le modèle et la QStringList membre sachant qu'il ne la connait pas initialement. (D'ailleurs, pourrait-on par exemple refaire le même procédé avec une liste différente d'une QStringList (par exemple QList de structures) tout en gardant ce rafraichissement à l'affichage après un setData() ?)

    J'espère que vous pourrez me débloquer un peu.

    Merci beaucoup !
    Fichiers attachés Fichiers attachés

  3. #3
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2009
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2009
    Messages : 1 009
    Par défaut
    Tout d'abord, la bible du modèle-vue de Qt : http://qt-project.org/doc/qt-4.8/mod...ogramming.html

    C'est plus simple que ça en a l'air. Tout modèle hérite de QAbstractItemModel. Toute vue hérite de QAbstractItemView. En fait les mécanisme de rafraichissement de la vue, etc., se trouvent dans les implémentations de ces classes. Quant au dessin, il est assuré par un délégué, héritant de QAbstractItemDelegate : la vue demande au délégué de peindre une zone visible (de l'index tant à l'index tant), et le délégué interroge le modèle pour récupérer les données à dessiner.

    Par exemple, dans qabstractitemview.cpp, si tu regardes à setModel(), tu vois que dataChanged() du modèle est connecté à un slot du même nom. Si tu regardes le code du slot, tu vois bien l'update() et donc tu comprends pourquoi cela provoque un rafraichissement.

    Donc un modèle, c'est une interface à utiliser pour afficher des données dans une vue sans se casser la tête vu que le mécanisme est déjà fait. Le modèle sert donc d'accesseur à la donnée. Ses méthodes d'accès, data() et setData(), sont à redéfinir pour travailler sur un type de donnée personnalisé, mais certaines classes courantes existent déjà (il y a par exemple une classe QStringListModel officielle, même si l'exemple permet de comprendre plus facilement).

    Concernant les index, chaque élément d'un modèle est indexé (ligne/colonne, dans le cas d'une simple liste on ne s'occupe pas de la colonne). L'indexation peut être gérée aussi par le développeur en redéfinissant la méthode index() et en appelant createIndex(), bon là c'est le niveau au-dessus. Là, l'index.row() correspond simplement à la place d'un élément dans la QStringList, et donc à l'affichage, si aucun tri n'est fait.

    Pour utiliser avec ton propre enum, il n'y a deux difficultés :
    - on travaille avec des QVariant. Il faut que ton enum devienne un QVariant. Dans setData(), bien sûr ce n'est pas toString() qu'il faut utiliser vu que ta donnée n'est pas une QString mais ton enum. Et pour ceci je te renvoie à la doc : http://qt-project.org/doc/qt-4.8/qme...CLARE_METATYPE
    - l'affichage. C'est le délégué (delegate) qui est chargé d'afficher chaque élément d'un modèle dans une vue, c'est dedans qu'il y a vraiment la méthode paint() qui détaille le dessin d'un item. Le delegate par défaut doit s'attendre à ce que data() renvoie un QString, donc il fait toString() et il dessine le texte. Tu as besoin de faire ton propre delegate, qui sâche traiter le QVariant renvoyé par data() si ce n'est plus un QString.

    Bonne lecture, n'hésite pas si tu as encore des questions

  4. #4
    Membre éclairé
    Avatar de betsprite
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    472
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 472
    Par défaut
    Salut Troudhyl et merci beaucoup pour ton aide

    Alors j'ai avancé un peu depuis hier et ton explication clarifie encore les choses!

    Actuellement, j'ai deux modèles qui héritent de QAbstractListItem et qui réimplémentent les méthodes rowCount(), data(), flags(), setData(), insertRows(), removeRows(), mimeData() et dropMimeData().
    Mont but étant toujours d'effectuer un drag&drop d'un modèle à l'autre tout en copiant la QString glissée quand on la dépose.

    Il y a cependant plusieurs points, que j'ai remarqué pendant mes tests, qui restent flous :

    1) Le signal itemChanged() émis dans la méthode réimplémentée setData() met à jour la vue c'est bien ça ?
    Dans ce cas, pourquoi lorsque je commente dans la réimplémentation de dropMimeData() l'appel au setData() tout en rajoutant dans la méthode insertRows() :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
            for (int row = 0; row < rows; ++row) 
                _list.insert(position, "bonjour");
    la vue se met à jour quand même ? (en gros je ne passe pas par le setData mais seulement par le insertRows(). D'ailleurs, en quoi setData() se différencie de insertRows() ? Je pourrais très bien surcharger insertRows avec en paramètre la QString que je veux rajouter et dans ce cas je ne passe jamais par setData() ).

    2) Par ailleurs, j'ai effectué des tests en commentant les méthodes mimeData() et dropMimeData() et là, je constate que le drag and drop a quand même lieu entre mes deux modèles. Je peux glisser et déposer une ligne d'un modèle à l'autre. Avez-vous une explication ?

    Merci beaucoup

  5. #5
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2009
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2009
    Messages : 1 009
    Par défaut
    1) Étant donné qu'à chaque fois que paint() est appelé (souvent de nombreuses fois par seconde si tu bouges la fenêtre, la souris dessus...), il interroge le modèle pour dessiner son contenu, c'est normal que la liste se mette à jour quand même. Cependant si tu n'appelles pas toi-même update() dès qu'il y a un changement, il n'est pas garanti que ce changement apparaisse tout de suite (tant que rien ne déclenche le re-dessin de la zone concernée), c'est pourquoi en règle général on appelle update() dès qu'on a besoin de rafraichir, ça ne coûte rien (appeler update() se fait ici implicitement en avertissant du changement avec dataChanged()).

    setData() : à l'index voulu tu alloues la donnée.
    insertRows() : tu insères des lignes avant une autre ligne, donc c'est vraiment pour gérer l'insertion (décalage des index et tout). Par défaut elle ne fait rien, mais tu peux avoir envie de faire une telle opération après tout.

    Donc elles ne font pas vraiment la même chose (j'ai un peu répété la doc, il faut absolument la lire attentivement à l'avenir).

    2) Oui j'en ai une (mais je n'ai pas le temps de développer là, peut-être ce soir), tu peux te renseigner sur le rôle du mimedata dans un drag and drop (une zone de drop n'accepte pas n'importe quel mimedata, ça permet d'une part de définir le mimedata d'un item dans mimeData(), et d'autre part de filtrer les éléments dropés dans dropMimeData()). Si tu as enlevé les méthodes, essaye de droper n'importe quoi (une icône de ton bureau par exemple) dans ta liste, tu verras bien...

  6. #6
    Membre éclairé
    Avatar de betsprite
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    472
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 472
    Par défaut
    Merci encore Troudhyl pour ta clarté et ta rapidité

    1) Du coup, si je veux assurer l'update() dans insertRows, il faut donc que je rajoute le signal dataChanged() après le endInsertRows() j'imagine ? Cependant je n'ai pas de modelIndex en param de la méthode à lui passer, il me suffit de faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    emit dataChanged(QModelIndex(), QModelIndex());
    ?

    2) Ok! je vais regarder plus attentivement la documentation pour que se soit plus clair. J'ai essayé de droper autre chose mais rien n'est accepté (îcône d'interdiction de drop dans la QTreeView). En fait ce que je trouve étrange c'est qu'il semble suffire d'accepter le drop dans la QTreeView pour que lors d'un drop la méthode insertRows réimplémentée (et non la surchargée) soit automatiquement appelée. (Un drop appel insertRows directement sans que je ne lui ai demandé)

    J'avais vu que mimeData permettait non seulement par l'intérmediaire de mimeTypes de définir le format accepté mais je pensais aussi que c'était vraiment par ce moyen que le drag & drop avait lieu.

    Au final, je suis quand même encore loin du résultat voulu. Tout ce que je fais pour l'instant c'est donc un appel automatique de insertRows() quand je drop et donc en aucun cas je récupère l'info sur le nom du champ de l'élément que je drag dans le modèle initial.. mais il semblerait que se soit le rôle du mimeData pour le coup!

    Mon explication serait donc peut être la suivante : mimeData et dropMimeData par défaut appel déjà bien insertRows si l'index du drop est pas occupé ou setData sinon avec l'échange d'un QString. Par contre, ce qui m'échappe c'est pourquoi le "value" en argument de setData() qui est appelé lors du drop sans redéfinition de mimData et dropMimeData est bien une QString et donc représentatif du format d'affichage plus que représentatif des données réelles du modèle ? (par données réelles, je veux dire qu'en membre j'ai une QList<Struct>.) Pourquoi value de setData n'est donc pas une Struct ?

    Merci pour ton aide encore

Discussions similaires

  1. Réponses: 6
    Dernier message: 25/11/2014, 11h36
  2. Glisser-déposer entre QWidget différents
    Par EvaBraun dans le forum Débuter
    Réponses: 1
    Dernier message: 24/11/2012, 10h59
  3. Glisser-déposer entre deux zones de listes
    Par Arkham46 dans le forum Contribuez
    Réponses: 2
    Dernier message: 13/04/2012, 12h20
  4. Glisser-déposer entre ListView
    Par Troudhyl dans le forum Qt Quick
    Réponses: 5
    Dernier message: 18/01/2012, 13h01
  5. Réponses: 5
    Dernier message: 25/03/2003, 19h43

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