Как взаимодействовать со значениями элемента qml после передачи их в cpp? - PullRequest
4 голосов
/ 02 мая 2019

Я пытаюсь получить значения элементов в 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 file:

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, которая подключена к номеру qml.Хотя я могу читать и записывать значение m_elemVal, я не могу записать его в свое сообщение CAN.

Извините, если сообщение слишком длинное, попытался быть как можно более конкретным.

Любая помощь приветствуется ...

Ответы [ 2 ]

2 голосов
/ 02 мая 2019

Объект Backend, который вы создали в QML

BackEnd {
    id: backend
}

и отличается от созданного в C ++:

BackEnd m_can;

Существует несколько возможных решений:

1. Экспортировать объект Backend через setContextProperty ()

Преимущество перед другим предложенным решением заключается в том, что теперь к бэкэнд-объекту можно получить доступ из любого 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

Иногда необходимо запустить определенные ресурсы после создания объекта, в этих случаях вы можете использовать Component.onCompleted в QML или QQmlParserStatus, в этом случае я буду использовать второй метод.

*. Ч

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;
};

*. Каст

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");
    }
}

main.cpp

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
    }
}
0 голосов
/ 03 мая 2019
  • Создайте вызываемый метод, который вы вызываете после создания экземпляра окна QML
  • Вызовите метод, используемый для отправки сообщений при изменении elemval:

backend.h:

#ifndef BACKEND_H
#define BACKEND_H

#include <QObject>
#include <QCanBusDevice>

class BackEnd : public QObject
{
    Q_OBJECT

    public:
        explicit BackEnd(QObject *parent = nullptr);

        // elemVal
        Q_PROPERTY(int elemVal READ getElemVal WRITE setElemVal NOTIFY elemValChanged)
        int getElemVal();
        void setElemVal(const int &elemVal);
        int m_elemVal;

        // The method for your first message
        Q_INVOKABLE void sendFirstMessage();

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

    signals:
        void elemValChanged();

    public slots:
        void sendCanFrame();
};

backend.cpp:

// Your actual backend.cpp here.

void Backend::sendFirstMessage() {
    oneShotConnectCan(); //creating CAN device
    run(); //sending the CAN message
}

main.qml:

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

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

    BackEnd{
        id: backend
        elemVal: dial.value
        onElemValChanged: backend.sendCanFrame()    // You should code this on the C++ side.
    }

    Dial {
        id: dial
        x: 181
        y: 38
        stepSize: 1
        to: 255
    }

    // Wiil be executed when window instanciation has just ended.
    Component.onCompleted: backend.sendFirstMessage();
}

Для получения дополнительной информации о Component.onCompleted, пожалуйста, посмотрите на это: https://doc.qt.io/qt-5/qml-qtqml-component.html#completed-signal.

Избегайте манипулирования QML со стороны C ++. Разъедините бэкэнд и фронтэнд настолько, насколько вы можете (вот почему я отклонил оба ответа: eyllanesc и Romha Korev).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...