文章目录
- 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
简单介绍一下文件操作的方法
-
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,简单记事本
下面是一个例子,实现了记事本的两个功能:
#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暂停音频