Применение заранее определенных моделей
Применение заранее определенных моделей
В Qt заранее определено несколько моделей, предназначенных для использования с классами представлений:
• QStringListModel — хранит список строк;
• QStandardltemModel — хранит данные произвольной иерархической структуры;
• QDirModel — формирует структуру локальной файловой системы;
• QSqlQueryModel — формирует набор результата SQL—запроса;
• QSqlTableModel — формирует SQL—таблицу;
• QSqlRelationalTableModel — формирует SQL—таблицу с внешними ключами (foreign keys);
• QSortFilterProxyModel — сортирует и/или пропускает через фильтр другую модель.
В данном разделе мы рассмотрим способы применения моделей QStringListModel, QDirModel и QSortFilterProxyModel. SQL—модели рассматриваются в главе 13.
Давайте начнем с простого диалогового окна, которое может применяться для добавления, удаления и редактирования списка строк QStringList, где каждая строка представляет лидера команды.
Ниже приводится соответствующий фрагмент конструктора:
01 TeamLeadersDialog::TeamLeadersDialog(const QStringList &leaders,
02 QWidget *parent)
03 : QDialog(parent)
04 {
05 model = new QStringListModel(this);
06 model->setStringList(leaders);
07 listView = new QListView;
08 listView->setModel(model);
09 listView->setEditTriggers(QAbstractItemView::AnyKeyPressed
10 | QAbstractItemView::DoubleClicked);
11 …
12 }
Рис. 10.6. Приложение Лидеры команд (Team Leaders).
Мы начнем с создания и заполнения модели QStringListModel. Затем создадим представление QListView и свяжем его с только что созданной моделью. Установим также некоторые переключатели редактирования, чтобы позволить пользователю редактировать строку, просто вводя символ или делая двойной щелчок. По умолчанию все переключатели редактирования сброшены для QListView, фактически делая это представление пригодным только для чтения.
01 void TeamLeadersDialog::insert()
02 {
03 int row = listView->currentIndex().row();
04 model->insertRows(row, 1);
05 QModelIndex index = model->index(row);
07 listView->setCurrentIndex(index);
08 listView->edit(index);
09 }
Когда пользователь нажимает на кнопку Insert (вставка), вызывается слот insert(). Этот слот начинает с получения номера строки текущего элемента в списке. Каждый элемент данных модели имеет соответствующий «индекс модели», представленный объектом QModelIndex. Мы подробно рассмотрим индексы модели в следующем разделе, а в данный момент нам достаточно знать, что индекс имеет три основных компонента: строку, столбец и указатель на модель, к которой он принадлежит. В модели одномерного списка столбец всегда равен 0.
Имея номер строки, мы вставляем одну новую строку в данную позицию. Вставка выполняется в модели, и модель автоматически обновляет списковое представление. Затем мы устанавливаем текущий индекс спискового представления на пустую строку, которую мы только что вставили. Наконец, мы устанавливаем в списковом представлении режим редактирования для новой строки, как будто пользователь нажал какую-нибудь клавишу клавиатуры или дважды щелкнул, чтобы начать редактирование.
01 void TeamLeadersDialog::del()
02 {
03 model->removeRows(listView->currentIndex().row(), 1);
04 }
В конструкторе сигнал clicked() кнопки Delete (удалить) связывается со слотом del(). Поскольку мы только что удалили текущую строку, мы можем вызвать removeRows() для текущей позиции индекса и для значения 1 счетчика строк. Как и при выполнении вставки, мы полагаемся на то, что модель должным образом обновит представление.
01 QStringList TeamLeadersDialog::leaders() const
02 {
03 return model->stringList();
04 }
Наконец, функция leaders() позволяет считывать отредактированные строки, когда диалоговое окно закрыто.
Создать TeamLeadersDialog можно было бы на основе универсального диалогового окна редактирования списка строк, просто параметризируя заголовок этого окна. Другое часто используемое пользователями универсальное диалоговое окно отображает список файлов или каталогов. В следующем примере применяется класс QDirModel, который моделирует файловую систему компьютера и позволяет показывать (или скрывать) различные атрибуты файлов. Эта модель может применять фильтр для ограничения типов элементов файловой системы при их выводе на экран и упорядочивать элементы различными способами.
Рис. 10.7. Приложение Просмотр каталога.
Мы начнем с создания и настройки модели и представления в конструкторе диалогового окна Просмотр каталога (Directory Viewer).
01 DirectoryViewer::DirectoryViewer(QWidget *parent)
02 : QDialog(parent)
03 {
04 model = new QDirModel;
05 model->setReadOnly(false);
06 model->setSorting(QDir::DirsFirst | QDir::IgnoreCase | QDir::Name);
07 treeView = new QTreeView;
08 treeView->setModel(model);
09 treeView->header()->setStretchLastSection(true);
10 treeView->header()->setSortIndicator(0, Qt::AscendingOrder);
11 treeView->header()->setSortIndicatorShown(true);
12 treeView->header()->setClickable(true);
13 QModelIndex index = model->index(QDir::currentPath());
14 treeView->expand(index);
15 treeView->scrollTo(index);
16 treeView->resizeColumnToContents(0);
17 …
18 }
После создания модели мы обеспечиваем возможность ее редактирования и устанавливаем различные начальные атрибуты упорядочивания. Затем мы создаем объект QTreeView для отображения на экране данных модели. Заголовок QTreeView может использоваться пользователем для управления сортировкой. Делая заголовок восприимчивым к щелчкам мышки, пользователь может сортирбвать данные по выбранному им в заголовке столбцу, причем повторные щелчки переключают направление сортировки, т.е сортировку по возрастанию на сортировку по убыванию и наоборот. После настройки заголовков представления данных в виде дерева мы получаем индекс модели текущего каталога и обеспечиваем просмотр содержимого этого каталога, раскрывая при необходимости его подкаталоги, используя expand(), и устанавливая изображение на его начало, используя scrollTo(). Затем мы обеспечиваем ширину первого столбца, достаточную для размещения всех элементов без вывода многоточия (…).
Во фрагменте конструктора, который здесь не показан, мы связываем кнопки Create Directory (создать каталог) и Remove (удалить) со слотами, выполняющими соответствующие действия. Нам не нужно иметь кнопку Rename (переименовать), поскольку пользователи могут переименовывать элементы каталога по месту, нажимая клавишу F2 и осуществляя ввод символов с клавиатуры.
01 voidDirectoryViewer::createDirectory()
02 {
03 QModelIndex index;
04 if (!index.isValid())
05 return;
06 QString dirName = QInputDialog::getText(this,
07 tr("Create Directory"), tr("Directory name"));
08 if (!dirName.isEmpty()) {
09 if (!model->mkdir(index,dirName).isValid())
10 QMessageBox::information(this,
11 tr("Create Directory"),
12 tr("Failed to create the directory"));
13 }
14 }
Если пользователь вводит имя каталога в диалоговом окне ввода, мы пытаемся создать в текущем каталоге подкаталог с этим именем. Функция QDirModel::mkdir() принимает индекс родительского каталога и имя нового каталога; она возвращает индекс модели созданного каталога. Если операция завершается неудачей, возвращается недействительный индекс модели.
Последний пример в этом разделе показывает, как следует применять модель QSortFilterProxyModel. В отличие от других заранее определенных моделей, эта модель использует какую-нибудь существующую модель и управляет данными, которые проходят между базовой моделью и представлением. В нашем примере базовой является модель QStringListModel, которая проинициализирована списком названий цветов, распознаваемых Qt (полученных функцией QColor::colorNames()). Пользователь может ввести строку фильтра в строке редактирования QLineEdit и указать ее тип (регулярное выражение, шаблон или фиксированная строка), используя поле с выпадающим списком.
Рис. 10.8. Приложение Названия цветов (ColorNames).
Ниже приводится фрагмент конструктора ColorNamesDialog:
01 ColorNamesDialog::ColorNamesDialog(QWidget *parent)
02 : QDialog(parent)
03 {
04 sourceModel = new QStringListModel(this);
05 sourceModel->setStringList(QColor::colorNames());
06 proxyModel = new QSortFilterProxyModel(this);
07 proxyModel->setSourceModel(sourceModel);
08 proxyModel->setFilterKeyColumn(0);
09 listView = new QListView;
10 listView->setModel(proxyModel);
11 syntaxComboBox = new QComboBox;
12 syntaxComboBox->addItem(tr("Regular expression"), QRegExp::RegExp);
13 syntaxComboBox->addItem(tr("Wildcard"), QRegExp::Wildcard);
14 syntaxComboBox->addItem(tr("Fixed string"), QRegExp::FixedString);
15 …
16 }
Модель QStringListModel создается и пополняется обычным образом. После этого создается модель QSortFilterProxyModel. Мы передаем базовую модель, используя функцию setSourceModel(), и указываем прокси на необходимость фильтрации по столбцу 0 базовой модеди. Функция QComboBox::addItem() принимает необязательный аргумент дополнительных данных типа QVariant; мы используем его для хранения значения QRegExp::PatternSyntax c текстом, определяющим тип фильтра данного элемента.
01 void ColorNamesDialog::reapplyFilter()
02 {
03 QRegExp::PatternSyntax syntax =
04 QRegExp::PatternSyntax(syntaxComboBox->itemData(
05 syntaxComboBox->currentIndex()).toInt());
06 QRegExp regExp(filterLineEdit->text(), Qt::CaseInsensitive, syntax);
07 proxyModel->setFilterRegExp(regExp);
08 }
Слот reapplyFilter() вызывается при всяком изменении пользователем строки фильтра или типа шаблона фильтрации в поле с выпадающим списком. Мы создаем объект QRegExp, используя текст в строке редактирования. Затем устанавливаем тип шаблона фильтрации на тот, который имеется в данных текущего элемента и отображается в соответствующем поле с выпадающим списком. Когда мы вызываем setFilterRegExp(), новый фильтр становится активным и автоматически обновляется представление данных.