qt5:如何从 qthread 中的静态函数创建和显示自定义 qdialog

Posted

技术标签:

【中文标题】qt5:如何从 qthread 中的静态函数创建和显示自定义 qdialog【英文标题】:qt5: how to create and display custom qdialog from static function within a qthread 【发布时间】:2013-07-30 23:35:21 【问题描述】:

假设您创建了一个新线程,然后在它启动后调用一个静态函数。在该静态函数中,您需要创建并显示自定义 qdialog。如何创建它以使其没有父级并位于正确的线程中?

构造函数将父级设置为 0,但它仍然报告无法为不同线程中的父级创建子级的错误。因为它是一个静态函数,所以我不能使用“this”对象,如果没有“this”,我就无法检索当前线程或线程 ID。我以为我可以调用 myCustomDialog->moveToThread() 但我不知道如何从静态函数中确定正确的线程。

如果我使用 QMessageBox 静态函数之一,一切正常。例如,调用 QMessageBox::information(0, tr("Title"), tr("Message")) 不会报告任何错误。如何编码我的自定义 qdialog,使其功能类似于 qmessagebox 静态函数?

有没有办法从 qApp 对象中检索所有正在运行的线程的列表?还有其他建议吗?

static int myFunction();

int myObject::myFunction()

    myCustomDialog *mcd = new myCustomDialog();
    // as soon as I call exec() it reports an error and crashes
    mcd->exec();
    // QObject: Cannot create children for a parent that is in a different thread.

    // can't call mcd->moveToThread() without knowing the current thread
    // how can I determine the current thread from this static function?
    // if parent = 0 then why is this error occurring?
    return 0;


myCustomDialog(QWidget *parent = 0);

myCustomDialog::myCustomDialog(QWidget *parent) : QDialog(parent)

    // create widgets and layout here

【问题讨论】:

【参考方案1】:

不要。从另一个线程创建 QWidget 对象是个坏主意。 Documentation 声明:

在 GUI 应用程序中,主线程也称为 GUI 线程 因为它是唯一允许执行 GUI 相关的线程 操作。

我通常解决这个问题的方法是从非 GUI 线程中的对象发出信号,该线程连接到生活在主线程中的对象(对我来说通常是 MainWindow)。然后接收对象在主线程上创建对话框。

正如另一个答案中提到的,如果需要,此连接可以通过将连接类型建立为Qt::BlockingQueuedConnection 来阻塞您的工作线程。

有关从其他线程调用函数的更多信息,请参阅this post。

【讨论】:

我已经考虑过该选项,但我需要将对话框阻止应用程序。在线程中执行的代码只提示用户输入作为最后的手段。当它确实显示对话框时,它需要阻止应用程序继续运行,直到用户决定采取行动。该线程包含一个while循环,因此如果我发出一个信号,那么代码可能会继续,至少是暂时的,直到可以生成对话框并在主线程中显示。即使这样,我也不确定它是否会在调用 dialog.exec() 后暂停辅助线程中的 while 循环。 如前所述,您不能从线程访问 gui。发送信号并另外设置一个QWaitCondition。这将使线程等待wake 信号。 如另一个答案中所述,您可以通过与Qt::BlockingQueuedConnection 建立连接来阻止您的工作线程与emit。如果除了主线程和工作线程之外,您还有其他需要阻塞的线程,那会稍微复杂一些。 感谢您的提示。使用 Qt::BlockingQueuedConnection 类型,然后从非 gui 线程发出信号效果很好。【参考方案2】:

示例...您可以调整它以从静态函数进行调用,但我认为这不是必需的。以下一种方式使用线程是一种很好的做法:您应该在 GUI 线程中创建对话框,并使用 Qt::BlockingQueuedConnection 将它的 exec() 插槽连接到您的信号

工人.h

#include <QObject>

class Worker
    : public QObject

    Q_OBJECT

signals:
    void showDialog();

public:
    Worker( QObject *parent = NULL );
    ~Worker();

public slots:
    void doLongWork();

private:
;

工人.cpp

#include <QThread>
#include <QMessageBox>
#include <QDebug>
#include <QCoreApplication>

#include <Windows.h>


Worker::Worker( QObject *parent )
    : QObject( parent )



Worker::~Worker()



void Worker::doLongWork() // You may override QThread::run with same effect, but it is bad practice

    qDebug() << "Worker thread id = " << QThread::currentThreadId();
    ::MessageBoxA( NULL, "Click to show dialog in 3 seconds", NULL, 0 );
    ::Sleep( 3000 );
    emit showDialog();  // "Showing" dialog from non-GUI thread. And wait for close
    ::MessageBoxA( NULL, "Dialog closed!", NULL, 0 );
    qApp->quit();

main.cpp

#include <QApplication>
#include <QDialog>
#include <QThread>
#include <QDebug>

#include "Worker.h"


int main(int argc, char *argv[])

    QApplication a( argc, argv );
    a.setQuitOnLastWindowClosed( false );

    QDialog dlg;
    QThread workerThread;
    Worker worker;

    qDebug() << "Main thread id = " << QThread::currentThreadId();

    QObject::connect( &workerThread, SIGNAL( started() ), &worker, SLOT( doLongWork() ) );
    QObject::connect( &worker, SIGNAL( showDialog() ), &dlg, SLOT( exec() ), Qt::BlockingQueuedConnection ); // !!!See connection type!!!

    worker.moveToThread( &workerThread );
    workerThread.start();

    return a.exec();

注意:WinAPI MessageBoxes 仅用于可视化线程确实在等待对话框关闭。

【讨论】:

Qt::BlockingQueuedConnection 上的好指针。我忘记了那种连接类型。

以上是关于qt5:如何从 qthread 中的静态函数创建和显示自定义 qdialog的主要内容,如果未能解决你的问题,请参考以下文章

QT5线程关闭

Qt QThread必须要了解的几个函数

QThread 中的堆栈大小

如何使用cpp中的QThread使用一个对象运行同一类的两个线程?

QThread创建多线程程序

QT5 Thread线程