Шаг 107.
Библиотека Qt.
Создание собственных моделей данных

    На этом шаге рассмотрим создание собственных моделей данных.

    Для создания своей собственной модели нужно унаследовать либо класс QAbstractItemModel, либо один из его потомков.

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


Рис.1. Демонстрация ролей

   Файл main.cpp

//создании объекта нашей модели мы в конструктор передаем список из пяти чисел
IntListModel model(QList<int>() << 123 << 2341 << 32 << 5342 << 723);

QListView list;
list.setModel(&model);
list.show();               //устанавливаем созданную модель,
                           //вызывая метод setModel(),
QTableView table;          //в двух представлениях
table.setModel(&model);
table.show();

    Заголовочный файл самой модели приведен ниже:

#ifndef _IntListModel_h_
#define _IntListModel_h_

#include <QAbstractListModel>
//модель реализуется на базе класса QAbstractListModel
class IntListModel : public QAbstractListModel {
    Q_OBJECT
private:
    QList<int> m_list;

public:
    IntListModel(const QList<int>& list, QObject* pobj = 0);
    //метод data() отвечает за доставку данных, которые он возвращает
    //в объектах класса QVariant
    QVariant data(const QModelIndex& index, int nRole) const;
    //метод rowCount() будет сообщать о количестве строк модели 
    int rowCount(const QModelIndex& parent = QModelIndex()) const;
    //переопределим метод headerData() для того, чтобы модель работала
    //с заголовками QTableView и QTreeView
    QVariant headerData(int             nSection,
                        Qt::Orientation orientation,
                        int             nRole = Qt::DisplayRole
                       ) const;
    //модель допускает изменение данных, следовательно,
    //переопределим методы flags() и setData()
    Qt::ItemFlags flags(const QModelIndex &index) const;
    bool setData(const QModelIndex& index,
                 const QVariant&    value,
                 int                nRole
                );
};
#endif  //_IntListModel_h_ 

    Конструктор класса модели списка целых чисел служит для инициализации атрибута m_list списком чисел и передачи указателя на объект предка унаследованному классу.

IntListModel::IntListModel(const QList<int>& list, QObject* pobj/*=0*/)
        : QAbstractListModel(pobj)
        , m_list(list)
{
}

    Модель не должна поставлять данных, это работа интерфейса модели, которую мы определили для связи со структурой данных, опрашиваемых представлением. Метод data() возвращает интересующую представление информацию об элементе в объекте класса QVariant. Помимо индекса в этот метод передаются значения ролей.

QVariant IntListModel::data(const QModelIndex& index, int nRole) const
{
    if (!index.isValid()) {
        return QVariant();
    }
    //если роли предназначены для отображения (Qt::DisplayRole)
    //или редактирования (Qt::EditRole), возвращаем значение,
    //записанное в атрибуте, представляющем собой список целых 
    //чисел, на позиции строки
    return (nRole == Qt::DisplayRole || nRole == Qt::EditRole)
           ? m_list.at(index.row())
           : QVariant();
    //если роль не предназначена для отображения
    //или редактирования, или же представление запросит о данных,
    //которых мы не имеем, то возвратим пустой объект QVariant
    //и тем самым укажем на то, что не располагаем этими данными
} 

    Для установки значения в методе setData() требуются три параметра: индекс, значение и роль.

bool IntListModel::setData(const QModelIndex& index,
                           const QVariant&    value,
                           int                nRole
                          )
{
    //проверяем индекс вызовом метода isValid()
    //если индекс не пуст, а роль предназначена для редактирования,
    if (index.isValid() && nRole == Qt::EditRole) {
        //то заменяем существующее значение в атрибуте списка
        //(m_list) новым, используя метод replace(),
        //перед тем как провести замену,преобразуем значение к нужному типу,
        //преобразуем атрибут value класса QVariant к целому типу
        //при помощи шаблонного метода QVariant::value<T>(),
        //параметризовав его типом int
        m_list.replace(index.row(), value.value<int>());
        //после замены отправляем сигнал dataChanged(), что необходимо для того,
        //чтобы все подсоединенные к модели представления могли
        //незамедлительно обновить свое содержимое
        emit dataChanged(index, index);
        //возвращаемое значение true сообщает об успешно проведенной операции
        //установки данных, а false сообщает представлению об ошибке
        return true;
    }
    return false;
}

    Метод rowCount() должен сообщать о количестве строк.

int IntListModel::rowCount(const QModelInde& parent/*=QModelIndex()*/
                          ) const
{
    Q_UNUSED(parent);
    //количество строк модели соответствует количеству элементов,
    //содержащихся в атрибуте списка целых чисел (m_list)
    return m_list.size();
}

    Метод headerData() нужен для того, чтобы модель была в состоянии подписывать горизонтальные и вертикальные секции заголовков, которыми располагают иерархическое и табличное представления. Иерархическое представление имеет только горизонтальные заголовки, табличное представление располагает обоими типами заголовков (горизонтальными и вертикальными).

QVariant IntListModel::headerData(int             nSection,
                                  Qt::Orientation orientation,
                                  int             nRole/*=DisplayRole*/
                                 ) const
{
    if (nRole != Qt::DisplayRole) {
        return QVariant();
    }
    //если представление запрашивает надпись для горизонтального заголовка,
    //то возвращаем строку "Number", а для вертикальных секций заголовков
    //возвращается номер переданной секции
    return (orientation == Qt::Horizontal) ? QString("Number")
                                           : QString::number(nSection);
}

    Метод flags() предоставляет возможность редактирования элементов.

Qt::ItemFlags IntListModel::flags(const QModelIndex& index) const
{
    Qt::ItemFlags flags = QAbstractListModel::flags(index);
    //возвращает для каждого элемента унаследованное значение 
    //с добавлением флага Qt::ItemIsEditable,
    //если индекс пуст, то возвращается значение без добавлений
    return index.isValid() ? (flags | Qt::ItemIsEditable)
                           : flags;
}

    Файлы приложения можно взять здесь.

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

    В качестве примера создадим программу, отображающую данные созданной табличной модели (рис. 2).


Рис.2. Отображение табличной модели данных

    Файлы приложения можно взять здесь.

    На следующем шаге рассмотрим промежуточную модель данных.




Предыдущий шаг Содержание Следующий шаг