Comprehension of Qt Model-View architecture

1. Architecture selection

1.1 The difference between controls

Mode/View is a technique for separating data and view in controls that work with datasets. Standard controls are not designed to separate data from view, which is why Qt has two different types of controls. Both types of controls look the same, but they interact with data differently.
Standard controls use data that is part of the controlinsert image description here

The view (View) class operates on external data (model) insert image description here
comparisons:

  • 1. Standard form control: A form control is a two-dimensional array of data elements that the user can change. The grid control can be integrated into the program flow by reading and writing the data elements provided by the grid control. This approach is very intuitive and useful in many applications, but displaying and editing database tables using standard table controls can be problematic. Two copies of the data must be coordinated: one outside the control; one inside the control. The developer is responsible for synchronizing the two versions. Besides that, the tight coupling of presentation and data makes it harder to write unit tests.
  • 2. Mode/View provides a solution using a more general architecture. Mode/View eliminates data consistency problems that may arise with standard controls. Mode/View also makes it easier to use multiple views of the same data, since a model can be passed to multiple views. The most important difference is that Mode/View controls do not store data behind table cells. In effect, they operate directly from your data. Because the view class doesn't know the structure of the data, it needs to provide a wrapper so that the data conforms to the QAbstractItemModel interface. Views use this interface to read and write data. Any instance of a class that implements QAbstractItemModel is called a model. Once the view receives a pointer to the model, it reads and displays its content and becomes its editor.

1.2 Whether to entrust

The model-view architecture diagram is shown below. The model-view architecture is derived from the MVC pattern: the model (Model) is an application object that represents data; the view (View) is the user interface of the model to display data; the control (Controller) defines the user interface pair How to react to user input. The model view architecture has 2 changes relative to the MVC pattern:

  • 1. Combining the two components of view and control, while separating data storage from data visualization, it provides a relatively simple framework;
  • 2. A delegate (Delegate, proxy) is introduced to flexibly process user input and customize the way data is rendered and edited.
    insert image description here
    Due to the introduction of delegation, when using the model view architecture, there is a problem with the selection strategy for delegation.
    The visualization of a data item in a model is determined in part by its item role, which is somewhat limited, as shown below.

insert image description here
For data items that require visual presentation other than simple text, icons, etc. and require more complex interactions with the user, use the delegate method, such as customizing the ready-made components (QLineEdit, QSpinBox, QComboBox, QProgressBar, QPixmap, QDateTimeEdit, QPushButton wait). The entrusted base classes include QAbstractItemDelegate, QItemDelegate, and QStyledItemDelegate in turn.

Inheriting QAbstractItemDelegate needs to implement paint() and other methods to realize custom rendering (you can also create delegate controls), pay attention to the implementation of paint() method: data can have another data class, as a member of the delegate class, the data class implements paint() method , but call its data members to execute paint() in the delegate class. In addition, there are many parameters of QStyleOptionViewItem and QModelIndex in its many virtual methods, which are convenient for customizing the rendering style.

Compared with QItemDelegate, QStyledItemDelegate is the default delegate implementation and is more officially recommended by Qt (it uses the current style to draw items, and it is strongly recommended to use it when implementing a custom delegate or applying it together with a Qt style sheet). The two virtual methods that need to be implemented are: createEditor, setModelData, setEditorData, updateEditorGeometry. It should be noted that after the completion of setModelData, the closeEditor signal is emitted to notify the view to ensure that the editor component is closed and destroyed. After the editor is edited, the commitData signal is emitted and submitted to the model (the closeEditor signal also needs to be emitted).

In the official examples provided by Qt, it is strongly recommended to browse and learn :

  • 1. Spin Box Delegate Example and Star Delegate Example with QStyledItemDelegate as the base class;
  • 2. Pixelator Example with QAbstractItemDelegate as the base class.

1.3 Relationship between model/view architecture and graphics view framework

