当前位置: 首页 > news >正文

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. 文件操作

继承关系图如下

image-20250218174016898

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");
}

运行结果如下

image-20250218203200549

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() 效果相同。

创建线程的步骤

  1. 自定义一个类,继承于 QThread,并且只有一个线程处理函数(和主线程不是同一个线程),这个线程处理函数主要就是重写父类中的run()函数。
  2. 线程处理函数里面写入需要执行的复杂数据处理
  3. 启动线程不能直接调用 run()函数,需要使用对象来调用 start()函数实现线程启动
  4. 线程处理函数执行结束后可以定义一个信号来告诉主线程
  5. 最后关闭线程

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中设置基本框架

image-20250222220256639

下面是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("");                                               // 清空
}

同时运行客户端与服务端,结果如下

image-20250222220455394

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如下

image-20250223200921286

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("");                               // 清空
}

运行结果如下

image-20250223201219842

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),会在 QNetworkAccessManagerpost 方法中通过单独的参数来传入 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中设置基本框架

image-20250223233224665

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);}
}

运行结果如下

image-20250223233337393

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&#xff0c;简单记事本1.3 例子2&#xff0c;输出文件的属性 2. Qt 多线程2.1 常用API2.2 例子1&#xff0c;自定义定时器 3. 线程安全3.1 互斥锁3.2 条件变量 4. 网络编程4.1 UDP Socket4.2 UDP Server4.3 UDP Client4.4 TCP Socket4.5 …...

如何使用ArcGIS Pro制作横向图例:详细步骤与实践指南

ArcGIS Pro&#xff0c;作为Esri公司推出的新一代地理信息系统&#xff08;GIS&#xff09;平台&#xff0c;以其强大的功能和灵活的操作界面&#xff0c;在地理数据处理、地图制作和空间分析等领域发挥着重要作用。 在地图制作过程中&#xff0c;图例作为地图的重要组成部分&…...

Kotlin 嵌套类和内部类

在Kotlin中&#xff0c;嵌套类&#xff08;Nested Class&#xff09;和内部类&#xff08;Inner Class&#xff09;是两种不同的类&#xff0c;它们在定义和使用上有一些区别。 1.嵌套类&#xff08;Nested Classes&#xff09;默认是静态的&#xff08;即等同于Java中的stati…...

蓝蝶(BlueStacks)模拟器Root、Magisk、LSPosed及Shamiko框架安装与过应用检测指南

蓝蝶&#xff08;BlueStacks&#xff09;模拟器Root、Magisk、LSPosed及Shamiko框架安装与过应用检测指南 蓝蝶bluestacks模拟器root和magisk以及Lsposed和shamiko框架的安装过应用检测 一、引言 蓝蝶&#xff08;BlueStacks&#xff09;模拟器是一款广受欢迎的安卓模拟器&…...

OpenCV计算摄影学(6)高动态范围成像(HDR imaging)

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 高动态范围成像&#xff08;HDR imaging&#xff09;是一种图像处理技术&#xff0c;旨在通过增加图像的动态范围来更准确地表示真实世界的亮度差…...

[ComfyUI][AI生图]如何在Comfyui中安装插件管理器

如何在ComfyUI便携版中安装插件管理器 在现代软件环境中,图形用户界面(GUI)提供了一种直观的方式来与应用程序交互。ComfyUI是一个出色的GUI框架,它使用户能够通过图形化方式配置和管理他们的应用程序。特别是ComfyUI的便携版,它允许用户在没有安装的情况下使用这一工具,…...

初探Ollama与deepseek

什么是Ollama&#xff1f;它与大模型有什么联系&#xff1f; 简单说&#xff0c;Ollama就像是你电脑上的一个 “大模型小助手”。 以前&#xff0c;很多强大的大语言模型&#xff0c;比如能回答各种问题、写文章、翻译等的那些模型&#xff0c;要么只能在网上的服务器上用&am…...

Linux top 常用参数记录

