2

我将 QThread 与MyObject->moveToThread(myThread);需要一段时间的通信功能一起使用。一些 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

主窗口.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

主窗口.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;
}

我的班级.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

我的类.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();
}
4

2 回答 2

5

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

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

这将阻塞您的线程,直到 UI 线程上的插槽返回,然后 UI 线程中的插槽可以自由显示消息框/模式对话框/您想做的任何事情。

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

于 2012-12-18T16:42:45.287 回答
2

我现在不能给出任何具体的代码,但我会这样做:

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

线程安全的 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;
}
于 2012-12-18T16:38:01.353 回答