The graph view framework provides a graph view programming method based on graph items. It consists of three parts: scene (QGraphicsScene), view (QGraphicsView), and graphic item (QGraphicsItem). The view (QGraphicsView) corresponds to the view (View), the scene (QGraphicsScene) basically corresponds to the model (Model), and the graphic item (QGraphicsItem) is related to the delegate. Classes are similar in that they interact and are also responsible for their own rendering (methods such as paint).

Since QCharts has an inheritance relationship with the graphical view framework, in fact, QCharts is also a model/view architecture.

2. Concrete implementation

2.1 Model selection strategy

insert image description here

2.1.1QAbstractListModel

When we need to use QListView to display data and cooperate with a custom model, we inherit from this class. For 1D data models only.

When subclassing, we just need to implement the rowCount() function to return the number of items in the list and the data() function to retrieve items from the list.
Since the model represents a one-dimensional structure, the rowCount() function returns the total number of items in the model. Well-behaved models also provide a HeaderData() implementation. For editable list models, you must also provide an implementation of setData(), and implement the flags() function so that it returns a value containing Qt::ItemIsEditable. A model that provides an interface to a scalable list data structure may provide implementations of insertRows() and removeRows(). When implementing these features, it is important to call the appropriate function so that all connected views are aware of any changes:

	insertRows()实现必须在将新行插入数据结构之前调用beginInsertRows(),并且之后必须立即调用endInsertRows()。
	removeRows()实现必须在将行从数据结构中删除之前调用beginRemoveRows(),并且必须立即调用endRemoveRows()。

Qt officially provides a derived class QStringListModel, which stores a simple list of QString items.

2.1.2QAbstractTableModel

When we need to use QTableView to display data and cooperate with custom models, we inherit from this class. For 2D array models only.

The rowCount() and columnCount() functions return the dimensions of the table. To retrieve the model index corresponding to an item in the model, use index() and provide only the row and column numbers. When subclassing QAbstractTableModel, you must implement rowCount(), columnCount(), and data(). QAbstractTableModel provides default implementations of the index() and parent() functions. Well-behaved models will also implement HeaderData(). Editable models need to implement setData(), and then implement flags() to return a value containing Qt::ItemIsEditable. A model that provides an interface to a scalable data structure may provide implementations of insertRows(), removeRows(), insertColumns(), and removeColumns(). When implementing these features, it is important to call the appropriate function so that all connected views are aware of any changes:

	insertRows()实现必须在将新行插入数据结构之前调用beginInsertRows(),并且之后必须立即调用endInsertRows()。
	insertColumns()实现必须在将新列插入数据结构之前调用beginInsertCo​​lumns(),并且此后必须立即调用endInsertColumns()。
	removeRows()实现必须在将行从数据结构中删除之前调用beginRemoveRows(),并且此后必须立即调用endRemoveRows()。
	removeColumns()实现必须在将列从数据结构中删除之前调用beginRemoveColumns(),并且此后必须立即调用endRemoveColumns()。

A series of derived classes QSqlQueryModel (encapsulating SQL result set), QSqlTableModel (encapsulating SQL table), QSqlRelationTableModel (encapsulating SQL table with foreign key) of QAbstractTableModel are used to access the database. I won't go into details here.

2.1.3QFileSystemModel

Provides information about files and directories in the local file system.

Similar to it is QDirModel.

2.1.4QStandardItemModel

Can be used as the standard model of QListView, QTableView, QTreeView. Manage complex tree-structured data items , each data item can contain arbitrary data.

This model has a built-in Item class: the QStandardItem class , which provides an item for the QStandardItemModel class. Items usually contain text, icons or checkboxes. Each Item can have its own background brush, set using the setbackground() function. The current background brush can be found using background(). The text label of each Item can be presented with its own font and brush. These are specified with the setFont() and setForeground() functions, and read using font() and foreground(). By default, items are enabled, editable, selectable, checkable, and can be used as a source for drag, drop operations, or as a target for drop. The flags of each Item can be changed by calling setFlags(). A checkable Item can be checked using the setCheckState() function. The corresponding checkState() function indicates whether the Item is checked.

Application-specific data can be stored in an Item by calling setData(). Each item can contain a two-dimensional table of subitems. This makes it possible to build a hierarchy of Items. A typical hierarchy is a tree, in which case the subtable is a table with a single column (list).

