Использование модели представления данных в Qt

Материал из Wiki семьи Белых
Перейти к: навигация, поиск

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

Теория

На основе статьи Qt 4.7: Программирование модель/представление

Архитектура модель/представление

Архитектура модель-представление-контроллер (Model-View-Controller, MVC) является шаблоном проектирования, берущим начало от Smalltalk, который часто используется для создания пользовательских интерфейсов.
MVC состоит из трех типов объектов. Модель - объект приложения, представление - его экранное представление и контроллер - определяет реакцию пользовательского интерфейса на пользовательский ввод. До MVC при разработке пользовательского интерфейса эти объекты смешивались вместе. MVC разделяет их, для увеличения гибкости и возможности повторного использования.[1]

Модели

Все модели элементов основаны на классе QAbstractItemModel. Этот класс определяет интерфейс, используемый представлениями и делегатами для доступа к данным. Сами данные не должны храниться в модели; они могут храниться в структуре данных или хранилище, предоставляемом отдельным классом, файле, базе данных или каком-либо другом прикладном компоненте.

Qt предоставляет несколько готовых моделей, которые могут использоваться для обработки элементов данных:

  • QStringListModel используется для хранения простого списка элементов QString.
  • QStandardItemModel управляет более сложными древовидными структурами элементов, каждый из которых может содержать произвольные данные.
  • QFileSystemModel предоставляет информацию о файлах и директориях локальной файловой системы.
  • QSqlQueryModel, QSqlTableModel и QSqlRelationalTableModel используются для доступа к базам данных, учитывающим соглашения архитектуры модель/представление.

Если эти стандартные модели не отвечают нужным требованиям, то для создания своих собственных моделей можно создать подкласс[2] QAbstractItemModel, QAbstractListModel или QAbstractTableModel.

Представления

Предоставляются полные реализации для различных видов представлений: QListView отображает список элементов, QTableView отображает данные модели в виде таблицы, а QTreeView отображает элементы модели в виде иерархического списка. Каждый из этих классов основан на базовом абстрактном классе QAbstractItemView. Хотя эти классы и готовы к использованию, они могут иметь подклассы, для более точной настройки представления.

Делегаты

QAbstractItemDelegate - это абстрактный базовый класс делегата в структуре модель/представление. Начиная с Qt 4.4, стандартная реализация делегата предоставляемая QStyledItemDelegate, и используется как делегат по умолчанию стандартных представлений Qt. Однако, QStyledItemDelegate и QItemDelegate являются независимыми альтернативами для рисования и предоставления редакторов элементов представлений. Разница между ними состоит в том, что QStyledItemDelegate использует текущий стиль для отрисовки своих элементов. По этой причине мы рекомендуем использовать QStyledItemDelegate в качестве базового при реализации пользовательских делегатов или когда работают с таблицами стилей Qt.

Сортировка

В архитектуре модель/представление имеется два подхода к сортировке; какой подход выбрать - зависит от вашей базовой модели.

Если ваша модель сортируемая, т.е. если она переопределит функцию QAbstractItemModel::sort(), то и QTableView и QTreeView предоставляют API, позволяющее программно сортировать данные вашей модели. Кроме того, вы можете разрешить интерактивную сортировку (interactive sorting) (т.е. позволить пользователям сортировать данные нажимая на заголовки представления), соединив сигнал QHeaderView::sortIndicatorChanged() со слотом QTableView::sortByColumn() или же со слотом QTreeView::sortByColumn(), соответственно.

Альтернативный подход, если ваша модель не имеет необходимого интерфейса или вы хотите использовать представление списка для отображения ваших данных, заключается в использовании модели-посредника для преобразования структуры вашей модели перед передачей данных в представление. Детально это рассматривается в разделе Модели-посредники[3].

Вспомогательные классы

Множество вспомогательных классов унаследованы от классов стандартных представлений для удобства использования в приложениях, зависящих от основанных на элементах представлениях Qt и классах таблиц. Они не предназначены для создания на их основе подклассов. Примерами таких классов служат QListWidget, QTreeWidget и QTableWidget. Эти классы менее гибки, чем классы представлений и не могут использоваться с произвольными моделями.

