beginresetmodel qt что делает
Оглавление
The QAbstractItemModel class provides the abstract interface for item model classes. Далее.
Открытые функции
Открытые слоты
Сигналы
Защищенные функции
Дополнительные унаследованные члены
Подробное описание
The QAbstractItemModel class provides the abstract interface for item model classes.
The QAbstractItemModel class defines the standard interface that item models must use to be able to interoperate with other components in the model/view architecture. It is not supposed to be instantiated directly. Instead, you should subclass it to create new models.
The QAbstractItemModel class is one of the Model/View Classes and is part of Qt’s model/view framework.
If you need a model to use with a QListView or a QTableView, you should consider subclassing QAbstractListModel or QAbstractTableModel instead of this class.
The underlying data model is exposed to views and delegates as a hierarchy of tables. If you do not make use of the hierarchy, then the model is a simple table of rows and columns. Each item has a unique index specified by a QModelIndex.
Every item of data that can be accessed via a model has an associated model index. You can obtain this model index using the index() function. Each index may have a sibling() index; child items have a parent() index.
Each item has a number of data elements associated with it and they can be retrieved by specifying a role (see Qt::ItemDataRole) to the model’s data() function. Data for all available roles can be obtained at the same time using the itemData() function.
Data for each role is set using a particular Qt::ItemDataRole. Data for individual roles are set individually with setData(), or they can be set for all roles with setItemData().
Items can be queried with flags() (see Qt::ItemFlag) to see if they can be selected, dragged, or manipulated in other ways.
If an item has child objects, hasChildren() returns true for the corresponding index.
The model has a rowCount() and a columnCount() for each level of the hierarchy. Rows and columns can be inserted and removed with insertRows(), insertColumns(), removeRows(), and removeColumns().
The model emits signals to indicate changes. For example, dataChanged() is emitted whenever items of data made available by the model are changed. Changes to the headers supplied 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 attached views that they should redisplay any items shown, taking the new structure into account.
The items available through the model can be searched for particular data using the match() function.
To sort the model, you can use sort().
Наследование
Note: Some general guidelines for subclassing models are available in the Model Subclassing Reference.
When subclassing QAbstractItemModel, at the very least you must implement index(), parent(), rowCount(), columnCount(), and data(). These functions are used in all read-only models, and form the basis of editable models.
You can also reimplement hasChildren() to provide special behavior for models where the implementation of rowCount() is expensive. This makes it possible for models to restrict the amount of data requested by views, and can be used as a way to implement lazy population of model data.
To enable editing in your model, you must also implement setData(), and reimplement flags() to ensure that ItemIsEditable is returned. You can also reimplement headerData() and setHeaderData() to control the way the headers for your model are presented.
The dataChanged() and headerDataChanged() signals must be emitted explicitly when reimplementing the setData() and setHeaderData() functions, respectively.
Custom models need to create model indexes for other components to use. To do this, call createIndex() with suitable row and column numbers for the item, and an identifier for it, either as a pointer or as an integer value. The combination of these values must be unique for each item. Custom models typically use these unique identifiers in other reimplemented functions to retrieve item data and access information about the item’s parents and children. See the Simple Tree Model Example for more information about unique identifiers.
It is not necessary to support every role defined in Qt::ItemDataRole. Depending on the type of data contained within a 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 the Qt::DisplayRole, and well-behaved models should also provide valid information for the Qt::ToolTipRole and Qt::WhatsThisRole. Supporting these roles enables models to be used with standard Qt views. However, for some models that handle highly-specialized data, it may be appropriate to provide data only for user-defined roles.
Models that provide interfaces to resizable data structures can provide implementations of insertRows(), removeRows(), insertColumns(),and removeColumns(). When implementing these functions, it is important to notify any connected views about changes to the model’s dimensions both before and after they occur:
The private signals that these functions emit give attached components the chance to take action before any data becomes unavailable. The encapsulation of the insert and remove operations with these begin and end functions also enables the model to manage persistent model indexes correctly. If you want selections to be handled properly, you must ensure that you call these functions. If you insert or remove an item with children, you do not need to call these functions for the child items. In other words, the parent item will take care of its child items.
To create models that populate incrementally, you can reimplement fetchMore() and canFetchMore(). If the reimplementation of fetchMore() adds rows to the model, beginInsertRows() and endInsertRows() must be called.
Описание функций-членов
QAbstractItemModel:: QAbstractItemModel ( QObject * parent = 0 )
Constructs an abstract item model with the given parent.
Model-View в QML. Часть четвертая: C++-модели
Поскольку основное предназначение QML — это создание интерфейсов, то в соответствии с шаблоном MVC, на нем реализуются представление и контроль. Для реализации же модели, совершенно логично напрашивается C++. Здесь у нас будет гораздо меньше ограничений и мы сможем реализовать модель любой сложности. Кроме того, если значительная часть программы написана на C++ и данные поступают именно оттуда, то лучше всего там же поместить и модель.
От использования такой модели может отпугнуть кажущаяся сложность реализации. Я не стану спорить с тем, что C++ не самый простой язык. Он посложнее QML и требует больше осторожности, чтобы не выстрелить себе в ногу, это факт. Несмотря на это, на практике не все так уж и страшно.
Во-первых, не будем забывать, что мы пишем не на чистом С++, а с использованием Qt. Такие вещи как parent-child в QObject, implicit sharing для контейнеров, сигналы и слоты, QVariant и многое другое очень сильно упрощают и автоматизируют работу с памятью, чем избавляют разработчика от массы головной боли и повышают надежность. Иногда даже создается впечатление, что пишешь на динамическом языке программирования. Это же сокращает пропасть между QML и C++, делая переход между ними более-менее плавным.
Во-вторых, все модели QML в конечном итоге приводятся к этим самым C++-моделям, только мы получаем упрощенный вариант и не самое максимальное быстродействие. Если уже есть понимание, как работать с моделями на QML, то с C++-моделями будет справиться проще. Мы просто узнаем в процессе чуть больше низкоуровневой информации, заодно улучшится понимание, как все это работает.
В общем, освоить C++-модели очень даже стоит. В особенности это касается QAbstractItemModel, с которой мы и начнем.
1. C++-модель QAbstractItemModel
Это стандартная модель из фреймворка Qt Model-View. Этот класс обладает богатыми возможностями и позволяет строить модели различной сложности.
Существуют три базовых класса для таких моделей. QAbstractTableModel представляет данные в виде таблицы, для доступа к данным используется номер строки и столбца. QAbstractListModel представляет данные в виде списка и, можно сказать, является частным случаем предыдущей модели с одним столбцом.
QAbstractItemModel наоборот, более обобщенная версия. Каждый элемент таблицы может иметь еще и дочерние элементы, тоже организованные в виде таблицы. Таким образом, при помощи этой таблицы можно организовать древовидную структуру. В Qt есть принятое правило, что дочерние элементы могут иметь только элементы первого столбца и при использовании представлений из Qt, таких как QTreeView нужен именно такой формат, но никто не запрещает организовать модель так, как удобно. Как примером такой модели, можно привести класс QFileSystemModel. В качестве первого столбца — имена файлов или каталогов. У элементов этого столбца также могут быть дочерние элементы, если это каталог. Остальные столбцы содержат различную информацию о файле — размер, время модификации и т.п. Такую структуру данных можно встретить в любом файловом менеджере:
Между моделью и представлением можно вставить специальную прокси-модель. Такие модели перехватывают вызовы к основной модели и могут скрывать определенные элементы, менять их порядок, влиять на получение и запись данных и т.п. В Qt есть готовый класс QSortFilterProxyModel, которая может представлять данные модели в отсортированном и/или отфильтрованном виде. Если ее функционала недостаточно, можно создать свою прокси-модель, отнаследовавшись от этого класса или от QAbstractProxyModel.
Представления в QML могут отображать только списки. При помощи VisualDataModel можно перемещаться по древовидной структуре, но отображать мы можем только элементы текущего уровня. Если данные нужно хранить в виде дерева и при этом отображать в QML, то стоит либо воспользоваться VisualDataModel либо писать свою прокси-модель, которая превратит это дерево в список.
Для того, чтобы создать свою модель, нам нужно отнаследоваться от одного из базовых классов для моделей и определить обязательные для этой модели методы. Я опишу кратко, что нужно сделать, более подробную информацию можно получить в документации. Рассматривать будем в порядке возрастания сложности.
Для модели-списка нужно создать производный класс от QAbstractListModel и определить такие методы:
Этого достаточно, если не планируется редактировать данные модели при помощи делегата. Редактируемую модель рассмотрим чуть позже.
Для модели-таблицы добавляется еще метод columnCount(), возвращающий количество столбцов. Табличные представления в QML используют элементы из первого столбца и при отображении распределяют роли этого элемента как столбцы. Таким образом, таблица в QML реализуется при помощи все того же списка и табличную модель вряд ли есть смысл использовать.
Если нам нужна модель с древовидной структурой, мы используем QAbstractItemModel. У этой модели надо будет дополнительно определить такие функции:
В моделях Qt, обращение к элементам идет через специальные индексы — объекты типа QModelIndex. Они содержат в себе номер строки и столбца, индекс родительского элемента и некоторые дополнительные данные. Корневой элемент модели имеет недействительный индекс QModelIndex(). Так что если у нас простой список или таблица — у всех элементов родительский элемент будет именно таким. В случае дерева, такой родитель будет только у элементов верхнего уровня. Функция index() получает индекс родителя и номер строки и столбца элемента, должна возвращать индекс элемента. Индексы создаются при помощи функции createIndex().
По сути, сложности начинаются тогда, когда нам нужна вложенность, а так все достаточно просто.
В качестве примера рассмотрим модель-список. Данные будем хранить в этом же объекте в виде списка строк. Еще сделаем функцию add(), которая будет добавлять еще один элемент в модель и пометим ее специальным макросом Q_INVOKABLE, чтобы ее можно было вызывать из QML.
Мы определяем две роли ColorRole и TextRole и используем для них значения больше Qt::UserRole — именно там заканчиваются зарезервированные значения для Qt. Соответственно, для пользовательских ролей надо использовать значения начиная с Qt::UserRole.
Реализация методов класса:
Поскольку QML обращается к ролям используя строковые имена вместо целочисленных констант, мы определим для них имена: color и text. Перед добавлением мы вызываем специальную функцию beginInsertRows(), которая издаст нужные сигналы, чтобы представление было в курсе, что готовится добавление элементов и куда они будут добавляться. А после, вызываем функцию endInsertRows(), которая опять таки издаст сигналы о том, что в модель добавились элементы. Все добавления нужно оборачивать таким образом. Есть подобные функции и для удаления и перемещения элементов.
В функции add() меняем текст первого элемента, чтобы он показывал количество элементов в списке. После этого издаем сигнал dataChanged(), чтобы информировать об этом представление. Сигналу передаем параметрами начальный и конечный индекс изменившихся данных (у нас один и тот же). Индекс получаем при помощи функции createIndex(), которой параметрами передается строка, столбец и указатель на приватные данные. В качестве последнего обычно используется указатель на объект с данными, но в нашем случае можно упростить и всегда использовать NULL.
В качестве программы на QML немного переделаем второй пример. C++-модель реализована в виде подключаемого модуля (плагина). В начале файла добавим его импортирование:
Создадим объект этого типа и используем его в качестве модели:
После запуска программы и добавления нескольких элементов получим примерно такой результат:
Для редактирования данных модели в делегате предусмотрен стандартный интерфейс и для его использования необходимо в нашей модели переопределить метод setData(). Возможность редактирования данных QAbstractItemModel из QML появилась в Qt 5.
Добавим в заголовочный файл такие объявления:
и в файл реализации определения:
Мы добавили в модель возможность редактировать свойство text прямо из делегата при помощи подобного кода:
Отредактируем делегат из нашего примера и добавим в него такой элемент:
Теперь при двойном клике на элементе, его текст будет меняться на «Edited».
Флаг Qt::ItemIsEditable используется для отображений Qt, чтобы показать, что элемент можно редактировать, поэтому метод flags() необходимо переопределить. На данный момент в QML этот флаг не проверяется и модель будет редактируемой и без его установки, но я бы рекомендовал не пренебрегать им, т.к. в будущих версиях проверку на это могут добавить.
2. C++-списки
В качестве модели можно использовать списки строк либо объектов типа QObject.
Сделаем простой класс с свойством типа QStringList:
Используем немного переделанный первый пример. Импортирование и создание объекта модели точно также как и в предыдущем примере. Но вместо самого объекта, в качестве модели используется его свойство:
а в качестве текста используется индекс элемента:
Такой список работает также, как и массив JavaScript. Соответственно, это пассивная модель и добавление/удаление элементов не влияет на представление.
3. QQmlListProperty
Этот класс позволяет сделать список, который можно наполнять как в C++, так и в QML. Наполнение в QML выполняется статически при создании объекта (как это делается с ListModel). В C++ можно и добавлять/удалять элементы, так что если сделать специальный метод и пометить его макросом Q_INVOKABLE, то можно будет это делать и из QML.
В списках такого типа могут хранится объекты типа QObject и производных от него типов. В типе стоит определить все свойства, которые будут использоваться (при помощи Q_PROPERTY).
Рассмотрим пример такого объекта.
Мы создали простой класс, содержащий два свойства — color и text, геттеры, сеттеры и нотификаторы для них.
Для того, чтобы объекты этого типа можно было использовать в QQmlListProperty, это тип должен быть виден в QML. А для этого нужно зарегистрировать этот тип при помощи функции qmlRegisterType(). Я использую C++-плагин, поэтому регистрирую этот тип в специальном обработчике, вместе с моделью:
Для того, чтобы использовать QQmlListProperty, нужно создать в каком-либо объекте свойство типа QQmlListProperty, где T — это тип объектов, которые нужно хранить. В нашем случае, будет свойство типа QQmlListProperty.
Конструктор QQmlListProperty принимает в качестве аргументов методы, которые будет вызывать движок QML при работе со списком. Это методы для добавления и получения элемента, получения количества элементов и очистки списка. Обязательным является только первый, но лучше определить их все.
Итак, код класса нашей модели:
Как и в примере с QAbstractItemModel, тут есть метод add() для добавления элементов и в конструкторе тоже добавляется элемент.
В методе data() создается объект типа QQmlListProperty. В конструкторе он получает родителя (QObject), указатель на приватные данные, который будет доступен в функциях для работы со списком и сами функции. Во всех функциях первым аргументом передается указатель на объект типа QQmlListProperty у которого в свойстве data находятся наши приватные данные. Я поместил туда список, в котором фактически хранятся объекты типа Element.
Сигнал для свойства data нужен, чтобы при добавлении/удалении объектов в процессе работы представления получили информацию об изменениях в модели. После такого сигнала, отображение будет перечитывать модель целиком.
Для демонстрации такой модели возьмем немного переделанный второй пример.
Свойство data определяется как обычный список. Поскольку мы зарегистрировали тип Element, то такие объекты теперь можно создавать в QML. Стоит заметить, что определение здесь элементов массива data не заменяет те элементы, которые уже есть. Эти элементы добавятся к тому, который определен в конструкторе класса TestModel.
В качестве модели используется не сам объект типа TestModel, а все то же свойство data:
Данные в делегате доступны через modelData:
В свойство data элементы можно добавить только статически, так что используем для этого написанную нами функцию add():
В итоге получим примерно такой результат:
В классе TestModel мы указали data как свойство по умолчанию (при помощи директивы Q_CLASSINFO). Это дает нам возможность определять объекты Element прямо в объекте TestModel и они сами добавятся в нужное свойство. Так что можно упростить определение модели и переписать его так:
Таким образом, используя QQmlListProperty, можно реализовать активную модель не используя классы QAbstractItemModel. Если данных не предполагается большое количество и они не должны часто меняться, такая модель вполне подойдет.
Резюме
Разработка моделей является важной частью не только программирования на QML, но и программирования в целом. Как говорил Фред Брукс: “Покажите блок-схемы, скройте таблицы и я буду озадачен, покажите мне ваши таблицы и, скорее всего, блок-схемы мне не потребуются, они будут очевидны”. Именно данные являются центральной темой в программировании. Проектирование структур данных и доступа к ним является ответственной задачей и во многом определяет архитектуру программы.
Знание инструментов, которые мы рассмотрели в этой и предыдущей части помогут вам организовать ваши данные наиболее подходящим образом, а затем вокруг данных и саму программу. Поскольку в QML концепция Model-View является одной из основополагающих, то этих инструментов хватает.
Я рассмотрел различные способы создания моделей. По своему опыту могу сказать, что самые используемые это QAbstractItemModel, ListModel и JavaScript-массивы. Так что именно на них я рекомендую в первую очередь обратить внимание.
Qt Documentation
Contents
The QAbstractItemModel class provides the abstract interface for item model classes. More.
Public Functions
QAbstractItemModel()
Public Slots
Signals
void | columnsAboutToBeInserted(const QModelIndex & parent, int start, int end) |
void | columnsAboutToBeMoved(const QModelIndex & sourceParent, int sourceStart, int sourceEnd, const QModelIndex & destinationParent, int destinationColumn) |
void | columnsAboutToBeRemoved(const QModelIndex & parent, int start, int end) |
void | columnsInserted(const QModelIndex & parent, int start, int end) |
void | columnsMoved(const QModelIndex & sourceParent, int sourceStart, int sourceEnd, const QModelIndex & destinationParent, int destinationColumn) |
void | columnsRemoved(const QModelIndex & parent, int start, int end) |
void | dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight) |
void | headerDataChanged(Qt::Orientation orientation, int first, int last) |
void | layoutAboutToBeChanged() |
void | layoutChanged() |
void | modelAboutToBeReset() |
void | modelReset() |
void | rowsAboutToBeInserted(const QModelIndex & parent, int start, int end) |
void | rowsAboutToBeMoved(const QModelIndex & sourceParent, int sourceStart, int sourceEnd, const QModelIndex & destinationParent, int destinationRow) |
void | rowsAboutToBeRemoved(const QModelIndex & parent, int start, int end) |
void | rowsInserted(const QModelIndex & parent, int start, int end) |
void | rowsMoved(const QModelIndex & sourceParent, int sourceStart, int sourceEnd, const QModelIndex & destinationParent, int destinationRow) |
void | rowsRemoved(const QModelIndex & parent, int start, int end) |
Protected Functions
void | beginInsertColumns(const QModelIndex & parent, int first, int last) |
void | beginInsertRows(const QModelIndex & parent, int first, int last) |
bool | beginMoveColumns(const QModelIndex & sourceParent, int sourceFirst, int sourceLast, const QModelIndex & destinationParent, int destinationChild) |
bool | beginMoveRows(const QModelIndex & sourceParent, int sourceFirst, int sourceLast, const QModelIndex & destinationParent, int destinationChild) |
void | beginRemoveColumns(const QModelIndex & parent, int first, int last) |
void | beginRemoveRows(const QModelIndex & parent, int first, int last) |
void | beginResetModel() |
void | changePersistentIndex(const QModelIndex & from, const QModelIndex & to) |
void | changePersistentIndexList(const QModelIndexList & from, const QModelIndexList & to) |
QModelIndex | createIndex(int row, int column, void * ptr = 0) const |
QModelIndex | createIndex(int row, int column, quint32 id) const |
void | endInsertColumns() |
void | endInsertRows() |
void | endMoveColumns() |
void | endMoveRows() |
void | endRemoveColumns() |
void | endRemoveRows() |
void | endResetModel() |
QModelIndexList | persistentIndexList() const |
void | reset() |
void | setRoleNames(const QHash & roleNames) |
Protected Slots
Additional Inherited Members
Detailed Description
The QAbstractItemModel class provides the abstract interface for item model classes.
The QAbstractItemModel class defines the standard interface that item models must use to be able to interoperate with other components in the model/view architecture. It is not supposed to be instantiated directly. Instead, you should subclass it to create new models.
If you need a model to use with a QListView or a QTableView, you should consider subclassing QAbstractListModel or QAbstractTableModel instead of this class.
The underlying data model is exposed to views and delegates as a hierarchy of tables. If you do not make use of the hierarchy, then the model is a simple table of rows and columns. Each item has a unique index specified by a QModelIndex.
Every item of data that can be accessed via a model has an associated model index. You can obtain this model index using the index() function. Each index may have a sibling() index; child items have a parent() index.
Each item has a number of data elements associated with it and they can be retrieved by specifying a role (see Qt::ItemDataRole) to the model’s data() function. Data for all available roles can be obtained at the same time using the itemData() function.
Data for each role is set using a particular Qt::ItemDataRole. Data for individual roles are set individually with setData(), or they can be set for all roles with setItemData().
Items can be queried with flags() (see Qt::ItemFlag) to see if they can be selected, dragged, or manipulated in other ways.
If an item has child objects, hasChildren() returns true for the corresponding index.
The model has a rowCount() and a columnCount() for each level of the hierarchy. Rows and columns can be inserted and removed with insertRows(), insertColumns(), removeRows(), and removeColumns().
The model emits signals to indicate changes. For example, dataChanged() is emitted whenever items of data made available by the model are changed. Changes to the headers supplied 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 attached views that they should redisplay any items shown, taking the new structure into account.
The items available through the model can be searched for particular data using the match() function.
To sort the model, you can use sort().
Subclassing
Note: Some general guidelines for subclassing models are available in the Model Subclassing Reference.
When subclassing QAbstractItemModel, at the very least you must implement index(), parent(), rowCount(), columnCount(), and data(). These functions are used in all read-only models, and form the basis of editable models.
You can also reimplement hasChildren() to provide special behavior for models where the implementation of rowCount() is expensive. This makes it possible for models to restrict the amount of data requested by views, and can be used as a way to implement lazy population of model data.
To enable editing in your model, you must also implement setData(), and reimplement flags() to ensure that ItemIsEditable is returned. You can also reimplement headerData() and setHeaderData() to control the way the headers for your model are presented.
The dataChanged() and headerDataChanged() signals must be emitted explicitly when reimplementing the setData() and setHeaderData() functions, respectively.
Custom models need to create model indexes for other components to use. To do this, call createIndex() with suitable row and column numbers for the item, and an identifier for it, either as a pointer or as an integer value. The combination of these values must be unique for each item. Custom models typically use these unique identifiers in other reimplemented functions to retrieve item data and access information about the item’s parents and children. See the Simple Tree Model Example for more information about unique identifiers.
It is not necessary to support every role defined in Qt::ItemDataRole. Depending on the type of data contained within a 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 the Qt::DisplayRole, and well-behaved models should also provide valid information for the Qt::ToolTipRole and Qt::WhatsThisRole. Supporting these roles enables models to be used with standard Qt views. However, for some models that handle highly-specialized data, it may be appropriate to provide data only for user-defined roles.
Models that provide interfaces to resizable data structures can provide implementations of insertRows(), removeRows(), insertColumns(),and removeColumns(). When implementing these functions, it is important to notify any connected views about changes to the model’s dimensions both before and after they occur:
The private signals that these functions emit give attached components the chance to take action before any data becomes unavailable. The encapsulation of the insert and remove operations with these begin and end functions also enables the model to manage persistent model indexes correctly. If you want selections to be handled properly, you must ensure that you call these functions. If you insert or remove an item with children, you do not need to call these functions for the child items. In other words, the parent item will take care of its child items.
To create models that populate incrementally, you can reimplement fetchMore() and canFetchMore(). If the reimplementation of fetchMore() adds rows to the model, beginInsertRows() and endInsertRows() must be called.
Member Function Documentation
QAbstractItemModel:: QAbstractItemModel ( QObject * parent = 0)
Constructs an abstract item model with the given parent.