I have an int available via WiringPiI2C
from an ADC at a max rate of 680 times per second. I'd like to sample and forward that value at regular intervals to various GUI objects, at 200-500Hz. I've tried a couple strategies for getting C++ data types into QML, and I always seem to fall just short.
I'm close to success with a custom Handler class and Q_PROPERTY
macro, but the value only appears once; it does not update on screen. I can call the data using myData
class all day in QML(console.log) or directly from the acquisition function in C++ (qDebug
) and it updates flawlessly - I can watch the value as the ADC's analog input voltage varies ever-so-slightly. Every time I run the program, the single frozen value that shows on my screen is different than the last, thus real data must be making it to the screen. But why doesn't it update?
Do I somehow have to run the
emit pressureChanged
line and point the AppEngine
engine.rootContext()->setContextProperty("myData",myData.data());
with the same instance of DataHandler? How would I do this?
UPDATE Indeed, the emit
line and AppEngine pointer must be within the same instance of DataHandler
. But I'm failing to see how I can do both with one instance. Seems something is always out of scope. I tried running emit
via a QTimer in main.qml
and it works, but it performs terribly. I need much faster refresh rate than 5-6Hz and it slowed down the rest of the GUI functions a lot. Any help getting myData
class's pressureChanged
signal sent out to QML at 60+ Hz?
Application output
qrc:/main.qml:31: ReferenceError: myData is not defined
qrc:/main.qml:31: ReferenceError: myData is not defined
I'm sampling, why isn't anyone getting this???
I'm sampling, why isn't anyone getting this???
25771
I'm sampling, why isn't anyone getting this???
25686
I'm sampling, why isn't anyone getting this???
25752
I'm sampling, why isn't anyone getting this???
qml: 25763 <--- this is a manual button push on screen
I'm sampling, why isn't anyone getting this???
qml: 25702 <--- this is a manual button push on screen
I'm sampling, why isn't anyone getting this???
25751
Why does QML allow me to use myData
to send data to console, but not allow me to use myData
as a property to manipulate QML objects?
Is there a much easier way to get simple data types (in my case I will have two integers) into QML constantly updating various text objects on screen?
This post came close to helping me understand what's going on, and I suspect my problem is closely related to what's said there: that is, somehow my binding isn't valid and thus only calls the function DataHandler::getPressure
1 time.
I tried following this tutorial, but it's a different situation (creating a class object in QML from C++, all I want is to move 1 data type to QML), so I wasn't capable enough to apply it to my problem very well...
I've tried for days now... 3 ways of instantiating myData
, tried with/without QScopedPointer
, tried various ways of accessing myData
within QML... I'm going out of my mind, please help! I appeal to the Gods of StackOverflow, for my stack doth truly overflow with ignorance...
datahandler.h
#ifndef DATAHANDLER_H
#define DATAHANDLER_H
#include <QObject>
#include <QPoint>
#include <QDebug>
class DataHandler : public QObject
{
Q_OBJECT
Q_PROPERTY(int pressure READ getPressure NOTIFY pressureChanged)
public:
explicit DataHandler(QObject *parent = 0);
void setupPressure();
int getPressureSample();
int getPressure();
void publishPressure();
signals:
void pressureChanged();
};
#endif // DATAHANDLER_H
important bits of datahandler.cpp
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include "datahandler.h"
#define SAMPLES 10
DataHandler::DataHandler(QObject *parent) : QObject(parent)
{
}
int DataHandler::getPressure() {
int totalSum = 0;
for (int i = 0; i < SAMPLES; i++){
totalSum += getPressureSample();
delay(5); // Sampling at ~200Hz, the ADC itself maxes at 680Hz so don't sample faster than that.
}
qDebug() << "I'm sampling, why isn't anyone getting this update???";
return totalSum/SAMPLES;
}
void DataHandler::publishPressure() {
emit pressureChanged();
}
important bits of main.cpp
#include <QCursor>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include "functions.h"
#include "datahandler.h"
PI_THREAD(updatePressure)
{
DataHandler pressureData(new DataHandler);
while (true){
delay(500);
pressureData.publishPressure();
qDebug() << pressureData.getPressure();
}
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
wiringPiSetup();
DataHandler().setupPressure();
app.setOverrideCursor( QCursor( Qt::BlankCursor ) ); //Hide the cursor, no one needs that thing showing!
QScopedPointer<Functions> myFunctions(new Functions);
QScopedPointer<DataHandler> myData(new DataHandler);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
engine.rootContext()->setContextProperty("myFunctions",myFunctions.data());
engine.rootContext()->setContextProperty("myData",myData.data());
piThreadCreate(updatePressure);
return app.exec();
}
important bits of main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.4
import QtQuick.Extras 1.4
//DECLARATIVE CONTENT
Window {
id: myWindow
visible: true
width: 800
height: 480
title: qsTr("Hello World")
Item {
focus: true
Keys.onEscapePressed: myWindow.close()
Keys.onSpacePressed: console.log("HOW?")
}
MainForm {
id: root
anchors.fill: parent
property var shiftArray: 0
property var tumblerArray: noteSelector.array
Text {
id: testText
z: 9
anchors.fill: parent
color: "#FF0000"
text: myData.pressure
}
customPressureClick.onClicked: {
console.log(myData.pressure)
toggleCustomPressureState()
attemptCustomPressure()
}
}
}
EXTRA INFO
As you can see above, I created a PI_THREAD
to constantly call the publishPressure
function which is just my way of emitting the signal from outside the class. I suppose I could somehow use QTimer
or some other method if that's what's screwing this up.
I used qDebug()
to prove that the PI_THREAD is indeed calling publishPressure
regularly, slowed it down to 500ms for sanity sake. I know the C++ data acquisition is successful because I've watched it crank out data to console at 500Hz. I know the emit function is reached, but maybe it is somehow not being executed?
I also found it very strange that QML bindings with Keys class worked fine in Window, but not in MainForm
. I wonder if that somehow clues to the problem
Some problems I spot right off the bat:
When you call setupPressure, you do so on a temporary object. Even if it works, I doubt that's what you want to do.
You create another two DataHandlers (one in main, which you correctly set (albeit too late) as context property for QML. The other you create in your PI_THREAD... thing, which you then manipulate. This is not the object QML has registered.
You're also binding a string QML property (Text.text) to an int C++ Q_PROPERTY. I'm not sure this works correctly. In any case, I'd suggest trying to match types better.
Fix these problems, and you'll be on your way.