Обновить текст QML из C ++ - PullRequest
1 голос
/ 02 апреля 2019

У меня есть некоторые проблемы при изменении текста окна QML в Qt. У меня есть файл C ++, который вызывает поток, и оттуда я пытаюсь изменить значение текстовой метки. Поток работает правильно, но текстовое значение из QML не меняется. Ниже часть моего кода:

main.cpp

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:///template.qml")));
    QQuickItem *label     = engine.rootObjects().at(0)->findChild<QQuickItem*>("myLabel");
    thread2 thread(label);
    thread.start();
}

Thread.cpp

thread2::thread2(QQuickItem *label) {
    this->label = label;
}

void thread2::run() {

    int test = 0;
    char buffer[10];
    int speed = 100;

    while(1) {
        speed++;
        sprintf(buffer,"%d km/h",speed);          
        this->label->setProperty("text", QString(buffer));
        QThread::msleep(1000);
        qDebug()<<"tic: "<<buffer<<endl;           
    }

template.qml

Window {
    id: window
    visible: true
    width: 360
    height: 360

    Component {
        id: fruitDelegate
        Row {
            spacing: 10
            Text { text: name }
            Text { text: '$' + cost }
        }
    }
    Text {
        width: 99
        height: 19
        text: qsTr("Speed: ")
        anchors.verticalCenterOffset: 1
        anchors.horizontalCenterOffset: 0
        anchors.centerIn: parent
        objectName: "lab"
    }
    Text {
        width: 38
        height: 19
        text: qsTr(" 0 ")
        anchors.verticalCenterOffset: 1
        anchors.horizontalCenterOffset: 46
        anchors.centerIn: parent
        objectName: "myLabel"
    }
}

Может кто-нибудь сказать мне, почему не работает? Где моя ошибка?

Спасибо!

Ответы [ 3 ]

2 голосов
/ 02 апреля 2019

У вас есть 2 ошибки:

  • Вы не должны обновлять GUI из другого потока, метод run выполняется в другом потоке, поэтому Qt не гарантирует его правильную работу.
  • Не экспортируйте элемент из QML в C ++, потому что это создает несколько проблем, поскольку получить объект через имя объекта во многих случаях невозможно, еще одним неудобством является то, что жизненный цикл элемента определяется QML, поэтому в данный момент его можно исключить, чтобы метка могла указывать на использование зарезервированной памяти и т. д. Вместо этого она экспортирует объект C ++ в QML.

Учитывая вышеизложенное, решение:

thread.h

#ifndef THREAD_H
#define THREAD_H

#include <QThread>

class Thread : public QThread
{
    Q_OBJECT
public:
    Thread(QObject *parent=nullptr);
   ~Thread() override;
    Q_SLOT void stop();
    Q_SIGNAL void textChanged(const QString & text);
protected:
    void run() override;
};

#endif // THREAD_H

thread.cpp

#include "thread.h"
#include <QDebug>

Thread::Thread(QObject *parent):
    QThread(parent)
{   
}

Thread::~Thread()
{
}

void Thread::stop()
{
    requestInterruption();
    wait();
}

void Thread::run()
{
    int speed = 100;
    QString text;
    while(!isInterruptionRequested()) {
        speed++;
        text = QString("%1 km/h").arg(speed);
        Q_EMIT textChanged(text);
        QThread::msleep(1000);
        qDebug()<<"tic: "<< text;
    }
}

main.cpp

#include "thread.h"

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);
    Thread thread;
    QObject::connect(&app, &QGuiApplication::aboutToQuit, &thread, &Thread::stop);
    thread.start();
    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("thread", &thread);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;
    return app.exec();
}

main.qml

// ...

Text {
    id: txt
    width: 38
    height: 19
    text: qsTr(" 0 ")
    anchors.verticalCenterOffset: 1
    anchors.horizontalCenterOffset: 46
    anchors.centerIn: parent
    objectName: "myLabel"
}
Connections{
    target: thread
    onTextChanged: txt.text = text
}

// ...

Для получения дополнительной информации читайте:

1 голос
/ 02 апреля 2019

Вы не должны изменять пользовательский интерфейс из другого потока. Вместо этого используйте сигнал / слот. Также не следует создавать дочерний класс из QThread (создайте worker и переместите его в другой поток).

Item {
    id: rooItem
    visible: true
    anchors.fill: parent

    signal change(string s);
    onChange: foobar.text = s
    Text {
        id: foobar
        text: "Empty"
    }
}

class Worker: public QObject
{
    Q_OBJECT
public slots:
    void run()
    {
        while(1) {
            QThread::msleep(1000);
            emit changed(QDateTime::currentDateTime().toString());
        }
    }
signals:
    void changed(QString);
};

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

    QThread* th = new QThread();
    Worker* worker = new Worker();
    worker->moveToThread(th);
    QObject::connect(th, &QThread::started, worker, &Worker::run);
    th->start();

    QQuickView view(QStringLiteral("qrc:/Main.qml"));

    QObject* o = view.rootObject();
    QObject::connect(worker, SIGNAL(changed(QString)), o, SIGNAL(change(QString)));
    view.showMaximized();

    return app.exec();
}
0 голосов
/ 03 апреля 2019

Вы используете неправильный механизм для обновления свойства qml, посмотрите на QQmlProperty для правильного пути. Вы также можете экспортировать экземпляр QObject в движок qml и связать свойство текста меток с некоторым свойством этого объекта. Всегда имейте в виду, что qml / qt quick по сути являются хаки. Существует способ безопасно обновить графический интерфейс из потока, не использующего графический интерфейс, без использования сигналов. вместо этого вы можете использовать события для выполнения работы.

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