I. L'article original

Cet article est la traduction de l'article Internal representation de Witold Wysota.

II. Travailler sur un arbre

La première chose à faire est de créer l'infrastructure nécessaire pour opérer sur la structure et sa hiérarchie. Comme on veut une structure arborescente, chaque nœud doit stocker une liste de ses enfants et un pointeur au nœud parent, ainsi que quelques méthodes.

 
Sélectionnez
QwwGLModelNode *_parent;                       // parent item
QList<QwwGLModelNode> _children;               // child items
void setParent(QwwGLModelNode*);               // set as parent
QwwGLModelNode* parent() const;                // get the parent
const QList<QwwGLModelNode>& children() const; // get list of children
int addChild(QwwGLModelNode*);                 // add a child

Il est aussi pratique de récupérer un enfant particulier et de savoir combien d'ancêtres il a (il sera donc plus facile, a posteriori, de fournir le nombre de lignes pour les index).

 
Sélectionnez
QwwGLModelNode* child(uint n);                 // return a child
uint row();                                    // return row number

Finalement, il est bon d'être à même de créer des items et, pour simplifier, on va fournir une manière de créer l'arbre en même temps.

 
Sélectionnez
QwwGLModelNode(QwwGLModelNode *p=0);           // create item

III. Les données spécifiques à GLModel

Après ça, on peut ajouter toutes les choses que l'on veut explicitement pour le modèle de données - le type des items et ses attributs, identifiés par des noms. Aussi, des accesseurs en écriture et en lecture doivent être implémentés.

 
Sélectionnez
QwwGLModelType _type;                              // item type
QMap<QString> _attributes;                         // attribute list
QwwGLModelType type() const;                       // get the type
void setType(QwwGLModelType);                      // set a type
void setAttribute(const QString &name,             // set an attribute
                  const QVariant &value);
QVariant attribute(const QString &name);           // get a single attribute
const QMap<QString> &attributes() const;           // get attribute list

L'implémentation de ces méthodes est triviale et ne sera donc pas commentée.

IV. La classe QwwGLModelNode

Au final, on obtient une classe du genre :

 
Sélectionnez
class QwwGLModelNode {
public:
    QwwGLModelNode(QwwGLModelNode *p=0) {
        setParent(p);
        if(p)
            _parent->addChild(this);
    }
    ~QwwGLModelNode(){
        qDeleteAll(_children);          // delete all child items
    }
    QwwGLModelNode *child(uint n) {
        return _children.at(n);
    }
    uint row(){
        Q_ASSERT(_parent);              // make sure we have a parent
        return _parent->children().indexOf(this);
    }
    void setParent(QwwGLModelNode *p) {
        _parent = p;
    }
    QwwGLModelNode* parent() const {
        return _parent;
    }
    const QList<QwwGLModelNode*>& children() const {
        return _children;
    }
    int addChild(QwwGLModelNode *ch) {
        _children << ch;
        return _children.size()-1;
    }
    QwwGLModelType type() const {
        return _type;
    }
    void setType(QwwGLModelType t) {
        _type = t;
    }
    void setAttribute(const QString &name, const QVariant &value) {
        _attributes[name] = value;
    }
    QVariant attribute(const QString &name) {
        if(!_attributes.contains(name)) // if attribute doesn't exist
            return QVariant();          // return an empty value
        return _attributes[name];
    }
    const QMap<QString> &attributes() const {
        return _attributes;
    }
private:
    QwwGLModelNode *_parent;
    QList<QwwGLModelNode*> _children;
    QwwGLModelType _type;
    QMap<QString> _attributes;
};

V. Types d'items

Maintenant, il faut décider comment créer les différents items. Il y a deux approches possibles. La première est de fournir des méthodes pour créer les différents types d'item et, en utilisant les méthodes existantes, remplir les données requises. L'autre est de dériver le nœud pour créer autant de classes enfant que de types d'item à utiliser. Une méthode plus rapide se passe de cette dérivation, mais les méthodes virtuelles pourraient se révéler très utiles plus tard, en fonction de ce qu'on veut faire avec le modèle. Comme ceci n'est qu'un exemple, évitons de créer trop de classes.

VI. Tests

Maintenant, on devrait pouvoir compiler et tester la classe. Un simple main() devrait faire l'affaire.

 
Sélectionnez
typedef int QwwGLModelType;
#include "qwwglmodelnode.h"
int main(){
    QwwGLModelNode *n = new QwwGLModelNode();
    for(int i=0;i<10;i++){
      QwwGLModelNode *cn = new QwwGLModelNode(n);
      for(int i=0;i<3;i++){
        new QwwGLModelNode(cn);
      }
  }
    delete n;
    return 0;
}

Si tout se passe bien, le code devrait compiler et se lancer sans de souci.

La prochaine fois, on décrira la manière d'implémenter la partie de lecture du modèle lui-même. À ce point, on pourra le voir en action.

VII. Remerciements

Merci à Louis du Verdier et à Claude Leloup pour leur relecture !