QT的学习第二部分

一、QTimer定时器

1、什么是定时器

1)定时器概念

Linux系统中通过SIGALRM信号配合alarm函数实现定时器机制,预设信号中断函数进行响应。Qt的QTimer定时器采用类似的思路,但基于信号与槽机制实现,内部独立计时,不会与Qt程序的其他部分产生冲突。

2)QTimer定时器类

QTimer是Qt提供的定时器类,能够定时触发信号,用于周期性执行操作。通过QTimer,可以轻松实现定时更新UI界面或循环执行特定任务等功能,使用便捷高效。

2、使用步骤

第一步:实例化QTimer对象

第二步:设置计时器的时间间隔 QTimer qter->setInterval(1000);

第三步:连接信号与槽

第四步:开启计时和关闭计时

3、时间显示的示例

mainwindow.h:建立槽函数,处理定时显示时间的操作。

mainwindow.cpp:实例化QTimer和QLabel的对象,设置时间间隔,建立信号与槽,开启计时;在析构函数中关闭计时并删除创建的堆对象。

main.cpp:设置标题,显示窗口

结果显示:

二、QT的对话窗口

1、基础对话框

在Qt中,QDialog类用于实现对话框,继承自QWidget类。当对话框的parent参数为NULL时,它会作为一个顶层窗口(独立窗口)显示,在任务栏中拥有独立位置;当指定了父组件时,则作为子对话框出现在父组件的中心位置,并与父组件共享任务栏位置。

2、对话框的分类

(1)模态对话框

特点:
1、模态顶层窗口与非顶层窗口的区别在于,顶层窗口在任务栏会有自己的位置,而非顶层窗口则会共享其父组件的位置。
2、在模态对话框显示时,应用程序的其他窗口将无法获得焦点,直到模态对话框被关闭。
3、模态对话框通常使用exec()函数打开。
常见的应用场景:
适用于优先级最高的功能对话框,比如用户输入和确认、错误和警告提示、进度和等待。
用户交互结果判断:
QDialog::Accepted:表示用户确认操作,通常是在用户点击“确定”按钮时触发。
QDialog::Rejected:表示用户取消操作,通常是在用户点击“取消”按钮时触发。
使用exec()函数显示对话框时,可以通过返回值判断用户是否接受了对话框。

(2)非模态对话框

特点:
1、非模态对话框是一种不会阻塞程序执行的对话框,非模态对话框 和当前进程同时使用的。
2、与模态对话框不同,非模态对话框通常不会返回执行结果(对话框关闭的时候才返回给进程的),而是直接在对话框内执行相应操作。
3、非模态对话框通常使用show()函数打开,而不是exec()函数。这样,对话框将以非模态的方式打开,并允许用户同时操作其他窗口。

3、标准对话框

标准对话框:一组预定义对话框类,用于快速实现文件选择、颜色选择、字体选择、消息提示、输入等常见交互功能,无需开发者自行设计界面。

(1)标准文件对话框

1、概念
QFileDialog 是Qt提供的标准文件对话框,主要用于获取要打开的文件路径或保存文件的路径,不直接操作文件本身,而是返回用户选择的文件路径字符串,供程序后续读写使用。
2、使用方式
创建文件对话框有两种方式:
方法一:直接实例化 QFileDialog 对象,进行详细配置后调用 exec() 显示。
方法二:调用其静态成员函数(如 getOpenFileName()、getSaveFileName()),一行代码快速创建并获取结果,适合简单场景。
3、常用接口
void setDirectory(const QString &directory):设置默认打开的路径
void setNameFilter(const QString &filter):设置文件过滤器
void setFileMode(FileMode mode):设置文件选择模式

通过创建QFileDialog对象创建标准文件对话框:

通过创建QFileDialog的静态函数创建标准文件对话框:

(2)标准颜色对话框

QColorDialog为标准颜色对话框类, 它主要是获取QColor,也就是一个颜色对象,
可以用与组件的前景和背景色设置,同样创建方式也有(创建对象和静态函数快速创建)

