Пятница, 11 декабря 2020 09:23

Дорабатываем модель QComboBox - Работа с моделями в Qt5 для отображения данных в виджетах. Часть 2

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

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

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

Для примера мы наполним QComboBox списком ряда стран.

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

Добавим новый класс CountryFlag

Заголовок:

#ifndef COUNTRYFLAG_H
#define COUNTRYFLAG_H

#include <QString>



class CountryFlag
{
public:
    CountryFlag();
    CountryFlag(int id, QString name);

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

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

private:
    int id;
    QString name;
};

#endif // COUNTRYFLAG_H

Реализация:

#include "countryflag.h"

CountryFlag::CountryFlag()
{

}

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

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;
}

 

Ничего сложного, класс содержит только методы для доступа к частным полям класса.

Обновляем модель

Далее нам нужно везде в коде заменить:

QList<QPair<int,QString>>

На

QList<CountryFlag>

И

QPair<int,QString>

На

CountryFlag

Внесем так же изменения в методы модели, в результате модель у нас примет вид:

Заголовок:

#ifndef QCOMBOBOXMODEL_H
#define QCOMBOBOXMODEL_H

#include "countryflag.h"

#include <QModelIndex>

class QComboBoxModel : public QAbstractListModel
{
public:
    QComboBoxModel(QObject *parent=nullptr);
    int rowCount(const QModelIndex &) const;
    QVariant data(const QModelIndex &index, int role) const;
    void populate(QList<CountryFlag> *newValues);
    void append(CountryFlag value);
    void update(int idx, CountryFlag value);
    void deleteRow(int idx);
    void insertAt(int idx, CountryFlag value);
private:
    QList<CountryFlag> *values;
};

#endif // QCOMBOBOXMODEL_H

Реализация

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

Рассмотрим каждый метод модели:

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

    QVariant value;

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

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

            default:
                break;
        }

    return value;
}

 

Здесь мы используем метод класса at() класса QList, чтобы получить доступ к экземпляру класса и вернуть значение, соответствующее Роли, нас вполне устроит доступ только для чтения.

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

Изменился только параметр передаваемый методу.

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

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

Изменился метод append() теперь он получает в качестве параметра экземпляр класса CountryFlag

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

    QModelIndex item_idx = this->index(idx,0);

    emit this->dataChanged(item_idx ,item_idx );
}

При обновлении мы так же целиком заменяем старый экземпляр класса на новый.

Обратите внимание, мы всё еще используем  (*this->values)[idx]

Метод deleteRow() не изменился.

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

    int newRow = idx;

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

        values->insert(newRow,value);

    endInsertRows();
}

Метод теперь получает всего два параметра внутренний индекс строки и экземпляр класса.

Изменим методы класса главной формы 

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


    values = new QList<CountryFlag>;

    values->append(CountryFlag(-1, "Select flag"));
    values->append(CountryFlag(1,"Russia"));
    values->append(CountryFlag(11,"Belarus"));
    values->append(CountryFlag(22,"Slovakia"));
    values->append(CountryFlag(33,"Slovenia"));
    values->append(CountryFlag(44,"China"));
    values->append(CountryFlag(55,"Mongolia"));


    model = new QComboBoxModel();
    model->populate(values);
    this->ui->comboBox->setModel(model);

    model->insertAt(4,CountryFlag(66,"Japan"));
   newidx=0;
}

Изменим методы слотов главного окна:

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

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

void MainWindow::on_pushButton_2_clicked()
{
    model->update(ui->comboBox->currentIndex(), CountryFlag(66,"Japan"));


}

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

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

Запустим

2020-12-10_11-21-33.png

Вы так же можете проверить как работают все кнопки.

Таким образом, вместо QPair мы используем класс CountryFlag.

Добавляем новое поле в класс CountryFlag

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

Изменим класс CountryFlag, добавив новое поле capital.

Заголовок

#ifndef COUNTRYFLAG_H
#define COUNTRYFLAG_H

#include <QString>



class CountryFlag
{
public:
    CountryFlag();
    CountryFlag(int id, QString name, QString capital);

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

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

    QString getCapital() const;
    void setCapital(const QString &value);

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

#endif // COUNTRYFLAG_H

Реализация

#include "countryflag.h"

CountryFlag::CountryFlag()
{

}

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

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;
}

QString CountryFlag::getCapital() const
{
    return capital;
}

void CountryFlag::setCapital(const QString &value)
{
    capital = value;
}

Изменим метод модели data() 

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

    QVariant value;

        switch ( role )
        {
            case Qt::DisplayRole: //string
            {
                value = this->values->at(index.row()).getName() + " - " + this->values->at(index.row()).getCapital();
            }
            break;

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

            default:
                break;
        }

    return value;
}

Внесем изменения в методы класса главной формы:

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


    values = new QList<CountryFlag>;

    values->append(CountryFlag(-1, "Select flag", "Capital"));
    values->append(CountryFlag(1," Russia", "Moscow"));
    values->append(CountryFlag(11, "Belarus", "Minsk"));
    values->append(CountryFlag(22, "Slovakia", "Bratislava"));
    values->append(CountryFlag(33, "Slovenia", "Ljubljana"));
    values->append(CountryFlag(44, "China", "Beijing"));
    values->append(CountryFlag(55, "Mongolia","Ulaanbaatar"));


    model = new QComboBoxModel();
    model->populate(values);
    this->ui->comboBox->setModel(model);

    model->insertAt(4,CountryFlag(66, "Japan", "Tokyo"));

 newidx=0;
}

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


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

    model->append(CountryFlag(newidx, "new country " + strIdx, "a Capital"));
}

void MainWindow::on_pushButton_2_clicked()
{
    model->update(ui->comboBox->currentIndex(), CountryFlag(66, "Japan", "Tokyo"));


}

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

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

Запустим:

2020-12-10_11-50-03.png

Вот и всё, подобным образом вы можете добавить любое количество нужных вам полей. Это нам пригодиться, при создании Делегата для QComboBox.

Заключение

Сегодня мы модернизировали нашу модель, для этого был создан класс для хранения данных - CountryFlag.

Мы переписали методы модели, для использования с этим классом.

Были обновлены методы главной формы, для работы с новым классом.

Мы добавили в класс новое поле – столица страны и соответственно обновили модель и главную форму, для работы с новым полем.

В следующей статье мы рассмотрим создание Делегата для QComboBox.

Исходный код вы найдете на Github

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