Introduction to the Qt Model View Framework

I. Overview

Qt includes a set of item view classes that use the model/view framework to manage the relationships between data and the way that data is presented to the user.

Model-View-Controller (MVC) design pattern, commonly used when building user interfaces.

  • MVC consists of three kinds of objects.
  • Model is the application object
  • View is the screen display of Model
  • Controller defines how the user interface reacts to user input
  • MVC decouples user interface design to increase flexibility and reusability.

If view and controller (V & C) objects are combined, the result is a model/view framework. turn out:

  • This still separates how data is stored from how it is presented to the user, but provides a simpler framework with the same principles.
  • This separation makes it possible to display the same data in several different views. New view types can be customized to display data without changing the underlying data structure.

In order to flexibly handle user input, we introduce the concept of delegation. The advantage of using delegates in this framework is that it allows customization of the way data items are rendered and edited.

 

1.1. Model

All models are based on the QAbstractItemModel class. Views and delegates use this class' interface to access data.

The data itself does not have to be stored in the model, it can be kept in a data structure or repository provided by a separate class, file, database or some other application component.

QAbstractItemModel provides a data interface that is flexible enough to handle views that represent data in the form of tables, lists, and trees. However, when implementing new models for list (1 column n rows) and table-like (n rows m columns) data structures, the QAbstractListModel and QAbstractTableModel classes are better starting points because they provide appropriate default implementations of commonly used functions. Each of these classes can be subclassed to provide models that support special types of lists and tables.

Qt provides some ready-made models for working with data items:

  • QStringListModel is used to store a simple list of QString items.
  • QStandardItemModel manages more complex tree structures of items, each of which can contain arbitrary data.
  • QFileSystemModel provides information about files and directories in the local file system.
  • QSqlQueryModel, QSqlTableModel, and QSqlRelationalTableModel are used to access the database using the model/view approach.

If these standard models do not meet your requirements, you can subclass QAbstractItemModel, QAbstractListModel, QAbstractTableModel to create custom models.

1.2. View

Qt provides complete implementations for different types of views:

  • QListView displays a list of items
  • QTableView displays the data in the model in a table
  • QTreeView displays model data items in a hierarchical list.

These classes are based on the QAbstractItemView abstract base class. Although these classes are ready-made implementations, they can also be subclassed to provide custom views.

1.3. Entrustment

  • QAbstractItemDelegate is the abstract base class for delegation in the model/view framework.
  • The default delegate implementation is provided by QStyledItemDelegate, which is used as the default delegate by Qt's standard views.
  • QStyledItemDelegate and QItemDelegate are two separate alternatives for drawing and providing editors for items in a view.

The difference between them is that QStyledItemDelegate uses the current style to paint its items. Therefore it is recommended to use QStyledItemDelegate as the base class when implementing custom delegates.

1.4. Convenience

Many convenience classes are derived from the standard view classes, and subclassing them is not recommended.

Examples of such classes include QListWidget, QTreeWidget, QTableWidget.

These classes are less flexible than view classes and cannot be used with arbitrary models. It is recommended to use the model/view method first to process the data in the project view, and only use these classes when it is really necessary.

2. Examples of using models and views

Qt provides two standard models QStandardItemModel and QFileSystemModel.

  • QStandardItemModel is a multipurpose model that can be used to represent various data structures required by lists, tables, and tree views.
  • QFileSystemModel is a model that maintains information about the contents of directories. Therefore, it does not hold any data items itself, but simply represents files and directories on the local file system.

The QListView and QTreeView classes are the most suitable views for use with QFileSystemModel.

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QSplitter *splitter = new QSplitter;

QFileSystemModel *model = new QFileSystemModel;
model->setRootPath(QDir::currentPath());

QTreeView *tree = new QTreeView(splitter);
tree->setModel(model);
tree->setRootIndex(model->index(QDir::currentPath()));

QListView *list = new QListView(splitter);
list->setModel(model);
list->setRootIndex(model->index(QDir::currentPath()));

splitter->show();
return app.exec();
}

 

3. Model class

3.1. Basic concepts

In a model/view framework, the model provides a standard interface that views and delegates use to access data. The standard interface is defined by the QAbstractItemModel class.

Regardless of how data items are stored in any underlying data structure, all subclasses of QAbstractItemModel represent data as a hierarchical structure containing a table of items.

Views use this convention to access data items in the model, but they are not restricted in how they present this information to the user.

 

3.2. Model index