The dimensions of subtables can be set using setRowCount() and setColumnCount(). Items can be placed in child tables with setChild(). Obtain a pointer to the child through the childe() method(). New rows and columns can also be inserted with insertRow() and insertColumn(), and with appendRow() and appendColumn(). When using append and insert functions, the size of the child table will grow as needed.
Existing children can be removed using removeRow() or takeRow(); correspondingly, columns can be removed using removeColumn() or takeColumn().
An item's children can be sorted by calling sortChildren().

When subclassing is used to provide custom items:

	可以为它们定义新类型,以便将它们与基类别区分开。应重新实现type()函数以返回等于或大于UserType的新类型值。
	如果要执行数据查询的自定义处理和/或控制项目\的数据表示如何表示,则重新实现data()和setData()。
	如果您希望QstandardItemModel能够按需创建自定义项目类的实例,需重新实现clone()。
	如果要控制项目的序列化形式表示如何表示项目,请重新实现read()和write()。
	如果要控制项目比较的语义,则重新实现operator<()。operator<()决定了用sortChildren()或QStandardItemModel:: sort()对Item进行排序顺序。

Has the following advantages:

  • 1. The implementation code is simple. QStandardItemModel can use QStandardItem to construct data with list, table, and tree structures by continuously adding child nodes. ;
  • 2. This class uses QStandardItem to store data items, and users do not need to define any data structure to store data items;
  • 3. QStandardItem uses self-association relationships, which can express lists, tables, trees and even more complex data structures, and can cover a variety of data sets;
  • 4. QStandardItem itself stores multiple "roles, data sub-items", and view classes, delegate classes or other user-defined classes can easily access each data sub-item according to roles.
    shortcoming:
  • 1. When there are many data items in the data set, the execution efficiency of some operations on the data set will be very low;
  • 2. When the data is too large, it takes up a huge amount of memory and the performance is low.

QStandardItemModel vs QAbstractItemModel
insert image description here

  • 1. For scenarios where the amount of data is small and does not need to be updated, we use QStandardItemModel to implement it, which is relatively simple and does not have as much code logic as a custom model.
  • 2. When the amount of data is small but needs to be updated, we use a custom model to implement it. Even if the amount of data is small, updating data is actually relatively slow, and it will take up more UI thread time. If other threads have heavy business, just It will affect the performance of the UI thread and cause the interface to freeze.
  • 3. In the case of a large amount of data, no matter whether it is updated or not, we use a custom model to achieve it.

2.1.5QAbstractItemModel (recommended to focus on)

When we need to use QTreeView to display data and cooperate with custom model, we inherit from this class.

This model is the ancestor base class of all models . It defines a standard interface for views and delegates to access data. The data itself is not necessarily stored in the model, but can also be stored in data structures , classes, files, databases, and other components. It provides a very flexible interface for data to process various views, and these views can represent data as tables, lists, trees, etc. For general table or list data structures, specific types can be processed by subclassing QAbstractTableModel, QAbstractListModel, or using the on-site derived models of these two officially provided by Qt. For a more general model, the above ready-made models cannot meet the requirements, you need to customize the data node class (not mandatory to inherit QObject, but the role is similar to QStandardItem), form a tree data structure, and cooperate with the model derived class to complete the model layer. Function.

Personally, I suggest that for complex data, directly use QAbstractItemModel derived class + custom data node class .

The underlying data model has views and delegates as a hierarchy of tables. If you don't take advantage of hierarchies, the model is a simple row and list. Each item has a unique index specified by QModelIndex.
insert image description here
Every piece of data that can be accessed through a model has an associated model index. You can get this model index using the index() function. Each index may have a sibling() index; children have a parent() index.

Each item has a number of data elements associated with it, which can be retrieved by assigning a role (see Qt::ItemDataRole ) to the model's data() function. Data for all available roles can be obtained simultaneously using the itemData() function.

Data for each role can be set using the specific Qt::ItemDataRole. Use setData() to set data for a single role individually, or setItemData() for all roles.