颜色对话框组合练习:

(3)标准输入对话框

QInputDialog为标准输入对话框类,主要是获取输入框里面的内容。
内容可以是字符串或者数值,同样也可以用静态函数快速创建如下:
QString passwd = QInputDialog::getText(this,"输入框测试","请输入密码",QLineEdit::Password);

创建密码框的输入并打印结果:

(4)标准字体对话框

QFontDialog标准字体对话框,它主要是获取QFont字体对象。
QFont font = QFontDialog::getFont(&ok,this);

创建设置按钮文本字体的对话框:

(5)消息对话框

QMessageBox类是一个模态对话框,用于通知用户或询问用户一个问题并接收答案。
QMessageBox::warning(&w,"消息对话框测试","确定删除文件",QMessageBox::No,QMessageBox::Yes);

创建删除文件的消息对话框并打印输出结果:

三、网络编程TCP

1、TCP编程

Qt网络模块通过QT += network引入,提供QTcpSocket等封装好的API,开发时调用就可以了。但真正的难点在于网络编程本身的通用问题——粘包、断线重连、并发、超时处理等,这些需要TCP/IP协议基础和工程经验,光学会Qt的API类远不够。

2、编程接口

1、QTcpSocket类
TCP客户端类,用于创建一个TCP套接字,实现TCP客户端功能。
QTcpSocket类提供了一组丰富的接口函数,包括连接服务器、发送数据、接收数据、断开连接等。
2、QTcpServer类
TCP服务器类,用于创建一个TCP服务器,实现TCP服务器功能。
QTcpServer类提供了一组接口函数,包括监听客户端连接请求、接受连接、发送接收数据、断开连接等。
3、QTcpServer类的常用成员函数
(1)构造函数和析构函数
QTcpServer(QObject *parent = nullptr)
~QTcpServer()
(2)监听和连接管理相关
bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0):开始监听指定地址和端口号上的连接。
void close():关闭服务器并停止监听。
QList<QTcpSocket *> pendingConnections() const:获取所有待处理的连接。
virtual QTcpSocket *nextPendingConnection():获取第一个
(3)信号和槽相关
void newConnection():当有新连接请求时触发该信号。
void acceptError(QAbstractSocket::SocketError socketError):当接受连接过程中发生错误时触发该信号。
(4)连接处理相关
void addPendingConnection(QTcpSocket *socket):将待处理的连接添加到连接列表中。也就是与客户端进行连接
void removePendingConnection(QTcpSocket *socket):从连接列表中移除指定的连接。
4、QHostAddress类
用于表示一个IP地址,QHostAddress类提供了一组接口函数,用于将IP地址转换为字符串、从字符串中解析出IP地址等
5、QNetworkInterface类
用于获取主机的网络接口信息,例如IP地址、子网掩码、MAC地址等。
6、QTcpSocket类的常用成员函数
(1)接收和发送消息
connectToHost(const QString &hostName, quint16 port):连接到指定的主机名和端口号。
disconnectFromHost():断开与主机的连接。
write(const QByteArray &byteArray):将指定的数据写入套接字。
waitForBytesWritten(int msecs = 30000):等待数据被写入套接字。
readyRead():当有新的数据可读时触发该信号。
bytesAvailable():返回当前缓冲区中可读取的字节数。
readAll() :读取所有可用的数据并将其作为 QByteArray 返回。
read(qint64 maxSize) 读取指定最大字节数的数据,并返回一个 QByteArray。
readData(char *data, qint64 maxSize):这是一个虚拟函数,用于从底层的设备读取数据
bool disconnect:断开连接

3、服务器和客户端的搭建流程

(1)服务器端(4步)

第一步:创建QTcpServer对象,用于监听和接受客户端连接。

第二步:调用listen(ip, port)启动服务器,绑定IP和端口,开始监听。

第三步:关联newConnection信号,槽函数中调用nextPendingConnection()获取与客户端通信的QTcpSocket对象。

第四步:关联QTcpSocket的readyRead信号接收客户端数据,调用write()发送数据。

