Now we get to the guts of the API, and what really separates our model from a table model. If we were just building a table model, subclassing QAbstractTableModel, our model would be done. Because we are subclassing QAbstractItemModel, we must provide two additional methods: index() and parent(). The view needs these methods to navigate among items in the tree.
We can view index() and parent() as inverse methods; parent() takes in a child index and returns the index of its parent, while index() takes in a parent index and returns the index of one of its children (Figure 7A). We implement both methods as outlined in Figure 7B, which you might want to study before looking at the details of the code: it is sometimes easy to lose the forest for the trees with these functions.
|
Figure 7: Implementing parent() and index() A. The basic logic of parent() and index(). TreeModel.parent() takes in the index of a child and returns the index of its parent, while TreeModel.index() takes in a parent index and returns the index of one of its children. B. Details about how parent() and index() are implemented. The flow of index() is counterclockwise (red arrows), and parent() is clockwise (green arrows). In each case, the given index's associated TreeItem is retrieved using internalPointer(). Then, its parent (child) item is accessed using the child() (parent()) method that was built into TreeItem. Finally, that item's index is created using createIndex(), which takes this item as one of its parameters. Figure 7B adapted from: http://qt-project.org/doc/qt-4.8/itemviews-editabletreemodel.html |
We'll start by looking at the implementation of index().
index(row, column, parent)
This method takes in the index of a parent item, and returns the index of one of its children. We implement it with:
def index(self, row, column, parent): if not self.hasIndex(row, column, parent): return QtCore.QModelIndex() if not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() #returns item, given index childItem = parentItem.child(row) #the actual item we care about if childItem: return self.createIndex(row, column, childItem) else: return QtCore.QModelIndex()
If hasIndex() returns False, then the coordinates submitted by the view are not valid, and we return the invalid index. Otherwise, we retrieve the parentItem that corresponds to parent, and then extract that item's desired child using TreeItem.child(row).
Once we have extracted the appropriate child item from its parent, we wrap it up into an index using createIndex(row, column, childItem). This is the built-in method that all models use to create new indexes. It requires that we specify the item's row number and column number, as well the TreeItem to which the resulting index will refer--this is the item that its internalPointer() will return.
Recall that each element in TreeItem.itemData corresponds to a different column in a row of our model (Figure 4, post IIB). Hence, in general there is a many-to-one relationship (in this case a 2:1 relationship) between model indexes and TreeItems. Given N rows in our tree,then 2N calls to index() would be required to specify all the indexes.
parent(index)
As discussed above, this method takes in an item's index, and returns the index of its parent:
def parent(self, index): if not index.isValid(): return QtCore.QModelIndex() childItem = index.internalPointer() parentItem = childItem.parent() if parentItem == self.rootItem: return QtCore.QModelIndex() return self.createIndex(parentItem.row(), 0, parentItem)
For all lower-level items, we create the parent index with createIndex(). This is a little more subtle than in the index() method: we must specify the row and column numbers of the parentItem relative to its parent (i.e, the grandparent of childItem). To find the row that parentItem occupies among its siblings, we use TreeItem.row(). For the column value, we follow the convention that only items in the first column of our model have children (that is, we set the column parameter for createIndex() to zero).
Conclusion
We are pretty much done going over the code. While we will briefly discuss setupModelData() in the next post, we are done going over the API provided by the model for the view. Once the tree structure and model are built, then the model is ready to be connected to a view. This is easily done with QTreeView.setModel(TreeModel). When we call show() on the view, the GUI should appear on our screen as in Figure 3 (post IIA).
No comments:
Post a Comment