2012-12-18 53 views
2

对于需要一段时间的通信功能,我使用QThread和MyObject->moveToThread(myThread);。一些信号和插槽可以让GUI张贴关于进度的信息。Qt GUI用户与QThread对象内的QMessageBox进行交互

然而,在线程通信期间可能会出现一些需要用户交互的情况 - 由于无法在线程内部创建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(); 
} 
+0

你可以暂停,传递参数并在'QThread'中创建'QMessageBox',你不能做的是直接从辅助线程调用一个小部件的函数,请发布你的代码示例以更好地理解你是什么试图完成 – 2012-12-18 13:19:36

+2

主线程和'thread'之间的通信应该使用全局http://qt-project.org/doc/qt-4.8/QWaitCondition.html和http://qt-project.org/doc来实现/qt-4.8/qmutex.html对象?所以'线程'可以发出信号并等待来自GUI线程的数据。 – handle

+1

+1不用于继承QThread。 –

回答

2

我不能给任何特定代码的权利,但我会做这样的:

  1. MyClass::start()锁定QMutex
  2. 发出信号例如messageBoxRequired()
  3. 等待最近互斥体上的共享QWaitCondition。这也将在线程等待时解锁互斥锁。
  4. 在MainWindow的插槽中,例如showMessageBox(),显示消息框。
  5. 将返回的值存储在MyClass的成员中。你可以通过提供一个使用互斥体的setter和getter来保护成员。显然MyClass本身只能访问那些设置者/ getters本身的成员。 (关于这一点,请参阅QMutexLocker)。
  6. 致电wakeOne()wakeAll()共享QWaitCondition
  7. 以前的wait()电话将返回并且MyClass::start()将继续执行。如果我正确理解文档,QWaitCondition将在从wait()返回之前再次锁定互斥锁。这意味着您必须在拨打wait()之后直接解锁互斥锁。
  8. 你可以从你的类成员访问该消息框的返回值(使用一个线程安全的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; 
} 
+0

关于设置getter常量:在互斥体上使用const_cast是这种转换的少数有效用例之一,我认为,因为锁定互斥体是从类数据的角度来看的一种元操作。 – hyde

+1

你可以用QAtomicInt使int值原子化,并且避免需要对setter和getter进行互斥锁定。 – hyde

+0

@hyde +1你是对的QAtomicInt。我将保留这个例子,因为它可以用于任何类型的值,而不仅仅是int。 –

5

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

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

这将阻止您的线程,直到在UI线程返回插槽,在UI线程的插槽就可以自由地显示一个消息框/模态对话框/任何你想做的事情。

必须确保您的工作线程实际上不是在UI线程上,因为文档说,这将导致死锁如果信号和槽是在同一个线程(因为它会阻止本身) 。

+1

+1我不知道BlockingQueuedConnection,但实际上这似乎以更舒适的方式解决了问题。 –

相关问题