Qt 5.1通过线程的QML属性

Qt 5.1 QML property through Threads

本文关键字:QML 属性 线程 1通 Qt      更新时间:2023-10-16

为了解决这个问题,我创建了一个重复我遇到的相同问题的TestApp。

我正在将我的软件从Qt 4.8移植到Qt 5.1。

我的第一个程序是多线程的,只要类是线程安全的,就可以顺利地使用QML。但现在我得到了这样的信息:

QObject::connect: No such slot TestApp::run() in ..ThreadingTestmain.cpp:21
QQmlEngine: Illegal attempt to connect to TestApp(0x29cfb8) that is in a different thread than the QML engine QQmlEngine(0x2f3e0f8).

这是再现错误的代码:

main.cpp:

#include <QtGui/QGuiApplication>
#include <QQmlContext>
#include <QThread>
#include "qtquick2applicationviewer.h"
#include "testapp.h"
int main(int argc, char *argv[])
{
    int out;
    QGuiApplication app(argc, argv);
    QtQuick2ApplicationViewer viewer;
    TestApp * testapp = new TestApp();
    QThread * testappThread;
    testappThread = new QThread();
    QObject::connect(testappThread, SIGNAL(started()), testapp, SLOT(run()));
    testapp->moveToThread(testappThread);
    testappThread->start();
    viewer.rootContext()->setContextProperty("TestApp", testapp);
    viewer.setMainQmlFile(QStringLiteral("qml/ThreadingTest/main.qml"));
    viewer.showExpanded();
    out = app.exec();
    testappThread->quit();
    testappThread->wait();
    delete testapp;
    delete testappThread;
    return out;
}

testapp.h:

#ifndef TESTAPP_H
#define TESTAPP_H
#include <QObject>
#include <QString>
#include <QTimer>
#include <QReadWriteLock>
#define HELLOWORLD "Hello World !"
extern QReadWriteLock HelloWorldLock;
class TestApp : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString HelloWorld READ getHelloWorld WRITE setHelloWorld NOTIFY HelloWorldChanged)
public:
    explicit TestApp(QObject *parent = 0);
    virtual ~TestApp();
    QString getHelloWorld();
    void setHelloWorld(QString);
public slots:
    void run();
    void toggleHelloWorld();
signals:
    void HelloWorldChanged();
private:
    QString m_HelloWorld;
    QTimer * m_Timer;
};
#endif // TESTAPP_H

testapp.cpp:

#include "testapp.h"
QReadWriteLock HelloWorldLock(QReadWriteLock::Recursive);
TestApp::TestApp(QObject *parent) :
    QObject(parent)
{
    HelloWorldLock.lockForWrite();
    m_HelloWorld = HELLOWORLD;
    HelloWorldLock.unlock();
    m_Timer = new QTimer(this);
    connect(m_Timer, SIGNAL(timeout()), this, SLOT(toggleHelloWorld()));
}
TestApp::~TestApp() {
    m_Timer->stop();
    delete m_Timer;
}
QString TestApp::getHelloWorld() {
    HelloWorldLock.lockForRead();
    QString out = m_HelloWorld;
    HelloWorldLock.unlock();
    return out;
}
void TestApp::setHelloWorld(QString text) {
    HelloWorldLock.lockForWrite();
    m_HelloWorld = text;
    HelloWorldLock.unlock();
    emit HelloWorldChanged();
}
void TestApp::run() {
    m_Timer->start(1000);
}
void TestApp::toggleHelloWorld() {
    HelloWorldLock.lockForWrite();
    if(m_HelloWorld == "") {
        m_HelloWorld = HELLOWORLD;
    }
    else {
        m_HelloWorld = "";
    }
    HelloWorldLock.unlock();
    emit HelloWorldChanged();
}

main.qml:

import QtQuick 2.0
Rectangle {
    width: 360
    height: 360
    Text {
        text: TestApp.HelloWorld
        anchors.centerIn: parent
    }
    MouseArea {
        anchors.fill: parent
        onClicked: {
            Qt.quit();
        }
    }
}

我的程序相当复杂(有很多属性和类要与接口共享),我不想只为了连接我的属性而创建接口类。。。你对解决这个问题有什么建议吗?

您不需要自己线程化应用程序,因为在Qt5中,QML2引擎已经是大规模多线程的,所以只需启动QQuickView,将您需要的C++部分公开到上下文中,在其中设置QML文件,并显示()。这就足够了。不要试图自己修改QML线程,这确实比QML1中更复杂。

这里有各种各样的东西:

  • 在线程之间移动QTimer是有问题的,尤其是在启动的情况下

  • 辅助线程从不调用exec(),因此其中的QTimer不会启动(但我怀疑它确实会启动,因为您创建了它,然后使用moveToThread

  • QML引擎在其线程(即本例中的主线程)上肯定会调用您的属性访问器,因此它们需要像您所说的那样是线程安全的。

我的建议是对事物进行重组,以避免完全使用moveToThread,然后看看还有什么问题。