Qt中另一种多线程与界面组件通信的方法
上一节中讲到了使用Qt平台独有的信号槽机制,实现子线程与界面组件的通信,来间接操作界面组件。那么Qt中有没有其他方式能够实现这一功能呢?或者其他不支持信号槽机制的平台该怎么实现子线程操作界面组件呢?
还是借助Qt平台,我们很容易就能想到另一种可以跨线程的通信方式:事件。Qt平台支持比较完善的事件系统,当然也能发送自定义的事件,发送自定义事件的方式主要依靠两个函数:sendEvent()和postEnent()。两者的区别主要有如下几点:
- sendEvent()会立即处理给定的事件;而postEvent()则会将事件放到等待队列中,当下一次Qt的主循环运行时才会处理。
- sendEvent()中的QEvent对象在发送后无法自动删除,因此需要在栈上创建对象;而postEvent()中的QEvent对象必须在堆上创建,当事件被发送后,事件队列自动删除它。
我们梳理一下实现功能的细节:首先需要新建自定义事件类,用来描述自定义事件的信息细节;其次要重写主线程中相应界面组件的事件处理函数event;最后要用postEvent(…)把自定义事件类对象抛出到事件循环中,主线程的界面组件在event函数中接受处理这条事件信息。
关键思路就是这样,我们就按这样的思考编程,下面看一下代码:
//以下是主函数
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
//以上是主函数
//以下是主窗口界面的声明和定义,包含event函数重写、实现
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "QPlainTextEdit"
#include "QEvent"
#include "cselfthread.h"
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
bool event(QEvent *event);
private:
QPlainTextEdit *plainText;
cselfThread selfThrad;
private slots:
};
#endif // WIDGET_H
//.cpp/
#include "widget.h"
#include "cselfevent.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
this->resize(300,300);
plainText = new QPlainTextEdit(this);
plainText->resize(300,300);
//设置并启动线程
selfThrad.setParent(this);
selfThrad.start();
}
Widget::~Widget()
{
}
bool Widget::event(QEvent *event)//重写event事件
{
bool ret = true;
if(event->type() == cselfEvent::selfType)
{
cselfEvent *eve = dynamic_cast<cselfEvent *>(event);//把接收到的事件强制转换为自定义的事件类型。
if(eve != nullptr)
{
plainText->appendPlainText(eve->getData());//获取自定义事件类中包含的数据
}
}
else
{
ret = QWidget::event(event);
}
return ret;
}
//以上是主窗口界面的声明和定义,包含event函数重写、实现
//以下是自定义事件的实现
#ifndef CSELFEVENT_H
#define CSELFEVENT_H
#include <QWidget>
#include "QEvent"
#include "QString"
class cselfEvent : public QEvent
{
QString updateString;
public:
explicit cselfEvent(QString m_string = " ");
const static Type selfType = static_cast<Type>(QEvent::User + 0x01);//定义自定义的事件类型
QString getData();
private:
};
#endif // CSELFEVENT_H
/.cpp//
include "cselfevent.h"
cselfEvent::cselfEvent(QString m_string):QEvent(selfType),updateString(m_string)
{
;
}
QString cselfEvent::getData()
{
return updateString;
}
//以上是自定义事件的实现
//以下是子线程的声明和实现
#ifndef CSELFTHREAD_H
#define CSELFTHREAD_H
#include <QObject>
#include "QThread"
#include "QEvent"
#include "QApplication"
class cselfThread : public QThread
{
Q_OBJECT
protected:
void run();
public:
cselfThread();
};
#endif // CSELFTHREAD_H
.cpp///
#include "cselfthread.h"
#include "cselfevent.h"
cselfThread::cselfThread()
{
}
void cselfThread::run()
{
QApplication::postEvent(this->parent(),new cselfEvent("selfThread Begian"));
for(int i = 0;i<5;i++)
{
msleep(200);
QString str = QString("selfThread i :").append(QString::number(i));
QApplication::postEvent(this->parent(),new cselfEvent(str));
}
QApplication::postEvent(this->parent(),new cselfEvent("selfThread End"));
}
//以上是子线程的声明和实现
可以看一下运行效果,应该是与信号槽效果一致的:
果然达到了我们所要的效果。
- 要重点把握自定义事件类的编程方法和主界面中重写event事件接受函数,处理自定义类的内容。
- 程序中用了postEvent来发送自定义事件对象,在子线程中用sendEvent发送自定义事件是不行的。 postEvent发送的事件进入事件循环,相当于异步的方式;
王雄 首次更新与2021.03.12