Четверг, 10.12.2020 22:23

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

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

В прошлой статье мы рассмотрели создание модели для 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() не изменился.

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

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.

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

Категория Модели
Теги Qt Cpp

Добавить комментарий

Простой текст

  • HTML-теги не обрабатываются и показываются как обычный текст
  • Строки и абзацы переносятся автоматически.
  • Адреса веб-страниц и email-адреса преобразовываются в ссылки автоматически.
Просмотров: 70