只有一个线程去处理,会导致界面卡顿,无法处理用户的相关操作。使用多线程,可以提高效率。
线程可以分为两类:
1、创建一个线程类的子类,让其继承QT中的线程类 QThread,重写父类的 run() 方法,在该函数内部编写子线程要处理的具体的业务流程
// .h
class MyThread_delay : public QThread
{
Q_OBJECT
public:
explicit MyThread_delay(QObject* parent = nullptr)
:QThread(parent){
};
~MyThread_delay() override{
if (m_timer->isActive()) {
m_timer->stop();
}
delete m_timer;
if (check_timer->isActive()) {
check_timer->stop();
}
delete check_timer;
if (setzero_timer->isActive()) {
setzero_timer->stop();
}
delete setzero_timer;
};
QTimer* m_timer; // Qt Timer
QTimer* check_timer; // Qt Timer
QTimer* setzero_timer; // Qt Timer
protected:
void run() override{
m_timer = new QTimer(this);
m_timer->setSingleShot(false);
connect(m_timer, &QTimer::timeout, this, &MyThread_delay::onTimerTimeout);
m_timer->start(1000); // 以1秒为间隔触发定时器
check_timer = new QTimer(this);
check_timer->setSingleShot(false);
connect(check_timer, &QTimer::timeout, this, &MyThread_delay::check_ue);
check_timer->start(5000); // 以1秒为间隔触发定时器
setzero_timer = new QTimer(this);
setzero_timer->setSingleShot(false);
connect(setzero_timer, &QTimer::timeout, this, &MyThread_delay::set_zero);
setzero_timer->start(1500); // 以1秒为间隔触发定时器
exec(); // 启动Qt事件循环,保持线程运行
};
void onTimerTimeout(){
//实现功能
// 定时器超时时,发送信号更新折线图
emit updateGraph(list_id,delay_list,list_name); // 传递适当的值,作为折线图的数据
}
void check_ue(){
//实现功能
}
void set_zero(){
//实现功能
}
signals:
void updateGraph(QList<QString> ,QList<float>,QList<QString>); // 自定义信号,用于更新折线图
private:
const int numRows =6;
const int numColumns = 2;
QSqlDatabase db; //数据库对象
int id;
QList<QString> list_id;
QList<float>delay_list;
QList<QString> list_name;
QList<QString>list_real_id={"1","2","21","22","41","42"};
QList<int> list_num={0,0,0,0,0,0};
QList<QList<float>> list2_passvalue;
QList<QList<int>> list2_count;
int Max_slice_num=6;
};
// .cpp 主线程中
m_thread_delay = new MyThread_delay;
//将当在线程中实现功能时 与通过信号槽的方式与主线程进行交互
connect(m_thread_delay, SIGNAL(updateGraph(QList<QString>, QList<float>, QList<QString>)), this, SLOT(show_delay(QList<QString>, QList<float>, QList<QString>)));
m_thread_delay->start();
缺点:当处理多个任务时,都需要写到run()函数中,这样该函数中的逻辑就会变得非常混乱,不容易维护
2、这种方式更加灵活,但是写起来会相对复杂一些
以下是一个接受视频数据流并显示在label里面的一个示例代码:
属于 QObject
// .h文件
class DataReceiverThread : public QObject
{
Q_OBJECT
public:
explicit DataReceiverThread(int PORT, QObject *parent = nullptr)
: QObject(parent), m_PORT(PORT)
{
}
public:
void work()
{
m_clientSocket = new QTcpSocket(this);
QObject::connect(m_clientSocket, &QTcpSocket::stateChanged, [=](QAbstractSocket::SocketState socketState) {
if (socketState == QAbstractSocket::ConnectedState) {
isConnect=true;
qDebug() << "Connected to the server.";
} else if (socketState == QAbstractSocket::UnconnectedState) {
isConnect=false;
m_clientSocket->abort(); // 中断当前连接
if (m_clientSocket->state() == QAbstractSocket::ConnectedState) {
m_clientSocket->waitForDisconnected(); // 等待连接关闭完成
}
qDebug() <<"disConnected to the server.";
QMetaObject::invokeMethod(this, [this]() {
m_clientSocket->connectToHost("192.168.31.XX", m_PORT); // 重新发起连接
}, Qt::QueuedConnection);
m_clientSocket->connectToHost("192.168.31.XX", m_PORT); // 重新发起连接
//当连接不上时发射信号 让主线程执行对应动作
emit no_connect();
// 重新启动接收数据的线程
}
});
m_clientSocket->connectToHost("192.168.31.XX", m_PORT);
QObject::connect(m_clientSocket, &QTcpSocket::readyRead, [=]() {
if(isConnect){
if (m_expectedSize == 0) {
// 判断是否接收到了期望的帧大小数据
if (m_clientSocket->bytesAvailable() < sizeof(quint32)) {
return;
}
// 读取四个字节的帧大小数据
QByteArray sizeData = m_clientSocket->read(sizeof(quint32));
// 将网络字节顺序(大端序)的数据转换为主机字节顺序
QDataStream sizeStream(sizeData);
sizeStream.setByteOrder(QDataStream::BigEndian);
sizeStream >> m_expectedSize;
// 清空已接收的数据
m_receivedData.clear();
m_receivedData.reserve(m_expectedSize); // 预分配接收数据的大小
}
// 继续接收剩余数据
m_receivedData.append(m_clientSocket->readAll());
// 检查是否已接收到完整的一帧数据
if (m_receivedData.size() >= m_expectedSize) {
emit dataReceived(m_receivedData);
}}}
);
}
public slots:
void clearData(){
m_receivedData.clear();
m_expectedSize = 0;
m_clientSocket->write("received");
}
void stopThread() {
// 停止线程的运行
m_stopThread = true;
// 关闭套接字连接
m_clientSocket->disconnectFromHost();
qDebug()<<"destruction";
// 将线程从事件循环中退出
QCoreApplication::postEvent(this, new QEvent(QEvent::Quit));
}
void deleteObject() {
delete this;
}
signals:
void dataReceived(const QByteArray& data);
void no_connect();
private:
QTcpSocket* m_clientSocket;
int m_PORT;
quint32 m_expectedSize=0;
QByteArray m_receivedData;
bool isConnect;
bool m_stopThread;
void stopConnection()
{
m_stopThread = true;
m_clientSocket->disconnectFromHost();
m_clientSocket->deleteLater();
m_clientSocket = nullptr;
}
};
// .cpp文件
DataReceiverThread* DreceiverThread = new DataReceiverThread(12101);
QThread* receiverThread= new QThread();
//将工作的类对象移动到创建的子线程对象中
DreceiverThread->moveToThread(receiverThread);
QObject::connect(this, &Video_Status::stopThread, DreceiverThread, &DataReceiverThread::stopThread);
//线程中接完一帧数据后 发出信号dataReceived 后然后运行该匿名函数 显示在label中
QObject::connect(DreceiverThread, &DataReceiverThread::dataReceived,this,[=](const QByteArray& data) {
QImage image;
image.loadFromData(data, "JPG");
// 在此处进行图像处理或显示
label_image[i][0]->setPixmap(QPixmap::fromImage(image));
emit over;
});
//显示在label中后 发射信号over 与线程中的clearData连接 清除历史数据
QObject::connect(this, &Video_Status::over, DreceiverThread, &DataReceiverThread::clearData);
//当没有数据是该类发射信号 然后主线程中显示无视频信号
QObject::connect(DreceiverThread, &DataReceiverThread::no_connect, this,[=]() {
label_image[i][0]->setPalette(pe);
label_image[i][0]->setText("无视频信号");
});
//将线程开始与定义的类的函数连接在一起 当线程开始时就运行该类中的work函数
QObject::connect(receiverThread_, &QThread::started, DreceiverThread, &DataReceiverThread::work);
//开启线程
receiverThread->start();
我这只是一个学习记录,具体的用法学习可以参考:Qt中多线程的使用 | 爱编程的大丙 (subingwen.cn)