Solved Populate C++ model from external source in Qt
-
// Main QML file import QtQuick 2.5 import QtQuick.Window 2.2 import QtQuick.Controls 1.5 import QtQuick.Layouts 1.2 import QtQuick.Controls.Styles 1.4 import QtQuick.Extras 1.4 // imported module import org.example 1.0 Window { visible: true width: 480 height: 480 // our Generator model HardwareDataModel{ id: hwDataModel } Label { id: myLabel text: hwDataModel.varVal visible: true } }
#include <QtGui> #include <QtQml> #include "hardwaredatamodel.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); qmlRegisterType<HardwareDataModel>("org.example", 1, 0, "HardwareDataModel"); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/ui/main.qml")));
#ifndef HARDWAREDATAMODEL_H #define HARDWAREDATAMODEL_H #include <QtCore> #include <QtGui> #include <QAbstractItemModel> class HardwareDataModel: public QAbstractItemModel { Q_OBJECT Q_PROPERTY(int hwValue READ hwValue WRITE setHwValue NOTIFY hwValueChanged) public: explicit HardwareDataModel(QObject *parent = nullptr); // Header: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; // Basic functionality: 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 = Qt::DisplayRole) const override; int hwValue() const; public slots: void setHwValue(int hwValue); // this is autogenerated from Q_PROPERTY, not using it signals: void hwValueChanged(); private: int m_hwValue; // this value will be updated in every few secs, from hw source. but I have no idea how to achieve this }; #endif // HARDWAREDATAMODEL_H
#include "hardwaredatamodel.h" #include "main.h" #include <chrono> #include <thread> HardwareDataModel::HardwareDataModel(QObject *parent) : QAbstractItemModel(parent) { } QVariant HardwareDataModel::headerData(int section, Qt::Orientation orientation, int role) const { // FIXME: Implement me! } QModelIndex HardwareDataModel::index(int row, int column, const QModelIndex &parent) const { // FIXME: Implement me! } QModelIndex HardwareDataModel::parent(const QModelIndex &index) const { // FIXME: Implement me! } int HardwareDataModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) return 0; // FIXME: Implement me! } int HardwareDataModel::columnCount(const QModelIndex &parent) const { if (!parent.isValid()) return 0; // FIXME: Implement me! } QVariant HardwareDataModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); // FIXME: Implement me! return QVariant(); } int HardwareDataModel::varVal() const { return m_varVal; } void HardwareDataModel::setVarVal(int varVal) { emit hwValueChanged(); }
-
- You don't need to start from scratch with a
QAbstractItemModel
subclass. It's usually easier to just useQStandardItemModel
and switch to the custom model only if you feel a need for performance reasons. - To update the model whenever you want and with whatever data you want from the C++ side you just call
QAbstractItemModel::setData
- As posted twice above, to control your model from the C++ side you need to instantiate the model in the c++ side (or have a factory on the C++ side). See the below from http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel-subclass
AnimalModel model; model.addAnimal(Animal("Wolf", "Medium")); model.addAnimal(Animal("Polar bear", "Large")); model.addAnimal(Animal("Quoll", "Small")); QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); QQmlContext *ctxt = view.rootContext(); ctxt->setContextProperty("myModel", &model);
- You don't need to start from scratch with a
-
@VRonin . Like I have said before, calling setData function will only set the model data once or as long as it is called. I want to populate it automatically using some thread or some different method.
-
I want to populate it automatically using some thread or some different method.
I get the feeling that I didn't really understand your needs. To me this is trivial: emit a signal from the different thread/method and connect it to a slot that calls
QAbstractItemModel::setData
. -
@VRonin. Sorry if I could not elaborate my problem enough. I am designing UI for which the backend data comes from the hardware connected to USB port. Now, I have console application that is continously requesting and sending data to/from the USB port (10 data per second). The console prints the data without problem. Next, I need to visualize those data using some GUI. I chose QT as backend data request is in C++. Looking at QT, I saw Model-view pattern is recommended for larger application. So, I tried smaller application with model, whose data-member will be updated from the data received from the USB port. I am thinking of some thread, but it is also not working. The received data has to be represented in the UI updating almost real-time if possible. I chose to update label just for testing, but it would be some different GUI in future. Please let me know my description is still not clear enough.
-
The description perfectly fits the above model of data update.
-
@VRonin. Thanks for your reply. I am using QQmlApplicationEngine instead of QQuickView. Can I use qmlRegisterType instead of setContextProperty from QQuickView. Still, I do not understand how can calling setData will solve this problem. It has to be called again and again.
-
@milan said in Populate C++ model from external source in Qt:
Can I use qmlRegisterType instead of setContextProperty from QQuickView
No, you need the model instance to be accessible to the C++ side.
I am using QQmlApplicationEngine instead of QQuickView.
QQmlApplicationEngine
inheritsQQmlEngine
so you can useapplicationEngine->rootContext()->setContextProperty("myModel", &model);
I do not understand how can calling setData will solve this problem. It has to be called again and again.
Yes it has, something has to be called again and again to update it though, doesn't it?
-
@VRonin. Thank you very much for your reply. I get error when I try to add this code
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); HardwareDataModel hardwareDataModel; QQmlApplicationEngine* engine; engine->rootContext()->setContextProperty("HardwareDataModel", &hardwareDataModel); engine->load(QUrl(QStringLiteral("qrc:/main.qml"))); if (engine->rootObjects().isEmpty()) return -1; return app.exec();
When I build this, I get linker error LNK2019.
main.obj:-1: error: LNK2019: unresolved external symbol "public: __cdecl HardwareDataModel::HardwareDataModel(class QObject *)" (??HardwareDataModel@@QEAA@PEAVQObject@@@Z) referenced in function main
Please help.
-
- you need to initialise engine
QQmlApplicationEngine* engine = new QQmlApplicationEngine
unresolved external symbol "public: __cdecl HardwareDataModel::HardwareDataModel(class QObject *)"
usually means that you have the declaration but not the implementation of that method (the constructor)
- you need to initialise engine
-
This post is deleted!