CopperSpice API  1.9.1
Model/View Architecture

CopperSpice contains view classes which use a model/view architecture to manage the relationship between data and the way it is graphically presented to the user. The ideas introduced by using this approach provide a better mechanism for displaying data and processing user edits.

  • Introduction

  • Model/View Design
  • Built-In View Classes
    • Existing Models
    • Existing View Classes
    • Using a Predefined View
    • Rows and Columns in a Model
    • Trees in a Model
    • Model Item Roles
    • Using a QFileSystemModel

  • Delegate Classes
    • Delegate Classes
    • Built In Delegates
    • Custom Delegate
    • Editing in a Custom Delegate
    • Handling Data in a Custom Delegate

  • Proxy Model
    • Using a Proxy Model
    • Customizing Proxy Models

  • Making Selections in a Model
    • Item Selection
    • Sharing Selections between Views
    • Custom Selection Models
    • Current Item vs Selected Items
    • Using a Selection Model
    • Selecting Items
    • Reading the Selection State
    • Updating a Selection
    • Row and Column Selections
    • Select all items in a Model

  • Custom Models
    • Inheriting from a Model
    • Headers and Data
    • Inserting and Removing Rows
    • Read Only Model
    • Editing Data in a Model

  • Drag and Drop
    • Using Drag and Drop in Item Views
    • Enabling Drag and Drop
    • Accepting Dropped Items
    • Custom Model to handle Dropped Data
    • Encoding Exported Data
    • Decoding Imported Data

  • Model/View Examples
    • Multiple Views with a Single Model
    • Updating the Editor Widget Geometry
    • Drag and Drop in a View
    • Read Only Model with Other Roles
    • Current Time Displayed in a Table Cell
    • Setting up Headers for Columns and Rows
    • Editing Data in a Table
    • Editing Data in a TreeView
    • Working with Selections

  • Widget Classes
    • List Widgets
    • Tree Widgets
    • Table Widgets
    • Hidden items
    • Selections
    • Searching
    • Using Widget Classes

  • Other Model/View Topics
    • Parents and Children
    • Parent Items
    • Read Only Access to a Model
    • Editing Items
    • Resizing Models
    • Lazy population of Model Data
    • Performance optimization for large Data sets

Introduction

UI developers should be familiar with the basic usage of a List, Table, Tree components which are frequently used in GUI applications.

There are two different ways how these widgets can access their data. The traditional way involves widgets which include internal containers for storing data. This approach is intuitive, however, in many non-trivial applications, it leads to data synchronization issues.

The second approach is model/view programming, in which widgets do not maintain internal data containers. They access external data through a standardized interface and therefore avoid data duplication. This may seem complicated at first, but once you take a closer look, it is not only easy to grasp, but the many benefits of model/view programming also become clearer.

Model/View is a technology used to separate data from the view. The visual appearance of the widget classes and the view classes look similar, however they interact with data very differently.

Widget classes which use data that is contained inside the widget
View classes use data stored in a model

Architecture

A design pattern is general solution to a commonly occurring problem in software design. It is not considered a final or complete solution. It is a narrative for a standardized way to solve a given set of problems. For more information about this subject the book "Design Patterns - Elements of Reusable Object-Oriented Software", is considered the authority.

One of the design patterns described in this book is the Model-View-Controller (MVC) architecture which can be used when developing GUI software. The general idea centers around structuring your application using three kinds of objects.

  • The Model contains all the data
  • The View displays the data on the screen
  • The Controller handles user input

If the view and the controller objects are combined, as they are in CopperSpice, the result is a model/view architecture. Separating the data from the view makes it easier to display the same data set in multiple views at the same time. The model/view approach also provides a way to implement new types of views, without changing the underlying data structures.

CopperSpice has delegate classes which can be used to override the way user input is processed or how the view will display certain data.

In the model/view architecture the model will communicate with a data source and provide data to the given view.

The view retrieves a model index from the model to access a specific piece of data.

A delegate can be used to change the appearance of the data displayed in a particular view.

Generally, the model/view classes can be separated into the three groups described above: models, views, and delegates. Each of these components is defined by abstract classes which provide common interfaces and in some cases, default implementations. Abstract classes are meant to be subclassed in order to provide the full set of functionality expected by other components. This also allows specialized components to be written.

Models, views, and delegates communicate with each other using signals and slots.

  • Signals from the model inform the view about changes to the data held by the data source.
  • Signals from the view provide information about the user's interaction with the items being displayed.
  • Signals from the delegate are used during editing to tell the model and view about the state of the editor.

Widget Classes vs View Classes

