Qt:Документация 4.3.2/model-view-creating-models

Материал из Wiki.crossplatform.ru

Перейти к: навигация, поиск
40px Внимание: Актуальная версия перевода документации находится здесь

__NOTOC__

Image:qt-logo.png

Главная · Все классы · Основные классы · Классы по группам · Модули · Функции

Image:trolltech-logo.png

[Предыдущая: Классы моделей ] [ Содержание ] [Следующая: Классы представлений ]

Содержание

Создание новых моделей

Введение

Разделение функциональных возможностей между компонентами архитектуры модель/представление позволяет создавать новые модели, способные использовать преимущества существующих представлений. Такой подход позволяет представлять данные из разнообразных источников, используя стандартные графические компоненты пользовательского интерфейса, такие как QListView, QTableView и QTreeView.

Класс QAbstractItemModel предоставляет интерфейс, который достаточно гибок для поддержки источников данных, хранящих информацию в иерархических структурах, позволяющих вставлять, удалять и изменять данные, или сортировать их различными способами. А также предоставляет поддержку операций drag and drop.

Классы QAbstractListModel и QAbstractTableModel предоставляют поддержку интерфейсов для более простых неиерархических структур данных, и более легки для использования в качестве отправной точки для моделей простых моделей списков и таблиц.

В этой главе мы создадим простую модель только-для-чтения для исследования основных принципов архитектуры модель/представление. Позже в этой главе, мы приспособим эту модель для того, чтобы данные могли быть изменены пользователем.

Для получения примера более сложной модели, смотрите пример Простая модель дерева.

Минимальные требования, которым должны удовлетворять подклассы QAbstractItemModel, описаны в документе Создание собственных моделей.

Разработка модели

При создании новой модели для существующей структуры данных очень важно решить, какой тип модели лучше всего подходит для обеспечения интерфейса к данным. Если структура данных может быть определена как список или таблица элементов, вы можете создать подкласс QAbstractListModel или QAbstractTableModel, так как эти классы предоставляют подходящие реализации функций по умолчанию.

Однако, если базовая структура данных может быть представлена только в виде иерархической древовидной структуры, возникает необходимость в создании подкласса QAbstractItemModel. Такой подход показан в примере Простая модель дерева.

В этой главе мы реализуем простую модель, основанную на списке строк, для создания которой идеальной основой является класс QAbstractListModel.

Безотносительно формы, которую принимает основная структура данных, хорошим тоном в специализированных моделях является добавление стандартного API QAbstractItemModel к тому, который предоставляет более естественный доступ к структуре данных. Это облегчает заполнение модели данными, но кроме того позволяет другим компонентам архитектуры модель/представление взаимодействовать с моделью, используя стандартный API. Нижеприведенная модель предоставляет пользовательский конструктор исключительно с этой целью.

Пример модели только-для-чтения

Реализованная здесь модель - это простая неиерархическая модель только-для-чтения, основанная на стандартном классе QStringListModel. Она имеет QStringList в качестве внутреннего хранилища данных и реализует лишь самое необходимое для функционирования модели. Для облегчения реализации, мы создаем подкласс QAbstractListModel так как он определяет удобное поведение по умолчанию для моделей списков и предоставляет более простой интерфейс, чем класс QAbstractItemModel.

При реализации модели следует помнить, что QAbstractItemModel не хранит данных, он лишь предоставляет интерфейс, используемый представлениями для доступа к данным. Для минимальной модели только-для-чтения, необходимы реализации лишь нескольких функций, которые предоставлены по умолчанию для большинства интерфейсов. Декларация класса следующая:

 class StringListModel : public QAbstractListModel
 {
     Q_OBJECT
 
 public:
     StringListModel(const QStringList &strings, QObject *parent = 0)
         : QAbstractListModel(parent), stringList(strings) {}
 
     int rowCount(const QModelIndex &parent = QModelIndex()) const;
     QVariant data(const QModelIndex &index, int role) const;
     QVariant headerData(int section, Qt::Orientation orientation,
                         int role = Qt::DisplayRole) const;
 
 private:
     QStringList stringList;
 };

