1
votes

Mettre à jour le texte QML à partir de C ++

J'ai quelques problèmes pour changer le texte d'une fenêtre QML dans Qt. J'ai un fichier C ++ qui appelle un thread et à partir de là, j'essaye de changer la valeur de l'étiquette de texte. Le thread fonctionne correctement mais la valeur de texte du QML ne change pas. Voici une partie de mon code:

main.cpp :

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

Thread.cpp : p >

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 :

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

Quelqu'un peut-il me dire pourquoi cela ne fonctionne pas? Où est mon erreur?

Merci!


2 commentaires

@TrebledJ Pour démarrer la thead, vous devez appeler la méthode start et elle invoquera la méthode run sur le thread approprié


En exécutant thread.start (), la méthode run () est appelée. Oui cela fonctionne. Je le sais parce que le message de débogage de la boucle while () est imprimé. La méthode run est donc appelée. Aussi, je téléchargerai le fichier .h que vous m'avez demandé.


3 Réponses :


1
votes

Vous ne devez pas modifier l'interface utilisateur d'un autre thread. Utilisez plutôt le signal / slot. Vous ne devez pas créer une classe enfant à partir de QThread , également (créez un worker et déplacez-le dans un autre thread).

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();
}
Item {
    id: rooItem
    visible: true
    anchors.fill: parent

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


0 commentaires

2
votes

Vous avez 2 erreurs:

  • Vous ne devez pas mettre à jour l'interface graphique à partir d'un autre thread, la méthode d'exécution est exécutée dans un autre thread donc Qt ne garantit pas qu'elle fonctionne correctement.
  • Ne pas exporter un élément de QML vers C ++ car cela pose plusieurs problèmes car il est souvent impossible d'obtenir l'objet via le nom de l'objet, un autre inconvénient est que le cycle de vie de l'élément est déterminé par QML donc à un moment donné il pourrait être éliminé afin que l'étiquette puisse pointer vers une mémoire non réservée faisant son utilisation, etc. Au lieu de cela, il exporte l'objet C ++ vers QML.

Compte tenu de ce qui précède, la solution est:

thread.h

// ...

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
}

// ...

thread .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.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 .qml

#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

Pour plus d'informations, lisez:


0 commentaires

0
votes

Vous utilisez le mauvais mécanisme pour mettre à jour une propriété qml, jetez un œil à < code> QQmlProperty pour la bonne manière. Vous pouvez également exporter une instance de QObject dans le moteur qml et lier la propriété text labels à une propriété de cet objet. Gardez toujours à l'esprit que qml / qt quick sont essentiellement des hacks. Il existe un moyen de mettre à jour l'interface graphique en toute sécurité à partir d'un thread non graphique sans utiliser de signaux. à la place, vous pouvez utiliser des événements pour effectuer le travail.


0 commentaires