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.