CopperSpice has three convenience classes which are derived from the view classes. These classes are available for the benefit of applications which rely on the older item-based technology. They are not intended to be subclassed and have less flexibility than using the model/view classes. Item-based classes can not be used with a predefined model or a user defined model.

Developers are encouraged to use the model/view classes shown below instead of the item-based classes listed above. Using one of the following model/view classes with the QStandardItemModel is the simplest way to transition to a model/view design.

The following table shows an overview of the widget classes and the model/view classes.

Sample Image Widget Classes Model/View Classes
QListWidgetQListView
QTableWidgetQTableView
QTreeWidgetQTreeView

Models

Every item model inherits from the QAbstractItemModel class. This class defines an interface which is used by views and delegates to access data. The data itself does not have to be stored in the model, it can be held in a data structure or repository provided by a separate class, a file, a database, or some other application component. The QAbstractItemModel class is usually flexible enough to be used by most views which represent data in the form of list, table, or tree.

Inheriting from QAbstractListModel or QAbstractTableModel should be considered if the data is a list or a table. These classes may provide a better starting point because they provide appropriate default implementations.

This is a list of the built in CopperSpice models which can be used directly or as the base class for a custom model.

QAbstractItemModel
Base class for all other models
QAbstractListModel
Base class for custom list models
QAbstractTableModel
Base class for custom table models
QAbstractProxyModel
Base class for custom proxy models
QStringListModel
Stores a list of QStrings
QStandardItemModel
Stores tree structures of items, each of which can contain arbitrary data
QDirModel
Not recommended, use QFileSystemModel instead
QFileSystemModel
Provides information about files and directories in the local file system
QSqlQueryModel
Retrieves data from an SQL result set
QSqlTableModel
Retrieves data from an SQL table
QSqlRelationalTableModel
Retrieves data from an SQL table with foreign keys
QSortFilterProxyModel
Sorts and/or filters another model

If none of these models shown above satisfy your application requirements, inherit from one of the abstract classes QAbstractItemModel, QAbstractListModel, or QAbstractTableModel to create your own custom models. This is discussed in more detail in the section about Custom Models.

The Model/view design eliminates the data consistency problems that may occur with standard widgets. This approach also makes it easier to display the same data in multiple ways, because one model can be used with different views at the same time. The most important difference is that view classes do not store data. Since view classes do not know your data's structure, you may need to provide a wrapper to make your data conform to the QAbstractItemModel interface.

Indexes

Views and delegates use a model index to access the data stored within a model. As a result, only the model needs to know how to retrieve the data. As data is added and deleted the model may reorganize the internal data structures and the model indexes may become invalid or modified. You should not store a model index or use it after modifying the model data.

If a long term reference to a piece of data is required, a persistent model index must be created. Temporary model indexes are provided by the QModelIndex class and persistent model indexes are provided by the QPersistentModelIndex class.

To obtain a model index corresponding to some data, three properties must be specified to the model. The model index of the parent is only required when the model is a tree.

  • row number
  • column number
  • model index of a parent item

A model will notify any attached views about changes to data by emitting various signals. For example, QAbstractItemModel::rowsRemoved() is emitted after a row or groups of rows has been removed from the model.

Delegates

QAbstractItemDelegate is the abstract base class for delegates in the model/view architecture. The default delegate implementation is provided by QStyledItemDelegate, and this is used as the default delegate by the CopperSpice standard views. However, QStyledItemDelegate and QItemDelegate are independent alternatives to painting and providing editors for items in views. The difference between them is that QStyledItemDelegate uses the current style to paint its items. We therefore recommend using QStyledItemDelegate as the base class when implementing custom delegates or when working with CopperSpice style sheets.

Delegates are described in the section Delegate Classes.

Sorting

There are two ways of implementing sorting in the model/view architecture.

(1) One way is to provide a method which overrides the QAbstractItemModel::sort() method.

(2) Another way is to use a proxy model to transform the structure of your model before presenting the data in the view. This is covered in detail in the section on Proxy Model.

With either approach, methods like QTableView::sortByColumn() and QTreeView::sortByColumn() can be used to define the sort order

To allow the user to set the sort order by clicking on a column header, connect the QHeaderView::sortIndicatorChanged() signal to the QTableView::sortByColumn() slot or the QTreeView::sortByColumn() slot, respectively.

Summary

  • A model index provides views and delegates information about the location of items which is not associated with the underlying data structures.
  • Items are accessed by their row and column numbers and the model index of their parent item.
  • Model indexes are constructed at the request of other components such as views and delegates.
  • The enum Qt::ItemDataRole is used to indicate which kind of data a view is requesting from a model.