Marcin Plebanek
Marcin Plebanek

Reputation: 121

Two way connection between QML/QtQuick and QWidget

I am trying to make an app that would use QSerialPort and QtQuick so I need to connect desktop-like app to QML somehow. I managed to (ok i copied and made some changes) send information from QML to main.cpp but I can't send anything the other way. The wanted effect is to manage everything from .cpp, add and delete ListElements and send parameters to draw a graph.

main.cpp
int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    QQuickView view(QUrl::fromLocalFile("path/main.qml"));
    QObject *item = view.rootObject();
    MyClass myClass,mySecondClass;
    QObject::connect(item, SIGNAL(qmlSignal(QVariant)),
                     &myClass, SLOT(cppSlot(QVariant)));
    QObject::connect(&myClass, SIGNAL(Nazwa(QVariant)),
                     item, SLOT(onNazwa(QVariant)));
    QVariant c=200;
    emit myClass.Nazwa(c);

    view.show();
    return app.exec();
}

myclass.h

#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
#include <QDebug>
#include <QQuickItem>
class MyClass : public QObject
{
    Q_OBJECT
public:
    explicit MyClass(QObject *parent = 0);

signals:
    void Nazwa(QVariant a);

public slots:
    void cppSlot(const QVariant &v) {
       qDebug() << "QVariant :):" << v;

       QQuickItem *item =
           qobject_cast<QQuickItem*>(v.value<QObject*>());
       qDebug() << "Wymiary:" << item->width()
                << item->height();
    }

};

#endif // MYCLASS_H

main.qml

import QtQuick.Window 2.0
import QtQuick 2.0

Item {
    id: item
    width: 100; height: 100
//    Item{
//        id: item2
//        onNazwa: {    }
//    }
    signal qmlSignal(var anObject)
    MouseArea {
        anchors.fill: parent
        onClicked: {
            parent.width=200;
            item.qmlSignal(item)
        }
    }
}

Also any changes to main.qml would break the 1st connection and I don't even know why. Can You give me some advise or an example? I spent 2nd day on Qt Documentation and I still can't do it :(

Upvotes: 3

Views: 865

Answers (2)

Shtol Krakov
Shtol Krakov

Reputation: 1280

OK. I see what you want, but I recommend you to go through engine way. Try code below. I used very simple example only to show how to, no more than.

MyClass .H:

#include <QObject>
#include <QSerialPort>

class MyClass : public QObject
{
    Q_OBJECT
    QSerialPort *_port;
public:
    explicit MyClass(QObject *parent = 0);

signals:
    void sendData(const QString &data);

public slots:
    void startCom(const QString &name, int baud);
    void read();
    void writeData(const QByteArray &data = QByteArray("data"));
};

MyClass .CPP:

MyClass::MyClass(QObject *parent) : QObject(parent)
{
}

void MyClass::startCom(const QString &name, int baud)
{
    _port = new QSerialPort(name, this);
    _port->setBaudRate(baud);
    if(_port->open(QIODevice::ReadWrite))
        connect(_port, &QSerialPort::readyRead, this, &MyClass::read);
    else
        emit sendData(tr("Error"));
}

void MyClass::read()
{
    emit sendData(QString::fromStdString(_port->readAll().toStdString()));
}

void MyClass::writeData(const QByteArray &data)
{
    _port->write(data);
}

main.cpp:

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

    qmlRegisterType<MyClass>("com.some.myclass", 1, 0, "MyClass");
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

main.qml:

import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
import com.some.myclass 1.0

Window {
    id: root
    signal startPort(string name, int baud)
    width: 640
    height: 480
    ColumnLayout {
        id: colLay
        TextField {
            id: comName
            placeholderText: qsTr("COM-port name")
        }
        ComboBox {
            id: baud
            model: [ 9600, 19200, 115200 ]
        }
        Button {
            id: portBut
            text: qsTr("Start serial")
            onClicked: root.startPort(comName.text, baud.currentText)
        }
        Button {
            id: sendBut
            text: qsTr("Write data")
            onClicked: comClass.writeData
        }
        Label {
            id: dataLbl
            text: qsTr("No data")
        }
    }
    MyClass {
        id: comClass
        onSendData: dataLbl.text = data
    }

    onStartPort: comClass.startCom(name, baud)

    Component.onCompleted: root.show();
}

Upvotes: 1

perencia
perencia

Reputation: 1542

You can set a property on the qml and react to its change.

main.qml

Item {
    id: item
    property alias nazwa: item2.nazwa

    Item{
        id: item2
        property var nazwa: null
        onNazwaChanged: {} 
    }
}

and on main.cpp

QQmlProperty::write(item, "nazwa", 10);

Alternatively you could invoke a method directly from c++ to qml.

Everything is very well explained here

Upvotes: 1

Related Questions