Модели-посредники

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

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

Для решения этой проблемы в архитектуре модель/представление используются модели-посредники (proxy models), управляющие данными, передаваемыми между отдельными моделями и представлениями. Модели-посредники - это компоненты, ведущие себя по отношению к представлению подобно модели данных и осуществляющие доступ к модели данных от имени представления. Механизм сигналов и слотов, используемый в архитектуре модель/представление, гарантирует, что все представления будут обновлены независимо от того, сколько моделей-посредников расположено между ними и моделью-источником.

Создание модели на основе QAbstractTableModel

Пример создания модели для файлового менеджера

Code: Header модели с основными методами, которые должны быть определены
#ifndef FILELISTMODEL_H
#define FILELISTMODEL_H

#include <QObject>
#include <QAbstractTableModel>
#include <QSortFilterProxyModel>
#include <QModelIndex>
#include <QVariant>
#include <QFile>
#include <QImage>
#include <QPixmap>

#include "Headers/Structures/filelist.h"

class fileListModel : public QAbstractTableModel
{
    Q_OBJECT

public:
    fileListModel(QWidget *parent = 0);
    ~fileListModel();

    int rowCount(const QModelIndex &parent = QModelIndex()) const;

private:
    int columnCount(const QModelIndex &parent) const;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
    QVariant data(const QModelIndex &index, int role) const;
};

#endif // FILELISTMODEL_H

Макрос Q_OBJECT нужен, чтобы в модели использовать сигналы и слоты.

Code: Исходник модели с основными методами, которые должны быть определены
#include "Headers/Models/filelistmodel.h"

fileListModel::fileListModel(QWidget *parent)
{

}

fileListModel::~fileListModel()
{

}

int fileListModel::rowCount(const QModelIndex &parent) const
{
    return(fileItem.keys().count());
}

int fileListModel::columnCount(const QModelIndex &parent) const
{
    return(7);
}

QVariant fileListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
    {
        switch (section)
        {
            case 1:
                {return(QVariant(tr("Icon")));}
            break;
            case 2:
                {return(QVariant(tr("Name")));}
            break;
            case 3:
                {return(QVariant(tr("Type")));}
            break;
            case 4:
                {return(QVariant(tr("Size")));}
            break;
            case 5:
                {return(QVariant(tr("Time")));}
            break;
            case 6:
                {return(QVariant(tr("Attributes")));}
            break;
            default:
                {return(QVariant());}
             break;
        }
    }
    else
        {return(QVariant());}
}

QVariant fileListModel::data(const QModelIndex &index, int role) const
{

}

Подключение модели в своем классе

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

Code: Header класса (описаны только необходимые переменные и функции)
#ifndef FILEMANAGERTAB_H
#define FILEMANAGERTAB_H

#include <QWidget>
#include <QItemSelectionModel>
#include "Headers/Models/filelistmodel.h"


namespace Ui {
class FileManagerTab;
}

class FileManagerTab : public QWidget
{
    Q_OBJECT

public:
    FileManagerTab(QWidget *parent = 0);
    ~FileManagerTab();

signals:

private slots:

private:
    Ui::FileManagerTab *ui;

    fileListModel *FileListModel;
    QSortFilterProxyModel *SortModel;
    QItemSelectionModel *ItemSelectionModel;

};

#endif // FILEMANAGERTAB_H
Code: Исходник класса (описаны только необходимые переменные и функции)
#include "Headers/Widgets/filemanagertab.h"
#include "ui_filemanagertab.h"

FileManagerTab::FileManagerTab(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::FileManagerTab)
{
    ui->setupUi(this);

    FileListModel = new fileListModel(BaseFunction, this);
    SortModel = new QSortFilterProxyModel(this);
    SortModel->setSourceModel(FileListModel);
    ItemSelectionModel = ui->tableView->selectionModel();
}

FileManagerTab::~FileManagerTab()
{
    delete ui;
}

Сноски

  1. Книга Design Patterns Гаммы (Gamma)
  2. свой класс, унаследованный от
  3. прокси-модели