Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to force a model to update QComboBox when data changed?

I created model for QComboBox:

#ifndef QCOMBOBOXMODEL_H
#define QCOMBOBOXMODEL_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(const QList<QPair<int,QString>> &values);

private:
    QList<QPair<int,QString>> values;
};

#endif // QCOMBOBOXMODEL_H

code

#include "qcomboboxmodel.h"

#include <QModelIndex>

QComboBoxModel::QComboBoxModel(QObject *parent)
    :QAbstractListModel(parent)
{
}

int QComboBoxModel::rowCount(const QModelIndex &) const
{
    return values.count();
}


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

    QVariant value;

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

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

            default:
                break;
        }

    return value;
}

void QComboBoxModel::populate(const QList<QPair<int,QString>> &values)
{
    this->values = values;
}

Now i use it

    values.append(QPair<int,QString>(-1,"Select item"));
    values.append(QPair<int,QString>(10,"item1(0)"));
    values.append(QPair<int,QString>(11,"item1(1)"));
    values.append(QPair<int,QString>(21,"item1(2)"));
    values.append(QPair<int,QString>(32,"item1(3)"));
    values.append(QPair<int,QString>(44,"item1(4)"));

    newidx = 50;


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

and on button click i add new item to combobox

newidx++;
QString strIdx = QString().number(newidx);
values.append(QPair<int,QString>(newidx,"New item("+strIdx+")"));

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

Its all seems works just fine, but problem here that i need to recreate model every time i add new item to combobox data

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

Is that a proper way to do so? Or there are another way to force model update combobox when data updated?

like image 601
Vasilij Altunin Avatar asked Sep 01 '25 03:09

Vasilij Altunin


2 Answers

According to "Model Subclassing Reference" in the documentation you have to do many more things to make an editable model. Is there a reason why you don't use a ready made model like QStandardItemModel?

    comboModel = new QStandardItemModel(0, 2, this);
    ui->comboBox1->setModel(comboModel);
    comboModel->insertRow(0);
    comboModel->setData(comboModel->index(0, 0), -1);
    comboModel->setData(comboModel->index(0, 1), "Select item");
        //and so on
    //and the data is available as
    int number = comboModel->data(comboModel->index(0, 0)).toInt();
    QString itemtext = comboModel->data(comboModel->index(0, 1)).toString();
like image 190
Steve J Avatar answered Sep 02 '25 15:09

Steve J


I find solution!

First i add new method to model

void QComboBoxModel::append(int index, QString value)
{

    int newRow = this->values.count();

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

        values.append(QPair<int,QString>(index,value));

    endInsertRows();
}

Now on button click method changed to this

void MainWindow::on_pushButton_clicked()
{
    qDebug() << "Clicked!";

    newidx++;
    QString strIdx = QString().number(newidx);

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

Point is to use beginInsertRows and endInsertRows to notify model that data actually changed!

Now all worked as expected!

Now you also can modify append method to batch add rows to it, but i think if you add many rows it better just recreate model and reassign it to combobox.

Update 1:

Also keep in mind, that you update values QList inside model, so if you add

qDebug() << values;

in on_pushButton_clicked() method, you always see

(QPair(-1,"Select item"), QPair(10,"item1(0)"), QPair(11,"item1(1)"), QPair(21,"item1(2)"), QPair(32,"item1(3)"), QPair(44,"item1(4)"))

Update 2:

Also i update populate method

void QComboBoxModel::populate(const QList<QPair<int,QString>> &newValues)
{
    int oldIdx = this->values.count();
    int newIdx = newValues.count();
    this->beginInsertRows(QModelIndex(), oldIdx, newIdx);
        this->values = newValues;
    endInsertRows();
}

Now you can just work with values list

void MainWindow::on_pushButton_clicked()
{
    qDebug() << "Clicked!";

    newidx++;
    QString strIdx = QString().number(newidx);

    values.append(QPair<int,QString>(newidx,"new item " + strIdx));
    model->populate(values);

    qDebug() << values;
}

Update 3:

Now, i find one big problem - i did not use pointer inside model, so when i pass QList to model it just create copy instead use already created, so i rewrite model and other code:

Model

#ifndef QCOMBOBOXMODEL_H
#define QCOMBOBOXMODEL_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<QPair<int,QString>> *newValues);
    void append(int index, QString value);

private:
    QList<QPair<int,QString>> *values;
};

#endif // QCOMBOBOXMODEL_H

#include "qcomboboxmodel.h"

#include <QModelIndex>
#include <QDebug>

QComboBoxModel::QComboBoxModel(QObject *parent)
    :QAbstractListModel(parent)
{
    values = new QList<QPair<int,QString>>();
}

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


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

    QVariant value;

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

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

            default:
                break;
        }

    return value;
}

void QComboBoxModel::populate(QList<QPair<int,QString>> *newValues)
{
    int oldIdx = this->values->count();
    int newIdx = newValues->count();
    this->beginInsertRows(QModelIndex(), oldIdx, newIdx);
        this->values = newValues;
    endInsertRows();
}

void QComboBoxModel::append(int index, QString value)
{

    int newRow = this->values->count();

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

        values->append(QPair<int,QString>(index,value));

    endInsertRows();
}

Main form

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include "qcomboboxmodel.h"

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_comboBox_currentIndexChanged(int index);

    void on_comboBox_currentIndexChanged(const QString &arg1);

    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
    int newidx;
    QList<QPair<int,QString>> *values;
    QComboBoxModel *model;
};
#endif // MAINWINDOW_H

#include "mainwindow.h"
#include "qcomboboxmodel.h"
#include "ui_mainwindow.h"

#include <QDebug>

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

    values = new QList<QPair<int,QString>>();

    values->append(QPair<int,QString>(-1,"Select item"));
    values->append(QPair<int,QString>(10,"item1(0)"));
    values->append(QPair<int,QString>(11,"item1(1)"));
    values->append(QPair<int,QString>(21,"item1(2)"));
    values->append(QPair<int,QString>(32,"item1(3)"));
    values->append(QPair<int,QString>(44,"item1(4)"));

    newidx = 50;


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

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


void MainWindow::on_comboBox_currentIndexChanged(int index)
{
    qDebug() << ui->comboBox->itemData(index).value<int>();
}

void MainWindow::on_comboBox_currentIndexChanged(const QString &arg1)
{
    qDebug() << arg1;
}

void MainWindow::on_pushButton_clicked()
{
    qDebug() << "Clicked!";

    newidx++;
    QString strIdx = QString().number(newidx);

    values->append(QPair<int,QString>(newidx,"new item " + strIdx));
    model->populate(values);

    qDebug() << values->toStdList();
}

Now all looks just fine and works as intended!

like image 32
Vasilij Altunin Avatar answered Sep 02 '25 15:09

Vasilij Altunin