2
votes

I am trying to write some Qt C++ code that interacts with QML objects. The goal is to have strings received on a TCP socket appended to a text log on the GUI. Each time a new string is received, I run the appendText() function. I have one implementation of this currently working that uses QWidgets and a .ui file. I need to have a QML implementation that is identical. My QWidget implementation uses a textBrowser and the append function such as the following. "theString" is changing as the program runs and each change is appended, filling up the text log.

//update the text log with data received on TCP socket
void MainWindow::appendText() {
    ui->textBrowser->append(theString);

}

This gives me the desired result, appending each string to the text box as they come in. The output should look like the following.

Control connection successful.
Data connection successful.
Control Packet Receieved: 
1
Control Packet Receieved: 
2
Control Packet Receieved: 
3
Control Packet Receieved: 
4
Control Packet Receieved: 
1
Control Packet Receieved: 
2
Control Packet Receieved: 
3
Control Packet Receieved: 
4

However, when doing what I believe to be the same function with a QML object with the following code...

//update the text log with data received on TCP socket
void MainWindow::appendText() {
    QMetaObject::invokeMethod(textbox, "append", Qt::DirectConnection, Q_ARG(QVariant, theString));
    //QQmlProperty(textbox, "text").write(theString);

}

It only appends the first two strings, and no more beyond that. The output looks like this instead.

Control connection successful.
Data connection successful.

I have looked over the documentation for invoking QML methods in C++ extensively and still haven't had any luck. Any help is appreciated. Thanks for your time.

1

1 Answers

1
votes

I'm unable to reproduce your problem.

Possible solution

It may be a solution to use import QtQuick.Controls 2.0.

In that case, I obtain the following error message:

QMetaObject::invokeMethod: No such method QQuickTextArea::append(QVariant)
Candidates are:
    append(QString)

As suggested by the error message, you should use QString now instead of QVariant as parameter type:

QMetaObject::invokeMethod(textbox, "append", Qt::DirectConnection, Q_ARG(QString, theString));

Better alternative

As mentioned by Qt, you should avoid manipulating QML object from C++ (deep into the object tree):

Warning: While it is possible to use C++ to access and manipulate QML objects deep into the object tree, we recommend that you do not take this approach outside of application testing and prototyping. One strength of QML and C++ integration is the ability to implement the QML user interface separately from the C++ logic and dataset backend, and this strategy breaks if the C++ side reaches deep into the QML components to manipulate them directly.

Therefore, it may be a better alternative to implement a signal in C++ which emit the newly received messages and connect to it from the QML side. This approach clearly separates user interface and programming logic.

Working example code

The following code appends "test" to the TextArea every second.

main.cpp:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QTimer>
#include <QQuickItem>

QObject *textbox;

void onTimeout()
{
  QMetaObject::invokeMethod(textbox, "append", Qt::DirectConnection, Q_ARG(QVariant, "test"));
}

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

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

  QTimer t;
  QObject::connect(&t, &QTimer::timeout, &onTimeout);
  textbox = engine.rootObjects().first()->children().first();

  t.start(1000);

  return app.exec();
}

main.qml:

import QtQuick 2.0
import QtQuick.Window 2.2
import QtQuick.Controls 1.0

Window
{
    visible: true
    width: 600
    height: 600

    TextArea
    {
        id: textbox
        anchors.fill: parent
    }
}