Utilisation des QTreeView
-
Bonjour à tous,
je galère a comprendre comment s'utilise les QTreeView et surtout comment faire le model associé.Je vous explique mon projet je souhaite faire une fenêtre qui doit être conçu un comme cette maquette (par soucis de confidentialité j'ai changé totalement mon architecture mais ça reste du même cru):
Je connais assez bien l'utilisation des QTableView, avec leur model et delegate mais c'est la première fois que je m'attaque au QtreeView.
Je souhaite que les Param 1 et 2 soient modifiables en tant que réel et le param 3 modifiable par le biais d'une combo Box.( donc utilisation d'un delegate j'imagine)
J’avoue me perdre sur comment créer mon model sachant qu'un "Elément" est propres à son "Etape" et qu'il ne peut pas y avoir de "sousEtape". Mais autant d'étape qu'on veut et autant d'Element par Etape qu'on veut également.
Ma logique de stockage des données et donc quelque chose comme ça :
typedef enum sens {HORIZONTAL, VERTICAL}enSens; typedef struct Element { double param1; double param2; sens param3; }stElement; typedef struct Etape { QString intitule; QVector<Element> lesElements; }stEtape; class MonModele : public QAbstractItemModel { private : QVector<Etape> mesEtapes; ... }
Mon problème est le suivant je ne comprend pas comment faire fonctionner ma structure de donnée avec cette histoire de "root.
par exemple j'ai vu que pour la méthode rowCount par exemple il fallait modifier la valeur en fonction de l'emplacement dans mon architecture( N'hésitez pas a me corrigé si je me trompe).
Voici en semi pseudo code se que je veux dire :int MonModele::rowCount(const QModelIndex &leParent) const { if ( leParent <correspond a l'etape n> ) return mesEtapes[n].lesElements.size(); else if (leParent <correspond a l'element root>) return mesEtapes.size(); else // si ca correspond a un "Element" return 1; }
dans mon stockage des données je n'ai pas vraiment de "root" du coup comment savoir si leParent est bien ce "root" en gros comment reconnaître qu'elle est l’élément actif.
LA methode rowCount n'est qu'un exemple mais en gros comment adapté mon modèle a mon architecture de donnée.
Je n'ai pas trouvé grand chose comme tutoriel qui n'utilise pas des classes pré-faites comme QStandardItem ou QFileSystemModel, ou alors avec des éléments qui peuvent s'imbriquer a l'infini.
Pour ma part les Etapes sont restreinte a un premier "niveau" et les "Element" au second niveau.Pourriez vous m'indiquer comment faire fonctionner tout cela au mieux?
Merci d'avance.
-
Salut,
Oui, QTreeView donne mal à la tête :)
Il faut absolument utiliser un arbre binaire pour les données, comme dans l'exemple: https://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.htmlVoici ce que j'utilise comme TreeItemModel:
TreeItem<DATA>* rootItem() const { return iTreeItem; } TreeItem<DATA>* itemAtIndex(const QModelIndex& parent) const { const TreeItem<DATA>* item=iTreeItem; if(parent.isValid()) item=static_cast<TreeItem<DATA>*>(parent.internalPointer()); return const_cast<TreeItem<DATA>*>(item); } int rowCount(const QModelIndex& parent) const { TreeItem<DATA>* item=itemAtIndex(parent); return item->childCount(); } QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const { TreeItem<DATA>* item=itemAtIndex(parent); if( (row<0) OR (row>=item->childCount()) ) return QModelIndex(); return createIndex(row,column,item->childAt(row)); } QModelIndex parent(const QModelIndex& index) const { if (!index.isValid()) return QModelIndex(); TreeItem<DATA>* childItem = static_cast<TreeItem<DATA>*>(index.internalPointer()); TreeItem<DATA>* parentItem =childItem->parent(); if (parentItem->parent() EQ NULL) return QModelIndex(); int row=parentItem->index(); return createIndex(row, 0,(void*)parentItem); } ... private: TreeItem<DATA>* iTreeItem; // arbre binaire
J'utilise un template pour TreeItem<DATA>, ainsi DATA peut être n'importe quel objet/structure.
Le TreeItemModel étant générique, j'ai juste à déclarer :typedef TreeItem<MaStruct> MonArbreDeDonnées; class MonTreeModel : public TreeItemModel<MaStruct> { ... }
Bon, je sais c'est pas simple, suis bien example Simple Tree Model, il faut absolument faire comme décrit, commence avec une structure de données simple.
Good luck ;) -
Tout d'abord merci d'avoir pris le temps de répondre, et de me montrer un exemple.
Oui au final c'est ce que j'ai fait.
J'ai fait un système d'arbre pour mes données.enum typeOfNode : char {ROOT,ETAPE,ELEMENT}; enum Direction :char{ HORIZONTAL,VERTICAL}; class Node { ///---------------------------------- /// Attributs ///---------------------------------- protected : Node* parent; public : QVector<Node*> child; ///---------------------------------- /// Methodes ///---------------------------------- public: Node(Node* leParent = nullptr); virtual ~Node(); virtual typeOfNode getType()const {return ROOT;} Node* getParent() {return parent;} virtual Node *GetChild(int row); int childCount()const ; int row()const ; }; class ElementNode : public Node { ///---------------------------------- /// Attributs ///---------------------------------- public : double param1; double param2; Direction param3; ///---------------------------------- /// Methodes ///---------------------------------- public : ElementNode(Node* p_parent=nullptr); virtual Node* GetChild(int row){Q_UNUSED(row);return nullptr;} virtual typeOfNode getType()const{return ELEMENT;} }; class EtapeNode : public Node { ///---------------------------------- /// Attributs ///---------------------------------- public : QString titre; ///---------------------------------- /// Methodes ///---------------------------------- public : EtapeNode (Node* p_parent =nullptr,QString label = "N.D."); virtual typeOfNode getType()const{return ETAPE;} };
class TreeModel : public QAbstractItemModel { ///---------------------------------- /// Attributs ///---------------------------------- protected : Node* root; ///---------------------------------- /// Methodes ///---------------------------------- public: explicit TreeModel(QObject *parent = nullptr); virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual int rowCount(const QModelIndex & parent) const; virtual int columnCount(const QModelIndex&)const; virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; virtual QModelIndex parent(const QModelIndex &child)const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole); bool AddEtape(QString Titre); bool AddElement(QModelIndex& etapeParente, QString Titre); protected : QModelIndex IndexForNode(Node *const node)const ; Node* NodeForIndex(QModelIndex index)const ; };
avec root qui stock les étapes et les étape qui stock les éléments.
Effectivement les QTreeView donne mal à la tête mais une fois qu'on a compris ça va. Dommage qu'il n'y ai pas plus de tutoriel généraliste sur le sujet.