Tuesday, January 20, 2015

PySide Tree Tutorial IB: Models and Views -- The mighty index

Part of a series on treebuilding in PySide: see Table of Contents

One useful innovation in the development of the model/view framework is the use of indexes. Indexes are unique objects created by the model, with one index created for each separate item of data to be displayed by the view (e.g., each box in Figure 2). They act as wrappers that contain useful information about each individual data item.

The view uses these indexes to specify the location of the item about which it is making a query. This is analogous to using an index to pull a value from a simple Python list. However, as illustrated in Figure 2, uniquely specifying the location of an item in a data model is a bit more complicated. For instance, in a table model, specifying an item's location requires two numbers (the row and column of the item), and in a tree model three parameters are required (the row, column, and the index of the parent).

Figure 2: Indexes in tables and trees
A. Table model (rows and columns) Visual representation of a table model in which the location of each item is fixed by a row and a column number. We obtain the model index that refers to a data item by passing the relevant row and column numbers (and  a third optional parent parameter) to the model's index() method:

    indexA = model.index(0, 0, QtCore.QModelIndex())
    indexB = model.index(1, 1, QtCore.QModelIndex())
    indexC = model.index(2, 1, QtCore.QModelIndex())

Note that every item in the model (e.g., items A, B, and C) has the same parent. In general, the top level items in a model always have the invalid index QModelIndex() as their parent (see Part III). Since this is the default parent, for table models it is not required to send it explicitly.

B. Tree model (rows, columns, and parents) Visual representation of a tree model in which each item's location is fixed via a row number, a column number, and the index of its parent. Items A and C are top-level items in the model. Their indexes are obtained with:

    indexA = model.index(0, 0, QtCore.QModelIndex())
    indexC = model.index(2, 1, QtCore.QModelIndex())

Item A has multiple children, including item B. The index for item B is obtained with:

    indexB = model.index(1, 0, indexA)

We see that specifying the position of an item in a tree requires we indicate its parent index in addition to its row and column position.

Figure source:

As we will see in Part III, model indexes are much more than bare coordinates. Each index includes multiple methods that supply additional useful information about the corresponding item in the model. For instance, index.internalPointer() returns the entire data item associated with index.

In general, indexes are one of the main mechanisms used to insulate views from raw data: they provide a universal format for the transmission of queries from the view to the model, and the view doesn't have to worry about the details of how the indexes were created.

We have purposely left open the question of how models interact with the data store. This is because data is messy, with no single universal format: you may end up using data from a SQL database, a text file, or even a simple Python list. If your data is in a SQL database and you want to display it as a table, then you will implement a QSqlTableModel. However, you will take a very different strategy if you want your data stored and displayed as a tree. In this case, the case dealt with in simpletreemodel, you will often subclass QAbstractItemModel (though see post IIC and part V).

Before this discussion gets too abstract to be helpful, let's take a break, and start looking at simpletreemodel in Part II.

No comments: