Qt GUI用户与QThread对象内的QMessageBox交互

时间:2012-12-18 12:58:53

标签: multithreading qt user-interface

我正在使用带MyObject->moveToThread(myThread);的QThread进行需要一段时间的通信功能。一些Signals和Slots会在GUI上发布有关进度的信息。

然而,在需要用户交互的线程通信期间可能会出现某些情况 - 因为无法在线程内创建QMessageBox,我想发出一个信号,允许我暂停线程并显示对话框。但首先,似乎没有办法暂停一个线程,其次,这种尝试可能会失败,因为它需要一种方法在恢复时将参数传递回线程。

一种不同的方法可能是事先将所有相关参数传递给线程,但这可能不是一种选择。

这通常是怎么做的?

修改

感谢评论#1并希望我的希望,但请详细说明如何创建例如来自线程内对象的对话框以及如何暂停它..

以下Qt 4.8.1和MSVC ++ 2010的示例代码导致:

MyClass::MyClass created 
MainWindow::MainWindow thread started 
MyClass::start run 
ASSERT failure in QWidget: "Widgets must be created in the GUI thread.", file kernel\qwidget.cpp, line 1299

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "myclass.h"
#include <QThread>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QThread *thread = new QThread();
    MyClass* myObject = new MyClass();

    myObject->moveToThread( thread );
    connect(thread,     SIGNAL( started()),     myObject, SLOT(start()));
    connect(myObject,   SIGNAL( finished()),    thread, SLOT(quit()));
    connect(myObject,   SIGNAL( finished()),    myObject, SLOT(deleteLater()));
    connect(thread,     SIGNAL( finished()),    thread, SLOT(deleteLater()));

    thread->start();
    if( thread->isRunning() )
    {
        qDebug() << __FUNCTION__ << "thread started";
    }
}

MainWindow::~MainWindow()
{
    delete ui;
}

myclass.h

#ifndef MYCLASS_H
#define MYCLASS_H

#include <QObject>

class MyClass : public QObject
{
    Q_OBJECT
public:
    explicit MyClass(QObject *parent = 0);

signals:
    void finished();

public slots:
    void start();

};

#endif // MYCLASS_H

myclass.cpp

#include "myclass.h"

#include <QMessageBox>
#include <QDebug>

MyClass::MyClass(QObject *parent) :
    QObject(parent)
{
    qDebug() << __FUNCTION__ << "created";
}

void MyClass::start()
{
    qDebug() << __FUNCTION__ << "run";

    // do stuff ...

    // get information from user (blocking)

    QMessageBox *msgBox = new QMessageBox();
    msgBox->setWindowTitle(      tr("WindowTitle") );
    msgBox->setText(             tr("Text") );
    msgBox->setInformativeText(  tr("InformativeText") );
    msgBox->setStandardButtons(  QMessageBox::Ok | QMessageBox::Cancel);
    msgBox->setDefaultButton(    QMessageBox::Ok);
    msgBox->setEscapeButton(     QMessageBox::Cancel);
    msgBox->setIcon(             QMessageBox::Information);

    int ret = msgBox->exec();

    // continue doing stuff (based on user input) ...

    switch (ret) 
    {
        case QMessageBox::Ok:
            break;

        case QMessageBox::Cancel:
            break;

        default:
            break;
    }

    // do even more stuff

    emit finished();
}

2 个答案:

答案 0 :(得分:5)

在信号/槽连接中使用Qt :: BlockingQueuedConnection(对QObject :: connect()的调用)。

http://doc.qt.digia.com/qt/qt.html#ConnectionType-enum

这会阻塞你的线程,直到UI线程上的插槽返回,然后UI线程中的插槽可以自由显示消息框/模态对话框/无论你想做什么。

必须确保你的工作线程实际上不在UI线程上,因为正如文档所说,如果信号和插槽在同一个线程上,这将导致死锁(因为它将阻止自己)。

答案 1 :(得分:2)

我现在不能提供任何具体代码,但我会这样做:

  1. MyClass::start()锁定QMutex
  2. 发出信号,例如messageBoxRequired()
  3. 等待最近的互斥锁上的共享QWaitCondition。这也将在线程等待时解锁互斥锁。
  4. 在MainWindow的插槽中,例如showMessageBox(),显示消息框。
  5. 将返回的值存储在MyClass的成员中。您可以通过提供使用互斥锁的setter和getter来保护成员。显然MyClass本身应该只使用那些setter / getters本身访问该成员。 (另请参阅QMutexLocker)。
  6. 在共享的wakeOne()上致电wakeAll()QWaitCondition
  7. 之前的wait()调用将返回,MyClass::start()将继续执行。如果我正确理解文档,QWaitCondition会在从wait()返回之前再次锁定互斥锁。这意味着您必须在wait()调用后直接解锁互斥锁。
  8. 您可以从类成员访问消息框的返回值(使用线程安全的getter)
  9. 线程安全setter / getter的实现如下:

    void MyClass::setVariable( int value )
    {
        QMutexLocker( &_mutex );
        _value = value;
    }
    
    int MyClass::getVariable() // Not sure if a 'const' modifier would work here
    {
        QMutexLocker( &_mutex );
        return _value;
    }