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

1.1 API
简单介绍一下文件操作的方法
-
QFile构造: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,简单记事本
下面是一个例子,实现了记事本的两个功能:
#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可以转换成QStringQString 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 ,该类用于获取文件或目录的详细信息,例如文件路径、大小、创建时间、修改时间等。下面是一个例子
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
/* 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类的代码
/* widget.h */
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include "thread.h"QT_BEGIN_NAMESPACE
namespace Ui
{
class Widget;
}
QT_END_NAMESPACEclass 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,下面是一个例子
不加互斥锁,让两个线程++同一个变量
/* 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如下
#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
#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中的代码改为
#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类,下面是一个例子,仅仅作为演示
/* 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();}
}
/* 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模块
QT += core gui network
在widget.ui中铺上一个QListWidget
下面是widget.h的代码
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QUdpSocket>QT_BEGIN_NAMESPACE
namespace Ui
{
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget* parent = nullptr);void handlerRequest();QString resolutionRequest(const QString& request);~Widget();private:QUdpSocket* socket;Ui::Widget* ui;
};
#endif // WIDGET_H
下面是wigdet.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的代码
#ifndef WIDGET_H
#define WIDGET_H#include <QUdpSocket>
#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui
{
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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的代码
#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如下
#ifndef WIDGET_H
#define WIDGET_H#include <QTcpSocket>
#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui
{
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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
#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
#ifndef WIDGET_H
#define WIDGET_H#include <QTcpSocket>
#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui
{
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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
#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如下
#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如下
#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); errsound->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暂停音频
相关文章:
Qt 文件操作+多线程+网络
文章目录 1. 文件操作1.1 API1.2 例子1,简单记事本1.3 例子2,输出文件的属性 2. Qt 多线程2.1 常用API2.2 例子1,自定义定时器 3. 线程安全3.1 互斥锁3.2 条件变量 4. 网络编程4.1 UDP Socket4.2 UDP Server4.3 UDP Client4.4 TCP Socket4.5 …...
如何使用ArcGIS Pro制作横向图例:详细步骤与实践指南
ArcGIS Pro,作为Esri公司推出的新一代地理信息系统(GIS)平台,以其强大的功能和灵活的操作界面,在地理数据处理、地图制作和空间分析等领域发挥着重要作用。 在地图制作过程中,图例作为地图的重要组成部分&…...
Kotlin 嵌套类和内部类
在Kotlin中,嵌套类(Nested Class)和内部类(Inner Class)是两种不同的类,它们在定义和使用上有一些区别。 1.嵌套类(Nested Classes)默认是静态的(即等同于Java中的stati…...
蓝蝶(BlueStacks)模拟器Root、Magisk、LSPosed及Shamiko框架安装与过应用检测指南
蓝蝶(BlueStacks)模拟器Root、Magisk、LSPosed及Shamiko框架安装与过应用检测指南 蓝蝶bluestacks模拟器root和magisk以及Lsposed和shamiko框架的安装过应用检测 一、引言 蓝蝶(BlueStacks)模拟器是一款广受欢迎的安卓模拟器&…...
OpenCV计算摄影学(6)高动态范围成像(HDR imaging)
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 高动态范围成像(HDR imaging)是一种图像处理技术,旨在通过增加图像的动态范围来更准确地表示真实世界的亮度差…...
[ComfyUI][AI生图]如何在Comfyui中安装插件管理器
如何在ComfyUI便携版中安装插件管理器 在现代软件环境中,图形用户界面(GUI)提供了一种直观的方式来与应用程序交互。ComfyUI是一个出色的GUI框架,它使用户能够通过图形化方式配置和管理他们的应用程序。特别是ComfyUI的便携版,它允许用户在没有安装的情况下使用这一工具,…...
初探Ollama与deepseek
什么是Ollama?它与大模型有什么联系? 简单说,Ollama就像是你电脑上的一个 “大模型小助手”。 以前,很多强大的大语言模型,比如能回答各种问题、写文章、翻译等的那些模型,要么只能在网上的服务器上用&am…...
Linux top 常用参数记录
top命令经常用来监控linux的系统状况,能实时显示系统中各个进程、线程的资源占用情况,是常用的性能分析工具。 一些常用参数记录 top的使用方式 top [-d number] | top [-bnp] # 5s 更新一次 top -d 5# 进行2次top命令的输出结果 top -n 2# 查看进程的…...
CCF-CSP认证 202104-1灰度直方图
题目描述 思路 首先输入矩阵长度、矩阵宽度和灰度范围,结果数组长度可固定,其中的元素要初始化为0。在输入灰度值的时候,结果数组中以该灰度值为索引的元素值1,即可统计每个灰度值的数量。 代码 C版: #include <…...
怎么下载安装yarn
安装 npm install --global yarn 是否安装成功 yarn -v Yarn 淘宝源安装,分别复制粘贴以下代码行到黑窗口运行即可 yarn config set registry https://registry.npm.taobao.org -g yarn config set sass_binary_site http://cdn.npm.taobao.org/dist/…...
Vulhub靶机 AppWeb认证绕过漏洞(CVE-2018-8715)(渗透测试详解)
一、开启vulhub环境 docker-compose up -d 启动docker ps 查看开放的端口 影响版本 Appweb 7.0.2以及之前的版本 二、访问靶机IP 8080端口 访问IP会弹出个登录框 1、随便输个用户名,利用burp抓包 2、修改数据包 ,发包 Authorization: Digest usern…...
CSS 系列之:grid 布局
基本概念 <template><div class"parent"><div class"box">p1-1</div><div class"box">p1-2</div><div class"box">p1-3</div></div><div class"parent"><…...
DeepSeek MLA(Multi-Head Latent Attention)算法浅析
目录 前言1. 从MHA、MQA、GQA到MLA1.1 MHA1.2 瓶颈1.3 MQA1.4 GQA1.5 MLA1.5.1 Part 11.5.2 Part 21.5.3 Part 3 结语参考 前言 学习 DeepSeek 中的 MLA 模块,究极缝合怪,东抄抄西抄抄,主要 copy 自苏神的文章,仅供自己参考&#…...
【计算机网络入门】初学计算机网络(七)
目录 1. 滑动窗口机制 2. 停止等待协议(S-W) 2.1 滑动窗口机制 2.2 确认机制 2.3 重传机制 2.4 为什么要给帧编号 3. 后退N帧协议(GBN) 3.1 滑动窗口机制 3.2 确认机制 3.3 重传机制 4. 选择重传协议(SR&a…...
Conda 环境搭建实战:从基础到进阶
在当今复杂多变的软件开发与数据科学领域,拥有一个稳定、可复现且易于管理的开发环境是项目成功的基石。Conda 作为一款强大的跨平台环境管理与包管理工具,为开发者提供了便捷高效的环境搭建与依赖管理解决方案。本文将深入探讨 Conda 环境搭建的实战技巧…...
大数据-236 离线数仓 - 会员活跃度 WDS 与 ADS 导出到 MySQL 与 广告业务 需求分析
点一下关注吧!!!非常感谢!!持续更新!!! Java篇开始了! 目前开始更新 MyBatis,一起深入浅出! 目前已经更新到了: Hadoop࿰…...
fps项目总结:关于攻击与受击
文章目录 战斗交互攻击方命中区间 双方命中响应 攻击方:指定攻击动画指定动画命中区间:在动画中指定攻击范围以及命中响应动画通知:动画中攻击的开关——调用蓝图攻击函数:实现攻击检测以及命中响应通道检测:——自定义…...
coze生成的工作流,发布后,利用cmd命令行执行。可以定时发日报,周报等。让他总结你飞书里面的表格。都可以
coze生成的工作流,发布后,利用cmd命令行执行。可以定时发日报,周报等。让他总结你飞书里面的表格。都可以。 很简单。 准备工作,先发布你的工作流,和发布应用。 然后,点击扣子API 。 申请一个࿰…...
Windows 10 远程桌面连接使用指南
目录 一、引言 二、准备工作 1、确认系统版本 2、服务器端设置 三、客户端连接 1、打开远程桌面连接程序 2、输入连接信息 3、输入登录凭证 4、开始使用远程桌面 四、移动端连接(以 iOS 为例) 1、下载安装应用 2、添加远程计算机 3、进行连接…...
Difyにboto3を変更したカスタムDockerイメージの構築手順
Difyにboto3を変更したカスタムDockerイメージの構築手順 はじめに1. Dockerfileの作成2. Dockerイメージのビルド3. docker-compose.yamlの更新変更点: 4. コンテナの再起動注意事項まとめ はじめに DifyのDockerイメージに特定バージョンのboto3を変更する手順を…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果,那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统
Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...
