13.4.  Tree Models

[ fromfile: treemodels.xml id: treemodels ]

To display trees of data in a QTreeView (parents and children), you have a few options:

  1. QAbstractItemModel is a general-purpose abstract model used with QTreeView, QListView, or QTableView.

  2. QStandardItemModel, used in Example 13.2 is a concrete class that can store QStandardItems, making it convenient to populate a concrete model with tree nodes.

  3. QTreeWidgetItem is not a model class, but can build trees in a QTreeWidget, derived from QTreeView.

The QStandardItemModel and QTreeWidgetItem classes are tree nodes that can be instantiated or extended. The individual objects connect together in a tree-like fashion, similar to QObject children (Section 8.2) or QDomNodes (Section 15.3). In fact, these classes are implementations of the Composite pattern.

Figure 13.12 is a screenshot of the next example, which shows the objects currently in memory that compose the user interface of the application.

Figure 13.12.  ObjectBrowser Tree

ObjectBrowser Tree

The class definition for this application is shown in Example 13.21. ObjectBrowserModel is a concrete tree model extended from QAbstractItemModel. It implements all the necessary methods to provide a read-only object browser tree.

Example 13.21. src/modelview/objectbrowser/ObjectBrowserModel.h

[ . . . . ]
#include <QAbstractItemModel>
class ObjectBrowserModel :public QAbstractItemModel {
 public:
    explicit ObjectBrowserModel (QObject* rootObject);
    int columnCount ( const QModelIndex& parent = QModelIndex() ) const;
    int rowCount ( const QModelIndex& parent = QModelIndex() ) const;
    QVariant data ( const QModelIndex& index, 
                    int role = Qt::DisplayRole ) const;
    QVariant headerData(int section, Qt::Orientation, 
                        int role = Qt::DisplayRole) const;
    QModelIndex index ( int row, int column, 
                        const QModelIndex& parent = QModelIndex()) const;
    QModelIndex parent ( const QModelIndex& index ) const;

 protected:
    QList<QObject*> children( QObject* parent ) const;
    QString label( const QObject* widget, int column ) const;
    QObject* qObject( const QModelIndex& ) const;
 private:
    QObject *rootItem;
};
[ . . . . ]

To enable tree views to navigate up and down the parent-child hierarchy of a QAbstractItemModel, you need to implement two methods that were not needed for table views: index() and parent(). Example 13.22 shows their implementation.

Example 13.22. src/modelview/objectbrowser/ObjectBrowserModel.cpp

[ . . . . ]

QModelIndex ObjectBrowserModel::
index(int row, int col, const QModelIndex& parent) const {
    if ((row < 0) || (col < 0) || row >= rowCount() || 
        col >= columnCount()) return QModelIndex();
    return createIndex( row, col, qObject(parent) ); 1
}

QModelIndex ObjectBrowserModel::parent( const QModelIndex& index ) const {
    if (!index.isValid()) return QModelIndex();
    QObject* obj = qObject(index)->parent();         2
    if ( obj == 0 )
        return QModelIndex();

    QObject* parent = obj->parent();
    int row = children( parent ).indexOf( obj );
    return createIndex( row, 0, parent );
}

QObject* ObjectBrowserModel::
qObject(const QModelIndex& index) const {            3
    if ( index.isValid() ) {
        QObject* parent = reinterpret_cast<QObject*>( index.internalPointer() );
        return children(parent)[index.row()];        4
    }
    return 0;                                        5
}

1

Store an internalPointer in the index.

2

qObject() returns the row child of this index but you want this index's parent QObject pointer which is stored in index.internalPointer().

3

My index's internalPointer is my parent QObject pointer. I am the row() child of my parent.

4

This is me!

5

This is the root.


index() can also be thought of as "childIndex", as it is used to find QModelIndex of children in the tree, while parent() calculates one step in the other direction. It is not important to implement these methods in table models that are only used with table views, but index() and parent() are necessary if you ever want to view the model with a tree view.