在QT中有自己的线程处理机制,大家都知道在处理大量的数据的时候,一般会单独的开启一个线程来处理,这样做的好处有两个方面:一个是提高数据处理运行的速度;另外一个就是在线程中处理数据,不会影响主线程的运行。如果在主线程中来加载大量的数据,主线程就会“卡住”,出现程序假死的情况,非常影响用户的使用体验。
在线程中运行接口
QThread这个类是QT提供的单独来运行线程的类,只要子类继承并且实现run这个方法即可。
virtual void run();
然后调用start就可以启动线程来运行run这个方法。关于线程的同步请大家看我之前写过关于线程同步的文章QT多线程同步。
大家都知道的,关于工作线程和主线程进行通信也是通过信号槽来实现的,具体请看我另一篇文章,QT信号槽的在不同线程或者在同一线程下的连接方式
在线程中进行加载大量的数据,但是经常会出现这样的场景,比如说有一个类,这个类除了实现数据加载功能之外还要实现数据在表格中展示、点击表格的后的触发事件等其他的功能,这些功能并不是全部在线程中完成,特别是和界面操作相关的功能,更不能在工作线程中来实现, 只能在主线程中来实现。也就是说一个类的功能有一部分是在线程中实现而另一部分则不需要在工作线程中来完成。那么怎么办呢?
最好的办法就是,把需要在线程中完成的工作单独提出类作为一个父类,子类继承这个父类除了实现这个接口外还能扩展其他的功能,子类对象同时无论是工作线程中还是主线程都共有这个类对象中成员变量。提出的父类咱们可以称之为“接口”这就是面向对象编程中的面向接口编程(这里提到的接口并不是指的是java语言中的接口,而是和java语言中接口具有相同功能的父类而已)。
1.定义接口
#pragma once
//数据加载接口
class ILoadData {
public:
ILoadData();
~ILoadData();
virtual void loadData() = 0;
};
#include "ILoadData.h"
ILoadData::ILoadData() {
}
ILoadData::~ILoadData() {
}
2.定义线程类,并且可以运行接口
#pragma once
#include "ILoadData.h"
#include <QThread>
class QtThreadLoad : public QThread {
Q_OBJECT
public:
QtThreadLoad(ILoadData* load, QObject *parent);
~QtThreadLoad();
void run()override;
private:
ILoadData* _loadData = nullptr;
};
#include "QtThreadLoad.h"
QtThreadLoad::QtThreadLoad(ILoadData* load, QObject *parent)
: QThread(parent) {
_loadData = load;
}
QtThreadLoad::~QtThreadLoad() {
}
void QtThreadLoad::run() {
if (_loadData != nullptr){
_loadData->loadData();
}
}
3.定义场景
先画一个界面:
#pragma once
#include <QWidget>
#include "ui_QtGuiTable.h"
#include "ILoadData.h"
#include <vector>
#include "QtThreadLoad.h"
class QtGuiTable : public QWidget,public ILoadData {
Q_OBJECT
public:
QtGuiTable(QWidget *parent = Q_NULLPTR);
~QtGuiTable();
void initTable();
virtual void loadData()override;
private slots:
void slotUpdateName();
void slotBtnLoad();
signals:
void sigUpdate();
private:
Ui::QtGuiTable ui;
QtThreadLoad* threadLoad = nullptr;
std::vector <QString> _nameList;//名称列表
};
#include "QtGuiTable.h"
#include <QTableWidgetItem>
QtGuiTable::QtGuiTable(QWidget *parent)
: QWidget(parent) {
ui.setupUi(this);
initTable();
connect(this, SIGNAL(sigUpdate()), this, SLOT(slotUpdateName()));
connect(ui.pushButton, SIGNAL(clicked()), this, SLOT(slotBtnLoad()));
threadLoad = new QtThreadLoad(this, this);
}
QtGuiTable::~QtGuiTable() {
}
void QtGuiTable::initTable() {
ui.tableWidget->setColumnCount(1);
ui.tableWidget->setRowCount(100);
}
void QtGuiTable::loadData() {
QString name;
for (int i = 0; i < 100; i++) {
name = QStringLiteral("张") + QString::number(i);
_nameList.push_back(name);
}
emit sigUpdate();
}
void QtGuiTable::slotUpdateName() {
for (int i = 0; i < 100; i++) {
ui.tableWidget->setItem(i, 0, new QTableWidgetItem(_nameList.at(i)));
}
}
void QtGuiTable::slotBtnLoad() {
threadLoad->start();
}
以上就完成了面向接口编程!
下面分析一下程序运行的过程,看是顺序图:
现在大家看到这个过程了,其实线程运行过程中,只调用了对象中的通过继承来的接口,然后通过信号槽来实现界面的刷新。