【发布时间】:2017-05-20 20:57:06
【问题描述】:
我通过WiringPiI2C 从 ADC 以每秒 680 次的最大速率提供了一个 int。我想以 200-500Hz 的频率定期采样并转发该值到各种 GUI 对象。我尝试了几种将 C++ 数据类型导入 QML 的策略,但我似乎总是功亏一篑。
自定义 Handler 类和 Q_PROPERTY 宏已经接近成功,但该值只出现一次;它不会在屏幕上更新。我可以整天在 QML(console.log) 中使用 myData 类调用数据,也可以直接从 C++ 中的采集函数 (qDebug) 调用数据,它会完美更新 - 我可以观察到 ADC 的模拟输入电压不断变化的值-那么轻微。每次我运行程序时,屏幕上显示的单个冻结值都与上次不同,因此必须将真实数据显示在屏幕上。但是为什么不更新呢?
我是否必须以某种方式运行
emit pressureChanged
和行指向 AppEngine
engine.rootContext()->setContextProperty("myData",myData.data());
与 DataHandler 的相同实例?我该怎么做?
更新确实,emit 行和 AppEngine 指针必须在 DataHandler 的同一实例中。但是我看不到如何在一个实例中同时做到这两点。似乎有些东西总是超出范围。我尝试通过main.qml 中的QTimer 运行emit,它可以工作,但性能非常糟糕。我需要比 5-6Hz 快得多的刷新率,这大大减慢了 GUI 的其余功能。将myData 类的pressureChanged 信号以 60+ Hz 发送到 QML 有什么帮助吗?
应用程序输出
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
为什么 QML 允许我使用 myData 将数据发送到控制台,但不允许我使用 myData 作为属性来操作 QML 对象?
有没有更简单的方法可以将简单的数据类型(在我的情况下,我将有两个整数)导入 QML,不断更新屏幕上的各种文本对象?
This post 几乎可以帮助我理解发生了什么,我怀疑我的问题与那里所说的密切相关:也就是说,不知何故我的绑定无效,因此只调用函数 DataHandler::getPressure 1 次.
我尝试关注this tutorial,但情况不同(在 QML 中从 C++ 创建一个类对象,我只想将 1 个数据类型移动到 QML),所以我没有足够的能力将它应用到我的问题很好...
我已经尝试了好几天...实例化myData 的3 种方法,尝试使用/不使用QScopedPointer,尝试在QML 中访问myData 的各种方法...我快疯了, 请帮忙!我呼吁 StackOverflow 之神,因为我的堆栈确实因无知而溢出......
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
重要的一点 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();
}
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();
}
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()
}
}
}
额外信息
正如您在上面看到的,我创建了一个PI_THREAD 来不断调用publishPressure 函数,这只是我从类外发出信号的方式。我想我可以以某种方式使用QTimer 或其他方法,如果这就是问题所在。
我使用qDebug() 来证明 PI_THREAD 确实定期调用publishPressure,为了保持理智,将其减慢到 500 毫秒。我知道 C++ 数据采集是成功的,因为我看到它以 500Hz 的频率将数据输出到控制台。我知道已经达到了 emit 函数,但也许它没有被执行?
我还发现很奇怪,带有 Keys 类的 QML 绑定在 Window 中运行良好,但在 MainForm 中不是。我想知道这是否以某种方式解决了问题
【问题讨论】:
-
您应该在加载 QML 文件之前设置所有上下文属性,但这与当前的问题无关。另外,什么是 PI_THREAD?
-
谢谢,会的。我在 Raspberry Pi 3 上构建。PI_THREAD 是wiringPi 库的本机,来自他们的网站:“Posix 线程的 Linux 实现的简化接口,以及访问互斥锁(互斥)的(简化)机制”
-
如果可能的话,我建议在编写 Qt 应用程序时尽可能使用 QThreads 而不是其他任何东西。当然,无论你在做什么都可能需要其他库的构造......
-
Welp,摆脱了“未定义”错误!现在更多地使用 QML(我对它还是很陌生)以在屏幕上刷新数据。我将研究 QThreads。由于我在 PI_THREAD 中调用的函数不使用 RPi3 GPIO,我想通用 Qt 线程可以正常工作。