Items can be queried using flags() (see Qt::ItemFlag) to see if they can be selected, dragged or manipulated in other ways.
If the item has children, haschildren() returns the corresponding indices.

The model has a rowCount() and columnCount() at each level of each hierarchy. Rows and columns can be inserted and deleted using InsertRows(), insertColumns(), removeRows(), and removeColumns().

The model emits a signal to indicate a change. For example, dataChanged() is emitted whenever a data item provided by the model is changed. Changes to the headers provided by the model cause headerDataChanged() to be emitted. If the structure of the underlying data changes, the model can emit LayoutChanged() to indicate to any accompanying views that any items displayed should be redisplayed, taking into account the new structure.

Items available through the model can be searched using the Match() function.

To sort the models, you can use sort().

When subclassing QAbstractItemModel, at least index(), parent(), rowcount(), colundCount() and data() must be implemented . These functions are used in all read-only models and form the basis of editable models.
Since rowCount() is expensive to implement, it is also possible to reimplement haschildren() to implement model-specific behavior. This allows the model to limit the amount of data the view asks for, and can be used as a way to enforce lazy model data.
To enable editing in your model, you must also implement setData() and reimplement flags() to ensure ItemIsEditable is returned. You can also reimplement headerData() and setHeaderData() to control how the model's header is displayed.
The dataChanged() and headerDatachanged() signals must be explicitly emitted when reimplementing the setData() and setHeaderData() functions respectively .
Custom models need to create model indexes for other components. To do this, call CreateIndex() with a row and identifier appropriate for the item, with a pointer or integer value as its identifier. The combination of these values ​​must be unique for each item. Custom models typically use these unique identifiers among other reimplementation functions to retrieve item data and access information about the item's parents and children. See Simple Tree Model Example for more information on unique identifiers.

It is not necessary to support every role defined in Qt::ItemDataRole. Depending on the type of data contained in the model, it may only be useful to implement the data() function to return valid information for some of the more common roles. Most models provide at least a textual representation of item data for Qt::DisplayRole, and well-behaved models should also provide valid information for Qt::ToolTipRole, Qt::WhatsThisRole. Support for these roles enables models to be used with standard QT views. However, for some models dealing with highly specialized data, it may be appropriate to provide data only for user-defined roles.
A model that provides an interface to a scalable data structure may provide implementations of insertRows(), removeRows(), insertColumns(), and removeColumns(). When implementing these features, it is important to notify all connected views about model size changes before and after they occur:

inserstRows()实现必须在将新行插入数据结构之前调用beginInsertRows(),完成后立即调用endInsertRows()。
insertColumns()实现必须在将新列插入数据结构之前调用beginInsertColumns(),完成立即调用endInsertColunms()。
removeRows()实现必须在从数据结构中删除行之前调用beginRemoveRows(),完成立即调用endRemoveRows()。
removeColumns()实现必须在将列从数据结构中删除之前调用beginRemoveColumns(),完成立即调用endRemoveColumns()。

The private signals sent by these functions give the accessory's components a chance to take action before any data becomes unusable. Encapsulation of insert and delete operations using these start and end functions also enables the model to properly manage persistent model indexes. You must ensure that these functions are called if you want your selections to be handled correctly . If you insert or delete items with children, you don't need to call these methods for those children. In other words, the parent item will handle its children.

To create gradually populated models, you can reimplement fetchMore() and canFetchMore(). If your reimplementation of fetchMore() adds rows to the model, you must call beginInsertRows() and endInsertRows().

In the official examples provided by Qt, it is strongly recommended to browse and learn (both are custom data structures):

  • 1.Simple Tree Model Example;
  • 2.Editable Tree Model Example。

2.1.6QSortFilterProxyModel

Sort and/or filter other models

2.2 Relationship between model and data

insert image description here

Model items are divided into standard Item defined by Qt and user-defined data node classes, which are characterized by a complete data structure tree and its variants. In addition, custom data node classes need to provide auxiliary function interfaces to ensure that their corresponding model classes can Implement several derived virtual methods that are necessary.