To ensure that the representation of data remains separate from how it is accessed, the concept of model indexes is introduced. Every piece of information that can be obtained through a model is represented by a model index. Views and delegates use these indexes to request data items to display.

Model index objects contain a pointer to the model that created them, which prevents confusion when dealing with multiple models.

QAbstractItemModel *model = index.model();

Model indexes provide temporary references to pieces of data that can be used to retrieve or modify data through the model. Since models may reorganize their internal structure from time to time, model indexes may become invalid and should not be stored.

If a long-term reference to a piece of information is required, a persistent model index must be created. This provides a reference to the latest information on the model.

Temporary model indexes are provided by the QModelIndex class, and persistent model indexes are provided by the QPersistentModelIndex class.

To obtain the model index corresponding to a data item, three attributes must be specified for the model: row number, column number, and the model index of the parent item.

3.3, row and column

In its most basic form, a model can be accessed as a simple table with items located by their row and column numbers. This does not mean that the underlying data is stored in an array structure, using row and column numbers is just a convention to allow components to communicate with each other. Information about any given item can be retrieved by specifying its row and column number to the model, and we receive an index representing that item:

QModelIndex index = model->index(row, column, ...);

 

The figure above shows a representation of a basic table model, where each item is located by a pair of row and column numbers. Obtain the model index of a referenced data item by passing the relevant row and column numbers to the model.

QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexB = model->index(1, 1, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());

A top-level item in a model is always referenced by specifying a QModelIndex() as its parent item.

The benefits of this article, free to receive Qt development learning materials package, technical video, including (C++ language foundation, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QT embedded development, Quick module, etc.) ↓↓↓↓↓↓See below↓↓Click on the bottom of the article to receive the fee↓↓

3.4, the parent item of the item

The above is an ideal situation. Structures such as tree views require the model to expose a more flexible interface to the items within it. That is, an item can also be the parent of another item list, just like a top-level item in a tree view can contain another item list.

When requesting an index for a model item, information about the item's parent must be provided. Outside of the model, the only way to refer to an item is through the model index (parent information is contained in the parent's reference):

QModelIndex index = model->index(row, column, parent);

 

The figure shows a representation of a tree model, where each item is referenced by a parent, row number, and column number.

Items "A" and "C" are represented as top-level siblings in the model:

QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());

Project "A" has many children. Use the following code to get the model index for item "B":

QModelIndex indexB = model->index(1, 0, indexA);

3.5. Project roles

Items in the model can play various roles for other components, allowing different types of data to be provided for different situations.

enum Qt::ItemDataRole: Each item in the model has a set of data elements associated with it, and each element has its own role. A view uses roles to indicate to the model what type of data it needs. Custom models should return these types of data.

  • Qt::DisplayRole: Key data to be presented in text form. (QString)
  • Qt::DecorationRole: Data presented as decoration in the form of icons. (QColor, QIcon, or QPixmap)
  • Qt::EditRole: Data in the form suitable for editing in the editor. (QString)
  • Qt::ToolTipRole: Data displayed in item tooltips. (QString)
  • Qt::StatusTipRole: The data displayed in the status bar. (QString)
  • Qt::WhatsThisRole: The data schema displayed for the "What's This?" item. (QString)
  • Qt::SizeHintRole: The size hint that will be given to the view's item. (QSize)
  • The standard roles defined in Qt::ItemDataRole cover the most common uses of item data. Additional roles can also be defined for application-specific purposes.

You can ask the model for the data of the item by passing the model index corresponding to the item to the model, and get the desired data type by specifying the role:

QVariant value = model->data(index, role);

3.6. Example of using model index

QFileSystemModel *model = new QFileSystemModel;
QFileSystemModel::connect(model, &QFileSystemModel::directoryLoaded, [model](const QString &directory)
{
QModelIndex parentIndex = model->index(directory);
int numRows = model->rowCount(parentIndex);
for (int row = 0; row < numRows; ++row)
{
QModelIndex index = model->index(row, 0, parentIndex);
QString text = model->data(index, Qt::DisplayRole).toString();
qDebug()<<text;
}
});
model->setRootPath(QDir::currentPath());

 

4. View class

In the model/view framework, the view fetches data items from the model and presents them to the user. The way the data is presented can be quite different from the underlying data structures used to store the data items.

The separation of content and presentation is achieved by using the standard model interface provided by QAbstractItemModel, the standard view interface provided by QAbstractItemView, and the use of model indexes that represent data items in a generic way.

  • Views generally manage the overall layout of the data obtained from the model. They can render individual data items themselves, or use delegates to handle rendering and editing functionality.
  • In addition to presenting data, views also handle operations such as navigation between items and item selection.
  • Views also implement basic user interface functionality, such as context menus and drag and drop.
  • A view can provide default editing tools for an item, or it can work with a delegate to provide a custom editor.
  • Views can be built without a model, but a model must be provided in order to display useful information.

