Понедельник, 21 декабря 2020 19:10

Создаем модель для QTableView - Работа с моделями в Qt5 для отображения данных в виджетах.

Россия
Оцените материал
(2 голосов)

Сегодня мы рассмотрим создание модели в Qt5 для виджета QTableView. В отличии от QListView у данного компонента есть не только строки, но и столбцы.

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

Создаем проект

Создадим новый проект Qt Widgets Application с именем QTableViewModel.

Добавим на главную форму виджет QTableView.

Создадим класс для хранения наших данных – CountryFlag

Заголовок

#ifndef COUNTRYFLAG_H
#define COUNTRYFLAG_H

#include <QString>



class CountryFlag
{
public:
    CountryFlag();
    CountryFlag(int id, QString name, QString icon);
    QString getIcon() const;
    void setIcon(const QString &value);

    QString getName() const;
    void setName(const QString &value);

    int getId() const;
    void setId(int value);

private:
    int id;
    QString icon;
    QString name;
};

#endif // COUNTRYFLAG_H

Реализация

#include "countryflag.h"

CountryFlag::CountryFlag()
{

}

CountryFlag::CountryFlag(int id, QString name, QString icon)
{
    this->id = id;
    this->name = name;
    this->icon = icon;
}

QString CountryFlag::getIcon() const
{
    return icon;
}

void CountryFlag::setIcon(const QString &value)
{
    icon = value;
}

QString CountryFlag::getName() const
{
    return name;
}

void CountryFlag::setName(const QString &value)
{
    name = value;
}

int CountryFlag::getId() const
{
    return id;
}

void CountryFlag::setId(int value)
{
    id = value;
}

Создаем модель для таблицы

Создадим класс модели для таблицы - QTableViewModel.

Заголовок:

#ifndef QTABLEVIEWMODEL_H
#define QTABLEVIEWMODEL_H


#include "countryflag.h"
#include <QModelIndex>

class QTableViewModel : public QAbstractListModel
{
public:    QTableViewModel(QObject *parent=nullptr);
    int rowCount(const QModelIndex &) const;
    int columnCount(const QModelIndex &parent) const;
    QVariant data(const QModelIndex &index, int role) const;
    void populate(QList<CountryFlag> *newValues);
private:
    QList<CountryFlag> *values;
};

#endif // QTABLEVIEWMODEL_H

Реализация: 

#include "qtableviewmodel.h"

#include <QModelIndex>
#include <QDebug>
#include <QPixmap>

QTableViewModel::QTableViewModel(QObject *parent)
    :QAbstractListModel(parent)
{
    values = new QList<CountryFlag>();
}

int QTableViewModel::rowCount(const QModelIndex &) const
{
    return values->count();
}

int QTableViewModel::columnCount(const QModelIndex &) const
{
    return 3;
}

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

    QVariant value;

        switch ( role )
        {
            case Qt::DisplayRole: //string
            {
                switch (index.column()) {
                    case 0: {
                        value = this->values->at(index.row()).getId();
                        break;
                    }
                    case 1: {
                        value = this->values->at(index.row()).getName();
                        break;
                    }
                    case 2: {
                        value = this->values->at(index.row()).getIcon();
                        break;
                    }
                }
            }
            break;

            case Qt::UserRole: //data
            {
                value = this->values->at(index.row()).getId();
            }
            break;

            default:
                break;
        }

    return value;
}

void QTableViewModel::populate(QList<CountryFlag> *newValues)
{
    int idx = this->values->count();
    this->beginInsertRows(QModelIndex(), 1, idx);
        this->values = newValues;
    endInsertRows();
 }

По сравнению с QComboBox, в модели для таблицы добавился еще один метод – columnCount() указывающий количество столбцов.

Так же в методе data() мы проверяем текущий столбец с помощью:

index.column()

и, в зависимости от номера столбца, возвращаем значение.

Обратите внимание, что метод data() вызывается для каждой строки и каждого столбца этой строки соответственно!