(2)客户端(3步)

第一步:创建QTcpSocket对象。

第二步:调用connectToHost(ip, port)连接服务器。

第三步:关联readyRead信号接收服务器数据,调用write()发送数据,使用disconnectFromHost()断开连接。

(3)核心信号列表

①newConnection:QTcpServer发出,有新客户端连接时触发,槽函数中调用nextPendingConnection()获取QTcpSocket。

②readyRead:QTcpSocket发出,对方有数据到达时触发,槽函数中调用read()或readAll()读取数据。

③disconnected:QTcpSocket发出,连接断开时触发,用于清理资源。

④errorOccurred:QTcpSocket发出,网络出错时触发,用于错误处理。

4、分析TCP搭建流程的接口

listen():设置IP和端口号,具有绑定和监听的作用。

创建服务器之后,启动服务器设置IP和端口号:
    //创建QT tcp 服务器对象
    this->qtcp_service = new QTcpServer;
    //启动监听服务器,设置IP和端口号
    this->qtcp_service->listen(QHostAddress::Any,8888);
参数说明:
    QHostAddress::Any表示服务器的所有IP地址
    8888:表示服务器的端口号

newConnection:等待客户端连接,设置信号与槽。

当客户端发起连接请求时,QTcpServer对象会自动发射newConnection信号。
通过connect将该信号与自定义槽函数绑定,在槽函数中调用nextPendingConnection()即可获取客户端的QTcpSocket对象,完成连接建立。
后续收发数据通过该QTcpSocket对象进行。
// 头文件声明槽函数
private slots:
    void Get_Connect_Client();
// cpp中绑定
connect(qtcp_service, &QTcpServer::newConnection, this, &Widget::Get_Connect_Client);
// 槽函数实现
void Widget::Get_Connect_Client()
{
    QTcpSocket* clientSocket = qtcp_service->nextPendingConnection();
    // 关联readyRead信号准备接收数据
    connect(clientSocket, &QTcpSocket::readyRead, this, &Widget::Read_Data);
}

5、如何发送结构体

在 Qt 中使用 QTcpSocket发送结构体数据需要一些额外的步骤,
因为QTcpSocket只能发送和接收字节流(即QByteArray)。
因此,你需要将结构体序列化为字节流,然后在接收端进行反序列化。
1、定义结构体类型
    #pragma pack(push, 1) // 禁止结构体内存对齐,确保字节对齐为1
    struct MyData{
        QString food_name;
        QString food_kind;
        double  food_price;
    };
#pragma pack(pop)
2、结构体序列化(发送时)
    QByteArray byteArray;
    QDataStream out(&byteArray, QIODevice::WriteOnly); //提前设置序列化输出入口
    out.setVersion(QDataStream::Qt_DefaultCompiledVersion);
    out << data.id << data.value;  //进行序列化输出
3、结构体反序列化(接收时)
    MyData data;
    QDataStream in(&byteArray, QIODevice::ReadOnly);
    in.setVersion(QDataStream::Qt_DefaultCompiledVersion);
    in >> data.id >> data.value;

四、HTTP通信

1、QT中http通信流程以及常用网络类

1)Qurl类:用于存储和解析URL(统一资源定位符)地址,是请求目标的地址容器。

2)QNetworkRequest类:封装请求的详细信息,包括URL、请求头(Content-Type等)、HTTP方法等,用于打包请求条件。

3)QNetworkAccessManager类:网络请求的核心控制类,负责发送请求和接收响应,支持HTTP、HTTPS、FTP等协议,与QNetworkRequest配合使用,通过 QNetworkReply 获取响应。

4)QNetworkReply类:承载服务器的响应数据,通过readAll()读取返回内容,通过error()检查错误状态,使用finished或readyRead信号触发读取操作。

2、Qt网络编程HTTP通信基本原理

3、常见的API集合

图片API合集&&Python批量下载API图片 - Kannic - 博客园

4、示例代码:获取网络图片