2.3 Internal communication method

For model, view, and delegate, see Using signal slots to communicate:

  • 1. When the data of the data source changes, the model sends a signal to inform the view;
  • 2. When the user interacts with the displayed items, the view emits signals to provide interactive information;
  • 3. When an item is edited, the delegate signals the state of the model and view editors.

There are a large number of signal slots in each model, delegate, and view. You need to refer to the Qt help for details. For example, after updating data in QAbstractItemModel, remember to send the signal dataChanged to explicitly notify the view to refresh.

2.4 Proxy Model

The QAbstractProxyModel class provides a base class for proxy item models that can perform sorting, filtering, or other data processing tasks.
This class defines the standard interface that proxy models must use in order to be able to interact correctly with other model/view components. It should not be instantiated directly.
All standard proxy models are derived from the QAbstractProxyModel class. If you need to create a new proxy model class, it is usually best to subclass a class that already exists and provides the behavior closest to the desired behavior.
Proxy models that filter or sort data from source models should be created by using or subclassing QSortFilterProxyModel.
To subclass QAbstractProxyModel, you need to implement mapFromSource() and mapToSource(). The mapSelectionFromSource() and mapSelectionToSource() functions need to be reimplemented only if behavior different from the default is desired.
Note: If the source model is deleted or no source model is specified, the proxy model will operate on an empty placeholder model.

2.5 Data-window mapper

The QDataWidgetMapper class provides a mapping between an area of ​​the data model and a widget, so that a row of data in a model can be displayed and edited on a widget. Its essence is an adapter between tables and models .
Similar to QDataWidgetMapper, QCompleter in Qt can automatically complete the adaptation, such as QComboBox, QLineEdit. QCompleter uses a model as its data source.

2.6 style under model/view architecture

