前言
Qt作为人机交互界面开发的主流工具,在工业控制领域也得到了广泛的应用。工业控制中,需要对大量的实时数据读取、显示、计算和保存,在一个线程中实现这些功能,很有可能会造成界面卡死的情况。本文介绍一种不需要重写子线程类的简单方法,希望能给读者一些启发。初学Qt,还在摸索中,所说内容难免有错误和不准确的地方,请大家多多指教。
编译环境:Qt5.9.8 Qt Creator 4.8.2 (32位)
一、不指定父对象实例化线程和定时器变量
1.在头文件中声明定时器指针变量和线程指针变量;
2.在构造函数中实例化时不指定父对象;
3.将定时器通过moveToThread的方式转移到子线程中;
4.将线程开启的信号和定时器开启的槽函数相连;
5.将定时器和对应的槽函数相连;
6.注意:连接定时器和槽函数时,不能使用默认的连接方式,应使用Qt::DirectConnection直接连接的方式。直接连接表示槽函数和信号发出者处于同一个线程中,只有这样才可以完成在子线程中使用定时器的槽函数;
7.PS:Connect第5个参数为默认的队列方式,是为不同线程设计的。
代码如下:
timer0 = new QTimer;//动态分配内存的方法,不指定父对象
timer0Thread0 = new QThread;//动态分配内存,不指定父对象
timer0->setTimerType(Qt::PreciseTimer);//设置精确的定时器
timer0->setInterval(1000);//每隔1秒触发一次定时器
timer0->moveToThread(timer0Thread0); //将定时器转移到子线程中
connect(timer0Thread0,SIGNAL(started()),timer0,SLOT(start()));//线程打开同时启动线程中的定时器
connect(timer0,SIGNAL(timeout()),this,SLOT(timer0outSlot()),Qt::DirectConnection);//子线程给主线程发信号
二、线程间通过信号和槽的方式传数据
1.通过带形参的信号和槽完成线程间的信息传递;
2.注意:在构造函数中信号和槽函数的括号中不能写实参,只写变量类型就好,否则会报错。
代码如下:
connect(this,SIGNAL(SendData(const QString)),this,SLOT(ReceiveDataAndShow(const QString)));//通过信号和槽的方式从子线程向主线程传数据
1.cpp程序中完成程序如下:
代码如下:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
timer0 = new QTimer;//动态分配内存的方法,不指定父对象
timer0Thread0 = new QThread;//动态分配内存,不指定父对象
timer0->setTimerType(Qt::PreciseTimer);//设置精确的定时器
timer0->setInterval(1000);//每隔1秒触发一次定时器
timer0->moveToThread(timer0Thread0); //将定时器转移到子线程中
connect(timer0Thread0,SIGNAL(started()),timer0,SLOT(start()));//线程打开同时启动线程中的定时器
connect(timer0,SIGNAL(timeout()),this,SLOT(timer0outSlot()),Qt::DirectConnection);//子线程给主线程发信号
connect(timer0Thread0,SIGNAL(finished()),timer0,SLOT(stop()));//关闭线程的同时关闭定时器
connect(this,SIGNAL(SendData(const QString)),this,SLOT(ReceiveDataAndShow(const QString)));//通过信号和槽的方式从子线程向主线程传数据
}
Widget::~Widget()
{
if(timer0Thread0->isRunning())
{
timer0Thread0->quit();
timer0Thread0->wait();
}
delete timer0;
delete timer0Thread0;
delete ui;
}
void Widget::timer0outSlot()
{
int id = (int) QThread::currentThreadId();
static int cnt = 0;
cnt = (cnt <= 100)? ++cnt : 1;
i = i + 100;
QString str = "Slot_Thread_id:" + QString::number(id) + " secondNum " + QString::number(i);
//ui->textBrowser->append(str);//可以直接用这种方法更改主线程界面的值
//但会报一个警告“QObject::connect: Cannot queue arguments of type 'QTextCursor'
//(Make sure 'QTextCursor' is registered using qRegisterMetaType().)”
//更好的方法是通过发送信号的方式
emit SendData(str);
str.clear();
str = "number: " + QString::number(cnt);
//ui->textBrowser->append(str);
emit SendData(str);
}
void Widget::on_pushButtonStart_clicked()
{
int id = (int)QThread::currentThreadId();
QString str = "GUI_Thread_ID: " + QString::number(id);
//ui->textBrowser->append(str);
emit SendData(str);
if(!timer0Thread0->isRunning())
{
timer0Thread0->start();
}
}
void Widget::on_pushButtonStop_clicked()
{
int id = (int)QThread::currentThreadId();
QString str = "GUI_Thread_ID: " + QString::number(id);
//ui->textBrowser->append(str);
emit SendData(str);
if(timer0Thread0->isRunning())
{
timer0Thread0->quit();
timer0Thread0->wait();
}
}
void Widget::on_pushButtonClear_clicked()
{
ui->textBrowser->clear();
}
void Widget::ReceiveDataAndShow(const QString &text)
{
ui->textBrowser->append(text);
}
总结
这里对文章进行总结:
本文简单介绍了子线程中定时器的使用方法,工程师们可以在定时器中操作板卡的数据读取函数来获取系统的信息。需要注意的有两点,Connect函数第5个参数的使用,以及通过信号和槽的方式完成线程间的通讯。完整的程序地址:https://download.csdn.net/download/ValiantFrank/13697707。