Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Qt accessing model data outside ItemDelegate

I have some model class that inherits QAbstractListModel:

VehiclesModel.h:

class VehiclesModel : public QAbstractListModel {
    Q_OBJECT

    public:
        enum Roles {
            ImagePathRole = Qt::UserRole + 1,   // QString
            NameRole                            // QString
        };

        virtual int rowCount(const QModelIndex & parent = QModelIndex()) const override { ... }
        virtual QVariant data(const QModelIndex & index, int role) const override { ... }
        virtual QHash<int, QByteArray> roleNames() const override {
            QHash<int, QByteArray> roles = QAbstractListModel::roleNames();

            roles[ImagePathRole] = "imagePath";
            roles[NameRole] = "name";

            return roles;
        }
};

main.cpp:

#include "VehiclesModel.h"

int main(int argc, char * argv[]) {
    QGuiApplication app(argc, argv);
    VehiclesModel vehiclesModel;
    QQmlApplicationEngine engine;

    engine.rootContext()->setContextProperty("vehiclesModel", &vehiclesModel);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    return app.exec();
}

And ComboBox that displays this model: main.qml:

ComboBox {
    id: control
    model: vehiclesModel
    delegate: ItemDelegate {
        contentItem: RowLayout {
            Image {
                source: imagePath
            }
            Label {
                text: name
            }
        }
        highlighted: control.highlightedIndex == index
    }
    contentItem: RowLayout {
        Image {
            source: ??imagePath??
        }
        Label {
            text: ??name??
        }
    }
}

I want to customize the ComboBox to show vehicle image and name. I can access to model data from ItemDelegate but how to access to model data outside the ItemDelegate? For example I want to access current index data (ImagePathRole and NameRole) to display vehicle image and name in contentItem.

Is it possible to do it without calling QAbstractListModel methods directly (i.e. index() and data() methods) and making them Q_INVOKABLE?

like image 924
Kamil Zaripov Avatar asked Nov 23 '25 08:11

Kamil Zaripov


2 Answers

Not in any sort of a decent built-in way at the present time, unfortunately, this is something I've found to be lacking for quite a while, and I've considered implementing something for this in the QML models functionality, but I haven't yet had the time to do so.

For the time being, you can either do it yourself (like you're discussing), at the cost of type-safety and so on, or (the way I've typically tackled this before), you can create a QObject subclass to represent a single item in the model (ItemDataThing or whatever you choose to call it); provide it with a source model & index, properties, and let it represent a single instance of data from the model.

Something like:

class ImageDataThing : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString imagePath READ imagePath NOTIFY imagePathChanged)
    Q_PROPERTY(QAbstractItemModel* model READ model WRITE setModel NOTIFY modelChanged)
    Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged)

public:
    QString imagePath() const;
    QAbstractItemModel *model() const;
    void setModel(const QAbstractItemModel *newModel);
    int index() const;
    void setIndex(int newIndex);
signals:
    void imagePathChanged(const QString &imagePath);
    void modelChanged(QAbstractItemModel *model);
    void indexChanged(int indexChanged);
};

... and in your implementation, whenever the model is set, hook the change signals (e.g. rowsInserted, rowsRemoved, ...) to alter the stored index (if provided) to keep it mapped to the correct place in the model.

In the model data getters (here, imagePath for instance), access the model instance (using the index) to grab the data out, and return it.

This has the obvious disadvantage of being a lot of boilerplate, but on the other hand, it's easy-to-write code if you are familiar with models, type-safe, and one could autogenerate it fairly easily.

like image 101
Robin Burchell Avatar answered Nov 24 '25 22:11

Robin Burchell


You could create your own function to get data from the model, like the one I'm currently using, VehiclesModel.h:

public slots:
    int size() const;   // to access from QML javascript
    QVariant getData(int index, int role);  // to access from QML javascript

VehiclesModel.cpp:

int VehiclesModel::size() const {
    return m_list.size();
}

QVariant VehiclesModel::getData(int index, int role) {
    if (index < 0 || index >= m_list.count())
        return QVariant();
    switch (role) {
    case ImagePathRole:
        return ...
        break;
    default:
        break;
    }
}
like image 30
Vedanshu Avatar answered Nov 24 '25 20:11

Vedanshu



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!