多线程的作用
我们在日常使用QT时经常会遇到使用多线程的情况,比如需要同时循环处理四个表的数据,如果只在主线程上处理,那么就需要等待四个循环的时间,这样就比较耗时,并且还会造成界面的阻塞造成卡顿,如果我们能同时开辟四个线程来处理,这样就相当于减少了等待的时间,提高效率。
如何创建线程
首先我们可以直接在头文件中添加一个类并继承QThread类
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread(MainWindow * My_ui);
~MyThread();
void run() override;
MainWindow * main_ui;
int C_value;
int C_time;
signals:
void sendSignal();
public slots:
void updateUi();
};
类里面的Q_OBJECT是一个宏,只要是继承了QOBJECT类,无论是直接子类还是间接子类,都需要在第一行添加Q_OBJECT宏,而Qthread类是继承自Qobject,并且只有加了这个宏才有使用信号槽的能力。
MyThread(MainWindow * My_ui);
~MyThread();
这两行分别是新类的构造函数和析构函数,我这里的构造函数给的变量是主窗口类,因为后面还会操作UI。析构函数是为了在该类销毁时运行的函数,一般里面会实现一些指针成员的释放,因为不及时释放的话容易造成内存泄露。
void run() override;
MainWindow * main_ui;
int C_value;
int C_time;
第一个函数为继承自Qthread的方法,需要进行重写,我们的实现逻辑就可以在run里面进行,override就代表该函数需要进行重写。后面三个就是创建了三个成员变量。
signals:
void sendSignal();
public slots:
void updateUi();
signals下的函数为信号函数,slots下为槽函数,信号函数顾名思义就是发出信号的作用,而槽函数则为接收信号而创建的,但是这时我们只是在类中创建了两个毫无联系的两个函数,如何把他们联系起来,这时我们就需要用到connect函数来连接他们俩个。
connect(this, SIGNAL(sendSignal()), this, SLOT(updateUi()));
当然这条语句我们需要写在合适的位置,一般我们希望在创建线程时就能把他们连接起来,而何时能实现,这我们就需要在新类中的构造函数中连接他们两个。实现的位置可以放在mainwindow.cpp中。
MyThread::MyThread(MainWindow * My_ui)
{
main_ui = My_ui;
connect(this, SIGNAL(sendSignal()), this, SLOT(updateUi()));
}
MyThread::~MyThread()
{
this->quit();
}
创建线程设置倒计时进度条
这里我们需要创建两个控件,一个是按钮控件一个是进度条控件
我们右键按键转到槽后,在该跳转的槽函数下实现我们的逻辑。
void MainWindow::on_pushButton_clicked()
{
ui->progressBar->setRange(0,30);//设置进度条的范围数值为0-30
ui->progressBar->setFormat("还剩(%v)秒");
MyThread *T = new MyThread(this);//创建一个进程,this代表当前窗口
T->C_time = 30;//给子成员时间赋值给30
T->start();//start()函数开始实现run函数
}
这边我们在run函数内重写
void MyThread::run()
{
for(int i = C_time; i >= 0; i--)
{
C_value = i;
emit(sendSignal());//emit用来发射信号
QThread::msleep(1000);
//每隔一秒发射一次信号,表示每隔一秒更新一次UI
}
}
尽管我们可以创建多线程来同时处理好几个任务,但是子线程是不能直接操作UI的,只能通过一个中转,也就是我们在子类中创建了一个窗口成员(main_ui),而窗口类中有一个更改UI的槽函数(changeProgress),这样间接使用是允许的。
void MyThread::updateUi()
{
main_ui->changeProgress(C_value);
}
void MainWindow::changeProgress(int value)
{
if(value == 0)
{
ui->progressBar->setFormat("定时结束");
}
ui->progressBar->setValue(value);
}
完整代码
mainwindow.h
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void changeProgress(int value);
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
};
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread(MainWindow * My_ui);
~MyThread();
void run() override;
MainWindow * main_ui;
int C_value;
int C_time;
signals:
void sendSignal();
public slots:
void updateUi();
};
#endif // MAINWINDOW_H
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->progressBar->setValue(0);
ui->progressBar->setFormat("");
}
MainWindow::~MainWindow()
{
delete ui;
}
MyThread::MyThread(MainWindow * My_ui)
{
main_ui = My_ui;
connect(this, SIGNAL(sendSignal()), this, SLOT(updateUi()));
}
MyThread::~MyThread()
{
this->quit();
}
void MyThread::run()
{
for(int i = C_time; i >= 0; i--)
{
C_value = i;
emit(sendSignal());
QThread::msleep(1000);
}
}
void MyThread::updateUi()
{
main_ui->changeProgress(C_value);
}
void MainWindow::changeProgress(int value)
{
if(value == 0)
{
ui->progressBar->setFormat("定时结束");
}
ui->progressBar->setValue(value);
}
void MainWindow::on_pushButton_clicked()
{
ui->progressBar->setRange(0,30);
ui->progressBar->setFormat("还剩(%v)秒");
MyThread *T = new MyThread(this);
T->C_time = 30;
T->start();
}
最终效果