目录
[setMouseTracking 鼠标追踪设置](#setMouseTracking 鼠标追踪设置)
键盘事件[QShortCut 底层实现原理](#QShortCut 底层实现原理)
定时器事件[定时器事件与 QTimer 关系](#定时器事件与 QTimer 关系)
[startTimer 函数](#startTimer 函数)
[killTimer 函数](#killTimer 函数)
[timerId 变量](#timerId 变量)
[timerEvent 函数](#timerEvent 函数)
窗口移动和大小改变事件mainwindow.cpp(文件打开、读取、写入、关闭操作)
[widget.cpp(QFileInfo 获取文件信息)](#widget.cpp(QFileInfo 获取文件信息))
[QFile 类](#QFile 类)
[QPlainTextEdit 类](#QPlainTextEdit 类)
[QFileInfo 类](#QFileInfo 类)
多线程与互斥锁[QThread 类](#QThread 类)
[QMutex 互斥锁](#QMutex 互斥锁)
[Qt 与 Linux 创建线程的区别](#Qt 与 Linux 创建线程的区别)
[线程入口函数 run 重写机制](#线程入口函数 run 重写机制)
[QWaitCondition 条件等待](#QWaitCondition 条件等待)
[QSemaphore 信号量](#QSemaphore 信号量)
UDP[QUdpSocket 类](#QUdpSocket 类)
[readyRead 信号](#readyRead 信号)
[服务端与客户端 QUdpSocket 作用](#服务端与客户端 QUdpSocket 作用)
[QNetworkDatagram 类](#QNetworkDatagram 类)
[QUdpSocket 核心成员函数](#QUdpSocket 核心成员函数)
[Qt UDP 函数与 Linux UDP 函数对应关系](#Qt UDP 函数与 Linux UDP 函数对应关系)
TCP[QTcpServer 类](#QTcpServer 类)
[QTcpSocket 类](#QTcpSocket 类)
HTTP[QNetworkAccessManager 类](#QNetworkAccessManager 类)
[QNetworkRequest 类](#QNetworkRequest 类)
[QNetworkReply 类](#QNetworkReply 类)
鼠标事件
widget.cpp(重写鼠标进入事件与离开事件)
------------------------------ mypushbutton.h ------------------------------
#ifndef MYPUSHBUTTON_H
#define MYPUSHBUTTON_H
#include <QWidget>
// 引入QPushButton基类,自定义按钮需要继承它
#include <QPushButton>
// 自定义按钮类 MyPushButton
// 继承自 Qt 标准按钮 QPushButton,实现功能扩展
// Q_OBJECT 宏:必须添加!支持Qt的信号槽、事件系统
class MyPushButton : public QPushButton
{
Q_OBJECT
public:
// 构造函数
// parent:父窗口指针,由Qt自动管理内存
explicit MyPushButton(QWidget* parent = nullptr);
protected:
// 重写【鼠标进入控件】事件
// 当鼠标移动到按钮上时,自动触发该函数
void enterEvent(QEvent* event) override;
// 重写【鼠标离开控件】事件
// 当鼠标从按钮上移开时,自动触发该函数
void leaveEvent(QEvent* event) override;
};
#endif // MYPUSHBUTTON_H
------------------------------ mypushbutton.cpp ------------------------------
#include "mypushbutton.h"
// 调试输出头文件,用于打印日志
#include <QDebug>
// 构造函数实现
// 初始化列表:调用父类 QPushButton 的构造函数,绑定父窗口
MyPushButton::MyPushButton(QWidget* parent) : QPushButton(parent)
{
// 构造函数:可在这里初始化按钮样式、文字等
}
// 鼠标进入按钮事件重写
void MyPushButton::enterEvent(QEvent *event)
{
// (void)event:忽略未使用的事件参数,避免编译器警告
(void)event;
// 鼠标进入按钮时,控制台打印日志
qDebug() << "enterEvent:鼠标进入了按钮";
}
// 鼠标离开按钮事件重写
void MyPushButton::leaveEvent(QEvent *event)
{
// 忽略未使用的事件参数
(void)event;
// 鼠标离开按钮时,控制台打印日志
qDebug() << "leaveEvent:鼠标离开了按钮";
}
------------------------------ widgt.cpp ------------------------------
#include "widget.h"
#include "ui_widget.h"
// 引入自定义按钮头文件
#include "mypushbutton.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
// 初始化UI界面
ui->setupUi(this);
// ==================== 创建自定义按钮对象 ====================
// 动态创建 MyPushButton 自定义按钮
// 指定父窗口为 this(当前Widget),Qt自动管理内存
MyPushButton* button = new MyPushButton(this);
// 设置按钮在窗口中的位置:x=100,y=100
button->move(100, 100);
// 设置按钮显示的文字
button->setText("按钮");
// 功能:鼠标移入/移出按钮时,控制台会自动打印日志
}
// 析构函数:释放UI对象,自定义按钮由Qt自动释放
Widget::~Widget()
{
delete ui;
}
widget.cpp(重写获取鼠标点击与释放事件)
------------------------------ label.h ------------------------------
#ifndef LABEL_H
#define LABEL_H
#include <QWidget>
// 继承QLabel标签控件
#include <QLabel>
// 鼠标事件需要的头文件(声明用)
#include <QMouseEvent>
// 自定义Label类,继承Qt标准标签控件 QLabel
// Q_OBJECT:Qt核心宏,必须添加,支持事件系统、信号槽
class Label : public QLabel
{
// Qt 元对象系统必需宏
Q_OBJECT
public:
// 构造函数,parent为父窗口,用于Qt自动管理内存
explicit Label(QWidget* parent = nullptr);
// 重写鼠标事件(protected权限)
protected:
// 鼠标按下事件
void mousePressEvent(QMouseEvent *ev) override;
// 鼠标释放事件
void mouseReleaseEvent(QMouseEvent *ev) override;
// 鼠标双击事件
void mouseDoubleClickEvent(QMouseEvent *ev) override;
};
#endif // LABEL_H
------------------------------ label.cpp ------------------------------
#include "label.h"
// 鼠标事件类头文件(实现用)
#include <QMouseEvent>
// 调试输出,打印日志
#include <QtDebug>
// 构造函数:调用父类QLabel的构造函数,绑定父窗口
Label::Label(QWidget* parent) : QLabel(parent)
{
// 可在此处初始化标签样式、文字等
}
// 重写:鼠标按下事件
// ev:鼠标事件参数,包含按键、坐标等信息
void Label::mousePressEvent(QMouseEvent *ev)
{
// 判断按下的鼠标按键
if(ev->button() == Qt::LeftButton)
{
qDebug() << "鼠标左键按下";
}
else if(ev->button() == Qt::RightButton)
{
qDebug() << "鼠标右键按下";
}
// 打印【局部坐标】:相对于标签控件左上角的坐标 (x,y)
qDebug() << "标签内局部坐标:" << ev->x() << ", " << ev->y();
// 打印【全局坐标】:相对于整个屏幕左上角的坐标
qDebug() << "屏幕全局坐标:" << ev->globalX() << ", " << ev->globalY();
}
// 重写:鼠标释放事件
void Label::mouseReleaseEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
{
qDebug() << "鼠标左键释放";
}
else if(ev->button() == Qt::RightButton)
{
qDebug() << "鼠标右键释放";
}
}
// 重写:鼠标双击事件
void Label::mouseDoubleClickEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
{
qDebug() << "双击左键";
}
else if(ev->button() == Qt::RightButton)
{
qDebug() << "双击右键";
}
}
------------------------------ widgt.cpp ------------------------------
#include "widget.h"
#include "ui_widget.h"
// 引入自定义标签头文件
#include "label.h"
// 主窗口构造函数
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
// 初始化UI界面
ui->setupUi(this);
// ==================== 创建自定义Label控件 ====================
// 动态创建自定义标签,父窗口为当前Widget
Label* label = new Label(this);
// 设置标签位置和大小
// setGeometry(x, y, width, height)
// 左上角坐标(100,100),宽300,高300
label->setGeometry(100, 100, 300, 300);
// 设置标签边框样式:矩形边框(方便可视化看到标签范围)
label->setFrameShape(QLabel::Box);
}
// 析构函数:释放UI对象,自定义标签由Qt自动回收内存
Widget::~Widget()
{
delete ui;
}
widget.cpp(重写鼠标移动事件与滚轮滚动事件)
#include "widget.h"
#include "ui_widget.h"
// Qt调试输出,打印坐标、滚轮数值
#include <QtDebug>
// 鼠标事件头文件(鼠标移动、按下、释放)
#include <QMouseEvent>
// 滚轮事件头文件
#include <QWheelEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 开启【鼠标追踪】功能
// 默认false:只有鼠标按下时,才会触发 mouseMoveEvent 鼠标移动事件
// 设置为true:鼠标在窗口上**移动就触发**,无需按下鼠标
this->setMouseTracking(true);
}
Widget::~Widget()
{
// 释放UI对象,Qt自动管理内存
delete ui;
}
// 重写:鼠标移动事件
// 鼠标在窗口内移动时,自动触发该函数
void Widget::mouseMoveEvent(QMouseEvent *event)
{
// 打印鼠标在**窗口局部坐标系**中的坐标 (x, y)
// 坐标原点:窗口左上角
qDebug() << "鼠标坐标:" << event->x() << ", " << event->y();
}
// 重写:鼠标滚轮事件
// 鼠标滚轮滚动时,自动触发该函数
void Widget::wheelEvent(QWheelEvent *event)
{
// event->delta():获取滚轮滚动的步长值
// 正数 → 滚轮**向上**滚动
// 负数 → 滚轮**向下**滚动
// 标准值:±120(每滚动一格的数值)
qDebug() << "滚轮滚动量:" << event->delta();
}
事件与信号槽的关系
事件与信号槽的层级关联 :信号槽是事件的上层封装 ,事件是信号槽的底层实现机制 。用户操作触发事件 ,事件处理完成后,控件发出对应信号 ,通过connect完成信号与槽函数的绑定。
信号槽执行流程:用户操作控件→触发底层事件→事件处理完成→控件发出信号→槽函数执行逻辑。
事件执行流程:用户操作→触发对应事件虚函数→执行自定义事件处理逻辑。
事件的实现原理
多态与虚函数重写 :事件通过重写父类虚函数 实现,依托 C++多态特性完成。Qt 控件基类内置事件相关虚函数,子类继承基类后重写对应虚函数,程序运行时调用子类重写后的函数实现。
自定义控件事件实现步骤:
-
创建子类,继承 Qt 标准控件类;
-
添加
Q_OBJECT宏,支持 Qt 事件与信号槽机制; -
在
protected权限下声明重写的事件函数,添加override关键字; -
实现事件函数,编写自定义处理逻辑;
-
主窗口中创建自定义控件对象,完成事件绑定。
class MyPushButton : public QPushButton
{
Q_OBJECT
protected:
void enterEvent(QEvent* event) override;
};
鼠标相关事件函数
enterEvent 鼠标进入事件:鼠标移入控件区域时自动触发,处理鼠标进入操作。
void MyPushButton::enterEvent(QEvent *event)
{
(void)event;
qDebug() << "enterEvent:鼠标进入了按钮";
}
leaveEvent 鼠标离开事件:鼠标移出控件区域时自动触发,处理鼠标离开操作。
void MyPushButton::leaveEvent(QEvent *event)
{
(void)event;
qDebug() << "leaveEvent:鼠标离开了按钮";
}
mousePressEvent 鼠标按下事件 :鼠标按键按下时触发,通过QMouseEvent::button()判断按键类型。
关键参数 :ev->x()、ev->y()为控件局部坐标 ,ev->globalX()、ev->globalY()为屏幕全局坐标。
void Label::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
qDebug() << "鼠标左键按下";
qDebug() << "标签内局部坐标:" << ev->x() << ", " << ev->y();
}
mouseReleaseEvent 鼠标释放事件:鼠标按键松开时触发,配合按下事件完成点击逻辑处理。
mouseDoubleClickEvent 鼠标双击事件:鼠标按键双击时触发,可区分左右键双击操作。
mouseMoveEvent 鼠标移动事件:鼠标在控件或窗口内移动时触发,默认需按下按键才会响应。
wheelEvent 滚轮滚动事件 :鼠标滚轮滚动时触发,event->delta()获取滚动数值,正数代表向上滚动 ,负数代表向下滚动。
void Widget::wheelEvent(QWheelEvent *event)
{
qDebug() << "滚轮滚动量:" << event->delta();
}
setMouseTracking 鼠标追踪设置
setMouseTracking 核心作用 :设置窗口或控件的鼠标追踪 状态,控制mouseMoveEvent的触发条件。
参数规则:
-
false(默认值):仅按下鼠标按键时,mouseMoveEvent才会触发; -
true:鼠标在窗口内移动即触发mouseMoveEvent,无需按下任何按键。this->setMouseTracking(true);
键盘事件
widget.cpp(重写键盘事件)
#include "widget.h"
#include "ui_widget.h"
// QKeyEvent:键盘事件类,处理按键按下/释放
#include <QKeyEvent>
// QDebug:调试输出,打印按键信息
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
// 初始化UI界面
ui->setupUi(this);
}
Widget::~Widget()
{
// 释放UI对象,Qt自动管理内存
delete ui;
}
// 重写【键盘按下事件】
// 当窗口获得焦点时,按下任意键盘按键都会触发该函数
// event:键盘事件参数,包含按键编码、修饰键(Ctrl/Shift/Alt)等信息
void Widget::keyPressEvent(QKeyEvent *event)
{
// 1. 打印当前按下按键的【键值编码】
// 每个按键对应一个唯一的数字(如A键是65)
qDebug() << "按键编码:" << event->key();
// 2. 判断是否按下了【普通A键】
// Qt::Key_A:Qt定义的A键常量
if(event->key() == Qt::Key_A)
{
qDebug() << "按下了 a 键";
}
// 3. 判断是否按下了【组合键 Ctrl + A】
// event->modifiers():获取修饰键(Ctrl/Shift/Alt)
// Qt::ControlModifier:Ctrl键修饰符常量
if(event->key() == Qt::Key_A && event->modifiers() == Qt::ControlModifier)
{
qDebug() << "按下了 Ctrl + a 键";
}
}
键盘事件概述
键盘事件定义 :Qt 中响应键盘按键操作的底层事件机制 ,窗口获得焦点时,按下或释放键盘按键会触发对应事件函数,完成键盘交互的自定义处理。
核心作用:实现单按键、组合键的操作监听与逻辑响应,是 Qt 键盘交互的基础实现方式。
键盘事件核心函数与类
keyPressEvent 函数 :Qt 提供的键盘按下事件虚函数,窗口获取焦点后,按下任意键盘按键会自动触发该函数,为键盘按下操作的核心处理入口。
// 重写键盘按下事件函数
void Widget::keyPressEvent(QKeyEvent *event)
{
// 自定义键盘按键处理逻辑
}
QKeyEvent 类 :Qt 专属的键盘事件参数封装类,存储按键编码、修饰键、按键状态等全部键盘操作信息,作为参数传递给键盘事件函数。
按键参数获取方法
event->key() :获取当前按下按键的键值编码,返回整型数值,每个按键对应 Qt 定义的唯一常量,用于精准判断按下的具体按键。
// 打印按键编码,判断A键
qDebug() << "按键编码:" << event->key();
if(event->key() == Qt::Key_A)
{
qDebug() << "按下了 a 键";
}
event->modifiers() :获取键盘操作的修饰键状态 ,用于检测 Ctrl、Shift、Alt 等辅助按键是否按下,是实现组合键功能的核心方法。
修饰键常量说明
Qt::ControlModifier :Qt 定义的Ctrl 键修饰符常量,对应键盘上的 Ctrl 按键,与 event->modifiers () 配合使用,完成 Ctrl 组合键的判断。
// 判断Ctrl+A组合键
if(event->key() == Qt::Key_A && event->modifiers() == Qt::ControlModifier)
{
qDebug() << "按下了 Ctrl + a 键";
}
QShortCut 底层实现原理
QShortCut 定位 :Qt 提供的快捷键封装组件 ,用于快速实现键盘快捷键功能。底层逻辑 :QShortCut 底层基于键盘事件 封装实现,内部通过解析 keyPressEvent 事件、调用 QKeyEvent 类参数、判断按键与修饰键状态,完成快捷键的触发与响应,属于键盘事件的上层封装组件。
定时器事件
widget.cpp(重写定时器事件)
#include "widget.h"
#include "ui_widget.h"
// 构造函数:初始化窗口与定时器
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 给 LCD 数字控件设置初始显示值:10
ui->lcdNumber->display(10);
// ==================== 开启定时器 ====================
// startTimer(毫秒数):启动一个定时器,每隔指定时间自动触发 timerEvent 事件
// 参数 1000 = 1秒,即每隔1秒触发一次定时器事件
// 返回值:定时器的唯一 ID(timerId),用于区分多个定时器
timerId = this->startTimer(1000);
}
// 析构函数:释放 UI 资源
Widget::~Widget()
{
delete ui;
}
// ==================== 重写定时器事件函数 ====================
// 定时器触发时,自动调用该函数
void Widget::timerEvent(QTimerEvent *event)
{
// 校验:只处理当前窗口的定时器(防止多个定时器冲突)
// 如果触发的定时器ID 不等于 我们启动的定时器ID,直接返回不处理
if(event->timerId() != this->timerId)
return;
// 获取 LCD 控件当前显示的整数值
int value = ui->lcdNumber->intValue();
// 判断:如果数值 ≤ 0,停止定时器,结束倒计时
if(value <= 0)
{
// killTimer(定时器ID):关闭并销毁指定的定时器
this->killTimer(this->timerId);
return;
}
// 数值自减 1,并更新显示到 LCD 控件(实现每秒减1的倒计时效果)
ui->lcdNumber->display(--value);
}
定时器事件与 QTimer 关系
QTimer 是 Qt 提供的高级定时器类 ,底层基于timerEvent 定时器事件封装实现,简化定时器的使用流程。
startTimer 函数
startTimer 用于启动定时器,参数为毫秒单位 的时间间隔,函数返回值为定时器唯一 ID。
// 启动定时器,每隔1000毫秒触发一次timerEvent
timerId = this->startTimer(1000);
killTimer 函数
killTimer 用于关闭指定 ID 的定时器,停止定时器事件触发,释放定时器资源。
// 关闭指定ID的定时器
this->killTimer(this->timerId);
timerId 变量
timerId 存储startTimer 返回的定时器唯一标识 ,用于区分多个定时器,在timerEvent 中校验触发来源。
// 校验定时器ID,确保处理指定定时器
if(event->timerId() != this->timerId)
return;
timerEvent 函数
timerEvent 是定时器触发的事件处理函数 ,重写该函数实现定时逻辑,通过QTimerEvent 参数获取触发的定时器 ID。
// 重写定时器事件
void Widget::timerEvent(QTimerEvent *event)
{
// 定时执行的逻辑代码
}
窗口移动和大小改变事件
widget.cpp(重写窗口移动事件与窗口大小改变事件)
#include "widget.h"
#include "ui_widget.h"
// 调试输出头文件(打印窗口位置、大小)
#include <QDebug>
// 窗口移动/大小改变事件头文件(Qt自动隐式包含,显式写出更规范)
#include <QMoveEvent>
#include <QResizeEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
// 初始化UI界面
ui->setupUi(this);
}
Widget::~Widget()
{
// 释放UI对象,Qt自动管理内存
delete ui;
}
// 重写:窗口移动事件
// 当**拖动窗口改变位置**时,自动触发该函数
void Widget::moveEvent(QMoveEvent *event)
{
// event->pos():获取窗口**左上角**移动后的**新坐标**
// 坐标格式:(x, y),相对于屏幕左上角
qDebug() << "窗口新位置:" << event->pos();
}
// 重写:窗口大小改变事件
// 当**拖动窗口边缘缩放大小**时,自动触发该函数
void Widget::resizeEvent(QResizeEvent *event)
{
// event->size():获取窗口缩放后的**新尺寸**
// 尺寸格式:(宽度, 高度)
qDebug() << "窗口新尺寸:" << event->size();
}
窗口移动事件
moveEvent:窗口位置发生改变时自动触发的事件函数,拖动窗口、代码设置窗口位置均会触发该函数。
void Widget::moveEvent(QMoveEvent *event)
{
qDebug() << "窗口新位置:" << event->pos();
}
QMoveEvent :封装窗口移动相关信息的事件类,event->pos() 获取窗口左上角的新坐标。
窗口大小改变事件
resizeEvent:窗口尺寸发生改变时自动触发的事件函数,拖动窗口边框、代码修改窗口大小均会触发该函数。
void Widget::resizeEvent(QResizeEvent *event)
{
qDebug() << "窗口新尺寸:" << event->size();
}
QResizeEvent :封装窗口尺寸变化相关信息的事件类,event->size() 获取窗口缩放后的新尺寸。
文件操作
mainwindow.cpp(文件打开、读取、写入、关闭操作)
// 需补充的头文件(代码中使用了对应组件,必须包含)
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMenuBar> // 菜单栏
#include <QMenu> // 菜单
#include <QAction> // 菜单项
#include <QPlainTextEdit> // 纯文本编辑框
#include <QFont> // 字体
#include <QFileDialog> // 文件对话框
#include <QStatusBar> // 状态栏
#include <QFile> // 文件读写
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 设置窗口标题(优化:增强用户体验)
this->setWindowTitle("简易记事本");
// ==================== 1. 创建并设置菜单栏 ====================
// 获取主窗口自带的菜单栏(QMainWindow内置组件)
QMenuBar* menubar = this->menuBar();
// 将菜单栏绑定到主窗口(固定写法)
this->setMenuBar(menubar);
// ==================== 2. 添加顶级菜单 ====================
// 创建"文件"菜单
QMenu* menu = new QMenu("文件");
// 将菜单添加到菜单栏中
menubar->addMenu(menu);
// ==================== 3. 添加菜单项(动作) ====================
QAction* action1 = new QAction("打开"); // 打开文件菜单项
QAction* action2 = new QAction("保存"); // 保存文件菜单项
menu->addAction(action1); // 把菜单项添加到文件菜单
menu->addAction(action2);
// ==================== 4. 设置中央文本编辑控件 ====================
// QPlainTextEdit:纯文本编辑框(记事本核心控件)
edit = new QPlainTextEdit();
// 将编辑框设为主窗口的中央控件(铺满窗口)
this->setCentralWidget(edit);
// ==================== 5. 设置编辑框默认字体大小 ====================
QFont font;
font.setPixelSize(20); // 字体大小20像素
edit->setFont(font); // 应用字体到编辑框
// ==================== 6. 绑定菜单项信号槽 ====================
// 点击"打开" → 触发 handleAction1 函数
connect(action1, &QAction::triggered, this, &MainWindow::handleAction1);
// 点击"保存" → 触发 handleAction2 函数
connect(action2, &QAction::triggered, this, &MainWindow::handleAction2);
}
MainWindow::~MainWindow()
{
delete ui;
}
// ==================== 槽函数:处理【打开】菜单项 ====================
void MainWindow::handleAction1()
{
// 弹出文件打开对话框,获取选中文件的路径
QString path = QFileDialog::getOpenFileName(this);
// 获取状态栏,显示文件路径
QStatusBar* statusbar = this->statusBar();
statusbar->showMessage(path);
// 创建文件对象,绑定文件路径
QFile file(path);
// 以【只读】方式打开文件
bool ret = file.open(QFile::ReadOnly);
// 打开失败:状态栏提示错误
if(ret == false)
{
statusbar->showMessage(path + " 打开失败.");
return;
}
// 读取文件所有内容(字符串格式)
QString text = file.readAll();
// 将文件内容显示到文本编辑框
edit->setPlainText(text);
// 关闭文件
file.close();
}
// ==================== 槽函数:处理【保存】菜单项 ====================
void MainWindow::handleAction2()
{
// ❌ BUG:保存文件应该用 getSaveFileName,此处误用了打开文件对话框
QString path = QFileDialog::getOpenFileName(this);
// 获取状态栏,显示文件路径
QStatusBar* statusbar = this->statusBar();
statusbar->showMessage(path);
// 创建文件对象
QFile file(path);
// 以【只写】方式打开文件
bool ret = file.open(QFile::WriteOnly);
if(ret == false)
{
statusbar->showMessage(path + " 打开失败.");
return;
}
// 获取编辑框中的所有文本
const QString text = edit->toPlainText();
// 将文本转为UTF-8编码,写入文件
file.write(text.toUtf8());
// 关闭文件
file.close();
}
widget.cpp(QFileInfo 获取文件信息)
#include "widget.h"
#include "ui_widget.h"
// 文件对话框:弹出选择文件的窗口
#include <QFileDialog>
// 文件信息类:专门获取文件的名称、后缀、大小、路径等元信息
#include <QFileInfo>
// 调试输出:在控制台打印文件信息
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
// 初始化UI设计师创建的界面(包含一个pushButton按钮)
ui->setupUi(this);
}
Widget::~Widget()
{
// 释放UI对象资源
delete ui;
}
// 按钮点击槽函数:点击按钮后执行
void Widget::on_pushButton_clicked()
{
// 1. 弹出文件选择对话框,获取用户选中文件的**完整路径**
// 取消选择则返回空字符串
QString path = QFileDialog::getOpenFileName(this);
// 2. 创建文件信息对象,绑定选中的文件路径
// QFileInfo:Qt提供的专用类,快速读取文件/目录的所有元信息
QFileInfo fileInfo(path);
// 3. 打印文件的各类信息
qDebug() << "文件名(带后缀):" << fileInfo.fileName(); // 获取文件名(如:test.txt)
qDebug() << "文件后缀(扩展名):" << fileInfo.suffix(); // 获取文件后缀(如:txt)
qDebug() << "文件所在路径:" << fileInfo.path(); // 获取文件路径(不含文件名)
qDebug() << "文件大小(字节):" << fileInfo.size(); // 获取文件大小,单位:字节
qDebug() << "是否是文件:" << fileInfo.isFile(); // 判断是否为文件(是返回true)
qDebug() << "是否是目录:" << fileInfo.isDir(); // 判断是否为文件夹(是返回true)
}
QFile 类
核心定义 :Qt 提供的文件操作核心类 ,用于实现文件的打开、读取、写入、关闭 等底层 IO 操作,支持文本文件、二进制文件的读写。初始化方式:通过文件路径构造对象,绑定目标文件。
// 绑定文件路径,创建QFile对象
QFile file(path);
open 函数 :打开文件,参数指定打开模式 ,返回 bool 类型表示打开结果。常用打开模式:QFile::ReadOnly (只读)、QFile::WriteOnly(只写)。
// 以只读模式打开文件
bool ret = file.open(QFile::ReadOnly);
// 以只写模式打开文件
bool ret = file.open(QFile::WriteOnly);
readAll 函数 :读取文件全部内容,返回 QString 类型的文本数据。
// 读取文件所有内容
QString text = file.readAll();
write 函数 :向文件写入数据,参数为字节数组类型数据。配合toUtf8将字符串转为 UTF-8 编码,适配中文写入。
// 写入文本数据
file.write(text.toUtf8());
close 函数:关闭文件,释放文件占用的系统资源,文件操作完成后必须调用。
// 关闭文件
file.close();
QPlainTextEdit 类
核心定义 :Qt 提供的纯文本编辑控件 ,作为记事本类应用的核心组件,支持文本的显示、编辑、多行输入。setCentralWidget :将控件设置为主窗口的中央控件,自动铺满整个主窗口区域。
// 创建文本编辑控件
QPlainTextEdit* edit = new QPlainTextEdit();
// 设置为中央控件
this->setCentralWidget(edit);
setPlainText 函数 :向控件中设置文本内容,覆盖原有内容。
// 将文件内容显示到编辑框
edit->setPlainText(text);
toPlainText 函数 :获取控件中的全部文本内容,返回 QString 类型数据。
// 获取编辑框内的文本
QString text = edit->toPlainText();
setFont 函数 :设置控件的文本字体,支持字体大小、样式配置。
QFont font;
font.setPixelSize(20);
edit->setFont(font);
QFileInfo 类
核心定义 :Qt 提供的文件信息获取类 ,用于读取文件 / 目录的元数据信息 ,不涉及文件内容的读写。初始化方式:通过文件路径构造对象,绑定目标文件。
// 创建文件信息对象
QFileInfo fileInfo(path);
fileName 函数 :获取完整文件名(包含后缀名)。
fileInfo.fileName();
suffix 函数 :获取文件后缀名(扩展名)。
fileInfo.suffix();
path 函数 :获取文件所在目录路径(不含文件名)。
fileInfo.path();
size 函数 :获取文件大小 ,单位为字节。
fileInfo.size();
isFile 函数 :判断路径是否为文件,返回 bool 类型结果。
fileInfo.isFile();
isDir 函数 :判断路径是否为目录,返回 bool 类型结果。
fileInfo.isDir();
本回答由AI生成,仅供参考,请仔细甄别,如有需求请咨询专业人士。
多线程与互斥锁
widget.cpp(重写run函数实现倒计时)
----------------------------------- thread.h -----------------------------------
#ifndef THREAD_H
#define THREAD_H
#include <QWidget>
// QThread:Qt线程基类,自定义线程必须继承它
#include <QThread>
// 自定义线程类 Thread
// 继承 QThread,重写 run() 方法实现子线程逻辑
// Q_OBJECT:Qt元对象宏,支持信号槽机制
class Thread : public QThread
{
Q_OBJECT
public:
// 线程构造函数
Thread();
protected:
// 重写 QThread 的核心函数 run()
// 线程启动后,**子线程**会自动执行该函数内的代码
void run() override;
signals:
// 自定义信号:用于子线程向主线程发送通知
// 信号只需声明,无需实现
void notify();
};
#endif // THREAD_H
----------------------------------- thread.cpp -----------------------------------
#include "thread.h"
// 线程构造函数(空实现,无需初始化逻辑)
Thread::Thread()
{
}
// 核心:线程执行函数
// 注意:此函数运行在【子线程】中,不会阻塞主线程/UI界面
void Thread::run()
{
// 循环10次,实现10秒倒计时通知
for(int i = 0; i < 10; i++)
{
sleep(1); // 线程休眠1秒(QThread::sleep,仅在子线程中使用)
emit notify(); // 发送自定义信号,通知主线程更新UI
}
}
----------------------------------- widget.h -----------------------------------
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
// 包含自定义线程类头文件
#include "thread.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; } // UI命名空间
QT_END_NAMESPACE
// 主窗口类(UI界面,运行在【主线程】)
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
// 自定义槽函数:接收子线程的信号,更新LCD数字
void handle();
private:
Ui::Widget *ui; // UI对象指针
// 定义自定义线程对象(栈对象,无需手动new/delete)
Thread thread;
};
#endif // WIDGET_H
----------------------------------- widget.cpp -----------------------------------
#include "widget.h"
#include "ui_widget.h"
// 主窗口构造函数
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this); // 初始化UI界面
// LCD数字控件初始显示 10
ui->lcdNumber->display(10);
// ==================== 核心:跨线程信号槽连接 ====================
// 连接 子线程的notify信号 → 主线程的handle槽函数
// Qt自动支持跨线程信号槽通信,无需额外处理
connect(&thread, &Thread::notify, this, &Widget::handle);
// ==================== 启动子线程 ====================
// start():调用run()函数,线程开始执行(不会阻塞主线程)
thread.start();
}
// 主窗口析构函数
Widget::~Widget()
{
delete ui;
}
// 槽函数:接收子线程信号,更新LCD数字(运行在【主线程/UI线程】)
void Widget::handle()
{
// 获取LCD当前显示的数字
int value = ui->lcdNumber->intValue();
// 数字自减1,更新显示(实现倒计时效果)
ui->lcdNumber->display(--value);
}
widget.cpp(加锁让多个线程对同一变量正确递增)
----------------------------------- thread.h -----------------------------------
#ifndef THREAD_H
#define THREAD_H
#include <QWidget>
// QThread:Qt线程基类
#include <QThread>
// QMutex:互斥锁,用于解决多线程同时访问共享变量的冲突问题
#include <QMutex>
// 自定义线程类,继承 QThread
class Thread : public QThread
{
Q_OBJECT
public:
Thread();
// 重写线程执行函数,子线程要做的任务写在这里
void run() override;
// 静态变量:所有 Thread 对象共享同一个 num(共享资源)
static int num;
// 静态互斥锁:所有线程共用一把锁,保护共享变量 num
static QMutex mutex;
};
#endif // THREAD_H
----------------------------------- thread.cpp -----------------------------------
#include "thread.h"
// 【静态成员变量必须在类外初始化】
// 初始化共享变量,初始值为 0
int Thread::num = 0;
// 初始化静态互斥锁
QMutex Thread::mutex;
Thread::Thread()
{
}
// 子线程执行函数
void Thread::run()
{
// 每个线程对共享变量 num 进行 50000 次 ++ 操作
for(int i = 0; i < 50000; i++)
{
// ==================== 互斥锁核心用法 ====================
// 加锁:只允许一个线程进入临界区
mutex.lock();
// 临界区代码:操作共享变量
num++;
// 解锁:释放锁,让其他线程可以访问
mutex.unlock();
}
}
----------------------------------- widget.cpp -----------------------------------
#include "widget.h"
#include "ui_widget.h"
// 调试输出,打印最终结果
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建两个线程对象
Thread t1;
Thread t2;
// 启动两个子线程,开始执行 run() 函数
t1.start();
t2.start();
// wait():主线程等待子线程执行完成
// 等 t1、t2 都执行完,主线程才继续往下走
t1.wait();
t2.wait();
// 打印最终结果
// 有锁:结果一定是 100000(5万+5万)
// 无锁:结果会小于 100000(线程安全问题)
qDebug() << "最终结果:" << Thread::num;
}
Widget::~Widget()
{
delete ui;
}
QThread 类
QThread 是 Qt 提供的线程基类 ,用于实现跨平台的多线程编程,封装了底层线程操作,简化线程的创建、启动、等待与销毁流程。核心成员函数:
-
start() :启动线程,自动调用run() 函数,线程进入运行状态,不阻塞主线程。
-
run() :线程的入口函数,子线程执行的逻辑代码均写在该函数内,需重写实现自定义逻辑。
-
wait():阻塞当前线程,等待目标子线程执行完毕后再继续执行。
-
sleep(int secs) :线程休眠指定秒数,仅能在子线程中调用。
// 启动线程
thread.start();
// 主线程等待子线程结束
t1.wait();
// 子线程休眠1秒
sleep(1);
QMutex 互斥锁
QMutex 是 Qt 提供的互斥锁类 ,用于解决多线程共享资源竞争 问题,保证同一时间只有一个线程访问临界区资源。核心成员函数:
-
lock():对共享资源加锁,若资源已被锁定,线程阻塞等待直至锁释放。
-
unlock() :释放锁,允许其他线程访问共享资源。使用规则:操作共享变量前后必须成对调用加锁与解锁,避免死锁。
// 加锁
mutex.lock();
// 操作共享资源
num++;
// 解锁
mutex.unlock();
Qt 与 Linux 创建线程的区别
Linux 线程创建 :基于 POSIX 标准,调用pthread_create函数,直接指定线程执行的普通函数,面向过程编程。Qt 线程创建 :基于面向对象 思想,必须自定义类继承 QThread ,重写类中的run() 函数作为线程入口,依托 Qt 元对象系统支持信号槽跨线程通信。核心差异:Linux 线程依赖函数指针,Qt 线程依赖类的继承与重写,跨平台性更强。
线程入口函数 run 重写机制
run() 是 QThread 类的虚函数 ,为线程的唯一入口函数。自定义线程类继承 QThread 后,必须重写 run () 函数,将子线程的业务逻辑写入该函数。调用start() 启动线程时,Qt 底层自动在子线程中执行重写后的run() 函数,主线程保持运行状态。
// 自定义线程类重写run函数
class Thread : public QThread
{
protected:
void run() override;
};
// 子线程逻辑实现
void Thread::run()
{
// 子线程执行代码
}
QWaitCondition 条件等待
QWaitCondition 是 Qt 提供的条件变量类 ,配合QMutex 使用,实现线程间的等待与唤醒机制。核心成员函数:
- wait(QMutex *mutex):解锁互斥锁并阻塞当前线程,等待唤醒信号。
- wakeOne():唤醒一个处于等待状态的线程。
- wakeAll() :唤醒所有处于等待状态的线程。适用场景:生产者 - 消费者模型、线程同步等待。
QSemaphore 信号量
QSemaphore 是 Qt 提供的计数信号量类 ,用于控制多线程对有限共享资源 的访问,支持多资源同步。核心成员函数:
- acquire(int n = 1):申请 n 个资源,资源不足时线程阻塞。
- release(int n = 1) :释放 n 个资源,唤醒等待的线程。适用场景:限制同时访问资源的线程数量,比互斥锁更灵活。
UDP
widget.cpp(编写UDP回显服务端)
#include "widget.h"
#include "ui_widget.h"
// UDP 套接字类
#include <QUdpSocket>
// 消息提示框
#include <QMessageBox>
// UDP 数据报类(封装数据、目标IP、端口)
#include <QNetworkDatagram>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// ==================== 1. 创建 UDP 套接字对象 ====================
// 指定父对象 this,由 Qt 自动管理内存
socket = new QUdpSocket(this);
// 设置窗口标题为"服务器"
this->setWindowTitle("服务器");
// ==================== 2. 绑定信号槽:当有数据到达时触发 ====================
// readyRead():UDP 套接字收到数据时自动发出此信号
// 连接到槽函数 processRequest() 处理消息
connect(socket, &QUdpSocket::readyRead, this, &Widget::processRequest);
// ==================== 3. 绑定端口,启动 UDP 服务 ====================
// bind(IP, 端口):监听本机 9090 端口的所有 UDP 消息
// QHostAddress::Any:监听所有网卡(本地/局域网)
bool ret = socket->bind(QHostAddress::Any, 9090);
// 如果绑定失败(端口被占用等),弹出错误提示
if(ret == false)
{
QMessageBox::critical(this, "服务器启动出错", socket->errorString());
return;
}
}
Widget::~Widget()
{
delete ui;
}
// ==================== 核心槽函数:处理收到的 UDP 数据 ====================
void Widget::processRequest()
{
// ==================== 1. 读取客户端发送的数据报 ====================
// receiveDatagram():读取一整条 UDP 数据报(包含数据+发送方IP+端口)
const QNetworkDatagram& requestDatagram = socket->receiveDatagram();
// 从数据报中提取 客户端发送的原始数据(转 QString)
QString request = requestDatagram.data();
// ==================== 2. 构造服务器回复消息 ====================
// 简单回显:客户端消息 + :Server
const QString& response = request + ":Server";
// ==================== 3. 构造回复数据报,发送给客户端 ====================
// 参数:(数据内容, 客户端IP, 客户端端口)
QNetworkDatagram responseDatagram(
response.toUtf8(), // 要发送的数据
requestDatagram.senderAddress(), // 客户端 IP
requestDatagram.senderPort() // 客户端 端口
);
// 写数据报 → 发送回客户端
socket->writeDatagram(responseDatagram);
// ==================== 4. 在界面日志窗口显示通信记录 ====================
QString log = "[" + requestDatagram.senderAddress().toString() +
":" + QString::number(requestDatagram.senderPort()) + "]" +
" 客户端请求:" + request + " | 服务端回应:" + response;
// 添加到列表控件显示
ui->listWidget->addItem(log);
}
widget.cpp(编写UDP回显客户端)
#include "widget.h"
#include "ui_widget.h"
// UDP 套接字
#include <QUdpSocket>
// UDP 数据报(封装数据、IP、端口)
#include <QNetworkDatagram>
// 服务器 IP 和端口(与服务器端保持一致)
const QString& SERVER_IP = "127.0.0.1"; // 本地回环地址
const quint16 SERVER_PORT = 9090; // 服务器端口 9090
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// ==================== 1. 创建 UDP 套接字 ====================
// 客户端不需要 bind,只需要创建 socket 对象即可
socket = new QUdpSocket(this);
// 设置窗口标题为"客户端"
this->setWindowTitle("客户端");
// ==================== 2. 绑定信号槽 ====================
// 当服务器回复消息时,socket 会发出 readyRead 信号
// 触发 processResponse 函数读取服务器数据
connect(socket, &QUdpSocket::readyRead, this, &Widget::processResponse);
}
Widget::~Widget()
{
delete ui;
}
// ==================== 槽函数:接收服务器的回复 ====================
void Widget::processResponse()
{
// 读取服务器发来的 UDP 数据报
const QNetworkDatagram& responseDatagram = socket->receiveDatagram();
// 提取数据内容
QString response = responseDatagram.data();
// 在界面列表中显示服务器消息
ui->listWidget->addItem("服务器说:" + response);
}
// ==================== 按钮点击:发送消息给服务器 ====================
void Widget::on_pushButton_clicked()
{
// 获取输入框中的文本
QString text = ui->lineEdit->text();
// ==================== 构造 UDP 数据报 ====================
// 参数:发送的数据、服务器IP、服务器端口
QNetworkDatagram requestDatagram(
text.toUtf8(), // 消息内容
QHostAddress(SERVER_IP),// 目标IP
SERVER_PORT // 目标端口
);
// ==================== 发送数据报 ====================
socket->writeDatagram(requestDatagram);
// 在界面显示自己发送的消息
ui->listWidget->addItem("客户端说:" + text);
// 清空输入框
ui->lineEdit->setText("");
}
网络模块配置
Qt 网络模块依赖 :使用 UDP/TCP 等网络 API,必须在项目的.pro文件中添加配置,加载network模块,否则无法识别网络相关类。
# .pro文件配置
QT += core gui network
QUdpSocket 类
QUdpSocket 是 Qt 提供的UDP 套接字封装类,实现无连接的 UDP 网络通信,封装底层 UDP 套接字操作,支持跨平台使用。
// 创建UDP套接字对象
socket = new QUdpSocket(this);
readyRead 信号
readyRead 是QUdpSocket 类的内置信号,当套接字接收到网络数据报时自动触发,是 UDP 数据接收的核心通知机制。
执行顺序规范
信号槽绑定优先 :必须先完成readyRead信号与槽函数的关联,再执行端口绑定操作。执行顺序错误会导致数据到达时无处理函数,造成数据丢失。
// 正确顺序:先绑定信号槽
connect(socket, &QUdpSocket::readyRead, this, &Widget::processRequest);
// 后绑定端口
socket->bind(QHostAddress::Any, 9090);
服务端与客户端 QUdpSocket 作用
服务端 socket :负责绑定端口 、监听网络数据、接收客户端数据、向客户端回发数据。客户端 socket :无需绑定端口,负责向服务端发送数据、接收服务端的回复数据。
QNetworkDatagram 类
QNetworkDatagram 是 Qt 封装的UDP 数据报类 ,统一封装数据内容、目标 IP 地址、目标端口、发送方 IP / 端口信息,简化 UDP 数据传输的参数管理。
// 构造UDP数据报
QNetworkDatagram requestDatagram(
text.toUtf8(),
QHostAddress(SERVER_IP),
SERVER_PORT
);
QUdpSocket 核心成员函数
bind 函数:服务端专用,绑定 IP 地址与端口号,开启端口监听,参数为监听地址与端口。
// 绑定本机所有网卡的9090端口
bool ret = socket->bind(QHostAddress::Any, 9090);
receiveDatagram 函数 :读取套接字中的 UDP 数据报,返回封装后的QNetworkDatagram对象。
const QNetworkDatagram& requestDatagram = socket->receiveDatagram();
writeDatagram 函数 :发送 UDP 数据报,支持直接传入QNetworkDatagram对象完成数据发送。
socket->writeDatagram(responseDatagram);
Qt UDP 函数与 Linux UDP 函数对应关系
bind :Qt 的bind函数 对应 Linux 的bind函数,完成套接字与端口的绑定。receiveDatagram :Qt 的receiveDatagram函数 对应 Linux 的recvfrom函数,接收 UDP 数据并获取发送方地址信息。writeDatagram :Qt 的writeDatagram函数 对应 Linux 的sendto函数,向指定地址端口发送 UDP 数据。
TCP
widget.cpp(编写TCP回显服务端)
#include "widget.h"
#include "ui_widget.h"
// TCP 服务器类
#include <QTcpServer>
// TCP 套接字类(客户端连接)
#include <QTcpSocket>
// 错误提示框
#include <QMessageBox>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置窗口标题
this->setWindowTitle("服务端");
// ==================== 1. 创建 TCP 服务器对象 ====================
// QTcpServer* tcpServer; (需要在头文件中声明)
tcpServer = new QTcpServer(this);
// ==================== 2. 绑定信号槽:有新客户端连接时触发 ====================
// newConnection():当有客户端尝试连接时,服务器发出此信号
connect(tcpServer, &QTcpServer::newConnection, this, &Widget::processConnection);
// ==================== 3. 启动监听 ====================
// listen(IP, 端口):监听本机 9090 端口,等待客户端连接
// QHostAddress::Any:监听所有网络地址(局域网/本地都能连)
bool ret = tcpServer->listen(QHostAddress::Any, 9090);
// 如果启动失败(端口被占用),弹出错误并退出程序
if(ret == false)
{
QMessageBox::critical(this, "服务端启动失败", tcpServer->errorString());
exit(-1); // 退出程序
}
}
Widget::~Widget()
{
delete ui;
}
// ==================== 核心槽函数:处理新客户端连接 ====================
void Widget::processConnection()
{
// ==================== 1. 获取与客户端通信的 Socket ====================
// nextPendingConnection():取出等待连接的客户端套接字
QTcpSocket* clientSock = tcpServer->nextPendingConnection();
// ==================== 2. 打印客户端上线日志 ====================
// peerAddress():客户端IP
// peerPort():客户端端口
QString log = "[" + clientSock->peerAddress().toString() +
":" + QString::number(clientSock->peerPort()) +
"] 客户端上线.";
ui->listWidget->addItem(log);
// ==================== 3. 绑定【客户端发消息】信号(Lambda 写法) ====================
// readyRead():客户端发送数据过来时触发
connect(clientSock, &QTcpSocket::readyRead, this, [=](){
// 读取客户端发送的所有数据
QString request = clientSock->readAll();
// 构造回复消息(回显机制)
const QString response = request + ":Server";
// 将回复消息发送给客户端
clientSock->write(response.toUtf8());
// 打印通信日志
QString log = "[" + clientSock->peerAddress().toString() +
":" + QString::number(clientSock->peerPort()) + "]" +
" client:" + request + ", server:" + response;
ui->listWidget->addItem(log);
});
// ==================== 4. 绑定【客户端断开连接】信号(Lambda 写法) ====================
connect(clientSock, &QTcpSocket::disconnected, this, [=](){
// 打印客户端下线日志
QString log = "[" + clientSock->peerAddress().toString() +
":" + QString::number(clientSock->peerPort()) +
"] 客户端下线.";
ui->listWidget->addItem(log);
// 安全释放客户端 Socket 内存(必须用 deleteLater)
clientSock->deleteLater();
});
}
widget.cpp(编写TCP回显客户端)
#include "widget.h"
#include "ui_widget.h"
// TCP 套接字(客户端用)
#include <QTcpSocket>
// 错误提示框
#include <QMessageBox>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget))
{
ui->setupUi(this);
// 设置窗口标题
this->setWindowTitle("客户端");
// ==================== 1. 创建 TCP 客户端套接字 ====================
// QTcpSocket* tcpSocket; (需要在头文件中声明)
tcpSocket = new QTcpSocket(this);
// ==================== 2. 主动连接服务器 ====================
// 参数:服务器IP、服务器端口
// 127.0.0.1 = 本地回环地址(自己连自己)
tcpSocket->connectToHost("127.0.0.1", 9090);
// ==================== 3. 绑定信号槽:收到服务端消息时触发 ====================
connect(tcpSocket, &QTcpSocket::readyRead, this, &Widget::processResponse);
// ==================== 4. 等待连接成功(阻塞等待) ====================
bool ret = tcpSocket->waitForConnected();
if(ret == false)
{
// 连接失败:弹出错误提示并退出
QMessageBox::critical(this, "连接服务器出错", tcpSocket->errorString());
exit(-1);
}
}
Widget::~Widget()
{
delete ui;
}
// ==================== 槽函数:处理服务端发来的消息 ====================
void Widget::processResponse()
{
// 读取服务端发送的所有数据
QString response = tcpSocket->readAll();
// 在界面列表显示服务端消息
ui->listWidget->addItem("服务端说:" + response);
}
// ==================== 按钮点击:发送消息给服务端 ====================
void Widget::on_pushButton_clicked()
{
// 获取输入框文本
const QString& text = ui->lineEdit->text();
// ==================== 发送数据给服务器 ====================
tcpSocket->write(text.toUtf8());
// 在界面显示自己发送的消息
ui->listWidget->addItem("客户端说:" + text);
// 清空输入框
ui->lineEdit->setText("");
}
QTcpServer 类
核心定义 :Qt 提供的TCP 服务器套接字类 ,用于监听端口、接收客户端连接请求,是 TCP 服务端的核心组件。构造函数:创建服务器对象,指定父对象实现自动内存管理。
tcpServer = new QTcpServer(this);
newConnection 信号 :QTcpServer 的核心信号,检测到新客户端连接请求时自动触发,是处理新连接的入口。
connect(tcpServer, &QTcpServer::newConnection, this, &Widget::processConnection);
listen 函数:启动服务端监听,绑定 IP 地址与端口号,等待客户端连接。
bool ret = tcpServer->listen(QHostAddress::Any, 9090);
nextPendingConnection 函数 :获取已建立连接的客户端 QTcpSocket 对象,完成连接接收。
QTcpSocket* clientSock = tcpServer->nextPendingConnection();
errorString 函数 :获取服务器操作的错误描述信息,用于异常提示。
QMessageBox::critical(this, "服务端启动失败", tcpServer->errorString());
QTcpSocket 类
核心定义 :Qt 提供的TCP 通信套接字类,服务端与客户端通用,实现 TCP 数据的收发、连接状态管理。
服务端使用场景 通过QTcpServer::nextPendingConnection获取客户端套接字,用于与单个客户端通信。
客户端使用场景 直接new创建对象,调用connectToHost主动连接服务端。
tcpSocket = new QTcpSocket(this);
核心信号 readyRead 信号 :套接字接收到对端数据时自动触发,用于读取接收数据。
connect(clientSock, &QTcpSocket::readyRead, this, [=](){});
disconnected 信号 :TCP 连接断开时自动触发,用于处理下线逻辑。
connect(clientSock, &QTcpSocket::disconnected, this, [=](){});
核心成员函数 connectToHost 函数:客户端专用,主动向服务端发起 TCP 连接,指定服务端 IP 与端口。
tcpSocket->connectToHost("127.0.0.1", 9090);
waitForConnected 函数 :客户端专用,阻塞等待连接建立完成,返回连接结果。
bool ret = tcpSocket->waitForConnected();
readAll 函数 :读取套接字缓冲区的所有数据,返回字符串类型数据。
QString request = clientSock->readAll();
write 函数:向对端发送数据,参数为字节数组类型。
clientSock->write(response.toUtf8());
peerAddress 函数 :获取对端 IP 地址,服务端获取客户端 IP,客户端获取服务端 IP。
clientSock->peerAddress().toString()
peerPort 函数 :获取对端端口号,获取通信对端的端口信息。
QString::number(clientSock->peerPort())
deleteLater 函数:安全释放套接字对象内存,避免内存泄漏。
clientSock->deleteLater();
HTTP
#include "widget.h"
#include "ui_widget.h"
// HTTP 网络请求管理类
#include <QNetworkAccessManager>
// HTTP 请求封装类
#include <QNetworkRequest>
// HTTP 响应(回复)类
#include <QNetworkReply>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置窗口标题
this->setWindowTitle("HTTP 客户端");
// ==================== 1. 创建网络访问管理器 ====================
// 整个程序只需要一个 manager,负责发送所有 HTTP 请求
manager = new QNetworkAccessManager(this);
}
Widget::~Widget()
{
delete ui;
}
// ==================== 按钮点击:发送 HTTP GET 请求 ====================
void Widget::on_pushButton_clicked()
{
// ==================== 2. 封装 URL ====================
// 从输入框获取网址(如 https://www.baidu.com)
QUrl url(ui->lineEdit->text());
// ==================== 3. 封装 HTTP 请求 ====================
QNetworkRequest request(url);
// ==================== 4. 发送 GET 请求 ====================
// manager->get() 发送请求,返回一个 QNetworkReply* 用于接收结果
QNetworkReply* response = manager->get(request);
// ==================== 5. 监听请求完成信号 ====================
// finished():当服务器返回数据、请求结束时触发
connect(response, &QNetworkReply::finished, this, [=](){
// 判断请求是否成功(无错误)
if(response->error() == QNetworkReply::NoError)
{
// 成功:读取服务器返回的所有数据(网页源代码)
QString html = response->readAll();
// 显示到文本框
ui->plainTextEdit->setPlainText(html);
}
else
{
// 失败:显示错误信息(如 无法连接、404、超时等)
ui->plainTextEdit->setPlainText("请求失败:" + response->errorString());
}
// ==================== 6. 释放内存 ====================
// 请求结束后必须释放 reply 对象,防止内存泄漏
response->deleteLater();
});
}
QNetworkAccessManager 类
QNetworkAccessManager 是 Qt 提供的HTTP 网络请求管理核心类,负责统一发送、调度所有 HTTP 请求,管控网络请求的完整生命周期,程序中创建单例对象即可满足所有请求需求。
// 创建网络访问管理对象
manager = new QNetworkAccessManager(this);
QNetworkRequest 类
QNetworkRequest 是 Qt 提供的HTTP 请求封装类 ,用于存储请求的 URL 地址、请求头、请求参数等核心信息,作为 HTTP 请求的载体传入发送接口。QUrl 是 Qt 提供的URL 地址封装类,将字符串格式的网址转换为 Qt 标准 URL 格式,适配网络请求规范。
// 封装请求URL
QUrl url(ui->lineEdit->text());
// 封装完整HTTP请求
QNetworkRequest request(url);
QNetworkReply 类
QNetworkReply 是 Qt 提供的HTTP 响应封装类 ,存储服务器返回的响应数据、错误码、错误描述、响应头等全部结果信息,由get函数自动创建并返回。
核心成员函数
get 函数 :QNetworkAccessManager 的成员函数,用于发送HTTP GET 请求,参数为封装好的 QNetworkRequest 对象,返回值为 QNetworkReply 指针。
QNetworkReply* response = manager->get(request);
error 函数 :QNetworkReply 的成员函数,获取 HTTP 请求的错误码 ,QNetworkReply::NoError为常量,表示请求无错误、执行成功。
if(response->error() == QNetworkReply::NoError)
errorString 函数 :QNetworkReply 的成员函数,获取请求失败的文本描述信息,直观展示网络错误原因。
response->errorString()
readAll 函数 :QNetworkReply 的成员函数,读取服务器返回的全部响应数据,GET 请求中对应网页的 HTML 源码。
QString html = response->readAll();
deleteLater 函数 :QNetworkReply 的成员函数,安全释放响应对象内存,避免请求完成后产生内存泄漏。
response->deleteLater();
核心信号
finished 信号 :QNetworkReply 的内置信号,HTTP 请求执行完毕(无论成功或失败)时自动触发,是处理服务器响应结果的核心入口。
connect(response, &QNetworkReply::finished, this, [=](){
// 处理响应结果逻辑
});