Qt系统
Qt文件概述
⽂件操作是应⽤程序必不可少的部分。Qt 作为⼀个通⽤开发库,提供了跨平台的⽂件操作能⼒。 Qt提供了很多关于⽂件的类,通过这些类能够对⽂件系统进⾏操作,如⽂件读写、⽂件信息获取、⽂件复制或重命名等。
输入输出设备类
在 Qt 中,⽂件读写的类为 QFile 。QFile 的⽗类为 QFileDevice ,QFileDevice 提供了⽂件交互操作的底层功能。 QFileDevice 的⽗类是 QIODevice,QIODevice 的⽗类为 QObject 。
QIODevice 是 Qt 中所有输⼊输出设备(input/output device,简称 I/O 设备)的基础类,I/O 设备就
是能进⾏数据输⼊和输出的设备,例如⽂件是⼀种 I/O 设备,⽹络通信中的 socket 是 I/O 设备, 串
⼝、蓝⽛等通信接⼝也是 I/O 设备,所以它们也是从 QIODevice 继承来的。Qt 中主要的⼀些 I/O 设备类的继承关系如下图所⽰:
- QFile: 用于文件操作和文件数据读写的类,使用QFile可以读写任意格式的文件;
- QSaveFIle: 是⽤于安全保存⽂件的类。使⽤ QSaveFile 保存⽂件时,它会先把数据写⼊⼀个临时⽂件,成功提交后才将数据写⼊最终的⽂件。如果保存过程中出现错误,临时⽂件⾥的数据不会被写
⼊最终⽂件,这样就能确保最终⽂件中不会丢失数据或被写⼊部分数据。 在保存⽐较⼤的⽂件或复
杂格式的⽂件时可以使⽤这个类,例如从⽹络上下载⽂件等。- QTemporaryFile 是⽤于创建临时⽂件的类。使⽤函数 QTemporaryFile::open() 就能创建⼀个⽂件
名唯⼀的临时⽂件,在 QTemporaryFile 对象被删除时,临时⽂件被⾃动删除。- QTcpSocket 和 QUdpSocket 是分别实现了 TCP 和 UDP 的类。
- QSerialPort 是实现了串⼝通信的类,通过这个类可以实现计算机与串⼝设备的通信。
- QBluetoothSocket 是⽤于蓝⽛通信的类。⼿机和平板计算机等移动设备有蓝⽛通信模块,笔记本
电脑⼀般也有蓝⽛通信模块。通过QBluetoothSocket类,就可以编写蓝⽛通信程。如编程实现笔
记本电脑与⼿机的蓝⽛通信。- QProcess 类⽤于启动外部程序,并且可以给程序传递参数。
- QBuffer 以⼀个 QByteArray 对象作为数据缓冲区,将 QByteArray 对象当作⼀个 I/O 设备来读写。
QFile
以对话框的方式来写入和读取文件;
- 先通过Qt Design 设计出大概轮廓:
- 给两个按钮,绑定对应的槽函数:
- 运行结果:
QFileInfo
QFileInfo 是 Qt 提供的⼀个⽤于获取⽂件和⽬录信息的类,如获取⽂件名、⽂件⼤⼩、⽂件修改⽇期等。
常用方法:
常用方法 | 说明 |
---|---|
isDir() | 检查该⽂件是否是⽬录 |
isExecutable() | 检查该⽂件是否是可执⾏⽂件; |
fileName() | 获得⽂件名 |
completeBaseName() | 获取完整的⽂件名 |
suffix() | 获取⽂件后缀名 |
completeSuffix() | 获取完整的⽂件后缀 |
size() | 获取⽂件⼤⼩ |
isFile() | 判断是否为⽂件 |
fileTime() | 获取⽂件创建时间、修改时间、最近访问时间等 |
在C++17标准库中引入了filesystem文件系统库来获取文件和目录的信息;
Qt多线程
在 Qt 中,多线程的处理⼀般是通过 QThread类 来实现。
QThread 代表⼀个在应⽤程序中可以独⽴控制的线程,也可以和进程中的其他线程共享数据。
QThread 对象管理程序中的⼀个控制线程。
Qt中线程的使用和Java中线程的使用类似;
Qt多线程常用API
API | 说明 |
---|---|
run() | 线程的⼊⼝函数… |
start() | 通过调⽤ run() 开始执⾏线程。操作系统将根据优先级参数调度线程。如果线程已经在运⾏,这个函数什么也不做。 |
currentThread() | 返回⼀个指向管理当前执⾏线程的 QThread的指针。 |
isRunning() | 如果线程正在运⾏则返回true;否则返回false。 |
sleep() / msleep() /usleep() | 使线程休眠,单位为秒 / 毫秒 / 微秒 |
wait() | 阻塞线程,直到满⾜以下任何⼀个条件:与此 QThread 对象关联的线程已经完成执⾏(即当它从run()返回时)。如果线程已经完成,这个函数将返回 true。如果线程尚未启动,它也返回 true。已经过了⼏毫秒。如果时间是 ULONG_MAX(默认值),那么等待永远不会超时(线程必须从run()返回)。如果等待超时,此函数将返回 false。这提供了与 POSIX pthread_join() 函数类似的功能。 |
terminate() | 终⽌线程的执⾏。线程可以⽴即终⽌,也可以不⽴即终⽌,这取决于操作系统的调度策略。在terminate() 之后使⽤ QThread::wait() 来确保。 |
常用信号:
信号 | 说明 |
---|---|
finished() | 当线程结束时会发出该信号,可以通过该信号来实现线程的清理⼯作。 |
这个信号可以连接到QObject::deleteLater(),以释放该线程中的对象。
使用Qt多线程
Qt多线程一般使用步骤:
- 创建一个类,并继承自QThread;
- run函数为线程的入口函数,因此要根据自己的需要重写run函数;
- 通过这个新创建的类实例化一个新对象;
- 通过这个新对象来调用其start函数,以启动线程;
- 这个启动的线程会以重写的run函数为入口函数开始运行;
- 最后回收线程
eg:通过线程来实现定时器
- 通过Qt Design设计基础界面
- 设计MyThread线程
- 给MyThread定制一个信号,名字教timeout吧:
- 重写run函数
- 给MyThread类的timeout绑定对应的槽函数:
- 为当前线程设置回收工作
- 启动线程
8. 运行结果
线程安全
Qt实现线程互斥和同步常用的手段有:
- 互斥锁: QMutex、QMutexLocker
- 条件变量: QWaitCondition
- 信号量: QSemaphore
- 读写锁: QReadLocker、QWriteLocker、QReadWriteLocker
互斥锁
实际上QMutex与C++标准中的Mutex用法一样,而QMutexLocker是QMutex的辅助类,采用RAII的思想来申请锁和释放锁;
eg:
两个线程对g_val同时自加1000次
运行结果:
读写锁
- QReadWriteLock是读写锁本身;
- QReadLocker:采用RAII的思想来获取读写锁的读锁;
- QWriteLocker: 采用RAII的思想来获取读写锁的写锁;
- 读写锁多用于读操作频繁的场景;
条件变量
在多线程编程中,假设除了等待操作系统正在执⾏的线程之外,某个线程还必须等待某些条件满⾜才能执⾏,这时就会出现问题。这种情况下,线程会很⾃然地使⽤锁的机制来阻塞其他线程,因为这只是线程的轮流使⽤,并且该线程等待某些特定条件,⼈们会认为需要等待条件的线程,在释放互斥锁或读写锁之后进⼊了睡眠状态,这样其他线程就可以继续运⾏。当条件满⾜时,等待条件的线程将被另⼀个线程唤醒。
在 Qt 中,专⻔提供了 QWaitCondition类 来解决像上述这样的问题。
对标标准库的condition_variable;
信号量
有时在多线程编程中,需要确保多个线程可以相应的访问⼀个数量有限的相同资源。例如,运⾏程序的设备可能是⾮常有限的内存,因此我们更希望需要⼤量内存的线程将这⼀事实考虑在内,并根据可⽤的内存数量进⾏相关操作,多线程编程中类似问题通常⽤信号量来处理。信号量类似于增强的互斥锁,不仅能完成上锁和解锁操作,⽽且可以跟踪可⽤资源的数量。
特点:QSemaphore 是 Qt 框架提供的计数信号量类,⽤于控制同时访问共享资源的线程数量。
⽤途:限制并发线程数量,⽤于解决⼀些资源有限的问题。
QSemaphore semaphore(2); //同时允许两个线程访问共享资源
//在需要访问共享资源的线程中
semaphore.acquire(); //尝试获取信号量,若已满则阻塞
//访问共享资源
//...
semaphore.release(); //释放信号量
//在另⼀个线程中进⾏类似操作
Qt网络
和多线程类似, Qt 为了⽀持跨平台, 对⽹络编程的 API 也进⾏了重新封装.
ps: 这里就不得不吐槽一下C++标准到现在(2024)都还没有一个统一的标准的网络库,这就导致开发者们如果要想进行网络开发就不得不使用系统调用或者其它三方网络库;
在进⾏⽹络编程之前, 需要在项⽬中的 .pro ⽂件中添加 network 模块.
QUdpSocket
在Qt中,QUdpSocket表示一个Udp的socket文件
常用方法如下:
常用方法 | 说明 |
---|---|
bind(const QHostAddress&,quint16) | 绑定指定的端⼝号. |
receiveDatagram() | 返回 QNetworkDatagram . 读取⼀个 UDP 数据报. |
writeDatagram(const QNetworkDatagram&) | 发送⼀个 UDP 数据报. |
信号
信号 | 说明 |
---|---|
readyRead | 在收到数据并准备就绪后触发. |
QNetworkDatagram
在Qt中,QNetworkDatagram表示一个Udp数据包
常用方法如下:
常用方法 | 说明 |
---|---|
QNetworkDatagram(const QByteArray&, const QHostAddress& , quint16 ) | 通过 QByteArray , ⽬标 IP 地址,⽬标端⼝号 构造⼀个 UDP 数据报.通常⽤于发送数据时. |
data() | 获取数据报内部持有的数据. 返回QByteArray |
senderAddress() | 获取数据报中包含的对端的 IP 地址. |
senderPort() | 获取数据报中包含的对端的端⼝号. |
设计一个UDP回显服务器
设计服务端
- 服务端大致界面:
- 设计UDP服务器大体逻辑
客户端设计:
- 设计客户端大致界面
- 客户端大致逻辑:
- 运行结果:
QTcpServer
QTcpServer ⽤于监听端⼝, 和获取客⼾端连接.
常用方法:
API | 说明 |
---|---|
listen(const QHostAddress&,quint16 port) | 绑定指定的地址和端⼝号, 并开始监听. |
nextPendingConnection() | 从系统中获取到⼀个已经建⽴好的tcp 连接.返回⼀个 QTcpSocket , 表⽰这个客⼾端的连接. 通过这个 socket 对象完成和客⼾端之间的通信. |
信号
信号 | 说明 |
---|---|
newConnection | 有新的客⼾端建⽴连接好之后触发. |
QTcpSocket
QTcpSocket ⽤⼾客⼾端和服务器之间的数据交互.
常用方法:
API | 说明 |
---|---|
readAll() | 读取当前接收缓冲区中的所有数据.返回 QByteArray 对象. |
write(const QByteArray& ) | 把数据写⼊ socket 中. |
deleteLater | 暂时把 socket 对象标记为⽆效. Qt会在下个事件循环中析构释放该对象. |
信号
信号 | 说明 |
---|---|
readyRead | 有数据到达并准备就绪时触发. |
disconnected | 连接断开时触发. |
Tcp版本的回显服务器
服务端设计
- 大致ui界面:
- 大致逻辑:
客户端:
- 大致ui设计:
- 大致逻辑设计:
运行结果:
HttpClient
核心API
QNetworkAccessManager
- 提供了 HTTP 的核⼼操作.
常用方法 | 说明 |
---|---|
get(const QNetworkRequest& ) | 发起⼀个 HTTP GET 请求. 返回 QNetworkReply 对象 |
post(const QNetworkRequest& , const QByteArray& ) | 发起⼀个 HTTP POST 请求. 返回 QNetworkReply 对象. |
QNetworkRequest
- 表⽰⼀个 HTTP 请求(不含 body).如果需要发送⼀个带有 body 的请求(⽐如 post), 会在 QNetworkAccessManager 的 post ⽅法中通过单独的参数来传⼊ body.
常用方法 | 说明 |
---|---|
QNetworkRequest(const QUrl& ) | 通过 URL 构造⼀个 HTTP 请求. |
setHeader(QNetworkRequest::KnownHeaders header,const QVariant &value) | 设置请求头. |
其中的 QNetworkRequest::KnownHeaders 是⼀个枚举类型, 常⽤取值:
取值 | 说明 |
---|---|
ContentTypeHeader | 描述 body 的类型. |
ContentLengthHeader | ContentLengthHeader |
ContentLengthHeader | ⽤于重定向报⽂中指定重定向地址. (响应中使⽤, 请求⽤不到) |
CookieHeader | CookieHeader |
UserAgentHeader | 设置 User-Agent |
QNetworkReply
- 表⽰⼀个 HTTP 响应. 这个类同时也是 QIODevice 的⼦类
常用方法 | 说明 |
---|---|
error() | 获取出错状态. |
errorString() | 获取出错原因的⽂本. |
readAll() | 读取响应 body |
header(QNetworkRequest::KnownHeaders header) | 读取响应指定 header 的值. |
此外, QNetworkReply 还有⼀个重要的信号 finished 会在客⼾端收到完整的响应数据之后触
发.
Qt 音频
在 Qt 中,⾳频主要是通过 QSound 类来实现。但是需要注意的是 QSound 类只⽀持播放 wav 格式的⾳频⽂件。也就是说如果想要添加⾳频效果,那么⾸先需要将 ⾮wav格式 的⾳频⽂件转换为 wav 格式。
注意: 使⽤ QSound 类时,需要添加模块:multimedia.
常用方法 | 说明 |
---|---|
play() | 开始或继续播放当前源。 |
eg:
Qt视频
在 Qt 中,视频播放的功能主要是通过 QMediaPlayer类 和 QVideoWidget类 来实现。在使⽤这两个类时要添加对应的模块 multimedia 和 multimediawidgets 。
核心API
API | 说明 |
---|---|
setMedia() | 设置当前媒体源 |
setVideoOutput() | 将QVideoWidget视频输出附加到媒体播放器,如果媒体播放器已经附加了视频输出,将更换⼀个新的。 |