Кроме конструктора модели, мы должны реализовать только две функции: rowCount(), возвращающую количество строк в модели, и data(), возвращающую элемент данных, соответствующий определенному модельному индексу.

Хорошо работающие модели также реализуют headerData() для получения представлениями деревьев и таблиц чего-либо для отображения в их заголовках.

Обратите внимание на то, что это неиерархическая модель, поэтому мы не должны беспокоиться о родительско-дочерних отношениях. Если наша модель иерархическая, мы также должны реализовать функции index() и parent().

Список строк хранится в закрытой переменной-члене stringList.

Измерения модели

Мы хотим, чтобы количество строк в модели было таким же, что и количество элементов в списке строк. Помня об этом, мы реализуем функцию rowCount():

 int StringListModel::rowCount(const QModelIndex &parent) const
 {
     return stringList.count();
 }

Так как модель неиерархическая, мы можем спокойно игнорировать модельный индекс, соответствующий родительскому элементу. По умолчанию, модели, производные от QAbstractListModel, содержат только один столбец, поэтому нам не нужно повторно реализовывать функцию columnCount().

Заголовки и данные модели

В качестве элементов в представлении мы хотим возвратить строки из списка строк. Функция data() ответственна за возвращение элемента данных, соответствующего аргументу-индексу:

 QVariant StringListModel::data(const QModelIndex &index, int role) const
 {
     if (!index.isValid())
         return QVariant();
 
     if (index.row() >= stringList.size())
         return QVariant();
 
     if (role == Qt::DisplayRole)
         return stringList.at(index.row());
     else
         return QVariant();
 }

Если переданный модельный индекс валиден, номер строки находится в пределах диапазона значений списка строк и требуемая роль нами поддерживается, мы возвращаем валидный QVariant.

Некоторые представления, такие как QTreeView и QTableView, могут отображать заголовки наряду с элементами данных. Если наша модель отображается в представлении с заголовками, мы хотим, чтобы заголовки содержали номера строк и столбцов. Мы можем предоставить информацию о заголовках реализовав функцию headerData():

 QVariant StringListModel::headerData(int section, Qt::Orientation orientation,
                                      int role) const
 {
     if (role != Qt::DisplayRole)
         return QVariant();
 
     if (orientation == Qt::Horizontal)
         return QString("Column %1").arg(section);
     else
         return QString("Row %1").arg(section);
 }

И снова мы возвращаем валидный QVariant только в том случае, если модельный индекс валиден и роль поддерживается. При решении, что должно быть возвращено, также принимается во внимание ориентация заголовка.

Не все представления отображают заголовки с элементами данных, и они могут быть настроены для сокрытия заголовков. Тем не менее, рекомендуется реализовывать функцию headerData() для предоставления важной информации о данных, предоставляемых моделью.

Элемент может иметь несколько ролей, предоставляя различные данные в зависимости от указанной роли. Элементы нашей модели имеют только одну роль, DisplayRole, так что мы возвращаем данные элемента независимо от указанной роли. Однако, данные, предоставляемые для роли DisplayRole, мы может повторно использовать в других ролях, таких как ToolTipRole, которую представления могут использовать для отображения информации об элементе во всплывающей подсказке.

Редактируемая модель

Модель только-для-чтения показывает, насколько просто данные могут быть предоставлены пользователю, но для многих приложений гораздо полезней редактируемая модель списка. Реализовав две дополнительные функции, flags() и setData(), мы можем модифицировать модель только-для-чтения и сделать элементы редактируемыми. Добавляем декларации следующих функций в определение класса:

     Qt::ItemFlags flags(const QModelIndex &index) const;
     bool setData(const QModelIndex &index, const QVariant &value,
                  int role = Qt::EditRole);

Создание редактируемой модели

Перед созданием редактора, делегат проверяет, является ли элемент редактируемым. Модель должна дать знать делегату, являются ли ее элементы редактируемыми. Мы делаем это, возвращая флаги для каждого элемента модели; в нашем случае, мы делаем доступными все элементы и позволяем им быть выбранными и редактируемыми:

 Qt::ItemFlags StringListModel::flags(const QModelIndex &index) const
 {
     if (!index.isValid())
         return Qt::ItemIsEnabled;
 
     return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
 }

