Unsolved Q_INVOKABLE from object used by QAbstractListModel
-
Hello Christian,
thank you, I changed it to pointers and now it's derived from QObject.
But now I have the problem that I don't know how to access items from the model directly to use their Q_PROPERTYs
Any ideas? -
Hi,
Do you mean replace:
dataSourceObject.setId(value.toInt());
by
dataSourceObject->setId(value.toInt());
?
-
I changed my model to this:
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(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; Q_INVOKABLE DataSourceObject *dataPointer(const QModelIndex &index); 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
Cpp:
#include "datasourcemodel.h" DataSourceModel::DataSourceModel(QObject *parent) : QAbstractListModel(parent) { } QVariantMap DataSourceModel::get(int row) const { return m_DataSourceObjects[row]->toMap(); } void DataSourceModel::addDataSourceObject(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(); 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; } DataSourceObject *DataSourceModel::dataPointer(const QModelIndex &index) { return m_DataSourceObjects[index.row()]; } 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 can access now the objects from QML, but it stops working after about 40 changes and the used memory raises:
dataSourceModel.dataPointer(dataSourceModel.index(row,0)).value
Why does ist stop working after some changes of the "value" of the object
When I debug the access in QML I see the address of the object in the console, but after some changes to it's value the function dataPointer returns NULL
Console.log
DataSourceObject(0x1fcc44a8) DataSourceObject(0x1fcc44a8) DataSourceObject(0x1fcc44a8) DataSourceObject(0x1fcc44a8) DataSourceObject(0x1fcc44a8) DataSourceObject(0x1fcc44a8) DataSourceObject(0x1fcc44a8) DataSourceObject(0x1fcc44a8) DataSourceObject(0x1fcc44a8) DataSourceObject(0x1fcc44a8) DataSourceObject(0x1fcc44a8) qrc:/qml/gauges/RPMBar1.qml:13: TypeError: Cannot read property 'value' of null null null null null null
If I understand it right, the Models are changing the addresses of it's items during runtime. I need a workaround for that
-
Can you provide a minimal compilable example that shows that behaviour ?
-
@SGaist
you can download a running example here:
https://www.dropbox.com/s/nbhsx8nkvc73jqz/powertune-2.zip?dl=0
Please copy the datasources.json file into the build folder before startingNormally the data is supplied over network but I changed it to make a running example
-
property int rpm : dataSourceModel.dataPointer(dataSourceModel.index(88,0)).value
What should this do? Do you want to print the pointer to the object? Since it's a QObject it gets deleted after it gets out of scope I would guess - at least this it what it looks like when running it with valgrind
-
@Christian-Ehrlicher
It returns the property "value" of an item of the model that I want to display in QML using Q_PROPERTY. I'm open for better ways to do that. In the Qt manuals is no way described to show single items of a model and get the GUI updatet automatically.
If you run my program you can see that the GUI prints the property "value" of the item with ID "88" from the model.
What do you mean with it gets out of scope? It is still part of the model and still visible when I display the whole model in a ListView in QML. -
This is what I mean and why your object gets destroyed: https://doc.qt.io/qt-5/qtqml-cppintegration-data.html#data-ownership
I don't see a reason why this function should be Q_INVOKEABLE and even no need to access the plain data instead going through the model. -
@Christian-Ehrlicher
Then can you give me a way how to do it with the model? How can I display a single item in the QML? The data() function is of no use to me. I need "realtime" updated values in the QML. -
What should be the difference to call data() or the property value which also only calls the function value() ?
-
@Christian-Ehrlicher
When I call in QML e.g. text: mymodel.data(index,role); this only shows the momentary value of this item, but it's not updating the GUI automatically when value changes.
This is my problem. I need the GUI updated when the item changes. -
@Slash200
I only know about Qt widgets, not QML. In Qt widgets when model gets updated a signal likedataChanged
(and others) is emitted, and a linked view will receive that and update itself as necessary. Are you supposed to do something like this from QML? Or is it just supposed to work automatically? Examples tend to use, say,ListView
; I don't know whether you using it for atext
means it needs to be told to update on data change. -
@Slash200 said in Q_INVOKABLE from object used by QAbstractListModel:
but it's not updating the GUI automatically when value changes.
so why not connect to the dataChanged signal then?
e.g. here: https://forum.qt.io/topic/71392/qml-receives-undefined-for-roles-from-datachanged-signal -
The dataChanged Signal is fired for every item and I've got about 200 items which are updated hundrets times per seconds, which are minimum 20.000 signals per second. (I'm receiving realtime data from a motorsports Engine control unit via CANBUS)
This is a dashboarding software which runs on a embedded device with not very much CPU power. I want only to update the QML items when the specific item is changed. otherwise the CPU is exhausted, because every gauge will update, even if a different item is changed. I hope I've explained my problem in a way that's reasonably understandable. -
So define a custom signal in your model and connect to this.
-
@Christian-Ehrlicher
yes this works. I've got a signal: void itemValueChanged(double value, int id);
The qml item can now see by the ID if it's it's value that has to be updatet,
But this means that all items need a if to check wether if it's it's id or not and will cause also a lot of load.
Like that:Connections { target: dataSourceModel onItemValueChanged: { if(myId === id){ text = value; } } }
Is this the way you meant to do it?
-
@Slash200 said in Q_INVOKABLE from object used by QAbstractListModel:
Is this the way you meant to do it?
More or less, but you can also emit a signal for the specific entry as you seem to know the row number (88?)