1) 实例化需要的网络类对象
    // 实例化封装http请求的类对象
    this->http_obj = new QNetworkRequest;  
    // 实例化发送http请求的类对象
    this->get_request = new QNetworkAccessManager; 
    // 收到响应报文的设置信号与槽
    QObject::connect(this->get_request,&QNetworkAccessManager::finished,
                    this,&Widget::Get_Pic_Data);
2)使用Qurl存放url链接,发送http请求报文   
    // 准备url
    QUrl url(ui->lineEdit->text()); 
    // 准备请求报文
    this->http_obj->setUrl(url); 
    this->get_request->get(*this->http_obj);
3) 接收网络图片数据的槽函数
    QByteArray array = reply->readAll();
    //图片类
    QPixmap pixmap; 
    pixmap.loadFromData(array);
    ui->label->setPixmap(pixmap);

五、AI机器人聊天软件

mainwindow.ui

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QMovie>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QLabel>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_send_btn_clicked();
    void replyFinished(QNetworkReply *reply);

private:
    Ui::MainWindow *ui;
    QNetworkAccessManager *manager;
    QMovie *movie;
    QLabel *windows_label;
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // 通过样式表设置透明度
    ui->robot_text->setStyleSheet("background-color: rgba(255, 255, 255, 128);");
    // 通过样式表设置透明度
    ui->user_line->setStyleSheet("background-color: rgba(255, 255, 255, 128);");
    // 设置窗口背景颜色
    QPalette windows_p;//创建调色板
    windows_p.setBrush(QPalette::Window,QPixmap(":/resource/AI.jpg"));
    this->setPalette(windows_p);//把图片通过调色板设置到背景里面
    // 设置标签和文本框的字体颜色
    QPalette label_p = this->palette();
    label_p.setColor(QPalette::WindowText,Qt::red);
    ui->robot_label->setPalette(label_p);
    ui->user_label->setPalette(label_p);
    QPalette text_p = this->palette();
    text_p.setColor(QPalette::Text,Qt::white);
    ui->robot_text->setPalette(text_p);
    ui->user_line->setPalette(text_p);
    // 设置标签的字体
    QFont font;
    font.setFamily("黑体");
    font.setPixelSize(15);
    ui->robot_label->setFont(font);
    ui->user_label->setFont(font);
    // 创建动画的对象
    movie = new QMovie(":/resource/display.gif");
    // 创建窗口标签 50x50
    windows_label = new QLabel(this);
    //创建网络访问类
    this->manager = new QNetworkAccessManager(this);
    connect(manager, &QNetworkAccessManager::finished,this,&MainWindow::replyFinished);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_send_btn_clicked()
{
    // 设置动画
    movie->setScaledSize(QSize(50,50));// 设置动画的大小
    windows_label->move(10,300); // 设置标签的位置
    windows_label->setFixedSize(50,50); // 设置标签和窗口一样大
    windows_label->setMovie(movie); // 把动画添加到标签里面
    movie->start(); // 播放动画
    QString problem = ui->user_line->text(); //获取文本框中的你输入的文字数据
    qDebug()<<problem;
    if(problem.contains("天气"))//判断是否是天气问题
    {
        problem.remove("天气");
        //这里是保存提供的获取天气的http api
        QString HTTP = QString("http://api.seniverse.com/v3/weather/now.json?key=SbslwZ6X47ih3u-bX&location=%1&language=zh-Hans&unit=c").arg(problem);
        this->manager->get(QNetworkRequest(QUrl(HTTP))); //发送获取天气的http网络请求对应的http
    }
    else //不是天气问题则是聊天
    {
       //这里是保存提供的聊天的http api
        QString HTTP = QString("http://api.qingyunke.com/api.php?key=free&appid=0&msg=%1").arg(problem);
        manager->get(QNetworkRequest(QUrl(HTTP)));//发送获取聊天的http网络请求到对应的http
    }
}
void MainWindow::replyFinished(QNetworkReply *reply)
{
    QString msg = reply->readAll(); //获取网络数据并且存放到msg里面,但是我们并不知道msg里面的网络数据是天气还是聊天
    qDebug() << msg; //显示接收到的网络数据
    if(msg.contains("now"))//检擦是否网络数据是否包含 now,如果有则是回答天气的
    {
        int loc = msg.indexOf("text");//查找text在数据中的位置
        msg.remove(0,loc); //从数据的开头删去数据删掉loc的位置
        msg.replace("text","天气");//把text替换成天气
        msg.replace("temperature","温度"); //把temperature替换成温度
        msg.replace("last_update","更新时间");//把last_update替换成更新时间
        msg.remove("}"); //删除}
        msg.remove("]"); //删除]
    }
    else //如果没有包含now 不是天气则获取的是聊天
    {
        msg.remove("{\"result\":0,\"content\":\""); // 移除网络数据中的{\"result\":0,\"content\":\"
        msg.remove("\"}");// 移除网络数据中的\"}
    }
    msg.push_front("◉");//在网络数据头部添加◉
    ui->robot_text->append(msg);//把机器人回复的网络数据添加到机器人文本框中显示
    return;
}

