首页 > 解决方案 > 将qml项目值传递给cpp后如何与它们交互?

问题描述

我正在尝试将项目的值添加qml到我的cpp代码中,从那里我想将这些值添加到 CAN 消息中并通过 CAN 总线发送它们。

到目前为止,我可以成功地将许多qml项目的值和状态获取到我的cpp. 此外,我可以使用静态值将 CAN 消息传输到 CAN 总线上。但是,其中一些值不应该是静态的,而是使用qml.

这是backend.h

#ifndef BACKEND_H
#define BACKEND_H

#include <QObject>
#include <QCanBusDevice>

class BackEnd : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int elemVal READ getElemVal WRITE setElemVal NOTIFY elemValChanged)

public:
    explicit BackEnd(QObject *parent = nullptr);
    //elemVal
    int getElemVal();
    void setElemVal(const int &elemVal);
    int m_elemVal;

    //can
    void run();
    void oneShotConnectCan();
    QCanBusDevice *m_canDevice = nullptr;

signals:
    void elemValChanged();

public slots:
    void sendCanFrame();
};

#endif // BACKEND_H

这是backend.cpp

BackEnd::BackEnd(QObject *parent) : QObject(parent)
{

}

//elemVal get set
int BackEnd::getElemVal()
{
    return m_elemVal;
}

void BackEnd::setElemVal(const int &elemVal)
{
    if(elemVal == m_elemVal)
        return;

    m_elemVal = elemVal;
    emit elemValChanged();
    qDebug() << "elemVal is: " << m_elemVal;
}
//end of elemVal get set

... 
CAN Bus initialization
...

void BackEnd::sendCanFrame()
{
    quint32 frameid = 131;
    QByteArray payload;
    payload[0] = 0x04;
    payload[1] = 0x03;
    payload[2] = m_elemVal;

    QCanBusFrame testFrame(frameid, payload);
    testFrame.setFrameType(QCanBusFrame::DataFrame);

    m_canDevice->writeFrame(testFrame);
    if (m_canDevice->writeFrame(testFrame)) {
    qDebug() << "test frame: " << testFrame.toString();
    }
    else {
        qFatal("Write failed");
    }
}

这是main.cpp

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    qmlRegisterType<BackEnd>("io.qt.examples.backend", 1, 0, "BackEnd");

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    BackEnd m_can;
    m_can.oneShotConnectCan(); //creating CAN device
    m_can.run(); //sending the CAN message

    return app.exec();
}

这是main.qml文件:

Window {
    id: window
    objectName: "window"
    visible: true
    visibility: Window.FullScreen

    onWindowStateChanged: {
        console.log( "onWindowStateChanged (Window), state: " + windowState );
    }

    BackEnd{
        id: backend
    }

    Dial {
        id: dial
        x: 181
        y: 38
        stepSize: 1
        to: 255
        value: backend.elemVal
        onValueChanged: backend.elemVal = value
    }
}

qDebug 的实际输出是这样的:

elemVal is:  0
test frame :  "     083   [3]  04 03 05"
test frame :  "     083   [3]  04 03 05"
elemVal is:  1
elemVal is:  2
elemVal is:  3
test frame :  "     083   [3]  04 03 05"
elemVal is:  4
elemVal is:  5
elemVal is:  6
test frame :  "     083   [3]  04 03 05"

我期望它是怎样的:

elemVal is:  0
test frame :  "     083   [3]  04 03 00"
test frame :  "     083   [3]  04 03 00"
elemVal is:  1
elemVal is:  2
elemVal is:  3
test frame :  "     083   [3]  04 03 03"
elemVal is:  4
elemVal is:  5
elemVal is:  6
test frame :  "     083   [3]  04 03 06"

消息的第三个字节,在实际输出中是 05,应该随着m_elemVal连接到 dial in 的变量动态变化qml。虽然我可以读取和写入 的值m_elemVal,但我无法将其写入我的 CAN 消息。

抱歉,如果帖子太长,请尽量具体。

任何帮助表示赞赏...

标签: c++qtqml

解决方案


您在 QML 中创建的 Backend 对象

BackEnd {
    id: backend
}

它与用 C++ 创建的不同:

BackEnd m_can;

