1
votes

I want to display a single item and it's roles in the GUI from a QAbstractItemModel. In the manual I can only find how to display the whole model.

Displaying a single item is working, but the data in the GUI isn't updated when the data in the model is changed.

The model emits dataChanged() in the setData() function.

Object in Model:

#ifndef DATASOURCEOBJECT_H
#define DATASOURCEOBJECT_H

#include <QString>
#include <QVariantMap>

class DataSourceObject
{
public:
    DataSourceObject(const int &id=0, const QString &name="", const QString &displayname="", const double &value=0.0);
    DataSourceObject(const QJsonObject &obj);
    int id() const;
    void setId(int id);

    QString name() const;
    void setName(const QString &name);

    QString unit() const;
    void setUnit(const QString &unit);

    double value() const;
    void setValue(double value);
    QVariantMap toMap() const;

private:
    int m_id;
    QString m_name;
    QString m_unit;
    double m_value;
};

#endif // DATASOURCEOBJECT_H

Model Header:

#ifndef DATASOURCEMODEL_H
#define DATASOURCEMODEL_H

#include "datasourceobject.h"
#include <QAbstractListModel>

class DataSourceModel : public QAbstractListModel
{
    Q_OBJECT

public:
    enum datasourceRoles {
        idRole = Qt::UserRole ,
        nameRole,
        unitRole,
        valueRole
    };

    explicit DataSourceModel(QObject *parent = nullptr);
    void addDataSourceObject(const DataSourceObject &dataSourceObject);
    Q_INVOKABLE QVariantMap get(int row) const;

    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    Q_INVOKABLE QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    bool setData(const QModelIndex &index, const QVariant &value,
                 int role = Qt::EditRole) override;
    Qt::ItemFlags flags(const QModelIndex& index) const override;
    QHash<int, QByteArray> roleNames() const override;
    //bool checkIndex(const QModelIndex &index) const;

private:
    QList<DataSourceObject> m_DataSourceObjects;
};

#endif // DATASOURCEMODEL_H

Model cpp:

#include "datasourcemodel.h"

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

QVariantMap DataSourceModel::get(int row) const
{
    return m_DataSourceObjects[row].toMap();
}

void DataSourceModel::addDataSourceObject(const DataSourceObject &dataSourceObject)
{
    beginInsertRows(QModelIndex(), rowCount(), rowCount());
    m_DataSourceObjects << dataSourceObject;
    endInsertRows();
}
int DataSourceModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    return m_DataSourceObjects.count();
}

QVariant DataSourceModel::data(const QModelIndex &index, int role) const
{
    if(index.row() < 0 || index.row() >= m_DataSourceObjects.count() || !index.isValid())
        return  QVariant();

    const DataSourceObject &dataSourceObject = m_DataSourceObjects[index.row()];
    if (role == idRole)
        return dataSourceObject.id();
    else if (role == nameRole)
        return dataSourceObject.name();
    else if (role == unitRole) {
        return dataSourceObject.unit();
    }
    else if (role == valueRole)
        return dataSourceObject.value();
    return QVariant();
}

bool DataSourceModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    DataSourceObject &dataSourceObject = m_DataSourceObjects[index.row()];
    if (data(index, role) != value) {
        if(role == idRole)
            dataSourceObject.setId(value.toInt());
        else if(role == nameRole)
            dataSourceObject.setName(value.toString());
        else if(role == unitRole)
            dataSourceObject.setUnit(value.toString());
        else if(role == valueRole)
            dataSourceObject.setValue(value.toDouble());
        emit dataChanged(index, index, QVector<int>() << role);
        return true;
    }
    return false;
}

Qt::ItemFlags DataSourceModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return Qt::NoItemFlags;

    return Qt::ItemIsEditable; // FIXME: Implement me!
}

QHash<int, QByteArray> DataSourceModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[idRole] = "id";
    roles[nameRole] = "name";
    roles[unitRole] = "unit";
    roles[valueRole] = "value";
    return  roles;
}

I tried to access a single item in QML like this:

 Text {
       text: dataSourceModel.data(dataSourceModel.index(88,0),259).toFixed(decimalplaces)
      }

This shows the data's role 259(valueRole), but the text isn't updated when changed.

I tried to derive the object inside the model from QObject, to use Q_INVOKABLE, but when derived from QOBject, I can't compile it.C:\Qt\5.10.0\mingw53_32\include\QtCore\qlist.h:435: Fehler: use of deleted function 'DataSourceObject::DataSourceObject(const DataSourceObject&)' if (QTypeInfo::isLarge || QTypeInfo::isStatic) n->v = new T(t);

                                                           ^
1
just to clarify dataSourceModel.data(dataSourceModel.index(88,0),259) 259 corresponds to the role?bardao
why don't you use Q_PROPERTY as in Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) in your DataSourceModel. Also do the same for the rest of your variables. That would create the binding for you when you access your value with data.modelData.name...bardao
How can I use Q_PROPERTY in the model? Can you show me a example?Basti An
correction: use it in the DataSourceObject like in doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes.htmlbardao
This needs that the object is derived from Qobject. But this does not work when it's part of an model. Also the manual or examples show that the objects that are part of an model arent derived from QObjectBasti An

1 Answers

1
votes

The text is not updated because you have only copied the value but there is no binding between the components.

A possible solution is to use a Repeater next to a Loader that creates an item if the row is the one specified.

Component {
    id: textcomponent
    Text {
        text: value.toFixed(decimalplaces)
    }
}
Repeater{
    model: dataSourceModel
    Loader{
        property real value: model.value
        sourceComponent: index == 88 ? textcomponent: null
    }
}

A complete example you can find here

Another possible solution is to make item 88 specifically a Q_PROPERTY.