Internal representation

In the previous part of the article I defined the structure and set of attributes that the model encapsulates. Now it is time to make it possible to actually store the data in a tree-like data structure.

There are two ways to do this – the hard way and the easy way. The hard way is to implement all the data structures from scratch, whereas the easy was is to subclass QStandardItemModel and use its way of storing the data. In out situation the latter should work (it works for every model which consists of data which can be stored in QVariant), but this wouldn’t make us learn much, so continue the hard way – by creating custom data structures and custom methods to operate on them.

Operating on a tree

First thing is to create all the infrastructure needed to operate on the structure and hierarchy. As we want a tree hierarchy, each node needs to hold a list of its children and a pointer to a parent node and some methods to manipulate them.

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

It is also nice to be able to grab a particular child and to know, how many older siblings you have (so that later on it is easy to provide proper row numbers for indexes).

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

Finally, it is good to be able to create items at all and to simplify, we’ll provide a way to create the tree in the same time.

QwwGLModelNode(QwwGLModelNode *p=0);           // create item

GLModel specific data

After that we can add all the things we want explicitely for our model data – item type and its attributes identified by names. Also getter and setter methods need to be implemented.

QwwGLModelType _type;                              // item type
QMap _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 &attributes() const;           // get attribute list

Implementation of above mentioned methods is trivial, so I won’t comment them.

QwwGLModelNode class

As a result we obtain a class like this one:

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& 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 &attributes() const {
        return _attributes;
    }
private:
    QwwGLModelNode *_parent;
    QList _children;
    QwwGLModelType _type;
    QMap _attributes;
};

Item types

Now it is time to decide how to actually create different items. There are two approaches to choose from. First one is to provide methods to create different item types and using existing methods fill all the needed data. Another approach is to subclass the node to create as many child classes as many item types there are to be used. A quicker method is to do it without subclassing, but virtual methods might prove to be very helpful later, depending on what you want to do with the model. But as this is only an example, let’s avoid spawning too many classes.

Testing

Now you should be able to compile and test the class. A simple main() should suffice.

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;
}

If all goes well, the code should compile and run quietly.

Next time I will describe how to implement the reading part of the model itself. At that point we’ll be able to see the model in action.

Leave a Reply