接入DeepSeek的API……

六、QT线程

1、QThread类

QThread 是 Qt 中用于创建和管理线程的类,每个 QThread 实例代表一条独立的线程。它提供了便捷的多线程运行机制,允许多个任务并发执行。但需要注意的是,QThread 不负责管理线程中使用的资源(如堆上分配的内存、打开的文件等),开发者必须在线程退出前主动释放这些资源,以避免内存泄漏或资源耗尽的问题。

2、QThread的常用成员函数

QThread(QObject* parent = nullptr):默认构造函数,创建一个没有指定父对象的线程对象。
QThread::create():静态函数,用于创建并启动一个新线程。
start(QThread::Priority priority = InheritPriority):启动线程,使其开始执行任务(指定代码段并执行)。
wait():阻塞当前线程,直到线程完成执行。
quit():请求线程退出,并等待它完成。它会导致线程的事件循环退出,然后调用wait()方法进行等待。该方法不涉及发送请求线程终止的信号。
exit(int returnCode = 0):终止线程的执行,直接退出线程。可以指定一个返回码,默认为0。
isFinished():检查线程是否已经完成执行。
isRunning():检查线程是否正在运行中。
currentThread():静态函数,返回当前线程的QThread对象。
sleep(unsigned long secs):静态函数,使当前线程休眠指定的秒数。
msleep(unsigned long msecs):静态函数,使当前线程休眠指定的毫秒数。
usleep(unsigned long usecs):静态函数,使当前线程休眠指定的微秒数。
此外,QThread类还提供了一些信号,用于与线程相关的通知和状态变化,例如:
started():当线程开始执行时发出的信号。
finished():当线程执行完成时发出的信号。
terminated():当线程被强制终止时发出的信号。

在Qt中,QT += core gui 是.pro文件中的一行声明,用于指定项目所需的Qt模块。具体含义如下:
QT += core 表示项目需要 Qt Core 模块,该模块包含了许多基本的非 GUI 功能,例如数据结构、文件操作、事件、线程处理等。
QT += gui 表示项目需要 Qt GUI 模块,该模块包含了创建图形用户界面所需的类和函数。

3、start启动线程函数的默认参数

enum Priority {
        IdlePriority,: 空闲优先级,表示线程在空闲时才会运行。
        LowestPriority,: 最低优先级。
        LowPriority,: 低优先级。
        NormalPriority,: 普通优先级,是所有线程的默认优先级。
        HighPriority,: 高优先级。
        HighestPriority,: 最高优先级。
        TimeCriticalPriority,: 时间关键优先级,表示线程必须立即运行以完成关键任务。比HighestPriority高,也是最高的
        InheritPriority: 继承优先级,表示线程的优先级从其父线程继承。
    };

如果是第一次创建线程,则线程优先级继承主进程的优先级,为NormalPriority(默认优先级)

4、public Q_SLOTS公共槽函数

public Q_SLOTS:
    void start(Priority = InheritPriority); 启动线程
    void terminate();  强制终止线程
    void quit(); 请求线程退出