4.1. Existing views

Qt provides three view classes that present data from models in a way most users are familiar with.

  • QListView can display items in a model as a simple list.
  • QTreeView displays the items in the model as a hierarchy of lists.
  • QTableView presents the items in the model in table form.

The default behavior of these standard views should be sufficient for most applications. They provide basic editing functionality and can be customized for a more specialized user interface.

4.2. Selection of Processing Items

The mechanism for handling selection of items in a view is provided by the QItemSelectionModel class. All standard views build their own selection model by default. The selection model used by the view can be obtained through the selectionModel() function, and the alternative selection model can be specified through setSelectionModel().

The ability to control the selection model used by a view is useful when it is desired to provide multiple consistent views on the same model data.

Although it is convenient for view classes to provide their own selection model by default, when using multiple views on the same model, it is often desirable that the model's data and the user's selections be displayed consistently across all views. Since view classes allow their internal selection model to be replaced, uniform selection across views can be achieved with the following line:

secondTableView->setSelectionModel(firstTableView->selectionModel());

 

5. Commissioned class

Unlike the Model-View-Controller pattern, Model/View design does not include a completely separate component for managing interaction with the user. Typically, a view is responsible for presenting model data to the user, as well as handling user input. To provide some flexibility in how this input is obtained, interactions are performed by delegates. These components provide input functionality and are also responsible for rendering individual items in some views. The standard interface for controlling delegation is defined in the QAbstractItemDelegate class.

Delegates render their content themselves by implementing the paint() and sizeHint() functions. Simple widget-based delegates can subclass QStyledItemDelegate instead of QAbstractItemDelegate and take advantage of the default implementations of these functions.

Delegated editors can be implemented by using widgets to manage the editing process or by handling events directly.

5.1. Using an existing delegate

The standard views provided by Qt use instances of QStyledItemDelegate to provide editing functionality. This default implementation of the delegate interface renders items in the usual style for standard views (QListView, QTableView, QTreeView).

The delegate used by the view is returned by the itemDelegate() function. The setItemDelegate() function allows standard views to install custom delegates.

5.2, a simple commission

Qt Model View Framework: custom delegates.

Six, the selection operation in the view

Information about items selected in a view is stored in instances of the QItemSelectionModel class. This maintains an item's model index within a single model and is independent of views.

Information about large selections of items can be efficiently maintained by recording only the start and end model indexes for each selected item range. A non-contiguous selection of items is constructed by using multiple selection ranges to describe the selection.

6.1, current item and selected item

In a view, there is always a current item and a selected item. An item can be the current item and be selected at the same time. The view is responsible for making sure that there is always a current item, such as pressing down the arrow keys for navigation requires the current item.

There can only be one current item. There can be multiple selections.

The current item is indicated by a focus rectangle. Selected items are indicated by a selection rectangle.

During a selection operation, a QItemSelectionModel object records the selection state of all items in the model. Once the selection model is set, a collection of items can be selected and deselected. Or their selection state can be toggled without knowing which items are already selected.

7. View Convenience Class

  • QListWidget provides a single-level list of items
  • QTreeWidget displays a multi-level tree structure
  • QTableWidget provides a table of cell items

Each class extends the QAbstractItemView class, which implements common behaviors for item selection and title management.

Eight, use drag and drop in the view

Qt model view framework: using drag and drop in views

9. Model subclassing reference

Model subclasses need to provide implementations of many of the virtual functions defined in the QAbstractItemModel base class. The amount of these features that need to be implemented depends on the type of model - whether it provides views with simple lists, tables or complex item hierarchies.

Models that inherit from QAbstractListModel and QAbstractTableModel can take advantage of the default implementations of the functions provided by these classes. Models that expose data items in a tree structure must provide implementations for a number of virtual functions in QAbstractItemModel.

9.1. Project data processing

Models can provide different levels of access to the data they provide: they can be simple read-only components, some models may support resizing operations, while others may allow editing of items.

9.2. Read-only access

