2.1.2 Model Pipe

Motivation

The Model Pipe is a variation similar in concept to a UNIX pipe: it intercepts and manipulates the data flowing from Model to View. Like in a UNIX pipe, Model Pipes with different manipulation logic can be chained together to perform sequential alteration of data. Typical application of Model Pipe are for filtering and sorting.

Design

The Model Pipe encapsulates data transformation logic in a dedicated, reusable class. Different Pipe classes can be created, each with specific capabilities.

To be compatible with the View, a Model Pipe implements the same interface of the SubModel, eventually extending it to provide access to additional state it might contain. For example, a filtering Model Pipe will host data about the current filter string. Controllers acting on the Model Pipe act on this state.

The Model Pipe is a listener of the SubModel and forwards its events. In addition, it performs notification when its internal state changes in a way that modifies the resulting data.

while the manipulation of the SubModel’s data is performed directly on the SubModel itself.

Practical Example: Qt QSortFilterProxyModel

Qt provides a Pipe Model with sorting and filtering functionality: QSortFilterProxyModel. It is worth noting that Qt calls this Model “Proxy Model”, but in the context of this book, Proxy Model refers to a different Model design.

The following code sets up a Pipe Model connection between MyModel (our implementation of QAbstractItemModel) and a Tree View. The Tree View observes the Pipe Model, which in turns uses MyModel as a data source.

tree_view = QTreeView()
model = MyModel()
pipe_model = QSortFilterProxyModel()
pipe_model.setSourceModel(model)
tree_view.setModel(pipe_model)

The Model Pipe provides an appropriate interface to configure the filter, such as setFilterRegExp, and mapping routines mapToSource() and mapFromSource() to convert between index of an Item in the Model and index of that Item in the View.

Note how the tree_view.setModel() accepts a QAbstractItemModel, which is implemented by both MyModel and by QSortFilterProxyModel. As explained earlier, this design allows the View to accept either Model, and remain unaware of the existence of the pipeline.