(1)公共槽函数的作用主要有两方面

①统一接口,提升代码质量:将线程的启动、退出、终止等控制操作封装为一致的接口,使代码结构清晰、易于阅读和维护。

②灵活连接,满足特定需求:这些槽函数虽然没有默认响应的信号,但可以与其他信号(如按钮的 clicked 信号)自由连接,例如将 start() 与按钮点击关联,实现便捷的线程启动控制。

(2)关于 quit() 退出机制

调用 quit() 会向线程发送退出请求,触发事件循环的退出,从而终止线程执行。需要注意的是,quit() 只是发送请求而非强制终止,线程实际退出的时间取决于内部事件循环及当前任务的执行情况——通常情况下,线程会在处理完当前任务后安全退出,并非立即发生。

5、使用QT线程的注意事项

1)在Qt GUI应用程序中,线程的运行状态通常由控件的信号(如按钮点击)触发控制,通过信号与槽机制实现用户交互对线程的启动、暂停或停止等操作。

2)线程对象(QThread子类)和任务对象(QRunnable或QObject子类)可以在窗口类中作为成员变量进行定义和管理,便于窗口与线程之间的交互。

3)控件类(如QWidget、QPushButton等)不能作为线程的任务在线程中执行。因为Qt规定所有与GUI相关的操作(包括控件的创建、更新和渲染)必须在主线程中执行,无法将窗口类或控件对象转移到子线程的上下文中。若在子线程中操作控件,会导致程序崩溃或不可预知的错误。

6、线程的示例:进度条

(1)线程的作用

①防止UI阻塞

如果把耗时操作(比如循环100次的任务)放在主线程(UI线程)中执行,整个界面会卡住,无法响应用户操作。把耗时任务放到子线程中,主线程可以继续处理UI事件(点击按钮、刷新界面等)。

②提高程序响应性

用户点击【开始】后,界面不会卡死,还能点击【暂停】和【停止】按钮。

③充分利用多核CPU

现代CPU都是多核的,使用多线程可以并行处理任务,提高效率。

(2)示例代码

mainwindow.ui:

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDebug>
#include <QThread>
#include <QTimer>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

// ============================================================
// 任务类:在子线程中执行具体任务
// ============================================================
class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = nullptr);
    bool get_isPaused();

public slots:
    // 启动任务
    void start_work();
    // 停止任务
    void stop_work();
    // 暂停/恢复切换
    void toggle_pause();

signals:
    // 向主线程发送信号,更新UI
    void progress_updated(int value);
    void status_updated(const QString &status);
    void work_finished();

private:
    bool m_isRunning;      // 是否运行
    bool m_isPaused;       // 是否暂停
};

// ============================================================
// 主窗口类
// ============================================================
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:
    void start();

private slots:
    void on_start_btn_clicked();
    void on_stop_btn_clicked();
    void on_pause_btn_clicked();
    void update_progress(int value);
    void update_status(const QString &status);
    void finished_work();

private:
    Ui::MainWindow *ui;
    QThread m_thread;   // 子线程对象
    Worker *m_worker;          // 任务对象
};
#endif // MAINWINDOW_H

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // 1. 创建任务对象
    m_worker = new Worker;
    // 2. 将任务对象移动到子线程
    m_worker->moveToThread(&m_thread);
    // 3. 连接信号与槽
    // Worker信号 -> MainWindow槽(UI更新)
    connect(m_worker, &Worker::progress_updated, this, &MainWindow::update_progress);
    connect(m_worker, &Worker::status_updated, this, &MainWindow::update_status);
    connect(m_worker, &Worker::work_finished, this, &MainWindow::finished_work);
//    // 线程信号 -> Worker槽(启动工作) started信号只会触发一次
//    connect(&m_thread, &QThread::started, m_worker, &Worker::start_work);
    connect(this, &MainWindow::start, m_worker, &Worker::start_work);
    // 启动子线程
    m_thread.start();
    // 4. 设置初始状态
    ui->progress_bar->setValue(0);
    ui->stop_btn->setEnabled(false);
    ui->pause_btn->setEnabled(false);
}

