[Предыдущая: Обработка выбора элементов в представлениях] [Содержание] [Следующая: Вспомогательные классы представлений элементов]
Классы делегатов
Концепции
В отличие от шаблона модель-представление-контроллер (MVC), архитектура модель/представление не включает в себя полностью независимые компоненты для управления взаимодействием с пользователем. Как правило, представление отвечает за представление пользователю данных модели и за обработку пользовательского ввода. Чтобы придать немного гибкости способу, которым этот ввод получается, взаимодействие осуществляется с помощью делегатов (delegates). Эти компоненты предоставляют возможности ввода, а также отвечают за отрисовку индивидуальных элементов в некоторых представлениях. Стандартный интерфейс управления делегатами определен в классе QAbstractItemDelegate.
Ожидается, что делегаты способны самостоятельно отрисовывать свое содержимое, реализовав функции paint() и sizeHint(). Однако, простые, основанные на виджетах, делегаты могут быть созданы как подкласс QItemDelegate вместо QAbstractItemDelegate, и получить преимущества реализации этих функций по умолчанию.
Редакторы для делегатов могут быть реализованы либо с использованием виджетов для управления процессом редактирования либо непосредственной обработкой событий. Первый подход раскрывается в этой главе, а также показан в примере Spin Box Delegate.
Пример Pixelator показывает как создавать пользовательский делегат, выполняющий специализированный рендеринг для представления таблицы.
Использование существующих делегатов
Стандартные представления, поставляемые вместе с Qt, используют экземпляры класса QItemDelegate для предоставления средств редактирования. Эта реализация по умолчанию интерфейса делегата отображает элементы в обычном стиле для каждого из стандартных представлений: QListView, QTableView и QTreeView.
Стандартные представления используют делегат по умолчанию, обрабатывающий все стандартные роли. Способ их интерпретации описывается в документации к QItemDelegate.
Функция itemDelegate() возвращает делегат, используемый представлением. Функция setItemDelegate() позволяет вам установить пользовательский делегат для стандартного представления и важно использовать эту функцию когда устанавливается делегат для пользовательского представления.
Простой делегат
Реализованный здесь делегат использует QSpinBox для предоставления средств редактирования и предназначен в основном для использования с моделями, которые отображают целые числа. Хотя для этой цели мы установили пользовательскую модель таблицы, основанную на целых числах, мы легко можем использовать вместо нее QStandardItemModel, так как управлять вводом данных будет пользовательский делегат. Мы создадим представление таблицы для отображения содержимого модели и оно будет использовать пользовательский делегат для редактирования.
Мы создаем подкласс делегата от QItemDelegate поскольку мы не хотим писать функции пользовательского вывода на экран. Однако, мы все же должны предоставить функции для управления виджетом-редактором:
class SpinBoxDelegate : public QItemDelegate
{
Q_OBJECT
public:
SpinBoxDelegate(QObject *parent = 0);
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
Обратите внимание, что при создании делегата виджеты-редакторы не создаются. Мы создаем виджет-редактор только когда это необходимо.
Предоставление редактора
В этом примере, когда представление таблицы нуждается в редакторе, оно просит делегат предоставить виджет-редактор, соответствующий модифицируемому элементу. Функцию createEditor() снабжаем всем, что необходимо делегату для установки соответствующего виджета:
QWidget *SpinBoxDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &,
const QModelIndex &) const
{
QSpinBox *editor = new QSpinBox(parent);
editor->setMinimum(0);
editor->setMaximum(100);
return editor;
}
Обратите внимание, что мы не нуждаемся в хранении указателя на виджет-редактор, так как представление берет на себя ответственность за его уничтожение когда он станет не нужен.
Мы установили стандартный фильтр событий делегата на редактор для гарантирования того, что он предоставит ожидаемые пользователями стандартные "горячие" клавиши редактирования. Чтобы сделать возможным более сложное поведение в редактор могут быть добавлены дополнительные "горячие" клавиши; это обсуждается в разделе Редактирование подсказок.
Представление убеждается в правильности установки данных редактора и геометрии с помощью вызова функций, которые для этих целей мы определим позднее. Мы можем создать различные редакторы, зависящие от предоставляемого представлением модельного индекса. Например, если у нас есть столбец целых чисел и столбец строк мы можем вернуть либо QSpinBox либо QLineEdit, в зависимости от того, какой столбец редактируется.
Делегат должен предоставить функцию копирования данных модели в редактор. В этом примере мы читаем данные, хранящиеся в роли отображения (display role), и соответственно устанавливаем значение в окошке счетчика.
void SpinBoxDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
int value = index.model()->data(index, Qt::EditRole).toInt();
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->setValue(value);
}
В этом примере мы знаем, что виджет-редактор это окошко счетчика, но мы можем предоставлять различные редакторы для разных типов данных в модели, в этом случае мы должны привести виджет к соответствующему типу до обращения к его функциям-членам.
Запись данных в модель
Когда пользователь завершает редактирования значения в окошке счетчика, представление просит делегата сохранить отредактированное значение в модели, вызвав функцию setModelData().
void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->interpretText();
int value = spinBox->value();
model->setData(index, value, Qt::EditRole);
}
Так как представление управляет виджетами-редакторами делегата, мы должны только обновить модель с предоставленным редактором содержимым. В данном случае мы удостоверяемся в том, что окошко счетчика обновилось и обновляем содержащую значение модель, используя заданный индекс.
Стандартный класс QItemDelegate сообщает представлению когда он завершил редактирование испуская сигнал closeEditor(). Представление проверяет, что виджет-редактор закрыт и уничтожен. В этом примере мы предоставили только простые средства редактирования, поэтому нам никогда не понадобится испускать этот сигнал.
Все операции над данными выполняются через интерфейс, предоставляемый QAbstractItemModel. Это делает делегата в значительной степени независимым от типа данных, с которыми он манипулирует, но должны быть сделаны некоторые допущения в порядке использования определенных типов виджетов-редакторов. В этом примере мы предполагали, что модель всегда содержит целые числа, но мы можем использовать этот делегат с другими видами моделей, поскольку QVariant предоставляет удобные значения по умолчанию для непредвиденных данных.
Обновление геометрии редактора
За управление геометрией редактора отвечает делегат. Геометрия должна быть установлена при создании редактора, при изменении размеров или положения элемента в представлении. К счастью, представление предоставляет всю необходимую информацию о геометрии внутри объекта опция представления (view option).
void SpinBoxDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &) const
{
editor->setGeometry(option.rect);
}
В данном случае мы используем только информацию, предоставляемую опцией представления в прямоугольнике элемента. Делегат, который отображает элементы с несколькими примитивами, не использует прямоугольник элемента непосредственно. Он поместит редактор в зависимости от других примитивов в элементе.
Редактирование подсказок
После редактирования делегаты должны предоставить другим компонентам подсказки о результате процесса редактирования и предоставить подсказки, которые будут помогать любым последующим операциям редактирования. Достигается это испусканием сигнала closeEditor() с соответствующей подсказкой. Эту обязанность берет на себя стандартный фильтр событий QItemDelegate, который мы установили на окошко счетчика при его создании.
Поведение окошка счетчика можно настраивать, чтобы сделать его более дружественным. В фильтре событий по умолчанию, предоставляемом QItemDelegate, если пользователь нажмет Return для подтверждения своего выбора в окошке счетчика, делегат фиксирует значение в модели и закрывает окошко счетчика. Мы можем изменить это поведение, установив на окошко счетчика свой собственный фильтр событий и предоставив подсказки редактирования, соответствующие нашим нуждам; например, мы можем испустить closeEditor() с подсказкой EditNextItem для автоматического начала редактирования следующего элемента в представлении.
Другим подходом, не требующим использования фильтра событий, является предоставление нашего собственного виджета-редактора, возможно унаследованный для удобства от QSpinBox. Альтернативным подходом будет передать нам больше контроля над поведением виджета-редактора за счет написания дополнительного кода. В большинстве случаев более удобно установить фильтр событий в делегате, если вам необходимо настраивать поведение стандартного виджета-редактора Qt.
Делегаты не испускают эти подсказки, но тогда они будут слабее интегрированы в приложения и будут менее удобны, чем те, что испускают подсказки для поддержки общепринятых действий редактирования.
[Предыдущая: Обработка выбора элементов в представлениях] [Содержание] [Следующая: Вспомогательные классы представлений элементов]
Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies) |
Торговые марки |
Qt 4.5.3 |
|