2

I am trying to expose QList with custom objects (Sample) into QML. Whenever I store those custom objects (they inherits form QObject) into QList<QObject *>, they show up, but without info, but when I try expose them as a QList<Sample *>, they don't.

sample.h

class Sample : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString getVar READ getVar WRITE setVar NOTIFY varChanged)
public:
    explicit Sample();

    //! Returns var
    QString getVar() const { return var; }

    //! Sets var
    void setVar(const QString &a);

signals:
    varChanged();

protected:
    QString var;
};

Class containing list looks like this

samplecontrol.h

class SampleManager : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QList<Sample *> getSampleList READ getSampleList NOTIFY sampleListChanged)
public:
    SampleManager(const QString &path);

    //! Returns sample list
    QList<Sample *> getSampleList() const { return sampleList_; }

signals:
    sampleListChanged();

protected:
    QList<Sample *> sampleList_;
};

I am setting context with

view_->rootContext()->setContextProperty("slabGridModel", QVariant::fromValue(samplecontrol.getSampleList()));

As I said, when i tired

QList<QObject *> datalist;
datalist.append(sampleManager.getSampleList().first());
datalist.append(sampleManager.getSampleList().last());

it worked. How can I make it work with QList<Sample *>?

Thanks for your help!

Brykyz
  • 587
  • 4
  • 30
  • so that you want to use getSampleList on the QML side ?, is it going to be a model of some view? – eyllanesc Jul 03 '18 at 08:34
  • @eyllanesc Yes, it should be model of GridView that delegates Sample – Brykyz Jul 03 '18 at 08:37
  • the problem is that a QList is a read-only model and is added once per copy, that is, if you add elements to the QList it will not notify you, so even if you add elements the view will always be empty. – eyllanesc Jul 03 '18 at 08:39
  • @eyllanesc How can I expose it then? – Brykyz Jul 03 '18 at 08:44
  • For production you have to implement a proper model, exposing lists directly is just too inefficient. Since your model items derive from `QObject` you can use a generic model implementation, there are a few available out there. – dtech Jul 03 '18 at 10:20

1 Answers1

3

You can pass a list of QObjects, but the problem is that it will not notify the view if more elements are added, if you want the view to be notified you must use a model that inherits from QAbstractItemModel. On the other hand how are you doing the datamodel as qproperty it is better to expose the SampleManager:

samplemodel.h

#ifndef SAMPLEMODEL_H
#define SAMPLEMODEL_H

#include "sample.h"

#include <QAbstractListModel>

class SampleModel : public QAbstractListModel
{
    Q_OBJECT
public:
    using QAbstractListModel::QAbstractListModel;
    ~SampleModel(){
        qDeleteAll(mSamples);
        mSamples.clear();
    }
    int rowCount(const QModelIndex &parent = QModelIndex()) const override{
        if (parent.isValid())
            return 0;
        return mSamples.size();
    }

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override{
        if (!index.isValid())
            return QVariant();
        if(role == Qt::UserRole){
            Sample *sample =  mSamples[index.row()];
            return QVariant::fromValue(sample);
        }
        return QVariant();
    }

    void appendSample(Sample * sample)
    {
        beginInsertRows(QModelIndex(), rowCount(), rowCount());
        mSamples << sample;
        endInsertRows();
    }

    QHash<int, QByteArray> roleNames() const{
        QHash<int, QByteArray> roles;
        roles[Qt::UserRole] = "sample";
        return roles;
    }

private:
    QList<Sample *> mSamples;
};


#endif // SAMPLEMODEL_H

samplemanager.h

#ifndef SAMPLEMANAGER_H
#define SAMPLEMANAGER_H

#include "samplemodel.h"

#include <QObject>

class SampleManager : public QObject
{
    Q_OBJECT
    Q_PROPERTY(SampleModel* model READ model WRITE setModel NOTIFY modelChanged)
public:
    using QObject::QObject;
    SampleModel *model() const{
        return mModel.get();
    }
    void setModel(SampleModel *model){
        if(mModel.get() == model)
            return;
        mModel.reset(model);
    }
signals:
    void modelChanged();
private:
    QScopedPointer<SampleModel> mModel;
};

#endif // SAMPLEMANAGER_H

main.cpp

#include "samplemanager.h"
#include "samplemodel.h"

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

#include <QTime>
#include <QTimer>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    SampleManager manager;
    manager.setModel(new SampleModel);

    // test
    QTimer timer;
    QObject::connect(&timer, &QTimer::timeout, [&manager](){
        manager.model()->appendSample(new Sample(QTime::currentTime().toString()));
    });
    timer.start(1000);

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("manager", &manager);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

main.qml

import QtQuick 2.9
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    GridView {
        anchors.fill: parent
        model: manager.model
        delegate:
            Rectangle {
            width: 100
            height: 100
            color: "darkgray"
            Text {
                text: sample.getVar
                anchors.centerIn: parent
            }
        }
    }
}

enter image description here

The complete example can be found in the following link.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • It works, but somehow it add only one sample and when i click on it (before it it wrote "Clicked!" to console), program throw SIGSEGV – Brykyz Jul 03 '18 at 11:42