Существующие классы представления элементов
Источник.
Продолжение. Начало - здесь.
Источник.
Продолжение. Начало - здесь.
Виджеты на основе элементов имеют имена, которые отражают их использование:
QListWidget предоставляет список элементов, QTreeWidget отображает многоуровневую древовидную структуру, а QTableWidget представляет таблицу из ячеек-элементов. Каждый класс наследует поведение класса QAbstractItemView, которое реализует общее поведение для выбора элементов и управления заголовками.Виджеты - списки
Одноуровневые списки элементов обычно отображаются с использованием
QListWidgetи и нескольких QListWidgetItem. Виджет списка создается так же, как и любой другой виджет:QListWidget *listWidget = new QListWidget(this);
Элементы списка могут быть добавлены непосредственно в виджет списка при их создании:
new QListWidgetItem(tr("Sycamore"), listWidget); new QListWidgetItem(tr("Chestnut"), listWidget); new QListWidgetItem(tr("Mahogany"), listWidget);
Они также могут быть созданы без родительского виджета списка и добавлены в список позже:
QListWidgetItem *newItem = new QListWidgetItem; newItem->setText(itemText); listWidget->insertItem(row, newItem);
Каждый элемент в списке может отображать текстовую метку и значок. Цвета и шрифт, используемые для визуализации текста, можно изменить, чтобы обеспечить индивидуальный внешний вид элементов. Всплывающие подсказки, подсказки о состоянии и справка "Что это?" легко настраиваются, для правильной интеграции списка в приложение.
newItem->setToolTip(toolTipText); newItem->setStatusTip(toolTipText); newItem->setWhatsThis(whatsThisText);
По умолчанию элементы в списке представлены в порядке их создания. Списки элементов могут быть отсортированы в соответствии с критериями, заданными в Qt::SortOrder, чтобы создать список элементов, отсортированных в прямом или обратном алфавитном порядке:
Виджеты - деревья
Деревья или иерархические списки элементов реализуются классами
QTreeWidget и QTreeWidgetItem. Каждый элемент в виджете дерева может иметь собственные дочерние элементы и отображать несколько столбцов информации. Древовидные виджеты создаются так же, как и любой другой виджет:QTreeWidget *treeWidget = new QTreeWidget(this);
Прежде чем элементы могут быть добавлены в виджет дерева, необходимо указать количество столбцов. Например, мы можем определить два столбца и создать заголовок для предоставления меток в верхней части каждого столбца:
treeWidget->setColumnCount(2); QStringList headers; headers << tr("Subject") << tr("Default"); treeWidget->setHeaderLabels(headers);
Самый простой способ установить метки для каждого раздела - это предоставить список строк. Для более сложных заголовков вы можете создать элемент дерева, украсить его по своему желанию и использовать его в качестве заголовка виджета дерева.
Элементы верхнего уровня в виджете дерева конструируются с виджетом дерева в качестве родительского виджета. Они могут быть вставлены в произвольном порядке, или вы можете убедиться, что они перечислены в определенном порядке, указав предыдущий элемент при создании каждого элемента:QTreeWidgetItem *cities = new QTreeWidgetItem(treeWidget); cities->setText(0, tr("Cities")); QTreeWidgetItem *osloItem = new QTreeWidgetItem(cities); osloItem->setText(0, tr("Oslo")); osloItem->setText(1, tr("Yes")); QTreeWidgetItem *planets = new QTreeWidgetItem(treeWidget, cities);
Древовидные виджеты работают с элементами верхнего уровня немного иначе, чем с другими элементами из глубины дерева. Элементы могут быть удалены из верхнего уровня дерева путем вызова функции takeTopLevelItem() виджета дерева, но элементы из более низких уровней удаляются путем вызова функции takeChild() их родительского элемента . Элементы вставляются в верхний уровень дерева с помощью функции insertTopLevelItem() . На более низких уровнях дерева используется функция insertChild() родительского элемента.
Легко перемещать предметы между верхним и нижним уровнями дерева. Нам просто нужно проверить, являются ли элементы элементами верхнего уровня или нет, и эта информация предоставляется функцией parent() каждого элемента . Например, мы можем удалить текущий элемент в виджете дерева независимо от его местоположения:
QTreeWidgetItem *parent = currentItem->parent(); int index; if (parent) { index = parent->indexOfChild(treeWidget->currentItem()); delete parent->takeChild(index); } else { index = treeWidget->indexOfTopLevelItem(treeWidget->currentItem()); delete treeWidget->takeTopLevelItem(index); }
Вставка элемента в любое место в дереве виджета происходит по той же схеме::
QTreeWidgetItem *parent = currentItem->parent(); QTreeWidgetItem *newItem; if (parent) newItem = new QTreeWidgetItem(parent, treeWidget->currentItem()); else newItem = new QTreeWidgetItem(treeWidget, treeWidget->currentItem());
Виджеты - таблички
Таблицы элементов, аналогичных тем, которые можно найти в электронных таблицах, построены с использованием
Таблицы могут быть созданы с заданным количеством строк и столбцов, или они могут быть добавлены в таблицу без размера по мере необходимости.QTableWidgetи QTableWidgetItem. Они предоставляют виджет таблицы с прокрутками, с заголовками и элементами для использования в нем.QTableWidget *tableWidget; tableWidget = new QTableWidget(12, 3, this);
Элементы создаются вне таблицы перед добавлением в таблицу в требуемом месте:
QTableWidgetItem *newItem = new QTableWidgetItem(tr("%1").arg( pow(row, column+1))); tableWidget->setItem(row, column, newItem);
В таблицу можно добавить горизонтальные и вертикальные заголовки, создав элементы вне таблицы и используя их в качестве заголовков:
QTableWidgetItem *valuesHeaderItem = new QTableWidgetItem(tr("Values")); tableWidget->setHorizontalHeaderItem(0, valuesHeaderItem);
Обратите внимание, что нумерация строк и столбцов в таблице начинается с нуля.
Общие черты
Существует ряд базовых функций, общих для каждого из удобных классов, которые доступны через одни и те же интерфейсы в каждом классе. Мы представим их в следующих разделах с некоторыми примерами для разных виджетов. Посмотрите на список Model/View classes для каждого из виджетов для получения более подробной информации об использовании каждой используемой функции.
Скрытые эелементы
Иногда полезно иметь возможность скрывать элементы в виджете представления элементов, а не удалять их. Элементы для всех вышеперечисленных виджетов могут быть скрыты и позже показаны снова. Вы можете определить, является ли элемент скрытым, вызвав функцию isItemHidden(), а элементы могут быть скрыты с помощью
Поскольку эта операция основана на элементах, одна и та же функция доступна для всех трех вспомогательных классов.setItemHidden().Выборка
Способ выбора элементов управляется режимом выбора виджета (QAbstractItemView::SelectionMode). Это свойство определяет, может ли пользователь выбрать один или несколько элементов, и, при выборе нескольких элементов, должен ли выбор быть непрерывным диапазоном элементов. Режим выбора работает одинаково для всех вышеперечисленных виджетов.
![]() | Выбор одного элемента: там, где пользователю нужно выбрать один элемент из виджета, SingleSelection режим по умолчанию является наиболее подходящим. В этом режиме текущий элемент и выбранный элемент совпадают. |
![]() | Выбор нескольких элементов: в этом режиме пользователь может переключать состояние выбора любого элемента в виджете, не изменяя существующий выбор, подобно тому, как неэксклюзивные флажки можно переключать независимо. |
![]() | Расширенные возможности выбора: для виджетов, которые часто требуют выбора множества смежных элементов, например, тех, которые находятся в электронных таблицах, требуется режим ExtendedSelection. В этом режиме непрерывные диапазоны элементов в виджете можно выбирать как мышью, так и клавиатурой. Сложный выбор, включающий множество элементов, которые не соседствуют с другими выбранными элементами в виджете, также может быть создан, если используются клавиши-модификаторы.Если пользователь выбирает элемент без использования клавиши-модификатора, существующий выбор очищается. |
Выбранные элементы в виджете читаются с использованием
selectedItems()функции, предоставляющей список соответствующих элементов, которые можно перебирать. Например, мы можем найти сумму всех числовых значений в списке выбранных элементов с помощью следующего кода:const QList<QTableWidgetItem *> selected = tableWidget->selectedItems(); int number = 0; double total = 0; for (QTableWidgetItem *item : selected) { bool ok; double value = item->text().toDouble(&ok); if (ok && !item->text().isEmpty()) { total += value; number++; } }
Обратите внимание, что для режима одиночного выбора текущий элемент будет в выборке. В режимах множественного выбора и расширенного выбора текущий элемент может не находиться в пределах выборки, в зависимости от способа, которым пользователь сформировал выбор.
Поиск
Часто полезно иметь возможность находить элементы в виджете представления элементов либо в качестве разработчика, либо в качестве службы для представления пользователям. Все три удобных класса представления элементов предоставляют общую функцию findItems(), которая делает это наиболее просто и последовательно.
Элементы ищутся по тексту, который они содержат в соответствии с критериями, указанными выбором значений из Qt::MatchFlags. Мы можем получить список подходящих предметов с помощью функции findItems():const QList<QTreeWidgetItem *> found = treeWidget->findItems( itemText, Qt::MatchWildcard); for (QTreeWidgetItem *item : found) { item->setSelected(true); // Show the item->text(0) for each item. }
Приведенный выше код вызывает выбор элементов в виджете - дереве, если они содержат текст, указанный в строке поиска. Этот шаблон также можно использовать в списочных и табличных виджетах.
Использование Drag and Drop с представлениями элементов
Инфраструктура перетаскивания Qt полностью поддерживается платформой model/view. Элементы списков, таблиц и деревьев могут быть перетянуты из представлений, а данные могут быть импортированы или экспортированы как MIME-кодированные данные.
Стандартные представления автоматически поддерживают drag and drop, при котором элементы перемещаются для изменения порядка их расположения при их отображении. По умолчанию, drag and drop не разрешен для этих представлений, так как они настроены для самых простых, наиболее общих случаев применения. Для разрешения перемещения, некоторые свойства представлений должны быть разрешены, а сами элементы также должны позволить перетаскивание.
Требования к модели, которая позволяет только экспортировать элементы из представления и не позволяет переносить в нее данные, меньше, чем требования для полностью включенной модели перетаскивания.
Более подробно о разрешениях для перетаскивания в новых моделях можно прочитать в Model Subclassing Reference .Использование вспомогательных представлений
Каждый тип элемента, используемого с QListWidget, QTableWidget, и QTreeWidget, настроен для использования для использования разных флагов по умолчанию. К примеру, каждый из QListWidgetItem или QTreeWidgetItem первоначально разрешен, разрешен для чекинга, выборки, и может быть использован в качестве источника в операции drag and drop; всякий QTableWidgetItem также может быть редактируемым и использован в качестве приемника операции drag and drop.
Хотя все стандартные элементы имеют один или оба флага, установленные для перетаскивания, вам обычно нужно установить различные свойства в самом представлении, чтобы воспользоваться встроенной поддержкой drag and drop:
- Для разрешения перетаскивания элемента, установите свойство представления dragEnabled в
true. - Чтобы разрешить пользователю сбрасывать на представление внешние или внутренние элементы, установите у viewport() свойство acceptDrops в
true. - Чтобы показать пользователю, куда будет перетаскиваться элемент, который в данный момент перетаскивается, при его сбрасывании, установите свойство представления showDropIndicator . Это предоставляет пользователю видеть постоянно обновляемую информацию о размещении элементов в представлении.
К примеру, нам нужно разрешить drag and drop в виджете-списке:
QListWidget *listWidget = new QListWidget(this); listWidget->setSelectionMode(QAbstractItemView::SingleSelection); listWidget->setDragEnabled(true); listWidget->viewport()->setAcceptDrops(true); listWidget->setDropIndicatorShown(true);
В результате получается виджет-список, который позволяет копировать элементы внутри представления и даже позволяет пользователю перетаскивать элементы между представлениями, содержащими данные одного типа. В обеих ситуациях элементы копируются, а не перемещаются.
Чтобы разрешить пользователю перемещать элементы внутри представления, нужно настроить свойство виджета dragDropMode:
listWidget->setDragDropMode(QAbstractItemView::InternalMove);
Использование классов model/view
Настройка вида для перетаскивания выполняется по той же схеме, что и во вспомогательных видах. Например, QListView можно настроить так же, как QListWidget:
QListView *listView = new QListView(this); listView->setSelectionMode(QAbstractItemView::ExtendedSelection); listView->setDragEnabled(true); listView->setAcceptDrops(true); listView->setDropIndicatorShown(true);
Поскольку доступ к данным, отображаемым представлением, контролируется моделью, используемая модель также должна обеспечивать поддержку операций перетаскивания. Действия, поддерживаемые моделью, могут быть определены путем переопределения функции QAbstractItemModel::supportDropActions (). Например, операции копирования и перемещения активируются с помощью следующего кода:
Qt::DropActions DragDropListModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; }
Хотя может быть задана любая комбинация значений из Qt::DropActions, должна быть написана модель для их поддержки. Например, чтобы разрешить правильное использование Qt::MoveAction с моделью-списком, модель должна обеспечивать реализацию QAbstractItemModel::removeRows() либо напрямую, либо путем наследования реализации от своего базового класса.
Разрешение drag and drop для элементов
Модели указывают представлениям, какие элементы можно перетаскивать, а какие будут принимать drop, путем переопределения функции QAbstractItemModel::flags() для предоставления подходящих флагов.
Например, модель, которая предоставляет простой список на основе QAbstractListModel, может включать перетаскивание для каждого из элементов, гарантируя, что возвращаемые флаги содержат значения Qt::ItemIsDragEnabled и Qt::ItemIsDropEnabled:Qt::ItemFlags DragDropListModel::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = QStringListModel::flags(index); if (index.isValid()) return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; else return Qt::ItemIsDropEnabled | defaultFlags; }
Обратите внимание, что элементы можно поместить на верхний уровень модели, но перетаскивание доступно только для допустимых элементов.
В приведенном выше коде, поскольку модель является производной от QStringListModel , мы получаем набор флагов по умолчанию, вызывая ее реализацию функции flags().Кодирование экспортируемых данных
Когда элементы данных экспортируются из модели в операции перетаскивания, они кодируются в соответствующий формат, соответствующий одному или нескольким типам MIME. Модели объявляют типы MIME, которые они могут использовать для предоставления элементов, путем переопределения функции QAbstractItemModel :: mimeTypes (), возвращающей список стандартных типов MIME.
Например, модель, которая предоставляет только простой текст, обеспечит следующую реализацию:
QStringList DragDropListModel::mimeTypes() const { QStringList types; types << "application/vnd.text.list"; return types; }
Модель также должна предоставлять код для кодирования данных в объявленном формате. Это достигается путем переопределения функции QAbstractItemModel::mimeData() для предоставления объекта QMimeData, как и в любой другой операции перетаскивания.
Следующий код показывает, как каждый элемент данных, соответствующий заданному списку индексов, кодируется в виде простого текста и сохраняется в объекте QMimeData.QMimeData *DragDropListModel::mimeData(const QModelIndexList &indexes) const { QMimeData *mimeData = new QMimeData; QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); for (const QModelIndex &index : indexes) { if (index.isValid()) { QString text = data(index, Qt::DisplayRole).toString(); stream << text; } } mimeData->setData("application/vnd.text.list", encodedData); return mimeData; }
Поскольку для функции предоставляется список модельных индексов, этот подход является достаточно общим, чтобы его можно было использовать как в иерархических, так и в неирахархических моделях.
Обратите внимание, что пользовательские типы данных должны быть объявлены как мета-объекты, и для них должны быть реализованы потоковые операторы. Подробности смотрите в описании класса QMetaObject .
Вставка dropped данных в модель
Способ, которым любая данная модель обрабатывает dropped данные, зависит как от ее типа (список, таблица или дерево), так и от того, каким образом ее содержимое, скорее всего, будет представлено пользователю. Как правило, подход, принятый для учета dropped данных, должен быть наиболее подходящим для базового хранилища данных модели.
Различные типы моделей имеют тенденцию обрабатывать сброшенные данные по-разному. Модели списков и таблиц обеспечивают только плоскую структуру, в которой хранятся элементы данных. В результате они могут вставлять новые строки (и столбцы), когда данные сброшены на существующий элемент в представлении, или они могут перезаписывать содержимое элемента в модели, используя некоторые из предоставленных данных. Древовидные модели часто могут добавлять дочерние элементы, содержащие новые данные, в свои базовые хранилища данных и, следовательно, будут вести себя более предсказуемо в том, что касается пользователя.
Сброшенные данные обрабатываются переопределением модели QAbstractItemModel::dropMimeData(). Например, модель, которая обрабатывает простой список строк, может обеспечить реализацию, которая обрабатывает данные, перенесенные на существующие элементы отдельно, для данных, сброшенных на верхний уровень модели (т. Е. На недопустимый элемент).
Модели отказать в обработке сброса на отдельные элементы, или, в зависимости от сбрасываемых данных и принимаемых элементов, путем переопределения QAbstractItemModel::canDropMimeData().
Сначала модель должна убедиться, что операция должна выполняться, предоставленные данные представлены в формате, который можно использовать, и что их назначение в модели является допустимым:
bool DragDropListModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(action); Q_UNUSED(row); Q_UNUSED(parent); if (!data->hasFormat("application/vnd.text.list")) return false; if (column > 0) return false; return true; } bool DragDropListModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (!canDropMimeData(data, action, row, column, parent)) return false; if (action == Qt::IgnoreAction) return true;
Простая модель списка строк с одним столбцом может указывать на сбой, если предоставленные данные не являются простым текстом или если номер столбца, указанный для сбрасывания, недопустим.
Данные, которые будут вставлены в модель, обрабатываются по-разному, в зависимости от того, сброшены они в существующий элемент или нет. В этом простом примере мы хотим разрешить переброс между существующими элементами, до первого элемента в списке и после последнего элемента.
Когда происходит сброс, индекс модели, соответствующий родительскому элементу, будет либо действительным, что указывает на то, что сброс произошел на элементе, либо будет недействительным, указывая, что сброс произошло где-то в представлении, которое соответствует верхнему уровню модели.
int beginRow; if (row != -1) beginRow = row;
Сначала мы изучаем предоставленный номер строки, чтобы увидеть, сможем ли мы использовать его для вставки элементов в модель, независимо от того, действителен ли родительский индекс или нет.
else if (parent.isValid()) beginRow = parent.row();
Если родительский модельный индекс действителен, сброс произошел для элемента. В этой простой модели списка мы узнаем номер строки элемента и используем это значение для вставки пропущенных элементов в верхний уровень модели.
else beginRow = rowCount(QModelIndex());
Когда сброс выполняется в каком-либо ином месте представления, а номер строки не может использоваться, мы добавляем элементы на верхний уровень модели.
В иерархических моделях, когда сброс происходит на элементе, было бы лучше вставить новые элементы в модель как дочерние элементы этого элемента. В простом примере, показанном здесь, модель имеет только один уровень, поэтому такой подход не подходит.
Декодирование импортируемых данных
Каждая реализация dropMimeData() также должна декодировать данные и вставлять их в базовую структуру данных модели.
Для простой модели списка строк закодированные элементы могут быть декодированы и переданы в QStringList:QByteArray encodedData = data->data("application/vnd.text.list"); QDataStream stream(&encodedData, QIODevice::ReadOnly); QStringList newItems; int rows = 0; while (!stream.atEnd()) { QString text; stream >> text; newItems << text; ++rows; }
Затем строки могут быть вставлены в основное хранилище данных. Для согласованности это можно сделать через собственный интерфейс модели:
insertRows(beginRow, rows, QModelIndex()); for (const QString &text : qAsConst(newItems)) { QModelIndex idx = index(beginRow, 0, QModelIndex()); setData(idx, text); beginRow++; } return true; }
Обратите внимание, что модель обычно должна предоставлять реализации функций QAbstractItemModel::insertRows() и QAbstractItemModel::setData().
Прокси модели
В системе model/view, данные, предоставляемые одной моделью, могут совместно использоваться любым числом представлений, и каждое из них может представлять одну и ту же информацию совершенно разными способами. Пользовательские представления и делегаты - это эффективные способы предоставления совершенно разных представлений одних и тех же данных. Однако приложениям часто требуется предоставить обычные представления для обработанных версий одних и тех же данных, например, отсортированные по-разному представления для списка элементов.
Хотя представляется целесообразным выполнять операции сортировки и фильтрации в качестве внутренних функций представлений, этот подход не позволяет нескольким представлениям совместно использовать результаты таких потенциально дорогостоящих операций. Альтернативный подход, включающий сортировку внутри самой модели, приводит к аналогичной проблеме, когда каждое представление должно отображать элементы данных, которые организованы в соответствии с самой последней операцией обработки.
Чтобы решить эту проблему, структура model/view использует прокси-модели для управления информацией, предоставляемой между отдельными моделями и представлениями. Прокси-модели - это компоненты, которые ведут себя как обычные модели с точки зрения представления и получают доступ к данным из исходных моделей от имени этого представления. Сигналы и слоты, используемые структурой model/view, гарантируют, что каждое представление обновляется надлежащим образом, независимо от того, сколько прокси-моделей размещено между собой и исходной моделью.
Применение прокси моделей
Прокси-модели могут быть вставлены между существующей моделью и любым количеством представлений. Qt поставляется со стандартной прокси-моделью QSortFilterProxyModel , которая обычно создается и используется напрямую, но также может быть разделена на подклассы для обеспечения настраиваемого поведения фильтрации и сортировки. Класс QSortFilterProxyModel можно использовать следующим образом:
QSortFilterProxyModel *filterModel = new QSortFilterProxyModel(parent); filterModel->setSourceModel(stringListModel); QListView *filteredView = new QListView; filteredView->setModel(filterModel);
Поскольку прокси-модели наследуются от QAbstractItemModel , они могут быть связаны с любым видом представления и могут совместно использоваться представлениями. Они также могут быть использованы для обработки информации, полученной из других моделей прокси в конвейерной схеме.
Класс QSortFilterProxyModel предназначен для создания экземпляров и использования непосредственно в приложениях. Более специализированные прокси-модели могут быть созданы путем создания подклассов этих классов и реализации необходимых операций сравнения.Настройка прокси моделей
Generally, the type of processing used in a proxy model involves mapping each item of data from its original location in the source model to either a different location in the proxy model. In some models, some items may have no corresponding location in the proxy model; these models are filtering proxy models. Views access items using model indexes provided by the proxy model, and these contain no information about the source model or the locations of the original items in that model.
QSortFilterProxyModel enables data from a source model to be filtered before being supplied to views, and also allows the contents of a source model to be supplied to views as pre-sorted data.
Пользовательские модели фильтрации
Класс QSortFilterProxyModel предоставляет модель фильтрации, которая является довольно универсальной и которая может использоваться в различных типичных ситуациях. Для опытных пользователей QSortFilterProxyModel может быть сабклассирован с целью предоставления механизма, позволяющего реализовывать пользовательские фильтры.
Наследники QSortFilterProxyModel могут переопределить две виртуальные функции, которые вызываются всякий раз, когда запрашивается или используется индекс модели из прокси-модели:
- filterAcceptsColumn() используется для фильтрации определенных столбцов из части исходной модели.
- filterAcceptsRow() используется для фильтрации определенных строк из части исходной модели.
Пользовательская сортировка моделей
Экземпляры QSortFilterProxyModel используют встроенную функцию Qt - qStableSort() для установки отображений между элементами в исходной модели и в прокси-модели, позволяя просматривать отсортированную иерархию элементов в представлениях без изменения структуры исходной модели. Чтобы обеспечить настраиваемое поведение сортировки, для выполнения пользовательских сравнений переопределите функция lessThan().
Справочник по созданию наследников моделей
- Наследники модели должны предоставлять реализации многих виртуальных функций, определенных в базовом классе QAbstractItemModel. Количество этих функций, которые необходимо реализовать, зависит от типа модели - предоставляет ли она представлениям простой список, таблицу или сложную иерархию элементов. Модели, которые наследуются от QAbstractListModel и QAbstractTableModel, могут использовать преимущества реализаций функций по умолчанию, предоставляемых этими классами. Модели, которые представляют элементы данных в древовидных структурах, должны предоставлять реализации для многих виртуальных функций в QAbstractItemModel.Функции, которые необходимо реализовать в подклассе модели, можно разделить на три группы:
- Обработка данных элементов: во всех моделях должны быть реализованы функции, позволяющие представлениям и делегатам запрашивать размерности модели, исследовать элементы и получать данные.
- Навигация и создание индекса. Иерархические модели должны предоставлять функции, которые представления могут вызывать для навигации по древовидным структурам, которые они представляют, и получения модельных индексов для элементов.
- Поддержка перетаскивания и обработка типов MIME. Модели наследуют функции, которые контролируют выполнение внутренних и внешних операций перетаскивания. Эти функции позволяют описывать элементы данных в терминах типов MIME, понятных для других компонентов и приложений.
Обработка элементов данных
Модели могут предоставлять различные уровни доступа к предоставляемым ими данным: они могут быть простыми readonly компонентами, некоторые модели могут поддерживать операции изменения размера, а другие могут разрешать редактирование элементов.
Read-Only доступ
Чтобы обеспечить доступ только для чтения к данным, предоставляемым моделью, в подклассе модели должны быть реализованы следующие функции:
| flags() | Используется другими компонентами для получения информации о каждом элементе, предоставленном моделью. Во многих моделях комбинация флагов должна включать Qt::ItemIsEnabled и Qt::ItemIsSelectable. |
| data() | Используется для предоставления данных об элементах представлениям и делегатам. Обычно модели должны предоставлять данные только для роли Qt::DisplayRole и любых пользовательских ролей, специфичных для приложения, но также рекомендуется предоставлять данные дляQt::ToolTipRole , Qt::AccessibleTextRole и Qt::AccessibleDescriptionRole. См. документацию по Qt::ItemDataRole для получения информации о типах, связанных с каждой ролью. |
| headerData() | Предоставляет представлениям информацию, отображаемую в их заголовках. Информация извлекается только представлениями, которые могут отображать информацию заголовка. |
| rowCount() | Предоставляет количество строк данных, представленных моделью. |
Эти четыре функции должны быть реализованы во всех типах моделей, включая модели списков ( подклассы QAbstractListModel ) и модели таблиц ( подклассы QAbstractTableModel ).
Кроме того, следующие функции должны быть реализованы в прямых подклассах QAbstractTableModel и QAbstractItemModel :
| columnCount() | Предоставляет количество столбцов данных, представленных моделью. Модели списков не предоставляют эту функцию, потому что она уже реализована в QAbstractListModel . |
Редактируемые элементы
Редактируемые модели позволяют изменять элементы данных, а также могут предоставлять функции, позволяющие вставлять и удалять строки и столбцы. Для включения редактирования должны быть правильно реализованы следующие функции:
| flags() | Необходимо вернуть соответствующую комбинацию флагов для каждого элемента. В частности, значение, возвращаемое этой функцией, должно включать Qt::ItemIsEditable в дополнение к значениям, применяемым к элементам в модели только для чтения. | |
| setData() | Используется для изменения элемента данных, связанных с указанным модельным индексом. Чтобы иметь возможность принимать пользовательский ввод, предоставляемый элементами пользовательского интерфейса, эта функция должна обрабатывать данные, связанные с Qt::EditRole. Реализация также может принимать данные, связанные со многими различными типами ролей, указанных в Qt::ItemDataRole . После изменения элемента данных модели должны послать сигнал dataChanged(), чтобы проинформировать другие компоненты об изменении. | |
| setHeaderData() |
|
Изменение размеров моделей
Все типы моделей могут поддерживать вставку и удаление строк. Табличные модели и иерархические модели также могут поддерживать вставку и удаление столбцов. Важно уведомить другие компоненты об изменениях размеров модели как до, так и после их возникновения. В результате могут быть реализованы следующие функции, позволяющие изменить размер модели, но реализации должны гарантировать, что соответствующие функции вызываются для уведомления о вложенных представлениях и делегатах:
| insertRows() | Используется для добавления новых строк и элементов данных во все типы моделей. Реализации должны вызывать beginInsertRows() перед вставкой новых строк в любые базовые структуры данных и вызывать endInsertRows() сразу после этого. |
| removeRows() | Используется для удаления строк и содержащихся в них элементов данных из всех типов моделей. Реализации должны вызвать beginRemoveRows(), прежде чем строки будут удалены из любых базовых структур данных и вызов endRemoveRows() сразу же после этого. |
| insertColumns() | Используется для добавления новых столбцов и элементов данных в модели таблиц и иерархические модели. Реализации должны вызывать beginInsertColumns() перед вставкой новых столбцов в любые базовые структуры данных и сразу же после этого вызывать endInsertColumns(). |
| removeColumns() | Используется для удаления столбцов и элементов данных, которые они содержат, из моделей таблиц и иерархических моделей. Реализации должны вызывать beginRemoveColumns() перед удалением столбцов из любых базовых структур данных и вызывать endRemoveColumns() сразу после этого. |
Как правило, эти функции должны возвращать true, если операция прошла успешно. Однако могут быть случаи, когда операция была выполнена только частично; например, если можно вставить меньше указанного количества строк. В таких случаях модель должна возвращать значение false, чтобы указать, что любые подключенные компоненты не могут обработать ситуацию.
Сигналы, испускаемые функциями, вызываемыми в реализациях API изменения размера, дают присоединенным компонентам возможность предпринять действия до того, как какие-либо данные станут недоступными. Инкапсуляция операций вставки и удаления с помощью функций начала и конца также позволяет модели правильно управлять постоянными модельными индексами.
Обычно функции начала и окончания способны информировать другие компоненты об изменениях в базовой структуре модели. Для более сложных изменений в структуре модели, например, внутренней реорганизации, сортировки данных или любых других структурных изменений, необходимо выполнить следующую последовательность действий:
- Послать сигнал layoutAboutToBeChanged().
- Обновить внутренние данные, которые представляют структуру модели.
- Обновить постоянные индексы, используя changePersistentIndexList().
- Послать сигнал layoutChanged() сигнал.
- .
Эта последовательность может использоваться для любого структурного обновления вместо более высокоуровневых и удобных защищенных методов. Например, если модели из двух миллионов строк необходимо удалить все нечетные строки, то это 1 миллион исключительных диапазонов по 1 элементу в каждом. Можно было бы использовать beginRemoveRows и endRemoveRows 1 миллион раз, но это, очевидно, было бы неэффективно. Вместо этого это может быть сигнализировано как одно изменение макета, которое обновляет все необходимые постоянные индексы сразу.
Ленивое заполнение данных модели
Ленивое заполнение данных модели позволяет эффективно откладывать запросы информации о модели до тех пор, пока она фактически не понадобится представлениям.
Некоторые модели должны получать данные из удаленных источников или выполнять трудоемкие операции для получения информации об организации данных. Поскольку представления обычно запрашивают как можно больше информации для точного отображения данных модели, может быть полезно ограничить объем возвращаемой им информации, чтобы уменьшить ненужные последующие запросы данных.
В иерархических моделях, где определение количества дочерних элементов данного элемента является дорогостоящей операцией, полезно убедиться, что реализация rowCount() модели вызывается только при необходимости. В таких случаях функция hasChildren() может быть переопределена, чтобы обеспечить недорогой способ проверки представлений на наличие дочерних элементов и, в случае QTreeView, нарисовать соответствующее оформление для их родительского элемента.
Независимо от того, возвращается ли переопределение hasChildren()
Если известно, что у многих элементов будут дочерние элементы, иногда полезно использовать hasChildren() для безусловного возврата true или false, для представления может не быть необходимости вызывать rowCount(), чтобы выяснить, сколько дочерних элементов присутствует. Например, QTreeView не нужно знать, сколько существует дочерних элементов, если родительский элемент не был развернут, чтобы показать их.true. Это гарантирует, что каждый элемент может быть позже исследован для детей, при этом начальная популяция данных модели будет максимально быстрой. Единственным недостатком является то, что элементы без дочерних элементов могут отображаться некорректно в некоторых представлениях, пока пользователь не попытается просмотреть скрытые дочерние элементы.Навигация и создание модельных индексов
Иерархические модели должны предоставлять функции, которые представления могут вызывать для навигации по древовидным структурам, которые они представляют, и получения модельных индексов для элементов.
Родители и дети
Поскольку структура, предоставляемая представлениям, определяется базовой структурой данных, каждый наследник класса модели может создавать свои собственные модельные индексы, предоставляя реализации следующих функций:
| index() | Учитывая модельный индекс для родительского элемента, эта функция позволяет представлениям и делегатам получать доступ к дочерним элементам этого элемента. Если допустимый дочерний элемент, соответствующий указанной строке, столбцу и родительскому модельному индексу, не найден, функция должна вернуть QModelIndex(), который является инвалидным модельным индексом. |
| parent() | Предоставляет модельный индекс, соответствующий родительскому элементу любого данного дочернего элемента. Если указанный индекс модели соответствует элементу верхнего уровня в модели или если в модели нет допустимого родительского элемента, функция должна вернуть инвалидный модельный индекс, создаваемый с помощью пустого конструктора QModelIndex(). |
Обе функции выше используют фабричную функцию createIndex() для генерации индексов для использования другими компонентами. Обычно модели предоставляют некоторый уникальный идентификатор для этой функции, чтобы впоследствии можно было повторно связать индекс модели с соответствующим элементом.
Поддержка Drag and drop и обработка типов MIME
Классы системы model/view поддерживают операции перетаскивания, обеспечивая поведение по умолчанию, достаточное для многих приложений. Однако также можно настроить способ кодирования элементов во время операций перетаскивания, независимо от того, копируются они или перемещаются по умолчанию и как они вставляются в существующие модели.
Кроме того, существующие классы представлений реализуют специализированное поведение, которое должно точно соответствовать ожидаемому существующими разработчиками. В разделе Convenience Views рассматривается это поведение.
По умолчанию встроенные модели и представления используют внутренний тип MIME (
application/x-qabstractitemmodeldatalist) для передачи информации о модельных индексах. Здесь указываются данные для списка элементов, содержащие номера строк и столбцов каждого элемента, а также информацию о ролях, которые поддерживает каждый элемент.
Данные, закодированные с использованием этого MIME-типа, могут быть получены вызовом QAbstractItemModel::mimeData() с QModelIndexList, содержащим элементы, подлежащие сериализации.
При реализации поддержки перетаскивания в пользовательской модели можно экспортировать элементы данных в специализированных форматах, реализуя следующую функцию:| mimeData() | Эта функция может быть переопределена для возврата данных в форматах, отличных от application/x-qabstractitemmodeldatalist внутреннего типа MIME по умолчанию .Подклассы могут получить объект QMimeData по умолчанию из базового класса и добавить в него данные в дополнительных форматах. |
Для многих моделей полезно предоставлять содержимое элементов в общем формате, представленном типами MIME, такими как
text/plainи image/png. Обратите внимание, что изображения, цвета и документы HTML могут быть легко добавлены в объект QMimeData с помощью функций QMimeData::setImageData(), QMimeData::setColorData() и QMimeData::setHtml().Прием dropped данных
Когда операция drag and drop выполняется над представлением, запрашивается базовая модель, чтобы определить, какие типы операций она поддерживает и какие типы MIME она может принять. Эта информация предоставляется функциями QAbstractItemModel::supportedDropActions() и QAbstractItemModel::Mimetypes(). Модели, которые не переопределяют реализации, предоставляемые QAbstractItemModel, поддерживают операции копирования и внутренний тип MIME по умолчанию для элементов.
Когда сериализованные данные элемента помещаются в представление, данные вставляются в текущую модель с использованием ее реализации QAbstractItemModel::dropMimeData(). Реализация этой функции по умолчанию никогда не перезапишет никакие данные в модели; вместо этого он пытается вставить элементы данных либо как родные элементы, либо как дочерние элементы этого элемента.
Чтобы воспользоваться преимуществами реализации QAbstractItemModel по умолчанию для встроенного MIME-типа, новые модели должны предоставлять повторные реализации следующих функций:
| insertRows() | Эти функции позволяют модели автоматически вставлять новые данные, используя существующую реализацию, предоставляемую QAbstractItemModel::dropMimeData(). |
| insertColumns() | |
| setData() | Позволяет заполнять новые строки и столбцы элементами. |
| setItemData() | Эта функция обеспечивает более эффективную поддержку для заполнения новых элементов. |
Чтобы принять другие формы данных, должны быть переопределены следующие функции:
| supportedDropActions() | Используется для возврата комбинации drop actions (действий перетаскивания) с указанием типов операций перетаскивания, которые принимает модель. |
| mimeTypes() | Используется для возврата списка типов MIME, которые могут быть декодированы и обработаны моделью. Как правило, типы MIME, которые поддерживаются для ввода в модель, совпадают с теми, которые она может использовать при кодировании данных для использования внешними компонентами. |
| dropMimeData() | Выполняет фактическое декодирование данных, передаваемых с помощью операций перетаскивания, определяет, где в модели они будут установлены, и вставляет новые строки и столбцы, где это необходимо. Как эта функция реализована в подклассах, зависит от требований к данным, предоставляемым каждой моделью. |
Если реализация функции dropMimeData () изменяет размеры модели, вставляя или удаляя строки или столбцы, или если элементы данных изменяются, необходимо позаботиться о том, чтобы все соответствующие сигналы излучались. Может быть полезно просто вызывать повторные реализации других функций в подклассе, таких как setData () , insertRows () и insertColumns () , чтобы убедиться, что модель ведет себя согласованно.
Для обеспечения правильной работы операций перетаскивания важно переопределить следующие функции, которые удаляют данные из модели:
Для получения дополнительной информации о перетаскивании с представлениями элементов, обратитесь к Using drag and drop with item views.
Готовые представления
Готовые представления (QListWidget, QTableWidget, и QTreeWidget) перекрывают поведение default drag по умолчанию, для обеспечения меньшей гибкости, но более естественного поведения, которое подходит для большинства приложений. К примеру, поскольку более распространено помещать данные в ячейки в QTableWidget , заменяя существующее содержимое передаваемыми данными, базовая модель будет устанавливать данные целевых элементов, а не вставлять новые строки и столбцы в модель. Для получения дополнительной информации о перетаскивании в удобных видах вы можете посмотреть документ Using drag and drop with item views.
Оптимизация производительности при работе с большим объемом данных
Функция canFetchMore() проверяет, есть ли еще данные у родителя, и возвращает соответственно true или false. Функция fetchMore() получает данные на основе спецификации родителя. Обе функции могут комбинироваться, например, в запросах к базе данных для инкрементального заполнения данных в QAbstractItemModel. Мы переопределяем canFetchMore(),чтобы казать, что данные могут быть получены, и fetchMore() для заполнения модели по мере надобности.
Другим примером могут быть динамически заполненные древовидные модели, в которых мы переопределяем fetchMore() при разворачивании ветви в древовидной модели.
Если ваше переопределение fetchMore() добавляет строки в модель, нужно вызвать beginInsertRows() и endInsertRows() . Кроме того, необходимо переопределить canFetchMore () и fetchMore (), так как их реализация по умолчанию возвращает false и ничего не делает.
Классы Model/View
Данные классы используют шаблон проектирования model/view, в котором данные (в модели) хранятся отдельно от способа представления данных и манипулирования ими пользователем (в представлении).
Классы абстрактного интерфейса к элементам модели
| |
Абстрактная модель, от которой можно наследоваться для создания одноразмерных списочных моделей
| |
Абстрактная модель, от которой можно наследоваться для создания одноразмерных списочных моделей
| |
Для поиска данных в модели данных
| |
Для поиска данных в модели данных
| |
Базовый класс для прокси элементов моделей, которые могут быть отсортированы, отфильтрованы или обработаны иначе
| |
Прокси для множества моделей, объединяющие их строки
| |
Прокси неизменяемых моделей
| |
Управляет информацией о выбранных элементах в модели
| |
Отслеживает выбранные элементы представления
| |
Управляет информацией о выбранных элементах в модели
| |
Поддержка сортировки и фильтрации данных, передаваемых между моделью и представлением
| |
Модель, которая предоставляет представлению строки
| |
Элемент, использующий класс QStandardItemModel
| |
Обобщенная модель для хранения пользовательских данных
| |
Для отображения и редактирования элементов данных модели
| |
Базовая функциональность классов представления элементов
| |
Реализация Model/view для представления столбца
| |
Сопоставление секции модели данных модели с виджетами
| |
Заголовок строки или столбца представлений
| |
Средства отображения и редактирования для элементов данных из модели
| |
Позволяет создавать создатель редактора элемента без наследования QItemEditorCreatorBase
| |
Абстрактный базовый класс, от которого следует наследоваться при реалитзации новых создателей редакторов элементов
| |
Виджеты для редактирования данных элемента в представлениях и делегатах
| |
Предоставляет возможность регистрировать виджеты без необходимости создавать подкласс QItemEditorCreatorBase
| |
Представление модели в виде списка строк или иконок
| |
Списковый виджет- представление
| |
Элемент для использования в классе-представлении QListWidget
| |
Средства отображения и редактирования для элементов данных из моделиDisplay
| |
Реализация представления model/view по умолчанию для табличнойго представления
| |
Таблично представление на основе элементов с моделью по умолчанию
| |
Элемент для использования совместно с классом QTableWidget
| |
Способ взаимодействия с выбором в модели без использования модельных индексов и модели выбора
| |
Реализация иерархического представления по умолчанию для в model/view
| |
Представление дерева, которое использует предопределенную иерархическую модель
| |
Элемент для использования совместно со вспомогательным классом QTreeWidget
| |
Способ перебрать все элементы в экземпляре QTreeWidget instance
| |
Модель данных для локальных файловых систем
|
Примеры к разделу
См. также Item Views Puzzle Example.



Комментариев нет:
Отправить комментарий