Home · All Classes · Main Classes · Grouped Classes · Modules · Functions |
[Предыдущая: Классы Моделей] [Содержание] [Следующая: Классы Представлений]
Разделение функциональных возможностей между компонентами архитектуры модель/представление позволяет создавать новые модели, способные использовать преимущества существующих представлений. Такой подход позволяет представлять данные из разнообразных источников, используя стандартные графические компоненты пользовательского интерфейса, такие как QListView, QTableView и QTreeView.
Класс QAbstractItemModel предоставляет интерфейс, который достаточно гибок, для поддержики источников данных, хранящих информацию в иерархических структурах, позволяющих вставлять, удалять и изменять данные, или сортировать их различными способами. А также предоставляет поддержку операций drag and drop.
Классы QAbstractListModel и QAbstractTableModel предоставляют поддержку интерфейсов для более простых неиерархических данных, и более легки для использования в качестве отправной точки для моделей простых списков и таблиц.
В этой главе мы создадим простую модель только-для-чтения для исследования основных принципов архитектуры модель/представление. Позже в этой главе, мы приспособим эту модель для того, чтобы данные могли быть изменены пользователем.
Для получения примера более сложной модели, см. Simple Tree Model.
При создании новой модели для существующей структуры данных, очень важно решить, какой тип модели лучше всего подходит для обеспечения интерфейса к данным. Если структура данных может быть определена как список или таблица элементов, Вы можете создать подкласс QAbstractListModel или QAbstractTableModel так как эти классы предоставляют подходящие реализации функций по умолчанию.
Однако, если структура данных может быть представлена только в виде иерархичесткой древообразной структуры, возникает необходимость в создании подкласса QAbstractItemModel. Такой подход показан в примере Simple Tree Model.
В этой главе мы реализуем простую модель, основанную на списке строк, для постройке которой идеальной основой является класс 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 &parent) { beginRemoveRows(QModelIndex(), position, position+rows-1); for (int row = 0; row < rows; ++row) { stringList.removeAt(position); } endRemoveRows(); return true; }
Перед удалением данных, вызывается функция beginRemoveRows(), которая определяет номера первой и последней удаляемых строк. Это позволяет другим компонентам получить доступ к данным, прежде чем они станут недоступны. Для завершения операции и уведомления других компонентов о том, что измерения модели изменились, после удаления строк модель испускает сигнал endRemoveRows().
Мы может отобразить данные, предоставленные моделью, используя класс QListView, который показывает элементы модели в виде вертикального списка. Для модели списка строк, это представление также предоставляет редактор по умолчанию, способный управлять элементами. Возможности, предоставляемые стандартными классами представлений, мы исследуем в главе Классы Представлений.
[Предыдущая: Классы Моделей] [Содержание] [Следующая: Классы Представлений]
Copyright © 2005 Trolltech | Trademarks | Qt 4.1.0 |