将 C++ 信号发送到 QML

Sending C++ signals to QML

我有一个 int 可以通过 WiringPiI2C 从 ADC 以每秒 680 次的最大速率获得。我想以 200-500Hz 的频率定期对该值进行采样并将其转发给各种 GUI 对象。我已经尝试了几种将 C++ 数据类型转换为 QML 的策略,但我似乎总是功亏一篑。

我使用自定义处理程序 class 和 Q_PROPERTY 宏接近成功,但该值只出现一次;它不会在屏幕上更新。我可以在 QML(console.log) 中整天使用 myData class 或直接从 C++ 中的获取函数 (qDebug) 调用数据,它会完美更新 - 我可以观看ADC 的模拟输入电压的值变化非常小。每次我 运行 程序时,屏幕上显示的单个冻结值都与上一个不同,因此真实数据必须显示在屏幕上。但是为什么不更新呢?

我是否必须 运行 emit pressureChanged 行指向 AppEngine engine.rootContext()->setContextProperty("myData",myData.data()); 使用相同的 DataHandler 实例?我该怎么做?

UPDATE 实际上,emit 行和 AppEngine 指针必须在 DataHandler 的同一个实例中。但是我看不出如何在一个实例中做到这两点。似乎有些事情总是超出范围。我通过 main.qml 中的 QTimer 尝试 运行ning emit 并且它有效,但它的表现非常糟糕。我需要比 5-6Hz 快得多的刷新率,它大大降低了其余 GUI 功能的速度。以 60+ Hz 的频率将 myData class 的 pressureChanged 信号发送到 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 不断更新屏幕上的各种文本对象?

几乎可以帮助我理解发生了什么,我怀疑我的问题与那里所说的密切相关:也就是说,我的绑定不知何故无效,因此只调用函数 DataHandler::getPressure 1次.

我尝试遵循 this tutorial,但情况不同(从 C++ 在 QML 中创建一个 class 对象,我只想将一种数据类型移动到 QML),所以我不是没有足够的能力将它很好地应用到我的问题中...

我已经尝试了好几天了...实例化 myData 的 3 种方法,尝试了 with/without QScopedPointer,尝试了在 QML 中访问 myData 的各种方法。 .. 我快疯了,请帮忙!我呼吁 Whosebug 的上帝,因为我的堆栈确实充满了无知...

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 函数,这只是我从 class 外部发出信号的方式。我想我可以以某种方式使用 QTimer 或其他一些方法,如果这就是问题所在。

我使用 qDebug() 来证明 PI_THREAD 确实在定期调用 publishPressure,出于理智考虑将其减慢到 500 毫秒。我知道 C++ 数据采集是成功的,因为我看到它以 500Hz 的频率将数据输出到控制台。我知道已达到 emit 功能,但也许它没有被执行?

我还发现非常奇怪的是,QML 与键 class 的绑定在 Window 中工作正常,但 MainForm 中不。我想知道这是否以某种方式解决了问题

我立即发现的一些问题:

  1. 当您调用 setupPressure 时,您是在临时对象上执行此操作。即使它有效,我怀疑那是你想要做的。

  2. 您创建了另外两个 DataHandler(一个在 main 中,您将其正确设置(尽管为时已晚)作为 QML 的上下文 属性。另一个在您的 PI_THREAD 中创建... 东西,然后你操作它。这不是 QML 注册的对象。

  3. 您还将字符串 QML 属性 (Text.text) 绑定到 int C++ Q_PROPERTY。我不确定这是否正常工作。无论如何,我建议尝试更好地匹配类型。

解决这些问题,您就可以上路了。