Обратите внимание на то, что нам нет надобности знать, как делегат осуществляет процесс редактирования. Мы лишь должны предоставить делегату способ занести данные в модель. Это достигается с помощью функции setData():

 bool StringListModel::setData(const QModelIndex &index,
                               const QVariant &value, int role)
 {
     if (index.isValid() && role == Qt::EditRole) {
 
         stringList.replace(index.row(), value.toString());
         emit dataChanged(index, index);
         return true;
     }
     return false;
 }

В этой модели, элемент списка строк, соответствующий модельному индексу, заменяется на предоставленное значение. Однако, прежде, чем мы изменим список строк, мы должны убедиться, что индекс валиден, элемент имеет корректный тип, а роль поддерживается. В соответствии с соглашениями, мы утверждаем, что роль - это EditRole, так как эта роль обычно используется стандартными делегатами представлений. Основные данные нашей модели одинаковы для всех ролей, это облегчает объединение модели со стандартными компонентами.

После того, как данные установлены, модель должна дать знать представлениям, что некоторые данные изменены. Модель делает это, испуская сигнал dataChanged(). Так как у нас изменился только один элемент данных, указанный в сигнале диапазон элементов данных ограничен одним модельным индексом.

Вставка и удаление строк

В модели можно менять количество строк и столбцов. В модели списка строк имеет смысл изменять только количество строк, поэтому мы должны заново реализовать только функции для вставки и удалении строк. Они объявлены в определении класса:

     bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex());
     bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex());

Так как строки модели соответствуют строкам списка, то функция insertRows() вставляет нужное количество пустых строк в список перед указанной позицией. Количество вставляемых строк эквивалентно указанному количеству строк.

Для определения, куда должны быть вставлены строки, обычно используется родительский индекс. В нашем случае, мы имеем один список строк верхнего уровня, поэтому мы вставляем пустые строки в этот список.

 bool StringListModel::insertRows(int position, int rows, const QModelIndex &parent)
 {
     beginInsertRows(QModelIndex(), position, position+rows-1);
 
     for (int row = 0; row < rows; ++row) {
         stringList.insert(position, "");
     }
 
     endInsertRows();
     return true;
 }

Сперва, для уведомления других компонентов о том, что количество строк собирается измениться, модель вызывает функцию beginInsertRows(). Функция определяет номера первой и последней строк, которые должны быть вставлены, а также модельный индекс их родительского элемента. После изменения списка строк, модель вызывает endInsertRows() для завершения операции и уведомления других компонентов о том, что изменились измерения модели, для сообщения о том, что действия выполнены успешно, возвращается true.

Функция для удаления строк из моделей также проста в написании. Строки, удаляемые из модели, задаются позицией и количеством. Для упрощения реализации, мы игнорируем родительский индекс и лишь удаляем строки из списка.

 bool StringListModel::removeRows(int position, int rows, const QModelIndex &amp;parent)
 {
     beginRemoveRows(QModelIndex(), position, position+rows-1);
 
     for (int row = 0; row < rows; ++row) {
         stringList.removeAt(position);
     }
 
     endRemoveRows();
     return true;
 }

Перед удалением данных всегда вызывается функция beginRemoveRows(), которая определяет номера первой и последней удаляемых строк. Это позволяет другим компонентам получить доступ к данным, прежде чем они станут недоступны. Для завершения операции и уведомления других компонентов о том, что измерения модели изменились, после удаления строк модель испускает сигнал endRemoveRows().

Следующие шаги

Мы можем отобразить данные, предоставленные моделью, используя класс QListView, который показывает элементы модели в виде вертикального списка. Для модели списка строк, это представление также предоставляет редактор по умолчанию, способный управлять элементами. Возможности, предоставляемые стандартными классами представлений, мы исследуем в главе Классы представлений

В документе Создание собственных моделей требования к наследуемым от QAbstractItemModel классам обсуждаются более подробно и предоставляется руководство по виртуальным функциям, которые должны быть реализованы для активирования различных возможностей в разных типах моделей.

[Предыдущая: Классы моделей ] [ Содержание ] [Следующая: Классы представлений ]



Copyright © 2007 Trolltech Trademarks
Qt 4.3.2