有几种可能的解决方案:

1.通过setContextProperty()导出一个Backend对象

与其他提议的解决方案相比,优势在于现在可以从任何 QML 访问后端对象,例如console.log().

#include <QQmlContext>
// ...
int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    BackEnd m_can;
    m_can.oneShotConnectCan(); //creating CAN device
    m_can.run(); //sending the CAN message

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("backend", &m_can);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

*.qml

Window {
    id: window
    objectName: "window"
    visible: true
    visibility: Window.FullScreen

    onWindowStateChanged: {
        console.log( "onWindowStateChanged (Window), state: " + windowState );
    }

    Dial {
        id: dial
        x: 181
        y: 38
        stepSize: 1
        to: 255
        onValueChanged: backend.elemVal = value
    }
}

与其他答案相比,一个优点是不使用会产生问题的旧连接样式,因为它的验证是在运行时而不是在编译时完成的。此外,代码依赖于 QML 结构。

我还假设您想在 elemVal 更改时发送帧,因此理想的做法是在 elemValChanged 和 sendCanFrame() 之间的信号之间建立连接:

BackEnd::BackEnd(QObject *parent) : QObject(parent)
{
    connect(this, &Backend::elemValChanged, this, &Backend::sendCanFrame);
}

2. 创建一个 QML 类型

有时需要在创建对象后启动某些资源,在这种情况下你可以使用 QML 中的 Component.onCompleted 或使用 QQmlParserStatus,在这种情况下我将使用第二种方法。

*。H

class BackEnd : public QObject, public QQmlParserStatus
{
    Q_OBJECT
    Q_INTERFACES(QQmlParserStatus)
    Q_PROPERTY(int elemVal READ getElemVal WRITE setElemVal NOTIFY elemValChanged)
public:
    explicit BackEnd(QObject *parent = nullptr);
    void classBegin();
    void componentComplete();
    //elemVal
    int getElemVal();
    void setElemVal(const int &elemVal);
signals:
    void elemValChanged();

public slots:
    void sendCanFrame();
private:
    //can
    void run();
    void oneShotConnectCan();
    QCanBusDevice *m_canDevice = nullptr;
    int m_elemVal;
};

*.cpp

BackEnd::BackEnd(QObject *parent) : QObject(parent)
{
    connect(this, &Backend::elemValChanged, this, &Backend::sendCanFrame);
}

void BackEnd::classBegin(){}
void BackEnd::componentComplete()
{
    oneShotConnectCan(); //creating CAN device
    run()
}

//elemVal get set
int BackEnd::getElemVal()
{
    return m_elemVal;
}

void BackEnd::setElemVal(const int &elemVal)
{
    if(elemVal == m_elemVal)
        return;

    m_elemVal = elemVal;
    emit elemValChanged();
    qDebug() << "elemVal is: " << m_elemVal;
}
//end of elemVal get set

// ... 
// CAN Bus initialization
// ...

void BackEnd::sendCanFrame()
{
    quint32 frameid = 131;
    QByteArray payload;
    payload[0] = 0x04;
    payload[1] = 0x03;
    payload[2] = m_elemVal;

    QCanBusFrame testFrame(frameid, payload);
    testFrame.setFrameType(QCanBusFrame::DataFrame);

    m_canDevice->writeFrame(testFrame);
    if (m_canDevice->writeFrame(testFrame)) {
    qDebug() << "test frame: " << testFrame.toString();
    }
    else {
        qFatal("Write failed");
    }
}

主文件

static void registerTypes()
{
    qmlRegisterType<BackEnd>("io.qt.examples.backend", 1, 0, "BackEnd");
}

Q_COREAPP_STARTUP_FUNCTION(registerTypes)

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    return app.exec();
}

*.qml

Window {
    id: window
    objectName: "window"
    visible: true
    visibility: Window.FullScreen

    onWindowStateChanged: {
        console.log( "onWindowStateChanged (Window), state: " + windowState );
    }

    BackEnd{
        id: backend
        elemVal : dial.value
    }

    Dial {
        id: dial
        x: 181
        y: 38
        stepSize: 1
        to: 255
        Component.onCompleted: value = backend.elemVal
    }
}

推荐阅读