top命令经常用来监控linux的系统状况&#xff0c;能实时显示系统中各个进程、线程的资源占用情况&#xff0c;是常用的性能分析工具。 一些常用参数记录 top的使用方式 top [-d number] | top [-bnp] # 5s 更新一次 top -d 5# 进行2次top命令的输出结果 top -n 2# 查看进程的…...

CCF-CSP认证 202104-1灰度直方图

题目描述 思路 首先输入矩阵长度、矩阵宽度和灰度范围&#xff0c;结果数组长度可固定&#xff0c;其中的元素要初始化为0。在输入灰度值的时候&#xff0c;结果数组中以该灰度值为索引的元素值1&#xff0c;即可统计每个灰度值的数量。 代码 C版&#xff1a; #include <…...

怎么下载安装yarn

安装 npm install --global yarn 是否安装成功 yarn -v Yarn 淘宝源安装&#xff0c;分别复制粘贴以下代码行到黑窗口运行即可 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、随便输个用户名&#xff0c;利用burp抓包 2、修改数据包 &#xff0c;发包 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 模块&#xff0c;究极缝合怪&#xff0c;东抄抄西抄抄&#xff0c;主要 copy 自苏神的文章&#xff0c;仅供自己参考&#…...

【计算机网络入门】初学计算机网络(七)

目录 1. 滑动窗口机制 2. 停止等待协议&#xff08;S-W&#xff09; 2.1 滑动窗口机制 2.2 确认机制 2.3 重传机制 2.4 为什么要给帧编号 3. 后退N帧协议&#xff08;GBN&#xff09; 3.1 滑动窗口机制 3.2 确认机制 3.3 重传机制 4. 选择重传协议&#xff08;SR&a…...

Conda 环境搭建实战:从基础到进阶

在当今复杂多变的软件开发与数据科学领域&#xff0c;拥有一个稳定、可复现且易于管理的开发环境是项目成功的基石。Conda 作为一款强大的跨平台环境管理与包管理工具&#xff0c;为开发者提供了便捷高效的环境搭建与依赖管理解决方案。本文将深入探讨 Conda 环境搭建的实战技巧…...

大数据-236 离线数仓 - 会员活跃度 WDS 与 ADS 导出到 MySQL 与 广告业务 需求分析

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; Java篇开始了&#xff01; 目前开始更新 MyBatis&#xff0c;一起深入浅出&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff0…...

fps项目总结:关于攻击与受击

文章目录 战斗交互攻击方命中区间 双方命中响应 攻击方&#xff1a;指定攻击动画指定动画命中区间&#xff1a;在动画中指定攻击范围以及命中响应动画通知&#xff1a;动画中攻击的开关——调用蓝图攻击函数&#xff1a;实现攻击检测以及命中响应通道检测&#xff1a;——自定义…...

coze生成的工作流,发布后,利用cmd命令行执行。可以定时发日报,周报等。让他总结你飞书里面的表格。都可以

coze生成的工作流&#xff0c;发布后&#xff0c;利用cmd命令行执行。可以定时发日报&#xff0c;周报等。让他总结你飞书里面的表格。都可以。 很简单。 准备工作&#xff0c;先发布你的工作流&#xff0c;和发布应用。 然后&#xff0c;点击扣子API 。 申请一个&#xff0…...

Windows 10 远程桌面连接使用指南

目录 一、引言 二、准备工作 1、确认系统版本 2、服务器端设置 三、客户端连接 1、打开远程桌面连接程序 2、输入连接信息 3、输入登录凭证 4、开始使用远程桌面 四、移动端连接&#xff08;以 iOS 为例&#xff09; 1、下载安装应用 2、添加远程计算机 3、进行连接…...

Difyにboto3を変更したカスタムDockerイメージの構築手順

Difyにboto3を変更したカスタムDockerイメージの構築手順 はじめに1. Dockerfileの作成2. Dockerイメージのビルド3. docker-compose.yamlの更新変更点&#xff1a; 4. コンテナの再起動注意事項まとめ はじめに DifyのDockerイメージに特定バージョンのboto3を変更する手順を…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...