首页 > 代码库 > 第84课 多线程与界面组件的通信(上)

第84课 多线程与界面组件的通信(上)

1. 有趣的问题:

【编程实验】是否可以在子线程中创建界面组件

//TestThread.h

技术分享
#ifndef TESTTHREAD_H
#define TESTTHREAD_H

#include <QThread>

class TestThread : public QThread
{
    Q_OBJECT

protected:
    void run();
public:
    explicit TestThread(QObject* parent = 0);
};

#endif // TESTTHREAD_H
View Code

//TestThread.cpp

#include "TestThread.h"
#include <QWidget>

TestThread::TestThread(QObject *parent) : QThread(parent)
{

}
void TestThread::run()
{
    QWidget w;  //错误!不能在子线程中创建GUI元素
    w.show();
    exec();
}

//Widget.h

技术分享
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
};

#endif // WIDGET_H
View Code

//Widget.cpp

#include "Widget.h"
#include "TestThread.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    TestThread* ptt = new TestThread();
    ptt->start();
}

Widget::~Widget()
{

}

//main.cpp

#include "Widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}
/*出错信息:
ASSERT failure in QWidget: "Widgets must be created in the GUI thread."
*/

2. GUI系统设计原则

(1)所有界面组件的操作只能在主线程中完成;因此,主线程也叫做UI线程

(2)子线程通过信号和槽机制通知界面组件进行更新

3. 多线程与界面组件通信的解决方案

技术分享 

(1)在子线程类定义界面组件更新的信号(如,updateUI)

(2)在主窗口类中定义更新界面组件的槽函数(如,setInfo)

(3)使用异步的队列方式连接更新信号到槽(updateUI→setInfo)

  ①子线程通过发射信号的方式更新界面组件;

  ②所有的界面组件对象只能依附于主线程

【编程实验】子线程中更新界面组件

技术分享

//UpdateThread.h

#ifndef UPDATETHREAD_H
#define UPDATETHREAD_H

#include <QThread>

class UpdateThread : public QThread
{
    Q_OBJECT
protected:
    void run();
public:
    explicit UpdateThread(QObject* parent = 0);

signals:
    void updateUI(QString text);
};

#endif // UPDATETHREAD_H

//UpdateThread.cpp

#include "UpdateThread.h"

UpdateThread::UpdateThread(QObject *parent) : QThread(parent)
{
}

void UpdateThread::run()
{
    emit updateUI("Begin"); //通知UI线程更新

    for(int i=0; i<10; i++){
        emit updateUI(QString::number(i));

        sleep(1);
    }

    emit updateUI("End");
}

//Widget.h

#ifndef WIDGET_H
#define WIDGET_H
#include <QPlainTextEdit>
#include <QWidget>
#include "UpdateThread.h"

class Widget : public QWidget
{
    Q_OBJECT
    UpdateThread m_thread;
    QPlainTextEdit textEdit;
protected slots:
    void appendText(QString text);
public:
    Widget(QWidget *parent = 0);
    ~Widget();
};

#endif // WIDGET_H

//Widget.cpp

#include "Widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    textEdit.setParent(this);
    textEdit.move(20, 20);
    textEdit.resize(200, 150);
    textEdit.setReadOnly(true);

    connect(&m_thread, SIGNAL(updateUI(QString)), this, SLOT(appendText(QString)));

    m_thread.start();
}

void Widget::appendText(QString text)
{
    textEdit.appendPlainText(text);
}


Widget::~Widget()
{

}

//main.cpp

#include "Widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}

4. 小结

(1)现代GUI平台只允许在主线程中直接操作界面组件

(2)Qt中可以借助信号与槽机制在子线程中操作界面组件

(3)进行信号和槽连接必须采用异步的队列连接方式

(4)界面组件对象只能依附于主线程

第84课 多线程与界面组件的通信(上)