文章目录
- [1. 文件操作](#1. 文件操作)
-
- [1.1 API](#1.1 API)
- [1.2 例子1,简单记事本](#1.2 例子1,简单记事本)
- [1.3 例子2,输出文件的属性](#1.3 例子2,输出文件的属性)
- [2. Qt 多线程](#2. Qt 多线程)
-
- [2.1 常用API](#2.1 常用API)
- [2.2 例子1,自定义定时器](#2.2 例子1,自定义定时器)
- [3. 线程安全](#3. 线程安全)
-
- [3.1 互斥锁](#3.1 互斥锁)
- [3.2 条件变量](#3.2 条件变量)
- [4. 网络编程](#4. 网络编程)
-
- [4.1 UDP Socket](#4.1 UDP Socket)
- [4.2 UDP Server](#4.2 UDP Server)
- [4.3 UDP Client](#4.3 UDP Client)
- [4.4 TCP Socket](#4.4 TCP Socket)
- [4.5 TCP Server](#4.5 TCP Server)
- [4.6 TCP Client](#4.6 TCP Client)
- [4.7 HTTP API](#4.7 HTTP API)
- [4.8 HTTP Client](#4.8 HTTP Client)
- [5. 播放音频](#5. 播放音频)
1. 文件操作
继承关系图如下

1.1 API
简单介绍一下文件操作的方法
-
QFil
e构造:QFile::QFile(const QString &name)
,通过给定的路径构造 -
打开文件 :使用
[override virtual] bool QFile::open(QIODeviceBase::OpenMode mode)
方法来打开文件。文件可以是文本文件或二进制文件。QIODevice::ReadOnly
:以只读模式打开文件。QIODevice::WriteOnly
:以只写模式打开文件。QIODevice::ReadWrite
:以读写模式打开文件。
-
读取文件 :
QByteArray QIODevice::readAll()
来读取所有的文件内容 -
编写文件 :
qint64 QIODevice::write(const QByteArray &data)
向文件写内容 -
关闭文件 :
[virtual] void QIODevice::close()
来关闭文件
1.2 例子1,简单记事本
下面是一个例子,实现了记事本的两个功能:
cpp
#include "mainwindow.h"
#include <QFile>
#include <QFileDialog>
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle("记事本Demo");
// 添加菜单栏
QMenuBar* menuBar = this->menuBar();
// 添加菜单
QMenu* menu = new QMenu("文件");
menuBar->addMenu(menu);
// 添加菜单项
QAction* action_read = new QAction("读取");
QAction* action_save = new QAction("保存");
menu->addAction(action_read);
menu->addAction(action_save);
// 设置文本输入框
edit = new QPlainTextEdit(this);
QFont font;
font.setPixelSize(18);
edit->setFont(font); // 设置字体大小
this->setCentralWidget(edit);
// 设置槽函数
this->connect(action_read, &QAction::triggered, this, &MainWindow::handle_read_file);
this->connect(action_save, &QAction::triggered, this, &MainWindow::handle_save_file);
}
void MainWindow::handle_save_file()
{
// 通过对话打开文件
QString path = QFileDialog::getSaveFileName(this, "保存文件");
// 构建QFile对象
QFile file = QFile(path);
// 打开文件
bool ret = file.open(QIODevice::WriteOnly);
if (!ret) {
qDebug() << "handle_save_file, 打开文件失败!";
return;
}
// 向文件中写数据
QString text = edit->toPlainText();
file.write(text.toUtf8());
// 显示到状态栏中
QStatusBar* statusBar = this->statusBar();
statusBar->showMessage(path + QString(" 写入成功!"), 10000);
// 关闭文件
file.close();
}
void MainWindow::handle_read_file()
{
QString path = QFileDialog::getOpenFileName(this, "读取文件");
QFile file = QFile(path);
bool ret = file.open(QIODevice::ReadOnly);
if (!ret) {
qDebug() << "handle_read_file, 打开文件失败!";
return;
}
// QByteArray可以转换成QString
QString text = file.readAll();
edit->setPlainText(text);
QStatusBar* statusBar = this->statusBar();
statusBar->showMessage(path + QString(" 读取成功!"), 10000);
file.close();
}
MainWindow::~MainWindow()
{
delete ui;
}
1.3 例子2,输出文件的属性
使用QFileInfo
,该类用于获取文件或目录的详细信息,例如文件路径、大小、创建时间、修改时间等。下面是一个例子
cpp
void Widget::on_pushButton_clicked()
{
QString path = QFileDialog::getOpenFileName(this);
QFileInfo info(path);
qDebug() << "File path:" << info.filePath();
qDebug() << "File name:" << info.fileName();
qDebug() << "Base name:" << info.baseName();
qDebug() << "Suffix:" << info.suffix();
qDebug() << "Size:" << info.size() << "bytes";
qDebug() << "Exists:" << info.exists();
qDebug() << "Is file:" << info.isFile();
qDebug() << "Is directory:" << info.isDir();
qDebug() << "Last modified:" << info.lastModified().toString("yyyy/MM/dd hh:mm:ss");
}
运行结果如下

2. Qt 多线程
使用QThread
类
2.1 常用API
API接口 | 描述 |
---|---|
run() |
线程的入口函数。开发者需要重写此函数来定义线程执行的任务。 |
start() |
通过调用 run() 函数开始执行线程。操作系统将根据优先级参数调度线程。如果线程已经在运行,则此方法不执行任何操作。 |
currentThread() |
返回一个指向管理当前执行线程的 QThread 的指针。 |
isRunning() |
如果线程正在运行则返回 true ;否则返回 false 。 |
sleep() / msleep() / usleep() |
使线程休眠,单位分别为秒、毫秒、微秒。这些函数允许线程暂停执行指定的时间。 |
wait() |
阻塞调用它的线程,直到与此 QThread 对象关联的线程完成执行(即从 run() 返回),或者等待时间已过(如果指定了等待时间)。如果线程已完成或尚未启动,则返回 true ;如果等待超时,则返回 false 。 |
terminate() |
尝试立即终止线程的执行。但请注意,由于操作系统的调度策略,线程可能不会立即终止。在调用 terminate() 后,应使用 QThread::wait() 来确保线程已真正停止。然而,通常不推荐使用 terminate() ,因为它可能会导致资源泄露或其他不可预知的行为。 |
finished() |
当线程结束时会发出此信号。可以通过连接此信号来执行清理工作或其他必要的操作。 |
isFinished() const |
判断线程中的任务是否处理完毕。 |
priority() const |
得到当前线程的优先级。 |
setPriority(Priority priority) |
设置线程的优先级。 |
exit(int returnCode = 0) |
退出线程,停止底层的事件循环。 |
quit() |
退出线程的事件循环,与调用 exit() 效果相同。 |
创建线程的步骤
- 自定义一个类,继承于 QThread,并且只有一个线程处理函数(和主线程不是同一个线程),这个线程处理函数主要就是重写父类中的
run()
函数。 - 线程处理函数里面写入需要执行的复杂数据处理
- 启动线程不能直接调用 run()函数,需要使用对象来调用 start()函数实现线程启动
- 线程处理函数执行结束后可以定义一个信号来告诉主线程
- 最后关闭线程
2.2 例子1,自定义定时器
不使用QTimer,实现定时效果,首先定义一个Thread类继承自QThread
cpp
/* thread.h */
#ifndef THREAD_H
#define THREAD_H
#include <QThread>
#include <QWidget>
class Thread : public QThread
{
Q_OBJECT
public:
Thread();
virtual void run();
signals:
void timeout(); // 自定义信号,每1s发送1次, 一共发送10次
};
#endif // THREAD_H
/* thread.cpp */
#include "thread.h"
Thread::Thread()
{
}
void Thread::run()
{
for (int i = 1; i <= 10; ++i) {
sleep(1);
emit timeout();
}
}
在widget.ui
中拖入一个QLcdNumber,下面是Widget
类的代码
cpp
/* widget.h */
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "thread.h"
QT_BEGIN_NAMESPACE
namespace Ui
{
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget* parent = nullptr);
void handlerTime();
~Widget();
private:
Ui::Widget* ui;
Thread timer;
};
#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->lcdNumber->display(10);
connect(&timer, &Thread::timeout, this, &Widget::handlerTime); // 连接信号槽
timer.start(); // 启动该线程
}
void Widget::handlerTime()
{
int val = ui->lcdNumber->intValue();
ui->lcdNumber->display(--val);
}
Widget::~Widget()
{
delete ui;
}
3. 线程安全
3.1 互斥锁
使用QMutex
,下面是一个例子
不加互斥锁,让两个线程++同一个变量
cpp
/* thread.h */
#ifndef THREAD_H
#define THREAD_H
#include <QThread>
#include <QWidget>
class Thread : public QThread
{
Q_OBJECT
public:
Thread();
virtual void run();
static int num;
};
#endif // THREAD_H
/* thread.cpp */
#include "thread.h"
int Thread::num = 0;
Thread::Thread()
{
}
void Thread::run()
{
for (int i = 1; i <= 50000; ++i) {
num++;
}
}
在Widget
中创建这两个线程,widget.cpp
如下
cpp
#include "widget.h"
#include "ui_widget.h"
#include "thread.h"
Widget::Widget(QWidget* parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
Thread t1, t2;
t1.start();
t2.start();
// 等待t1, t2执行完
t1.wait();
t2.wait();
qDebug() << Thread::num;
}
Widget::~Widget()
{
delete ui;
}
由于++操作并不是原子的,所以结果可能并不全是100000
,要想结果一致,需要加互斥锁,修改thread.cpp
cpp
#include "thread.h"
int Thread::num = 0;
QMutex Thread::mutex = QMutex();
Thread::Thread()
{
}
void Thread::run()
{
for (int i = 1; i <= 50000; ++i) {
mutex.lock();
num++;
mutex.unlock();
}
}
这样,结果就能稳定了
RAII风格的锁,使用[explicit noexcept] QMutexLocker::QMutexLocker(Mutex *mutex)
,将thread.cpp
中的代码改为
cpp
#include "thread.h"
int Thread::num = 0;
QMutex Thread::mutex = QMutex();
Thread::Thread()
{
}
void Thread::run()
{
for (int i = 1; i <= 50000; ++i) {
QMutexLocker locker(&mutex);
num++;
}
}
仍能起到同样的效果
3.2 条件变量
使用QWaitCondition
类,下面是一个例子,仅仅作为演示
cpp
/* thread.cpp */
#include "thread.h"
QMutex Thread::mutex = QMutex();
QWaitCondition Thread::condition = QWaitCondition();
int Thread::_cnt = 0;
Thread::Thread(int num)
: _num(num) {};
void Thread::run()
{
qDebug() << "Thread-" << _num << "created done.";
for (;;) {
QMutexLocker locker(&Thread::mutex);
// 阻塞当前线程,等待别的线程使用notify_one()或wakeAll()来唤醒它。
condition.wait(&mutex);
_cnt++;
printf("Thread-%d, cnt: %d\n", _num, _cnt);
condition.notify_one();
}
}
cpp
/* widget.cpp */
#include "widget.h"
#include <Windows.h>
#include "thread.h"
#include "ui_widget.h"
Widget::Widget(QWidget* parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
Thread t1(1);
Thread t2(2);
Thread t3(3);
t1.start();
t2.start();
t3.start();
QThread::msleep(1000);
qDebug() << "Main thread start control.";
for(;;) {
QMutexLocker locker(&Thread::mutex);
Thread::condition.notify_one(); // 唤醒等待队列中等待的一个线程, 默认是第一个
}
}
Widget::~Widget()
{
delete ui;
}
运行后会发现,线程以1,2,3的顺序一直在运行
4. 网络编程
4.1 UDP Socket
QUdpSocket
表示一个UDP的socket文件
API 接口 | 类型 | 说明 | 对标原生 API |
---|---|---|---|
bind(const QHostAddress&, quint16) | 方法 | 绑定指定的本地地址和端口号,准备接收数据报 | bind |
receiveDatagram() | 方法 | 接收一个 UDP 数据报并返回 QNetworkDatagram 对象,包含数据报的内容和发送方信息 |
recvfrom |
writeDatagram(const QNetworkDatagram&) | 方法 | 发送一个 UDP 数据报,包含目标地址和端口号 | sendto |
readyRead | 信号 | 当有新的数据报到达并准备好读取时触发,通知应用程序可以读取数据 | 无(类似于 I/O 多路复用的通知机制) |
QNetworkDatagram
表示一个UDP数据报
方法/构造函数 | 类型 | 说明 | 对标原生 API |
---|---|---|---|
QNetworkDatagram(const QByteArray&, const QHostAddress& ip, quint16 port) | 构造函数 | 通过 QByteArray 数据、目标 IP 地址和目标端口号构造一个 UDP 数据报。通常用于发送数据时封装数据报内容。 |
无 |
data() | 方法 | 获取数据报内部持有的数据,返回 QByteArray 类型,包含数据报的原始字节数据。 |
无(在网络编程中,原生 API 通常通过读取缓冲区获得数据) |
senderAddress() | 方法 | 获取数据报中包含的对端的 IP 地址,返回 QHostAddress ,表示发送该数据报的远端主机地址。 |
无,但在原生 UDP 编程中,recvfrom 函数包含了获取发送方地址的功能。 |
senderPort() | 方法 | 获取数据报中包含的对端的端口号,返回 quint16 类型,表示发送该数据报的远端主机的端口号。 |
无,但在原生 UDP 编程中,recvfrom 函数包含了获取发送方端口号的功能。 |
4.2 UDP Server
下面是一个UDP回显服务器
首先,要在.pro文件中加上network
模块
cpp
QT += core gui network
在widget.ui
中铺上一个QListWidget
下面是widget.h
的代码
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui
{
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget* parent = nullptr);
void handlerRequest();
QString resolutionRequest(const QString& request);
~Widget();
private:
QUdpSocket* socket;
Ui::Widget* ui;
};
#endif // WIDGET_H
下面是wigdet.cpp
的代码
cpp
#include "widget.h"
#include <QMessageBox>
#include <QNetworkDatagram>
#include "ui_widget.h"
Widget::Widget(QWidget* parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
socket = new QUdpSocket(this);
this->setWindowTitle("服务端");
// 连接信号槽,用于处理来自客户端的请求
this->connect(socket, &QUdpSocket::readyRead, this, &Widget::handlerRequest);
// 绑定端口号和IP(该套接字将会监听所有本地网络接口)
bool ret = socket->bind(QHostAddress::Any, 9000);
if (!ret) {
QMessageBox::critical(this, "绑定出错!", socket->errorString());
return;
}
qDebug() << "绑定端口和IP成功";
}
void Widget::handlerRequest()
{
// 读取请求
const QNetworkDatagram& requestDatagram = socket->receiveDatagram();
QString requet = requestDatagram.data();
// 获取客户端的IP和端口号
QHostAddress peerIp = requestDatagram.senderAddress();
qint16 peerPort = requestDatagram.senderPort();
// 解析请求, 得到响应
QString response = resolutionRequest(requet);
// 构建数据包,将数据发送给客户端
QNetworkDatagram sendDatagram = QNetworkDatagram(response.toUtf8(), peerIp, peerPort);
socket->writeDatagram(sendDatagram);
// 自己这里要显示数据
QString log = "[" + peerIp.toString() + ":" + QString::number(peerPort) + "] request: " +
requet + " response: " + response;
ui->listWidget->addItem(log);
}
QString Widget::resolutionRequest(const QString& request)
{
// 用于解析请求, 这里仅做简单的字符串处理(将字符串逆转)
QString res;
for (int i = request.size() - 1; i >= 0; --i) {
res += request[i];
}
return res;
}
Widget::~Widget()
{
delete ui;
}
4.3 UDP Client
在wiget.ui
中设置基本框架

下面是widget.h
的代码
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QUdpSocket>
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui
{
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget* parent = nullptr);
void handlerResponse();
~Widget();
private slots:
void on_pushButton_clicked();
private:
const static QHostAddress SERVER_IP;
const static qint16 SERVER_PORT;
QUdpSocket* socket;
Ui::Widget* ui;
};
#endif // WIDGET_H
下面是widget.cpp
的代码
cpp
#include "widget.h"
#include <QNetworkDatagram>
#include <QShortcut>
#include "ui_widget.h"
const QHostAddress Widget::SERVER_IP = QHostAddress("127.0.0.1");
const qint16 Widget::SERVER_PORT = 9000;
Widget::Widget(QWidget* parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
socket = new QUdpSocket(this);
this->setWindowTitle("客户端");
this->connect(ui->lineEdit, &QLineEdit::editingFinished, this, &Widget::on_pushButton_clicked); // 按回车发送
this->connect(socket, &QUdpSocket::readyRead, this, &Widget::handlerResponse);
qDebug() << "连接服务端成功!";
}
void Widget::handlerResponse()
{
QNetworkDatagram responseDatagram = socket->receiveDatagram(); // 接受数据报
ui->listWidget->addItem(QString("Server say: ") + responseDatagram.data()); // 显示
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
const QString& text = ui->lineEdit->text();
if (text == "") {
qDebug() << "LineEdit Empty";
return;
}
QNetworkDatagram requestDatagram(text.toUtf8(), SERVER_IP, SERVER_PORT); // 构建数据报
socket->writeDatagram(requestDatagram); // 发送数据报
ui->listWidget->addItem(QString("Client say: ") + text); // 显示
ui->lineEdit->setText(""); // 清空
}
同时运行客户端与服务端,结果如下

4.4 TCP Socket
QTcpServer
用于监听端口和获取客户端连接
名称 | 类型 | 说明 | 对标原生 API |
---|---|---|---|
listen(const QHostAddress&, quint16 port) | 方法 | 绑定指定的地址和端口号,并开始监听。 | bind()和listen() |
nextPendingConnection() | 方法 | 从系统中获取一个已经建立好的 TCP 连接。返回一个 QTcpSocket ,表示这个客户端的连接。通过这个 socket 对象完成和客户端之间的通信。 |
accept() |
newConnection | 信号 | 有新的客户端建立连接后触发。类似于 IO 多路复用中的通知机制。 | 无(但类似于 IO 多路复用中的通知机制) |
QTcpSocket
用于客户端和服务器之间的数据交互
名称 | 类型 | 说明 | 对标原生 API |
---|---|---|---|
readAll() | 方法 | 读取当前接收缓冲区中的所有数据。返回 QByteArray 对象。 |
read() |
write(const QByteArray&) | 方法 | 把数据写入 socket 中。 |
write() |
deleteLater | 方法 | 把 socket 对象标记为无效。Qt 会在下个事件循环中析构释放该对象。 |
无(但类似于"半自动化的垃圾回收") |
readyRead | 信号 | 有数据到达并准备就绪时触发。 | 无(但类似于 IO 多路复用中的通知机制) |
disconnected | 信号 | 连接断开时触发。 | 无(但类似于 IO 多路复用中的通知机制) |
4.5 TCP Server
下面是一个TCP回显服务器,不要忘记在pro文件中加上network,首先在wigdet.ui
中添加一个QListWidget
wiget.h
如下
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QTcpSocket>
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui
{
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget* parent = nullptr);
void handleResponse();
~Widget();
private slots:
void on_pushButton_clicked();
private:
const static QHostAddress SERVER_IP;
const static qint16 SERVER_PORT;
Ui::Widget* ui;
QTcpSocket* socket;
};
#endif // WIDGET_H
widget.cpp
cpp
#include "widget.h"
#include <QMessageBox>
#include "ui_widget.h"
const QHostAddress Widget::SERVER_IP = QHostAddress("127.0.0.1");
const qint16 Widget::SERVER_PORT = 9000;
Widget::Widget(QWidget* parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("客户端");
socket = new QTcpSocket(this);
socket->connectToHost(Widget::SERVER_IP, Widget::SERVER_PORT); // 连接客户端,这是一个非阻塞的函数
bool ret = socket->waitForConnected(); // 等待连接服务器, 若ret为0表示三次握手成功
if (!ret) {
QMessageBox::critical(this, "等待连接失败", socket->errorString());
return;
}
this->connect(ui->lineEdit, &QLineEdit::editingFinished, this, &Widget::handleResponse); // 按回车发送
this->connect(socket, &QTcpSocket::readyRead, this, &Widget::handleResponse); // 设置信号槽, 当有数据来时执行
}
void Widget::handleResponse()
{
QString text = socket->readAll(); // 读取
ui->listWidget->addItem(QString("Server say: ") + text); // 显示
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
QString text = ui->lineEdit->text();
if (text == "") {
qDebug() << "Text empty!";
return;
}
ui->listWidget->addItem(QString("Client say: ") + text); // 显示
socket->write(text.toUtf8()); // 写给客户端
ui->lineEdit->setText(""); // 清空
}
4.6 TCP Client
widget.ui
如下

wigdet.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QTcpSocket>
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui
{
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget* parent = nullptr);
void handleResponse();
~Widget();
private slots:
void on_pushButton_clicked();
private:
const static QHostAddress SERVER_IP;
const static qint16 SERVER_PORT;
Ui::Widget* ui;
QTcpSocket* socket;
};
#endif // WIDGET_H
widget.cpp
cpp
#include "widget.h"
#include <QMessageBox>
#include "ui_widget.h"
const QHostAddress Widget::SERVER_IP = QHostAddress("127.0.0.1");
const qint16 Widget::SERVER_PORT = 9000;
Widget::Widget(QWidget* parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("客户端");
socket = new QTcpSocket(this);
socket->connectToHost(Widget::SERVER_IP, Widget::SERVER_PORT); // 连接客户端,这是一个非阻塞的函数
bool ret = socket->waitForConnected(); // 等待连接服务器, 若ret为0表示三次握手成功
if (!ret) {
QMessageBox::critical(this, "等待连接失败", socket->errorString());
return;
}
this->connect(ui->lineEdit, &QLineEdit::editingFinished, this, &Widget::on_pushButton_clicked); // 按回车发送
this->connect(socket, &QTcpSocket::readyRead, this, &Widget::handleResponse); // 设置信号槽, 当有数据来时执行
}
void Widget::handleResponse()
{
QString text = socket->readAll(); // 读取
ui->listWidget->addItem(QString("Server say: ") + text); // 显示
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
QString text = ui->lineEdit->text();
if (text == "") {
qDebug() << "Text empty!";
return;
}
ui->listWidget->addItem(QString("Client say: ") + text); // 显示
socket->write(text.toUtf8()); // 写给客户端
ui->lineEdit->setText(""); // 清空
}
运行结果如下

4.7 HTTP 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 | 描述 body 的长度。 |
LocationHeader | 用于重定向报文中指定重定向地址(响应中使用,请求用不到)。 |
CookieHeader | 设置 cookie 。 |
UserAgentHeader | 设置 User-Agent 。 |
QNetworkReply
表示一个HTTP响应
方法 | 说明 |
---|---|
error() | 获取出错状态。 |
errorString() | 获取出错原因的文本。 |
readAll() | 读取响应 body。 |
header(QNetworkRequest::KnownHeaders header) | 读取响应指定 header 的值。 |
QNetworkReply
还有一个信号finished
会在客户端收到完整的响应数据触发
在读取完后调用deleteLater()
来释放该响应
4.8 HTTP Client
下面是一个简单的HTTP Client
在widget.ui
中设置基本框架

widget.cpp
如下
cpp
#include "widget.h"
#include <QMessageBox>
#include <QNetworkReply>
#include "ui_widget.h"
Widget::Widget(QWidget* parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("HTTP客户端");
manager = new QNetworkAccessManager(this);
this->connect(ui->lineEdit, &QLineEdit::editingFinished, this, &Widget::on_pushButton_clicked);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
QString text = ui->lineEdit->text();
if (text.isEmpty()) {
qDebug() << "Text Empty!";
QMessageBox::warning(this, "警告", "输入框中没有内容!");
return;
}
// 构建请求
QUrl url(text);
QNetworkRequest request(url);
// 发送请求
QNetworkReply* response = manager->get(request);
if (response->error() == QNetworkReply::NoError) {
// 没有错误
connect(response, &QNetworkReply::finished, this, [=]() { // 当在客户端收到完整的响应数据触发
qDebug() << "读取到了数据";
QString resultHtml = response->readAll(); // 读取数据
ui->extEdit->setPlainText(resultHtml);
});
} else {
// 有错误
QString errorStr = response->errorString();
qDebug() << errorStr;
QMessageBox::warning(this, "警告", errorStr);
}
}
运行结果如下

5. 播放音频
使用QSoundEffect
类,下面是一个例子
首先需要在pro文件中加上multimedia
模块
widget.cpp
如下
cpp
#include "widget.h"
#include <error.h>
#include <QFile>
#include "ui_widget.h"
Widget::Widget(QWidget* parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
sound = new QSoundEffect(this);
// QString filePath = "D:/bit/QT/QTPro/25_2_24QSound_1/666.wav"; // 使用正斜杠
QString filePath = ":/sound/666.wav";
if (!QFile::exists(filePath)) {
qDebug() << "音频文件不存在!";
return;
}
// 正确转换为QUrl(必须使用QUrl,使用QSting会加载失败
const QUrl path = QUrl::fromLocalFile(filePath);
// const QUrl path = QUrl(filePath); err
sound->setSource(path);
sound->setLoopCount(QSoundEffect::Infinite);
connect(sound, &QSoundEffect::statusChanged, this, [this]() {
if (sound->status() == QSoundEffect::Ready) {
qDebug() << "音频加载成功!";
} else if (sound->status() == QSoundEffect::Error) {
qDebug() << "音频加载失败!原因: 文件格式不支持或路径错误";
}
});
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
sound->play();
}
void Widget::on_pushButton_2_clicked()
{
sound->stop();
}
点击按钮1播放音频,按钮2暂停音频