Taking QTreeView as an example, it draws 3 swordsmen (the QStyleOptionViewItem style directly determines the rendering effect, which needs to be investigated in detail)

	drawBranches(QPainter *, const QRect &, const QModelIndex &) const
	drawRow(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const
	drawTree(QPainter *, const QRegion &) const

QTableView does not have these overrideable virtual drawing methods, which indirectly proves that its versatility and scalability are far inferior to QTreeView. Remember remember!

3. Model subclassing

It is strongly recommended to read Qt's official Model/View Programming, and the Model Subclassing Reference section needs to be read carefully. Some translations are as follows:

Model subclasses need to provide implementations of many of the virtual functions defined in the QAbstractItemModel base class. The number of these functions that need to be implemented depends on the type of model - whether it provides a simple list, a table or a complex item hierarchy for the view. 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.
The functions that need to be implemented in model subclasses can be divided into three groups:

  • 1. Item data handling: All models need to implement functionality that enables views and delegates to query the model's dimensions, inspect items, and retrieve data.
  • 2. Navigation and index creation: Hierarchical models need to provide functions that views can call to navigate the tree structure they expose and obtain the model index for an item.
  • 3. Drag and drop support and MIME type handling: The model inherits functions that control how internal and external drag and drop operations are performed. These functions allow data items to be described with MIME types that other components and applications can understand.

3.1 Project data processing

A model can provide varying degrees of access to the data it provides. They can be simple read-only components, some models may support resizing operations, others may allow editing of items.

3.1.1Read-Only access

To provide read-only access to data provided by a model, the following functions can be used. Must be implemented in subclasses of the model.

flags():
Used by other components to get per-item information provided by the model. In many models, the combination of flags should include Qt::ItemIsEnabled and Qt::ItemIsSelectable.

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 and Qt::AccessibleDescriptionRole. See the Qt::ItemDataRole enumeration documentation for information on the types associated with each role.

headerData():
Provides the view with information to display in its header. This information is only retrieved by views capable of displaying header information.

rowCount():
Provides the number of data rows exposed by the model.

These four functions must be implemented in all types of models, including list models (QAbstractListModel subclasses) and table models (QAbstractTableModel subclasses) .
In addition, 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.

3.1.2Editable items

The editable model allows modification of data items and may also provide functions that allow insertion and deletion of rows and columns. To implement editing, the following functions must be implemented correctly:

flags():
Must return the appropriate combination of flags for each item. In particular, the value returned by this function must include Qt::ItemIsEditable and the value applied to the item in the read-only model.

setData() :
Used to modify the data item associated with the specified model index. In order to be able to accept user input provided by the user interface element, this function must handle the data associated with the 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.

3.1.3 Scalable models

All types of models can support row insertion and deletion, and table models and hierarchical models can also support column insertion and deletion. Table models and hierarchical models can also support inserting and deleting columns. It is important to notify other components of model dimension changes before and after they occur. Therefore, the following functions can be implemented to allow the model to be resized, but the implementation must ensure that the appropriate functions are called to notify attached views and delegates.

insertRows():
Used to add new data rows and data items to all types of models. Implementations must call beginInsertRows() before inserting new rows into any underlying data structures, and endInsertRows() immediately upon completion.

removeRows() :
Used to remove rows and the data items they contain from all types of models. Implementations must call beginRemoveRows() before removing rows from any underlying data structures, and endRemoveRows() immediately upon completion.

insertColumns():
Used to add new columns and data items to table models and hierarchical models. Implementations must call beginInsertColumns() before inserting new columns into any underlying data structures, and call endInsertColumns() immediately after completion.

removeColumns():
Used to remove columns and the data items they contain from table models and hierarchical models. Implementations must call beginRemoveColumns() before removing columns from any underlying data structures, and call endRemoveColumns() immediately upon completion.

In general, 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 rows than specified can be inserted. In this case, the model should return false to indicate that no add-on has been made to handle the situation.

The signal emitted by the function called in the implementation of the resizing API gives the attached component a chance to take action before any data becomes unavailable. Encapsulating insert and remove operations with begin and end functions also enables the model to properly manage persistent model indexes.

Typically, start and end functions are able to inform other components of changes to the model's underlying structure. For more complex changes in model structure, which may involve internal reorganization, data ordering, or any other structural change, it is necessary to perform the following sequence:

  • emit the layoutAboutToBeChanged() signal
  • Updates internal data representing the structure of the model.
  • Use changePersistentIndexList() to update persistent indexes
  • Emit the layoutChanged() signal.
    This sequence can be used for any structural update in place of more advanced and more convenient protection methods. For example, if a model with 2 million rows needs to drop all odd rows, that's 1 million discounted ranges, each with 1 element. It is possible to use beginRemoveRows and endRemoveRows 1 million times, but this is obviously inefficient. Instead, this can be used as a single layout change signal, updating all necessary persistent indexes at once.

3.1.4 Lazy population of model data

The lazy population of model data effectively allows requests for model information to be deferred 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 generally request as much information as possible in order to accurately display model data, it is useful to limit the amount of information returned to them to reduce unnecessary subsequent data requests.

In hierarchical models where finding the number of children of a given item is an expensive operation, it is useful to ensure that the model's rowCount () implementation is called only when necessary. In this case, the hasChildren() function can be reimplemented to provide an inexpensive way for views to check for the presence of children and, in the case of QTreeViews, draw appropriate decorations for their parents.

Regardless of whether the reimplementation of hasChildren() returns true or false, the view may not need to call rowCount() to find out how many children exist. For example, 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 checked later, while doing the initial population of model data as quickly as possible. The only downside is that items with no children may be displayed incorrectly in some views until the user tries to view a child that does not exist.

3.2 Navigation and Index Creation

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.

3.2.1 Father and Son

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

index() :
Given a parent item's model index, this function allows views and delegates to access the item's children. If no valid child is found - corresponding to the specified row, column and parent model index, the function must return QModelIndex(), which is 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 in the model, the function must return an invalid model Index, which is created with an empty QModelIndex(). If the specified model index corresponds to the top-level item in the model, or if there is no valid parent item in the model, the function must return an invalid model index, as created by the empty QModelIndex() constructor.

Both of the above functions use the createIndex() factory function to generate an index for use by other components . Models will usually provide this function with some unique identifier to ensure that the model index can later be reassociated with its corresponding item.

3.3 Drag and drop support and MIME type handling

The model/view classes support drag-and-drop operations and provide default behavior that is sufficient for many applications. However, 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.

Additionally, the convenience view classes implement specialized behavior that should strictly follow existing developer expectations. An overview of this behavior is provided in the Convenience Views section.

3.3.1MIME 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 a 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, data items can be exported 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. Subclasses can obtain the default QMimeData object from the base class and add data to it in other formats.

For many models, it is useful to provide the item's content in a common format represented by a MIME type (such as text/plain and image/png). Note that images, colors and HTML documents can be easily added to a QMimeData object using the QMimeData::setImageData(), QMimeData::setColorData() and QMimeData::setHtml() functions.

3.3.2 Accept Drop data

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

When serialized item data is dropped onto a view, the data is inserted into the current model using an implementation of QAbstractItemModel::dropMimeData(). The default implementation of this function never overwrites any data in the model; instead, it attempts to insert the data item as a sibling of an item or as a child of that item.

To take advantage of QAbstractItemModel's default implementations of built-in MIME types, new models must provide reimplementations of the following functions:

insertRows():
These functions enable the model to automatically insert new data using the existing implementation provided by QAbstractItemModel::dropMimeData().

insertColumns()

setData() :
Allows new rows and columns to be populated with items.

setItemData():
This function provides more efficient support for populating new items.

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

supportedDropActions():
Used to return a combination of drag and drop operations, indicating the type of drag and drop operations accepted by the model.

mimeTypes():
Used to return a list of MIME types that can be decoded and processed by the model. In general, the MIME types that support an input model are the same MIME types that the model can use when encoding data, for use by external components.

dropMimeData():
does the actual decoding of the data transferred via drag-and-drop, determines its position in the model, and inserts new rows and columns if necessary. How this function is implemented in subclasses depends on the requirements of the data exposed by each model.

If the implementation of the dropMimeData() function changes the dimensions of the model by inserting or removing rows or columns, or if data items are modified, care must be taken to ensure that all relevant signals are emitted. It can be useful to simply call reimplementations of other functions in subclasses such as setData (), insertRows () and insertColumns () to ensure consistent behavior of the model.

In order to ensure that the drag operation works correctly, the following functions that remove data from the model must be reimplemented.

  • removeRows()
  • removeRow()
  • removeColumns()
  • removeColumn()
    See Using drag and drop with item views for more information on drag and drop with item views.

3.3.3Convenience views

Convenience views (QListWidget, QTableWidget, and QTreeWidget) override the default drag-and-drop functionality to provide less flexible but more natural behavior, suitable for many applications. For example, since it is more common to put data into a QTableWidget's cell, replacing the existing content with the data being transferred, the underlying model will set the target item's data instead of inserting new rows and columns into the model. For more information on drag and drop in the convenience view, you can see Using drag and drop in the project view.

3.4 Performance optimization for large amounts of data

The canFetchMore() function checks if the parent has more data available and returns true true false accordingly. The fetchMore() function fetches data based on the specified parent object. These two functions can be combined, for example, in database queries involving incremental data to populate a QAbstractItemModel. We reimplement canFetchMore() to indicate if there is more data to fetch, and reimplement fetchMore() to populate the model if necessary.

Another example is a dynamically populated tree model, where we reimplement fetchMore() when a branch in the tree model is expanded.

If you reimplement fetchMore() to add rows to the model, you need to call beginInsertRows() and endInsertRows(). Also, both canFetchMore() and fetchMore() must be reimplemented because their default implementations return false and do nothing.

4. References

[1] "Qt Creator Quick Start 3rd Edition". Huo Yafei
[2] A series of tutorials on using QTreeView. Baili Yang
[3]Qt Model/View Tutorial.FlyWM_
[4] Qt 6.2 [Chinese] Qt Widgets Model/View Programming.Runebook.dev
[5] Qt Assistant

Guess you like

Origin blog.csdn.net/langshuibaren/article/details/131277292