从C++更新 QML 文本

Update QML text from C++

本文关键字:文本 QML 更新 C++      更新时间:2023-10-16

我在Qt中更改QML窗口的文本时遇到了一些问题。我有一个调用线程的C++文件,从那里我正在尝试更改文本标签的值。线程运行正常,但 QML 中的文本值未更改。下面是我代码的一部分:

主.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();
}

线程.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;           
}

模板.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"
}
}

谁能告诉我为什么不起作用?我的错误在哪里?

谢谢!

您有 2 个错误:

  • 您不应该从另一个线程更新GUI,run方法在另一个线程中执行,因此Qt不保证它正常工作。
  • 不要将元素从 QML 导出到 C++,因为它会带来几个问题,因为很多时候不可能通过对象名获取对象,另一个不便之处是项目的生命周期由 QML 决定,因此在给定时刻它可以被消除,以便标签可以指向正在使用它的未保留内存, 等。相反,它将C++对象导出到 QML。

考虑到上述情况,解决方案是:

线程.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

线程.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;
}
}

主.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();
}

主.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
}
// ...

有关更多信息,请阅读:

  • https://doc.qt.io/qt-5/qtquick-bestpractices.html#interacting-with-qml-from-c
  • https://doc.qt.io/qt-5/thread-basics.html#gui-thread-and-worker-thread

不应从其他线程修改 UI。请改用信号/插槽。 您也不应该从QThread创建子类(创建一个工作线程并将其移动到另一个线程中)。

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

您使用错误的机制来更新 qml 属性,请查看QQmlProperty以获取正确的方法。您还可以将 QObject 实例导出到 qml 引擎中,并将标签文本属性绑定到该对象的某个属性。永远记住,qml/qt quick本质上是黑客。有一种方法可以在不使用信号的情况下从非 gui 线程安全地更新 gui。相反,您可以使用事件来完成工作。