【问题标题】:Sending C++ signals to QML将 C++ 信号发送到 QML
【发布时间】: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 线程可以正常工作。

标签: c++ qt qml qobject


【解决方案1】:

我马上就发现了一些问题:

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

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

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

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

【讨论】:

  • (3) 应该不是问题
  • 1 和 2 帮助解决了我的问题。我仍然坚持如何使用另一个线程来调用myData.publishPressure。对此的任何帮助表示赞赏。我尝试使用 QTimer,但它太慢了,只能在屏幕上提供 5-6Hz 的刷新率,而且效率非常低,大大降低了 GUI 性能。所以看起来 C++ 中的某种线程将是我的解决方案。
  • @daGriggs:反转更新会起作用吗?如:定期刷新来自 QML 的值,而不是来自 C++ 的信号。
  • 抱歉,澄清一下,我使用 QML 中的 QTimer 通过定期调用 myData::publishPressure 来刷新值。我希望这算作“定期刷新 QML 中的值”,还是您有其他方法可以做到这一点?我只能想到在 QML 中使用 QTimer 或在 C++ 中的任务上设置 QThread,但我不知道如何使用来自同一类对象的数据跨越线程。除非有人给我一个更好的选择哈哈,否则找出这是从这里开始的攻击计划。
  • 在我看来,我致力于以某种方式定期从 C++ 或 QML 调用 emit pressureChanged 行。但是,如果除了 Q_PROPERTY 之外还有更好的方法,我一无所知。我尝试连接信号/插槽,但无法完全理解。
猜你喜欢
  • 1970-01-01
  • 2014-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多