How to use QAbstractItemModel for ComboBox

Viewed 318

I have implemented QAbstractItemModel as a C++ class and I'm trying to use it as a model for QComboBox. The model is storing QList of QStrings, which are names of project apps INI files. I can print out the list into console, and I'am certainly getting the right list of files. However I'm getting an error Unable to assign [undefined] to QString. Can you please help me, what am I doing wrong?

LcuModel.cpp

#include "LcuModel.h"
#include "logging/LoggingQtCategories.h"

#include <QCoreApplication>
#include <QSettings>
#include <QFileInfo>
#include <QDir>

LcuModel::LcuModel(QObject *parent)
    : QAbstractItemModel(parent)
{
    QSettings settings;
    QFileInfo fileInfo(settings.fileName());
    QDir directory(fileInfo.absolutePath());
    iniFiles_ = directory.entryList({"*.ini"},QDir::Files);
    if (iniFiles_.empty())
    {
        qCWarning(lcLcu) << "No ini files were found.";
    }
    qCInfo(lcLcu) << iniFiles_; //this prints out all the files to console, so the list is certainly not empty
}

QModelIndex LcuModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(row)
    Q_UNUSED(column)
    Q_UNUSED(parent)
    return QModelIndex();
}

QModelIndex LcuModel::parent(const QModelIndex &index) const
{
    Q_UNUSED(index)
    return QModelIndex();
}

int LcuModel::rowCount(const QModelIndex & parent) const {
    Q_UNUSED(parent)
    return iniFiles_.count();
 }

int LcuModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return 1;
}

QVariant LcuModel::data(const QModelIndex & index, int role) const
{
    Q_UNUSED(role);
    int row = index.row();
    qCInfo(lcLcu) << QString::number(row) << " ";
    if (row < 0 || row >= iniFiles_.count()) {
        return QVariant();
    }

    return QVariant(iniFiles_.at( index.row() ));
}
QHash<int, QByteArray> LcuModel::roleNames() const
{
    QHash<int, QByteArray> names;
    names[textRole] = "fileName";
    return names;
}

LcuModel.h

#ifndef LCUMODEL_H
#define LCUMODEL_H

#include <QAbstractItemModel>

class LcuModel : public QAbstractItemModel
{
    Q_OBJECT

public:
    explicit LcuModel(QObject *parent = nullptr);

    enum {
        textRole
    };

    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex &index) const override;

    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;

    QVariant data(const QModelIndex &index, int role) const override;

protected:
    virtual QHash<int, QByteArray> roleNames() const override;

private:
    QList<QString> iniFiles_;
};

#endif // LCUMODEL_H

LcuMain.qml

import QtQuick 2.12
import QtQuick.Controls 2.12
import project.lcu 1.0

Item {
    id: lcuMain

    LcuModel{
        id:lcuModel
    }

    Component.onCompleted: {
        console.info("Completed "+ comboBox.count + "items")
    } //this gives me right number of items from the list
    Column{
        ComboBox{
            id: comboBox
            model: lcuModel
            delegate: ItemDelegate
            {
                contentItem: Text {
                    text: fileName
                }
            }
            onActivated: console.info(comboBox.currentText + comboBox.currentIndex) //it prints out an empty string
        }
    }
}

console output

18:02:25.543 [ info  ] project.lcu: LcuModel::LcuModel - ("first.ini", "second.ini", "third.ini", "fourth.ini", "fifth.ini", "sixth.ini", "seventh.ini", "eight.ini", "nineth.ini")
18:02:25.616 [warning] unknown - ComboBox.qml:47:5: QML Connections: Cannot assign to non-existent property "onCountChanged"
18:02:25.675 [ info  ] qml: onCompleted - 9
18:02:25.678 [warning] unknown - qrc:/LcuMain.qml:46:25: Unable to assign [undefined] to QString
1 Answers

It is unnecessary for the model to inherit from QAbstractItemModel, QAbstractListModel is enough so you don't have to implement multiple methods. On the other hand, you must establish the role that you want to show through textRole and in the delegate you must use modelData:

#ifndef LCUMODEL_H
#define LCUMODEL_H

#include <QAbstractListModel>

class LcuModel : public QAbstractListModel
{
    Q_OBJECT

public:
    enum {
        textRole = Qt::UserRole
    };
    explicit LcuModel(QObject *parent = nullptr);
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    QHash<int, QByteArray> roleNames() const override;
private:
    QStringList iniFiles_;
};

#endif // LCUMODEL_H
#include "lcumodel.h"

#include <QDir>
#include <QFileInfo>
#include <QSettings>


LcuModel::LcuModel(QObject *parent)
    : QAbstractListModel(parent)
{

    QSettings settings;
    QFileInfo fileInfo(settings.fileName());
    QDir directory(fileInfo.absolutePath());
    iniFiles_ = directory.entryList({"*.ini"}, QDir::Files);
}

int LcuModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    return iniFiles_.length();
}

QVariant LcuModel::data(const QModelIndex &index, int role) const
{
    if(!checkIndex(index)){
        return QVariant();
    }
    if(role == textRole){
        return iniFiles_.at(index.row());
    }
    return QVariant();
}

QHash<int, QByteArray> LcuModel::roleNames() const
{
    QHash<int, QByteArray> names;
    names[textRole] = "fileName";
    return names;
}
import QtQuick 2.12
import QtQuick.Controls 2.12
import project.lcu 1.0

Item{
    id: lcuMain

    LcuModel{
        id:lcuModel
    }

    Column{
        ComboBox{
            id: comboBox
            model: lcuModel
            textRole: "fileName"
            delegate: ItemDelegate{
                text: modelData
                width: parent.width
            }
        }
    }
}
Related