More complex example. Don't work
-
I continue study QML and create more complex example, based on
https://forum.qt.io/topic/140468/periodically-update-data-in-qml-solved/4
I add new container class for hub(with 8 sensors).
hubset.h#ifndef HUBSET_H #define HUBSET_H #include "hub.h" class HubSet : public Hub { Q_PROPERTY(QList<double> dataFromHubnumber READ dataFromHubnumber WRITE setDataFromHubnumber NOTIFY dataFromHubnumberChanged) public: HubSet(Hub *currentHub); void addHub(int numberPos, Hub *currentHub); void removeHub(int numberPos); QList<double> dataFromHubnumber(int hubNumber); void setDataFromHubnumber(int hubNumber, QList<double> newData); int number_Of_Hub(void); protected: QList<Hub *> m_hubset; int m_number_of_hub=0; signals: void dataFromHubnumberChanged(); }; #endif // HUBSET_H
hubset.cpp
#include "hubset.h" HubSet::HubSet(Hub *currentHub) { m_hubset.append(currentHub); m_number_of_hub++; } void HubSet::addHub(int numberPos, Hub *currentHub) { m_hubset.insert(numberPos, currentHub); m_number_of_hub++; } void HubSet::removeHub(int numberPos) { m_hubset.removeAt(numberPos); m_number_of_hub--; } QList<double> HubSet::dataFromHubnumber(int hubNumber) { return m_hubset[hubNumber]->channelData(); } void HubSet::setDataFromHubnumber(int hubNumber, QList<double> newData) { m_hubset[hubNumber]->setChannelData(newData); } int HubSet::number_Of_Hub(void) { return m_number_of_hub; }
I modify main.cpp . Now It create 5 set of sensors data. On C++ side it works fine.
main.cpp#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include <QTimer> #include <QtDebug> #include <QRandomGenerator> #include "Hub/hub.h" #include "Hub/hubset.h" #define numberOfHub 4 void setItem(QQmlApplicationEngine *pEngine); HubSet tractor(new Hub); int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; for (int i=0;i<numberOfHub;i++) { tractor.addHub(i, new Hub); } engine.rootContext()->setContextProperty("tr", &tractor); engine.load(QUrl(QStringLiteral("qrc:/QML/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; QTimer timer; QObject::connect(&timer, &QTimer::timeout, [&engine]() { setItem(&engine); }); timer.start(5000); return app.exec(); } void setItem(QQmlApplicationEngine *pEngine) { for (int i=0;i<tractor.number_Of_Hub();i++) { QList<double> temp; for (int j=0;j<8;j++) { temp.append(QRandomGenerator::global()->bounded(30, 120)); } tractor.setDataFromHubnumber(i, temp); qDebug() << "Hub number " << i << ", last data = " << tractor.dataFromHubnumber(i); } qDebug() << "/**************************************************************/"; qDebug() << "Number of Hub" << tractor.number_Of_Hub(); }
I see messages like this
Hub number 0 , last data = QList(66, 67, 59, 97, 95, 97, 53, 52) Hub number 1 , last data = QList(93, 91, 89, 44, 39, 112, 76, 33) Hub number 2 , last data = QList(63, 92, 44, 74, 50, 69, 103, 64) Hub number 3 , last data = QList(41, 114, 118, 98, 68, 42, 67, 52) Hub number 4 , last data = QList(65, 96, 80, 110, 59, 45, 106, 34) /**************************************************************/ Number of Hub 5 Hub number 0 , last data = QList(92, 46, 95, 33, 61, 40, 114, 78) Hub number 1 , last data = QList(59, 52, 101, 94, 119, 56, 100, 44) Hub number 2 , last data = QList(101, 58, 60, 86, 31, 39, 68, 64) Hub number 3 , last data = QList(64, 43, 67, 50, 83, 106, 52, 77) Hub number 4 , last data = QList(114, 68, 98, 99, 42, 59, 87, 56) /**************************************************************/ Number of Hub 5
On QML side I have problems
main.qmlimport QtQuick 2.0 Window { id: root width: 400 height: 150 visible: true Rectangle { id: currentHub color: "green" border.color: Qt.lighter(color) anchors.fill: parent Row { height: currentHub.height width: currentHub.width Repeater { id: hub_id model: tr.dataFromHubnumber(0)//8 Rectangle { id: oneChartBar anchors.bottom: parent.bottom color: "red" height: modelData //tr.dataFromHubnumber(0)[index] width: currentHub.width/8 border.width: 1 border.color: Qt.lighter(color) Text { id: showchannel anchors.horizontalCenter: oneChartBar.horizontalCenter anchors.bottom: oneChartBar.bottom text: qsTr("%1").arg(index+1) color: "white" } Text { id: showchanneldata anchors.horizontalCenter: oneChartBar.horizontalCenter anchors.top: oneChartBar.top text: qsTr("%1").arg(oneChartBar.height) color: "white" } } } } } }
In this example I want show sensors data (8 channels) of different hubs. I try to define model: tr.dataFromHubnumber(0)//8 and set height: modelData . This doesn't works. I get
qrc:/QML/main.qml:18: TypeError: Property 'dataFromHubnumber' of object Hub(0x5616d303c520) is not a function
When I use model :8 and height: tr.dataFromHubnumber(0)[index] I get
qrc:/QML/main.qml:23:21: TypeError: Property 'dataFromHubnumber' of object Hub(0x55e7826e7520) is not a function qrc:/QML/main.qml:23:21: TypeError: Property 'dataFromHubnumber' of object Hub(0x55e7826e7520) is not a function qrc:/QML/main.qml:23:21: TypeError: Property 'dataFromHubnumber' of object Hub(0x55e7826e7520) is not a function qrc:/QML/main.qml:23:21: TypeError: Type error qrc:/QML/main.qml:23:21: TypeError: Type error qrc:/QML/main.qml:23:21: TypeError: Type error qrc:/QML/main.qml:23:21: TypeError: Type error qrc:/QML/main.qml:23:21: TypeError: Type error
On c++ side dataFromHubnumber(0) return QList <double> and theoretically can be use as model. What is a code for this situation?
-
@Bob64 said in More complex example. Don't work:
@Kuzma30 you need to mark methods that you want to access from QML as Q_INVOKABLE in your header file.
I add Q_INVOKABLE QList<double> dataFromHubnumber(int hubNumber); (in hubset.h)
But get same errorsqrc:/QML/main.qml:18: TypeError: Property 'dataFromHubnumber' of object Hub(0x55581f1b4520) is not a function
-
@Kuzma30 said in More complex example. Don't work:
Q_INVOKABLE QList<double> dataFromHubnumber(int hubNumber);
Shouldn't it be
Q_INVOKABLE QList<double> setDataFromHubnumber(int hubNumber);
?
-
I have 2 functions
QList<double> dataFromHubnumber(int hubNumber); // get latest data from specific Hub number (hubNumber)
void setDataFromHubnumber(int hubNumber, QList<double> newData); // set data newData to specific Hub number (hubNumber)
I use set-function on C++ side (to store random data) and get-function on QML side to show this data. -
I add Q_OBJECT (my mistake). Now I can use
model: tr.dataFromHubnumber(1)
for
Q_INVOKABLE QList<double> dataFromHubnumber(int hubNumber);
But it update date (function dataFromHubnumber ) only one time (at start);
I periodically add new data to QListvoid setItem(QQmlApplicationEngine *pEngine) { for (int i=0;i<tractor.number_Of_Hub();i++) { QList<double> temp; for (int j=0;j<8;j++) { temp.append(QRandomGenerator::global()->bounded(30, 120)); } tractor.setDataFromHubnumber(i, temp); // qDebug() << "Hub number " << i << ", last data = " << tractor.dataFromHubnumber(i); } qDebug() << "/**************************************************************/"; qDebug() << "Number of Hub" << tractor.number_Of_Hub(); }
QList <double> HubSet::dataFromHubnumber(int hubNumber) { qDebug() << " hubNumber = " << hubNumber << "channelData = " << m_hubset[hubNumber]->channelData(); return m_hubset[hubNumber]->channelData(); } void HubSet::setDataFromHubnumber(int hubNumber, QList<double> newData) { m_hubset[hubNumber]->setChannelData(newData); emit dataFromHubnumberChanged(); }
Repeater { id: hub_id model: tr.dataFromHubnumber(1) Rectangle { id: oneChartBar anchors.bottom: parent.bottom color: "red" height: modelData width: currentHub.width/8 }
-
There is latest code.
main.qmlimport QtQuick 2.0 Window { id: root width: 400 height: 150 visible: true Rectangle { id: currentHub color: "green" border.color: Qt.lighter(color) anchors.fill: parent Row { height: currentHub.height width: currentHub.width Repeater { id: hub_id model: tr.dataFromHubnumber(1) Rectangle { id: oneChartBar anchors.bottom: parent.bottom color: "red" height: modelData width: currentHub.width/8 border.width: 1 border.color: Qt.lighter(color) Text { id: showchannel anchors.horizontalCenter: oneChartBar.horizontalCenter anchors.bottom: oneChartBar.bottom text: qsTr("%1").arg(index+1) color: "white" } Text { id: showchanneldata anchors.horizontalCenter: oneChartBar.horizontalCenter anchors.top: oneChartBar.top text: qsTr("%1").arg(oneChartBar.height) color: "white" } } } } Timer { interval: 1000; running: true; repeat: true onTriggered: { // ch.channelPrevDataChanged() // ch.channelDataChanged() // ch.channelStatusChanged() // ch.barColorChanged() // tr.dataChanged() tr.dataFromHubnumberChanged() } } } }
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include <QTimer> #include <QtDebug> #include <QRandomGenerator> #include "Hub/hub.h" #include "Hub/hubset.h" #define numberOfHub 4 void setItem(QQmlApplicationEngine *pEngine); HubSet tractor(new Hub); int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; for (int i=0;i<numberOfHub;i++) { tractor.addHub(i, new Hub); } engine.rootContext()->setContextProperty("tr", &tractor); engine.load(QUrl(QStringLiteral("qrc:/QML/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; QTimer timer; QObject::connect(&timer, &QTimer::timeout, [&engine]() { setItem(&engine); }); timer.start(1000); return app.exec(); } void setItem(QQmlApplicationEngine *pEngine) { for (int i=0;i<tractor.number_Of_Hub();i++) { QList<double> temp; for (int j=0;j<8;j++) { temp.append(QRandomGenerator::global()->bounded(30, 120)); } tractor.setDataFromHubnumber(i, temp); // qDebug() << "Hub number " << i << ", last data = " << tractor.dataFromHubnumber(i); } qDebug() << "/**************************************************************/"; qDebug() << "Number of Hub" << tractor.number_Of_Hub(); }
hub.h
#ifndef Hub_H #define Hub_H #include <QObject> #include <QMap> #include <QDebug> class Hub : public QObject { Q_OBJECT Q_PROPERTY(QList<double> channelData READ channelData WRITE setChannelData NOTIFY channelDataChanged) public: explicit Hub(QObject *parent = nullptr) { m_channelData.append({1,2,3,4,5,6,7,8}); } void setChannelData(QList<double> channelData) { m_channelData.append(channelData); //set current data value emit channelDataChanged(); } QList<double> channelData() // return current data list { return m_channelData.last(); } private: QList<QList<double>> m_channelData; // list of current datas int dataSize; signals: void channelDataChanged(); }; #endif // Hub_H
hubset.h
#ifndef HUBSET_H #define HUBSET_H #include "hub.h" class HubSet : public Hub { Q_OBJECT public: HubSet(Hub *currentHub); void addHub(int numberPos, Hub *currentHub); void removeHub(int numberPos); Q_INVOKABLE QList<double> dataFromHubnumber(int hubNumber); void setDataFromHubnumber(int hubNumber, QList<double> newData); int number_Of_Hub(void); protected: QList<Hub *> m_hubset; int m_number_of_hub=0; signals: void dataFromHubnumberChanged(); void dataTestChanged(); private: QList<double> m_dataTest; }; #endif // HUBSET_H
hubset.cpp
#include "hubset.h" HubSet::HubSet(Hub *currentHub) { m_hubset.append(currentHub); m_number_of_hub++; } void HubSet::addHub(int numberPos, Hub *currentHub) { m_hubset.insert(numberPos, currentHub); m_number_of_hub++; } void HubSet::removeHub(int numberPos) { m_hubset.removeAt(numberPos); m_number_of_hub--; } QList <double> HubSet::dataFromHubnumber(int hubNumber) { qDebug() << " hubNumber = " << hubNumber << "channelData = " << m_hubset[hubNumber]->channelData(); return m_hubset[hubNumber]->channelData(); } void HubSet::setDataFromHubnumber(int hubNumber, QList<double> newData) { m_hubset[hubNumber]->setChannelData(newData); emit dataFromHubnumberChanged(); } int HubSet::number_Of_Hub(void) { return m_number_of_hub; }
I have (numberOfHub+1) sized data set of QList<double> (size = 8). I periodically add new data to the end of QList with setDataFromHubnumber(i, QList<double>) and want show on screen latest data. For this purpose I create function QList <double> HubSet::dataFromHubnumber(int hubNumber). I register class in QML
engine.rootContext()->setContextProperty("tr", &tractor);
When I run program it show only default values on screen and don't update with latest value.
For modeldata I use Q_INVOKABLE QList<double> dataFromHubnumber(int hubNumber) function.
When new data stored to the array I emit signal
emit dataFromHubnumberChanged();
But this not produce update data on the screen.
How to resolve this problem? -
@Kuzma30 said in More complex example. Don't work:
I emit signal
emit dataFromHubnumberChanged();You don't capture that signal from QML, so nothing should change. A
QList
is just that - a list of items, it doesn't know squat about changes, notifications and such.But this not produce update data on the screen.
How to resolve this problem?Write yourself a proper model would be the conventional advice.
-
I change some data organization logic. And now I can show and update my charts. I show all channels in one chart (similar to
https://forum.qt.io/topic/140468/periodically-update-data-in-qml-solved/4, but model = numberOfHub*8)
After generating dates from all channel I update corresponded Hub.void HubSet::setLatestChannelData(QList<double> newLatestChannelData) { m_latestChannelData = newLatestChannelData; int j=0; QList<double> temp_8_sized; for (int i = 0; i < 8*m_hubset.count(); ++i) { temp_8_sized.append(m_latestChannelData[i]); if (((i+1) % 8)==0) { m_hubset[j]->setChannelData(temp_8_sized); j++; temp_8_sized.clear(); } } emit latestChannelDataChanged(); } ..... protected: QList<Hub *> m_hubset;