微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

从非主线程引导 QT Gui

如何解决从非主线程引导 QT Gui

我知道以下不是正常的 Qt 模式,但我想从非主线程创建/引导主线程 GUI 元素。

我们的目标是启动一个独立的线程,该线程存在很长时间并做自己的事情。我不想在我的主窗口中为子线程可能做的每一个可以想象的 GUI 事情都添加钩子。我希望子线程能够显示异步通知,例如在后台处理进度条等。

我创建了一个工作对象并将其移动到辅助线程。当线程启动时,它会向工作对象发出信号,以实例化 QProgressDialog 的包装器以模拟辅助线程“做自己的事情”。然后这个包装器被移回主线程,并发布一个事件,导致包装器实际创建 QProgressDialog,这发生在主线程中。到现在为止还挺好。然后在辅助线程的一个长时间运行的循环中,我发出一个信号,期望它跨越线程边界并更新 QProgressDialog。信号似乎确实到达了连接到信号的主循环的 lambda,但它 没有到达移动到主线程的对象。有趣的是,我可以发布一个事件来更新进度,所以我知道事件循环正在运行。我尝试了许多认和排队连接的变体,在移动到新线程之前和之后连接,没有实际的 gui 组件(只是调试文本),基于堆栈与堆对象等。我的理解是 Qt 自动确定是否接收对象与发送对象位于不同的线程中,如果是,则发布一个由事件循环拾取的事件。问题可能是我在启动子线程后启动了主线程 (QApplication::exec())?

在下面的代码中,main.cpp 中的 debug msg 正确接收事件并打印消息,因此信号跨越了线程边界。它只是没有到达我的包装对象。为什么不呢?

谢谢。

Main.cpp

#include "task.h"

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

    TestClass worker("Tc1");
    QThread *thr = new QThread;

    QObject::connect(thr,&QThread::started,&worker,&TestClass::doWork);
    QObject::connect(&worker,qOverload<int,int>(&TestClass::progress),[](int x,int y){
        qDebug() << x << "out of" << y;});
    QObject::connect(&worker,&TestClass::done,[]{qDebug() << "Done";});

    worker.movetoThread(thr);

    thr->start();
    a.exec();
    thr->wait();
    thr->quit();
    qDebug("Done");

}

task.h

#ifndef TASK_H
#define TASK_H

#include <QApplication>
#include <QEvent>
#include <QDebug>
#include <QObject>
#include <QProgressBar>
#include <QProgressDialog>
#include <QString>
#include <QThread>
#include <functional>
#include <chrono>

static const QEvent::Type CREATE = static_cast<QEvent::Type>(QEvent::User + 0);
static const QEvent::Type SET_VALUE = static_cast<QEvent::Type>(QEvent::User + 1);

// Wraps QProgressDialog
// Create an instance,then move to another thread
class ProgressDialogWrapper : public QObject {
    Q_OBJECT
private:
    QProgressDialog *mWidget;
    QString mLabelText;
    QString mCancelButtonText;
    int mMinimum;
    int mMaximum;
    QWidget *mParent;
    const Qt::WindowFlags mF;
    void create() {mWidget = new QProgressDialog(mLabelText,mCancelButtonText,mMinimum,mMaximum,mParent,mF);}
public:
    ProgressDialogWrapper(const ProgressDialogWrapper &) {}
    ~ProgressDialogWrapper() override {
        qDebug() << "ProgressDialogWrapper destructor";
    }
    ProgressDialogWrapper(const QString &labelText,const QString &cancelButtonText,int minimum,int maximum,QWidget *parent = nullptr,Qt::WindowFlags f = Qt::WindowFlags()) :
        mWidget(nullptr),mLabelText(labelText),mCancelButtonText(cancelButtonText),mMinimum(minimum),mMaximum(maximum),mParent(parent),mF(f) {}
    ProgressDialogWrapper(QWidget *parent = nullptr,mF(f){}
    QProgressDialog *widget() {return mWidget;}
    // Receiving events works across thread boundary
    bool event(QEvent *e) override {
        switch(e->type()) {
            case QEvent::User:
                create();
                mWidget->show();
                return true;
            case SET_VALUE:
                mWidget->setValue(mWidget->value() + 1);
                return true;
            default:
                return false;
        }
    }

public slots:
    // These slots don't seem to work across thread boundary
    void show() {mWidget->show();}
    void setMaximum(int x) {mWidget->setMaximum(x);}
    void setMinimum(int x) {mWidget->setMinimum(x);}
    void setValue(int x) {
        qDebug() << "setValue";
        mWidget->setValue(x);
    }
    void requestValue() {emit requestedValue(mWidget->value());}
signals:
    void requestedValue(int);
};

// This didn't help
Q_DECLARE_MetaTYPE(ProgressDialogWrapper)


class TestClass : public QObject {
    Q_OBJECT
private:
    QString name;
public:
    TestClass() {}
    TestClass(QString n) : name(n) {}
    ~TestClass() override {
        qDebug() << "TestClass destructor";}
public slots:
    void doWork() {
        int loopcount = 10;
        ProgressDialogWrapper pdw("LabelText","TheButton",loopcount,nullptr);
        pdw.movetoThread(QApplication::instance()->thread());
        bool okc = QObject::connect(this,qOverload<int>(&TestClass::progress),&pdw,&ProgressDialogWrapper::setValue,Qt::QueuedConnection);
        qDebug() << "connect:" << okc;
        emit progress(5); // nope this doesn't work
        QApplication::postEvent(&pdw,new QEvent(CREATE));  // this does work

        std::chrono::milliseconds t(500);
        for (int i = 0; i < loopcount; i++) {
            // emitting to wrapper doesn't work -- no debug output            
            emit progress(i);
            
            // emitting to main.cpp lambda does work
            emit progress(i,loopcount);
            
            // Posting event works -- progress bar is updated
            QApplication::postEvent(&pdw,new QEvent(SET_VALUE));

            std::this_thread::sleep_for(t);
            QApplication::processEvents(); // no effect,I just made this up in desperation
        }
        emit done();
    }
signals:
    void progress(int);
    void progress(int,int);
    void done();
};


#endif // TASK_H

我也在考虑发送一个 QEvent 导数,它可以将一个 lambda 作为参数,然后让它在主线程中运行,但我还没有探索过。

解决方法

考虑您的 ProgressDialogWrapper::event 实现...

bool event (QEvent *e) override
{
    switch(e->type()) {
    case QEvent::User:
        create();
        mWidget->show();
        return true;
    case SET_VALUE:
        mWidget->setValue(mWidget->value() + 1);
        return true;
    default:
        return false;
    }
}

您只处理 CREATESET_VALUE 情况:此 ProgressDialogWrapper 实例有效丢弃所有其他事件,包括排队连接使用的 QEvent::MetaCall 事件。>

用...替换当前的实现

bool event (QEvent *e) override
{
    switch(e->type()) {
    case QEvent::User:
        create();
        mWidget->show();
        return true;
    case SET_VALUE:
        mWidget->setValue(mWidget->value() + 1);
        return true;
    default:

        /*
         * Defer to the base class implementation.
         */
        return return QObject::event(e);
    }
}

应该解决问题。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?