Qt——多线程与界面组件的通信

1.示例:在子线程中创建QWidget

TestThread.h

复制代码
#ifndef TESTTHREAD_H
#define TESTTHREAD_H

#include <QObject>
#include <QThread>

class TestThread : public QThread
{
    Q_OBJECT

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

#endif // TESTTHREAD_H

TestThread.cpp

复制代码
#include "TestThread.h"
#include <QWidget>

TestThread::TestThread(QObject *parent)
    : QThread{parent}
{}
void TestThread::run()
{
    QWidget w;
    w.show();
    exec();
}

Widget.h

复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget() override;
};
#endif // WIDGET_H

Widget.cpp

复制代码
#include "Widget.h"
#include "TestThread.h"

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

Widget::~Widget() = default;

main.cpp

复制代码
#include "Widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return QCoreApplication::exec();
}

运行结果会报错,原因是所有界面组件的操作都只能在主线程中完成,因此,主线程也叫做UI线程

2.子线程如何对界面组件进行更新?

解决方案------信号与槽

  • 在子线程类中定义界面组件的更新信号(updateUI)
  • 在主窗口类中定义更新界面组件的槽函数(setInfo)
  • 使用异步方式连接更新信号到槽函数(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 = nullptr);

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");

    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 <QWidget>
#include "UpdateThread.h"
#include <QPlainTextEdit>

class Widget : public QWidget
{
    Q_OBJECT
    UpdateThread m_thread;
    QPlainTextEdit textEdit;

protected slots:
    void appendText(QString text);

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget() override;
};
#endif // WIDGET_H

Widget.cpp

复制代码
#include "Widget.h"
#include <QObject>

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() = default;

main.cpp

复制代码
#include "Widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return QCoreApplication::exec();
}

运行结果:

本质分析:子线程发射信号通知主线程界面更新请求;主线程根据具体信号以及信号参数对界面组件进行修改

3.是否有其他间接的方式可以让子线程更新界面组件的状态------发送自定义事件类

  • 自定义事件类用于描述界面更新细节
  • 在主窗口类中重写事件处理函数event
  • 使用postEvent函数异步发送自定义事件类对象
    • 子线程指定接受消息的对象为主窗口对象
    • 在event事件处理函数更新界面状态

StringEvent.h

复制代码
#ifndef STRINGEVENT_H
#define STRINGEVENT_H

#include <QEvent>
#include <QString>

class StringEvent : public QEvent 
{
    QString m_data;
public:
    const static Type TYPE = static_cast<Type>(QEvent::User + 0xFF);
    explicit StringEvent(QString data = "");
    QString data();
};

#endif // STRINGEVENT_H

StringEvent.cpp

复制代码
#include "StringEvent.h"


StringEvent::StringEvent(QString data) : QEvent(TYPE)
{
    m_data = data;
}
QString StringEvent::data()
{
    return m_data;
}

UpdateThread.h

复制代码
#ifndef UPDATETHREAD_H
#define UPDATETHREAD_H

#include <QThread>

class UpdateThread : public QThread
{
    Q_OBJECT

protected:
    void run();
public:
    explicit UpdateThread(QObject *parent = nullptr);
};

#endif // UPDATETHREAD_H

UpdateThread.cpp

复制代码
#include "UpdateThread.h"
#include <QApplication>
#include "StringEvent.h"

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

void UpdateThread::run()
{
    
    QApplication::postEvent(parent(), new StringEvent("Begin"));
    for(int i=0; i<10; i++)
    {
        QApplication::postEvent(parent(), new StringEvent(QString::number(i)));
        sleep(1);
    }
    QApplication::postEvent(parent(), new StringEvent("End"));
}

main.cpp

复制代码
#include "Widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return QCoreApplication::exec();
}

运行结果:

相关推荐
个 人 练 习 生2 分钟前
C++ string 类模拟实现:从底层理解字符串(上)
开发语言·c++·经验分享·学习·程序人生
垆边人似月.10 分钟前
华为机试题 :有效的括号
c++·算法·华为
2601_9669496511 分钟前
多市场量化策略的数据接口应该如何设计:从数据层架构到策略接入
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
坤坤子吖14 分钟前
C++智能指针:RAII、shared_ptr与内存泄漏
开发语言·c++·笔记·学习
奋斗的阿狸_198624 分钟前
ESP32-S3 + ES7210 四通道麦克风录音上传方案
c语言·开发语言·fpga开发
weixin1997010801639 分钟前
《1688图片空间API踩坑:img.upload 与 album.* 的防盗链与CDN缓存问题》(附Python源码)
开发语言·python·缓存
A加ITIL教练长河achotsao1 小时前
ITIL Master 战略领导者课后系列分享:AI编程正在重新划分研发与运维的边界
算法
hahaha60161 小时前
FPGA+ARM实现AWB的全流程
人工智能·嵌入式硬件·算法·计算机视觉·fpga开发
李洱1 小时前
LangChain 和 LangGraph 的区别:核心原理与使用场景
算法
夏幻灵1 小时前
每日一题:LeetCode 206:反转链表——三指针与原地反转详解
算法·leetcode·链表