Добавим код в конструктор главной формы, чтобы наполнить таблицу данными:

    values = new QList<CountryFlag>;

    values->append(CountryFlag(1," Russia", "russia.png"));
    values->append(CountryFlag(11, "Belarus", "belarus.png"));
    values->append(CountryFlag(22, "Slovakia", "slovakia.png"));
    values->append(CountryFlag(33, "Slovenia", "slovenia.png"));
    values->append(CountryFlag(44, "China", "china.png"));
    values->append(CountryFlag(55, "Mongolia","mongolia.png"));


    model = new QTableViewModel();
    model->populate(values);
    this->ui->tableView->setModel(model);

Запустим:

2020-12-20_11-28-03.png 

Добавляем методы для работы с данными в таблице

Давайте доработаем модель, добавив методы для добавления и удаления строк.

В заголовок модели добавим:

    void append(CountryFlag value);
    void update(int idx, CountryFlag value);
    void deleteRow(int idx);
    void insertAt(int idx, CountryFlag value);

В реализацию:

void QTableViewModel::append(CountryFlag value)
{
    int newRow = this->values->count()+1;

    this->beginInsertRows(QModelIndex(), newRow, newRow);
        values->append(value);
    endInsertRows();
}

void QTableViewModel::update(int idx, CountryFlag value)
{
    (*this->values)[idx] = value;

    QModelIndex item_idx_s = this->index(idx,0);
    QModelIndex item_idx_e = this->index(idx,this->columnCount(QModelIndex()));

    emit this->dataChanged(item_idx_s ,item_idx_e );
}

void QTableViewModel::deleteRow(int idx)
{
    this->beginRemoveRows(QModelIndex(), idx,idx);

        (*this->values).removeAt(idx);

    this->endRemoveRows();
}

void QTableViewModel::insertAt(int idx, CountryFlag value)
{

    int newRow = idx;

    this->beginInsertRows(QModelIndex(), newRow, newRow);

        values->insert(newRow,value);

    endInsertRows();
}

Обратите внимание, по сравнению с моделями для QComboBox и QListView изменился метод update(). Для того, чтобы обновить все столбцы, мы вычисляем индексы первого и последнего столбцов, и потом обновляем все столбцы обновляемой строки!

Добавляем кнопки для управления строками таблицы

Добавим кнопки на главную форму:

2020-12-20_11-33-54.png

Добавим слоты для кнопок:

void MainWindow::on_pushButton_clicked()
{
    newidx++;
    QString strIdx = QString().number(newidx);

    model->append(CountryFlag(newidx, "new country " + strIdx, "russia.png"));
}

void MainWindow::on_pushButton_2_clicked()
{
    model->update(ui->tableView->currentIndex().row(), CountryFlag(66, "Japan", "japan.png"));
}

void MainWindow::on_pushButton_3_clicked()
{
    model->deleteRow(0);
}

void MainWindow::on_pushButton_4_clicked()
{
    model->insertAt(0, CountryFlag(66, "Japan", "japan.png"));
}

Добавим приватное поле для главной формы:

 

int newidx;

И в конец конструктора формы инициализацию:

newidx = 100;

Запустим:

2020-12-20_11-42-20.png

Кнопки работают и строки обновляются.

Добавляем названия столбцов

До сих пор вместо названий столбцов у нас были цифры, пришла пора добавить им нормальные значения.

В модель добавим новый метод:

QVariant QTableViewModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
        switch (section) {
        case 0:
            return QString("ID");
        case 1:
            return QString("Name");
        case 2:
            return QString("Icon");
        }
    }
    return QVariant();
}

 

Он работает так же как и метод data() только вызывается один раз для каждого столбца.

Запускаем

2020-12-20_17-42-04.png

Вот мы и создали модель для QTableView. Хочу отметить, что данная модель подойдет только данных, которые хранятся в виде строк. Если у вас есть матрица или массив чисел, потребуется модернизация модели, но об этом мы поговорим в следующей статье.

Заключение

Сегодня мы создали модель для QTableView.

Мы добавили новый метод columnCount() указывающий количество столбцов в таблице.

Обновили метод update(), для работы с таблицей.

Добавили метод headerData() для отображения заголовков столбцов.

В следующей статье мы поговорим о том, как использовать модель с массивами.

Исходный код проекты вы можете найти на Github.

Прочитано 4582 раз Последнее изменение Среда, 13 января 2021 12:13