MainWindow::~MainWindow()
{
    // 停止子线程
    m_thread.quit();
    m_thread.wait();
    delete ui;
}

// 开始的按钮
void MainWindow::on_start_btn_clicked()
{
    ui->stop_btn->setEnabled(true);
    ui->pause_btn->setEnabled(true);
    // 发射信号触发子线程任务
    emit start();
}

// 停止的按钮
void MainWindow::on_stop_btn_clicked()
{
    m_worker->stop_work();
    if(ui->pause_btn->text() == "继续")
        ui->pause_btn->setText("暂停");
}

// 暂停的按钮
void MainWindow::on_pause_btn_clicked()
{
    m_worker->toggle_pause();
    if(m_worker->get_isPaused())
        ui->pause_btn->setText("继续");
    else
        ui->pause_btn->setText("暂停");
}

// ui界面更新进度条
void MainWindow::update_progress(int value)
{
    ui->progress_bar->setValue(value);
}

// ui界面更新状态标签
void MainWindow::update_status(const QString &status)
{
    ui->status_label->setText(status);
}

// ui界面任务完成处理
void MainWindow::finished_work()
{
    ui->stop_btn->setEnabled(false);
    ui->pause_btn->setEnabled(false);
}

// 线程任务类
Worker::Worker(QObject *parent):QObject(parent)
{
    m_isPaused = false;
    m_isRunning = false;
}

// 获取是否暂停的状态
bool Worker::get_isPaused()
{
    return m_isPaused;
}

// 线程任务的开始工作函数
void Worker::start_work()
{
    m_isPaused = false;
    m_isRunning = true;
    emit status_updated("运行中...");
    // 模拟耗时任务
    for(int i=0; i<=100 && m_isRunning; i++)
    {
        // 检查是否暂停
        while (m_isPaused && m_isRunning)
            QThread::msleep(100);  // 等待恢复
        if (!m_isRunning) break;
        // 发射进度更新信号
        emit progress_updated(i);
        // 模拟工作(例如:数据处理、文件读写、网络请求等)
        QThread::msleep(50);  // 模拟耗时操作
        // 每10%更新一次状态
        if (i % 10 == 0 && i > 0)
            emit status_updated(QString("处理中... %1%").arg(i));
    }
    // 任务结束
    if (m_isRunning)
    {
        emit progress_updated(100);
        emit status_updated("任务完成!");
        emit work_finished();
    }
    m_isRunning = false;
}

// 线程任务的停止工作函数
void Worker::stop_work()
{
    m_isRunning = false;
    m_isPaused = false;
    emit progress_updated(0);
    emit status_updated("已停止");
}

// 线程任务的暂停工作函数
void Worker::toggle_pause()
{
    if (m_isRunning)
    {
        m_isPaused = !m_isPaused;
        emit status_updated(m_isPaused ? "已暂停" : "运行中...");
    }
}

(3)示例讲解

🎯主线程(UI线程)

职责 :负责所有与界面相关的操作

工作内容具体例子
处理用户交互监听按钮点击、键盘输入、鼠标操作
更新UI显示更新进度条、状态标签、按钮文字
管理窗口生命周期创建窗口、处理关闭事件
调度任务通过信号触发子线程工作

主线程做的事情 :

- 点击按钮时发射 start 信号

- 接收子线程的 progress_updated 信号,更新进度条

🔧子线程(Worker线程)
职责 :执行耗时的后台任务,避免阻塞UI

工作内容具体例子
执行耗时操作数据处理、文件读写、网络请求、循环计算
发送状态通知通过信号向主线程报告进度和状态
响应控制命令接收暂停、停止指令并执行

子线程做的事情 :

- 执行循环任务(从0到100)

- 检查暂停状态,等待恢复

- 发射 progress_updated 信号报告进度

- 发射 status_updated 信号报告状态

- 任务完成时发射 work_finished 信号

⚠️ 重要规则
禁止在子线程中直接操作UI!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值