通过QThread进行进度控制

QT线程的使用

Posted by 雯饰太一 on June 25, 2023

通过QThread进行进度控制

说明:

  • 线程类 QSatelliteTrackThread
  • 界面类 QSatelliteTreeWidget
  • 在界面中通过【开始】【暂停】【继续】【停止】来控制线程的运行状态
  • 线程的初始化与销毁是在界面中完成的

线程类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class QSatelliteTrackThread : public QThread
{
	Q_OBJECT
public:

	enum ThreadRunState
	{
		TRS_Begin,
		TRS_Run,
		TRS_Pause,
		TRS_Stop
	};

	QSatelliteTrackThread();
	virtual ~QSatelliteTrackThread();

	void SetThreadRunState(ThreadRunState s);
signals:
void featureCreated(int fId,int t);//传入动目标id,类型 [0:星下点 1:卫星]
	void eventloopBreak();//打破时间循环,以继续或终止
public slots:
	void on_featureCreated(int fId, int t);
	void on_threadTimerWakeUp();		//执行处理逻辑
protected:
	virtual void run();
	bool ContinueCtrl();//进度控制

private:
	QTimer* m_pThreadTimer;
	ThreadRunState m_cState;
};
  • 需要继承Qthread
  • 需要支持外部设置状态,内部进行状态切换
  • 能够在run函数里面进入具体的工作函数

构造函数

1
2
3
4
5
QSatelliteTrackThread::QSatelliteTrackThread()
{
	m_cState = TRS_Begin;
	pThreadTimer = nullptr;
}
  • 设置初始状态
  • 设置timer为空

run函数

1
2
3
4
5
6
7
8
9
10
11
void QSatelliteTrackThread::run()
{
	m_pThreadTimer = new QTimer;
	m_pThreadTimer->setSingleShot(true);
	connect(m_pThreadTimer, &QTimer::timeout, this, &QSatelliteTrackThread::on_threadTimerWakeUp,Qt::DirectConnection); //保证子程创建工作
	connect(this, &QSatelliteTrackThread::featureCreated, this, &QSatelliteTrackThread::on_featureCreated, Qt::QueuedConnection);//保证主线程创建电磁特效
	m_pThreadTimer->start();
	exec();
	delete m_pThreadTimer;
	disconnect(this, &QSatelliteTrackThread::featureCreated, this, &QSatelliteTrackThread::on_featureCreated);
}
  • 将timer实例化,绑定信号,使其能够在线程中运行 [在线程中运行timer还需要线程启用事件循环]
  • 启动timer,一旦超时,将会激活工作函数on_threadTimerWakeUp
  • 启动事件循环
  • 事件循环结束后,销毁timer并解除自身的绑定

过程控制函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <QEventLoop>
bool QSatelliteTrackThread::ContinueCtrl()
{
	switch (m_cState)
	{
	case QSatelliteTrackThread::TRS_Begin:
		return true;
		break;
	case QSatelliteTrackThread::TRS_Run:
		return true;
		break;
	case QSatelliteTrackThread::TRS_Pause:
	{
		QEventLoop loop;
		connect(this, &QSatelliteTrackThread::eventloopBreak, &loop, &QEventLoop::quit, Qt::QueuedConnection);
		loop.exec();
		return !(m_cState == TRS_Stop);
		break;
	}
	case QSatelliteTrackThread::TRS_Stop:
		return false;
		break;
	default:
		break;
	}
	return false;
}
  • 只有是pause状态的情况下,才需要使用eventloop阻塞线程的运行
  • 相当于只要切换状态的时候,自己发一个信号,就能打破loop,使其继续运行

具体做事情的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <QTimer>
void QSatelliteTrackThread::on_threadTimerWakeUp()
{
	m_cState = TRS_Run;

	for (qint64 t = m_dtStart; t < m_dtEnd; t += 1000)
	{
		for (int i = 0; i < m_vecSatellite.size(); i++)
		{
			if (!ContinueCtrl()) break;
			//... do something
			//Q_EMIT updateTrack(pSat);
			msleep(10);
		}
		if(!ContinueCtrl()) break;
	}
}
  • 在关键的位置,对状态进行检测,通常这种for循环中才需要

界面类

开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void QSatelliteTreeWidget::on_pbPlay_clicked(bool checked)
{
	//调整状态
	setPushbuttonEnableState(ui.pbPlay, false);
	setPushbuttonEnableState(ui.pbPause, true);
	setPushbuttonEnableState(ui.pbStop, true);
	ui.pbPause->setText(QString::fromLocal8Bit("暂停"));

	if (m_pSatTrackThread == nullptr)
	{
		m_pSatTrackThread = new QSatelliteTrackThread();
	}
	m_pSatTrackThread->start();
}
  • 设置按钮状态
  • 实例化线程,线程对象是在主线程实例化的,出了他的run函数
  • 其他的被外部调用时,也都是在主线程中的

暂停与继续

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void QSatelliteTreeWidget::on_pbPause_clicked(bool checked)
{
	if (!m_pSatTrackThread) return;
	QString text = ui.pbPause->text();
	if (text.compare(QString::fromLocal8Bit("暂停")) == 0)
	{
		m_pSatTrackThread->SetThreadRunState(QSatelliteTrackThread::TRS_Pause);
		ui.pbPause->setText(QString::fromLocal8Bit("继续"));
	}
	else
	{
		m_pSatTrackThread->SetThreadRunState(QSatelliteTrackThread::TRS_Run);
		ui.pbPause->setText(QString::fromLocal8Bit("暂停"));
	}
}

结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void QSatelliteTreeWidget::on_pbStop_clicked(bool checked /*= false*/)
{
	if (!m_pSatTrackThread) return;
	m_pSatTrackThread->SetThreadRunState(QSatelliteTrackThread::TRS_Stop);

	setPushbuttonEnableState(ui.pbPlay, true);
	setPushbuttonEnableState(ui.pbPause, false);
	setPushbuttonEnableState(ui.pbStop, false);
	ui.pbPause->setText(QString::fromLocal8Bit("暂停"));

	m_pSatTrackThread->quit();
	m_pSatTrackThread->wait();
	delete m_pSatTrackThread;
	m_pSatTrackThread = nullptr;
}
  • 通过quit停止线程
  • 通过wait等待线程finished
  • 只有线程的run函数里面exec后,quit才会生效
  • 销毁线程对象

QRunnable + QObject应该也是可以满足条件的。但是这种方式都是在循环存在的时候成立,在没有循环的情况下,并不能立刻冻结或暂停一个线程。

期望的状态是,通过一个函数能够在任何时间,直接对线程自身的运行施以操控。