To provide read-only access to data provided by a model, the following functions must be implemented in a subclass of the model:

  • flags(): Used by other components to get per-item information provided by the model.
  • data(): Used to provide item data to views and delegates. Typically, models only need to provide data for Qt::DisplayRole and any application-specific user roles, but it is also good practice to provide data for Qt::ToolTipRole, Qt::AccessibleTextRole, Qt::AccessibleDescriptionRole.
  • headerData(): Provides the view with information to display in its header.
  • rowCount(): Provides the number of data rows exposed by the model.

These four functions must be implemented in all types of models.

Additionally, the following functions must be implemented in direct subclasses of QAbstractTableModel and QAbstractItemModel:

  • columnCount(): Provides the number of data columns exposed by the model. List models do not provide this functionality, as it is already implemented in QAbstractListModel.

9.2. Editable items

The editable model allows data items to be modified and may also provide functionality that allows rows and columns to be inserted and deleted. To enable editing, the following functions must be properly implemented:

  • flags(): Must return the appropriate combination of flags for each item. In particular, the value returned by this function must also include Qt::ItemIsEditable.
  • setData(): Used to modify the data item associated with the specified model index. In order to be able to accept user input provided by user interface elements, this function must handle data related to Qt::EditRole. The implementation can also accept data associated with many different types of roles specified by Qt::ItemDataRole. When a data item is changed, the model must emit the dataChanged() signal to notify other components of the change.
  • setHeaderData(): Used to modify horizontal and vertical header information. When a data item is changed, the model must emit the headerDataChanged() signal to notify other components of the change.

9.3. Resizable models

All types of models can support row insertion and deletion. Table models and hierarchical models can also support column insertion and deletion. It is important to notify other components before and after a model size change. The following functions can be implemented to allow model resizing:

  • insertRows(): Used to add new rows and data items to all types of models. Implementations must call beginInsertRows(), followed immediately by endInsertRows(), before inserting new rows into any underlying data structures.
  • removeRows(): Used to remove rows and the data items they contain from all types of models. Implementations must call beginRemoveRows(), followed immediately by endRemoveRows(), before removing rows from any underlying data structures.
  • insertColumns(): Used to add new columns and data items to table models and hierarchical models. Implementations must call beginInsertColumns() followed immediately by endInsertColumns() before inserting new columns into any underlying data structure.
  • removeColumns(): Used to remove columns and the data items they contain from table models and hierarchical models. Implementations must call beginRemoveColumns() followed immediately by endRemoveColumns() before removing columns from any underlying data structures.

Typically, these functions should return true if the operation was successful. However, there may be cases where the operation is only partially successful; for example, if fewer than the specified number of rows could be inserted. In this case the model should return false.

Signals from functions called in the implementation of the resizing API to cause the add-on to take necessary actions before the data becomes unavailable. Encapsulation of insert and delete operations with start and end functions also enables the model to properly manage persistent model indexes.

Typically, the begin and end functions are capable of notifying other components about changes to the underlying structure of the model. However, this method has limitations. For example, if a model with 2 million rows needs to remove all odd rows, you can use beginRemoveRows() and endRemoveRows() 1 million times, but this is obviously inefficient.

For more complex changes to model structure, which may involve internal reorganization, data ordering, or any other structural change, avoiding inefficiencies requires the following:

  • Signal layoutAboutToBeChanged().
  • Updates internal data representing the structure of the model.
  • Use changePersistentIndexList() to update persistent indexes.
  • Emit the layoutChanged() signal.

This method can be used for any structure update.

9.4. Lazy filling of model data

Lazy population of model data effectively allows delaying requests for model information until the view actually needs it.

Some models require data to be fetched from remote sources, or must perform time-consuming operations to obtain information about how the data is organized. Since views typically request as much information as possible to accurately display model data, it can be useful to limit the amount of information returned to them to reduce unnecessary subsequent requests for data.

In a hierarchical model, finding the number of children of a given item is an expensive operation, and it is useful to ensure that the model's rowCount() implementation is called only when necessary. In this case, the QAbstractItemModel's hasChildren() function can be reimplemented to provide an inexpensive way for the view to check for the presence of children.

For example, a QTreeView doesn't need to know how many children it has if the parent doesn't expand to show them. Reimplementing hasChildren() to return true unconditionally is sometimes a useful approach if you know that many items will have children. This ensures that each item's children can be inspected later. The downside is that items with no children may display incorrectly in some views.

9.5. Indexed Parents and Children

Hierarchical models need to provide functions that views can call to navigate the tree structure they expose, and to obtain an item's model index.

Since the structure exposed to the view is determined by the underlying data structure, each model subclass creates its own model index by providing an implementation of the following functions:

  • index(): Given the model index of a parent item, this function allows views and delegates to access the item's children. If no valid child - corresponding to the specified row, column and parent model index is found, the function must return QModelIndex() (an invalid model index)
  • parent(): Provides the model index corresponding to the parent of any given child. If the specified model index corresponds to a top-level item in the model, or if there is no valid parent item in the model, the function must return QModelIndex() (an invalid model index)

Both functions above use the createIndex() factory function to generate indexes for use by other components. It is normal for the model to provide some unique identifier to this function, to ensure that the model index can later be reassociated with its corresponding item.

9.6. Drag and drop support and MIME type handling

The model/view classes support drag and drop, providing default behavior sufficient for many applications. It is also possible to customize how items are coded during drag and drop operations, whether they are copied or moved by default, and how they are inserted into existing models.

9.6.1. MIME data

By default, built-in models and views use an internal MIME type (application/x-qabstractitemmodeldatalist) to pass information about model indexes. This specifies the data for the list of items, containing the row and column numbers for each item, and information about the roles each item supports.

Data encoded using this MIME type can be obtained by calling QAbstractItemModel::mimeData() with a QModelIndexList containing the items to be serialized.

When implementing drag and drop support in custom models, it is possible to export data items in special formats by reimplementing the following functions:

  • mimeData(): This function can be reimplemented to return data in a format other than the default application/x-qabstractitemmodeldatalist internal MIME type.

9.6.2. Accept dragged-in data

When a drag-and-drop operation is performed on a view, the underlying model is queried to determine the types of operations it supports and the MIME types it can accept. This information is provided by the QAbstractItemModel::supportedDropActions() and QAbstractItemModel::mimeTypes() functions. The default implementation of QAbstractItemModel supports the copy operation and the default internal MIME type of the item.

When item data is dropped onto a view, the data is inserted into the current model using its implementation of QAbstractItemModel::dropMimeData(). The default implementation of this function is to attempt to insert the data item as a sibling of the item or as a child of the item.

In order to take advantage of QAbstractItemModel's default implementation of built-in MIME types, new models must provide reimplementations of the following functions:

  • insertRows()
  • insertColumns()
  • setData(): Allows filling new rows and columns with items
  • setItemData(): Added more efficient support for populating new items

To accept other forms of data, these functions must be reimplemented:

supportedDropActions():用于返回拖放动作的组合,表示模型接受的拖放操作类型。

mimeTypes(): Used to return a list of MIME types that the model can decode and handle. In general, the MIME types supported for input into a model are the same MIME types that can be used when encoding data for consumption by external components.

dropMimeData(): Performs the actual decoding of the data transferred via drag and drop, determining where it will be set in the model, and inserting new rows and columns if necessary.

9.6.3. Convenience view

Convenience views (QListWidget, QTableWidget, QTreeWidget) override the default drag and drop functionality to provide less flexible but more natural behavior. For example, since it is more common in a QTableWidget to put data into a cell, replacing the existing content with the data being transferred, the underlying model will set the target item's data rather than inserting new rows and columns into the model.

Ten, class list

  • QAbstractItemDelegate
  • QAbstractItemModel
  • QAbstractItemView
  • QAbstractListModel
  • QAbstractProxyModel
  • QAbstractTableModel
  • QColumnView
  • QConcatenateTablesProxyModel
  • QDataWidgetMapper
  • QFileSystemModel
  • QHeaderView
  • QIdentityProxyModel
  • QItemDelegate
  • QItemEditorCreator
  • QItemEditorCreatorBase
  • QItemEditorFactory
  • QItemSelection
  • QItemSelectionModel
  • QItemSelectionRange
  • QListView
  • QListWidget
  • QListWidgetItem
  • QModelIndex
  • QModelRoleData
  • QModelRoleDataSpan
  • QPersistentModelIndex
  • QSortFilterProxyModel
  • QStandardItem
  • QStandardItemEditorCreator
  • QStandardItemModel
  • QStringListModel
  • QStyledItemDelegate
  • QTableView
  • QTableWidget
  • QTableWidgetItem
  • QTableWidgetSelectionRange
  • QTreeView
  • QTreeWidget
  • QTreeWidgetItem
  • QTreeWidgetItemIterator

The benefits of this article, free to receive Qt development learning materials package, technical video, including (C++ language foundation, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QT embedded development, Quick module, etc.) ↓↓↓↓↓↓See below↓↓Click on the bottom of the article to receive the fee↓↓

Original link: https://blog.csdn.net/kenfan1647/article/details/119081770

Guess you like

Origin blog.csdn.net/hw5230/article/details/131987500