本篇开始讲解 Qt 的一些常用类或常用模块。这对我们开发有很大的帮助。包括文件读写操作,绘图,图表的使用,多线程,网络编程,多媒体,及数据库的使用。
这些都是嵌入式里常用的类或模块,我们需要掌握它们的使用方法。
文本读写
在很多时候我们需要读写文本文件进行读写,比如写个 Mp3 音乐播放器需要读 Mp3 歌词
里的文本,比如修改了一个 txt 文件后保存,就需要对这个文件进行读写操作。本章介绍简单
的文本文件读写,内容精简,让大家了解文本读写的基本操作。
QT的文件读写使用的是C++原生库吗?
Qt 的文件读写操作并非依赖 C++ 原生库,而是借助 Qt 自身提供的 I/O 类来实现。这些类具备跨平台特性,能够在不同操作系统上保持一致的行为表现。下面为你介绍 Qt 中进行文件读写的主要类和方法:
- QFile 类
这是 Qt 里用于文件操作的基础类,可对任意类型的文件开展读写操作。以下是一个简单的使用示例:
#include <QFile> #include <QTextStream> #include <QDebug> // 写入文件 void writeFile(const QString& filePath, const QString& content) { QFile file(filePath); if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&file); out << content; file.close(); } else { qDebug() << "无法打开文件进行写入:" << file.errorString(); } } // 读取文件 QString readFile(const QString& filePath) { QFile file(filePath); QString content; if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in(&file); content = in.readAll(); file.close(); } else { qDebug() << "无法打开文件进行读取:" << file.errorString(); } return content; }
QFileInfo 类
该类可用于获取文件的相关信息,像文件的大小、修改日期、权限等。示例如下:
#include <QFileInfo> void fileInfoExample(const QString& filePath) { QFileInfo fileInfo(filePath); if (fileInfo.exists()) { qDebug() << "文件名称:" << fileInfo.fileName(); qDebug() << "文件大小:" << fileInfo.size() << "字节"; qDebug() << "最后修改时间:" << fileInfo.lastModified().toString(); qDebug() << "是否为目录:" << fileInfo.isDir(); } }
QTextStream 类
此类别专门用于处理文本流,能自动处理不同平台的换行符以及字符编码问题。例如:
#include <QTextStream> // 逐行读取文本文件 void readLineByLine(const QString& filePath) { QFile file(filePath); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in(&file); while (!in.atEnd()) { QString line = in.readLine(); // 处理每一行数据 qDebug() << "行内容:" << line; } file.close(); } }
QDataStream 类
该类用于处理二进制数据流,能够对 Qt 的基本数据类型(如 QString、QImage 等)进行序列化和反序列化操作。示例如下:
#include <QDataStream> // 写入二进制数据 void writeBinaryData(const QString& filePath) { QFile file(filePath); if (file.open(QIODevice::WriteOnly)) { QDataStream out(&file); out.setVersion(QDataStream::Qt_5_15); // 设置数据流版本 out << QString("Hello, Qt!") << 12345 << QByteArray("Binary Data"); file.close(); } } // 读取二进制数据 void readBinaryData(const QString& filePath) { QFile file(filePath); if (file.open(QIODevice::ReadOnly)) { QDataStream in(&file); in.setVersion(QDataStream::Qt_5_15); QString str; int number; QByteArray data; in >> str >> number >> data; file.close(); qDebug() << "读取的字符串:" << str; qDebug() << "读取的数字:" << number; qDebug() << "读取的字节数组:" << data; } }
为何不使用 C++ 原生库?
跨平台性:Qt 的 I/O 类能够自动处理不同操作系统在文件路径分隔符、文本编码等方面的差异。
对象化设计:Qt 的 I/O 类采用面向对象的设计,使用起来更加直观便捷。
集成性:这些类可以与 Qt 的信号槽机制、事件循环等其他组件很好地集成。
错误处理:Qt 的 I/O 类提供了更为友好的错误处理方式,例如通过
errorString()
方法获取详细的错误信息。特殊场景下的选择
要是你有特殊的需求,比如需要进行底层的文件操作,或者要与 C 库进行交互,也可以在 Qt 项目中使用 C++ 原生的文件流(如
std::fstream
)或者 C 标准库函数(如fopen
)。不过,这种做法会丧失 Qt 跨平台的部分优势。更多参考正点原子的QT开发手册以及网络资料。
绘图与图表
绘图与图表在嵌入式里有的比较多,尤其是图表,我们常在股票里看到的“图表折线/曲线图/饼状图等”都可以用 Qt 的图表来实现。绘图和图表的内容本章主要介绍绘图和图表的基本操作,以简单的例子呈现绘图与图表的用法,目的就是快速入门绘图与图表,关于绘图与图表详解最好是看 Qt 官方的帮助文档。
Qt 提供了强大的绘图和表格功能,下面分别介绍这两部分的核心类和使用方法:
一、Qt 绘图系统
核心类介绍
QPainter:负责实际的绘制操作,提供了绘制线条、矩形、椭圆、文本、图像等功能。
QPaintDevice:可绘制的设备抽象基类,如 QWidget、QPixmap、QImage 等。
QPaintEngine:负责将绘图命令转换为特定设备的绘制指令,一般由 Qt 自动处理。
自定义绘制示例
以下是一个自定义 QWidget 绘制的示例:
#include <QWidget> #include <QPainter> #include <QPen> #include <QBrush> class CustomWidget : public QWidget { public: CustomWidget(QWidget* parent = nullptr) : QWidget(parent) {} protected: void paintEvent(QPaintEvent* event) override { Q_UNUSED(event); QPainter painter(this); // 设置抗锯齿 painter.setRenderHint(QPainter::Antialiasing); // 绘制文本 painter.drawText(rect(), Qt::AlignCenter, "Hello, Qt Drawing!"); // 绘制线条 QPen pen(Qt::blue, 2, Qt::DashLine); painter.setPen(pen); painter.drawLine(10, 10, width()-10, height()-10); // 绘制矩形 QBrush brush(Qt::red, Qt::Dense3Pattern); painter.setBrush(brush); painter.drawRect(50, 50, 100, 70); // 绘制椭圆 painter.setPen(QPen(Qt::green, 3)); painter.setBrush(Qt::NoBrush); painter.drawEllipse(200, 50, 80, 80); // 绘制弧线 painter.drawArc(50, 150, 100, 100, 0, 16*90); // 90度弧线 } };
高级绘图功能
渐变填充:
QLinearGradient gradient(0, 0, width(), height()); gradient.setColorAt(0.0, Qt::white); gradient.setColorAt(1.0, Qt::black); painter.fillRect(rect(), gradient);
图像绘制:
QPixmap pixmap("image.jpg"); painter.drawPixmap(100, 100, pixmap.scaled(200, 200, Qt::KeepAspectRatio));
路径绘制:
QPainterPath path; path.moveTo(50, 50); path.lineTo(150, 50); path.lineTo(100, 150); path.closeSubpath(); painter.fillPath(path, Qt::yellow); painter.drawPath(path);
二、Qt 表格系统
核心类介绍
QTableView:表格视图类,负责显示表格数据,支持自定义外观和交互。
QStandardItemModel:标准项模型,提供通用的表格数据存储。
QAbstractItemModel:所有模型的抽象基类,可用于创建自定义模型。
QHeaderView:表头视图类,可自定义表头。
简单表格示例
#include <QApplication> #include <QTableView> #include <QStandardItemModel> int main(int argc, char *argv[]) { QApplication a(argc, argv); // 创建模型 QStandardItemModel model(4, 3); // 4行3列 model.setHorizontalHeaderLabels({"姓名", "年龄", "职业"}); // 设置数据 QList<QStandardItem*> row1; row1.append(new QStandardItem("张三")); row1.append(new QStandardItem("25")); row1.append(new QStandardItem("工程师")); model.appendRow(row1); QList<QStandardItem*> row2; row2.append(new QStandardItem("李四")); row2.append(new QStandardItem("30")); row2.append(new QStandardItem("设计师")); model.appendRow(row2); // 创建视图 QTableView tableView; tableView.setModel(&model); tableView.setWindowTitle("简单表格示例"); tableView.resize(400, 300); tableView.show(); return a.exec(); }
自定义表格功能
设置单元格样式:
QStandardItem* item = new QStandardItem("高亮项"); item->setBackground(Qt::lightGray); item->setForeground(Qt::blue); item->setFont(QFont("Arial", 10, QFont::Bold)); model.setItem(0, 0, item);
设置单元格编辑标志:
item->setFlags(item->flags() & ~Qt::ItemIsEditable); // 不可编辑
表格选择模式:
tableView.setSelectionBehavior(QAbstractItemView::SelectRows); // 整行选择 tableView.setSelectionMode(QAbstractItemView::SingleSelection); // 单选
隐藏列:
tableView.setColumnHidden(2, true); // 隐藏第三列
三、绘图与表格结合应用
在某些场景下,你可能需要在表格中显示自定义绘制的内容,例如在单元格中绘制图表。这时可以通过自定义委托(Delegate)来实现:
#include <QItemDelegate> #include <QPainter> class ProgressBarDelegate : public QItemDelegate { public: ProgressBarDelegate(QObject* parent = nullptr) : QItemDelegate(parent) {} void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override { // 获取单元格数据(假设为进度值,0-100) int progress = index.model()->data(index, Qt::DisplayRole).toInt(); // 绘制进度条背景 painter->fillRect(option.rect, Qt::lightGray); // 绘制进度条 QRect progressRect(option.rect); progressRect.setWidth(progressRect.width() * progress / 100); painter->fillRect(progressRect, Qt::green); // 绘制文本 painter->drawText(option.rect, Qt::AlignCenter, QString("%1%").arg(progress)); } }; // 在表格中使用委托 tableView.setItemDelegateForColumn(1, new ProgressBarDelegate(&tableView));
四、总结
绘图系统:Qt 绘图提供了灵活且强大的 API,可以绘制从简单图形到复杂图表的各种内容,适用于自定义控件、数据可视化等场景。
表格系统:Qt 表格采用 MVC(模型 - 视图 - 控制器)架构,分离了数据存储和显示,支持高度自定义,适用于数据展示、编辑等场景。
结合应用:通过自定义委托,可以在表格中集成复杂的绘制内容,实现更丰富的用户界面。
多线程
QT的多线程使用的是C++原生库吗?
Qt 的多线程实现并非基于 C++ 原生库,而是拥有一套独立且强大的多线程框架。这套框架提供了比 C++ 标准库更高级的抽象,能让开发者更便捷地处理线程同步、信号传递等复杂问题。下面为你介绍 Qt 中主要的多线程类和方法:
QThread 类
这是 Qt 多线程编程的核心类,可用于创建和管理线程。下面是几种常见的使用方式:
方式一:继承 QThread 并重写 run () 方法
#include <QThread> #include <QDebug> class WorkerThread : public QThread { Q_OBJECT protected: void run() override { // 在线程中执行的代码 for (int i = 0; i < 10; ++i) { qDebug() << "Worker thread:" << i; msleep(100); // 线程休眠100毫秒 } emit finished(); // 发出完成信号 } signals: void finished(); };
使用示例:
WorkerThread* thread = new WorkerThread(); connect(thread, &WorkerThread::finished, thread, &QObject::deleteLater); thread->start(); // 启动线程
方式二:使用 QObject 和 moveToThread ()
#include <QObject> #include <QThread> #include <QDebug> class Worker : public QObject { Q_OBJECT public slots: void doWork() { // 在线程中执行的代码 for (int i = 0; i < 10; ++i) { qDebug() << "Worker:" << i; QThread::msleep(100); } emit resultReady("Work done"); } signals: void resultReady(const QString &result); }; // 在主线程中使用 QThread* thread = new QThread; Worker* worker = new Worker; worker->moveToThread(thread); connect(thread, &QThread::started, worker, &Worker::doWork); connect(worker, &Worker::resultReady, [](const QString &result) { qDebug() << "Result:" << result; }); connect(worker, &Worker::resultReady, thread, &QThread::quit); connect(worker, &Worker::resultReady, worker, &Worker::deleteLater); connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); // 启动线程
QtConcurrent 命名空间
该命名空间提供了高层级的 API,能以函数式编程的风格处理并行任务,无需直接操作线程。
并行执行函数
#include <QtConcurrent> #include <QFuture> #include <QFutureWatcher> #include <QDebug> // 要并行执行的函数 int calculateSum(int a, int b) { return a + b; } // 在主线程中调用 QFuture<int> future = QtConcurrent::run(calculateSum, 10, 20); // 使用 QFutureWatcher 监听结果 QFutureWatcher<int>* watcher = new QFutureWatcher<int>(); connect(watcher, &QFutureWatcher<int>::finished, [watcher]() { qDebug() << "Sum result:" << watcher->result(); watcher->deleteLater(); }); watcher->setFuture(future);
并行处理容器
#include <QtConcurrent> #include <QVector> #include <QDebug> // 处理函数 int square(int value) { return value * value; } // 在主线程中调用 QVector<int> numbers = {1, 2, 3, 4, 5}; QFuture<QVector<int>> future = QtConcurrent::mapped(numbers, square); future.waitForFinished(); // 等待所有任务完成 QVector<int> results = future.results(); qDebug() << "Squared results:" << results;
线程同步机制
Qt 提供了多种线程同步工具,功能与 C++ 标准库类似,但使用起来更为便捷:
QMutex(互斥锁)
#include <QMutex> QMutex mutex; int sharedData = 0; void incrementSharedData() { mutex.lock(); ++sharedData; // 临界区 mutex.unlock(); } // 更安全的方式:使用 QMutexLocker void incrementSharedDataSafely() { QMutexLocker locker(&mutex); ++sharedData; // 锁在 locker 析构时自动释放 }
QWaitCondition(条件变量)
#include <QMutex> #include <QWaitCondition> QMutex mutex; QWaitCondition condition; bool ready = false; // 等待线程 void waitForReady() { QMutexLocker locker(&mutex); while (!ready) { condition.wait(&locker); // 释放锁并等待信号 } // 继续处理... } // 唤醒线程 void setReady() { QMutexLocker locker(&mutex); ready = true; condition.wakeOne(); // 唤醒一个等待的线程 }
QReadWriteLock(读写锁)
#include <QReadWriteLock> QReadWriteLock lock; QVector<int> data; // 允许多个线程同时读取 void readData() { lock.lockForRead(); // 读取 data... lock.unlock(); } // 只允许一个线程写入 void writeData() { lock.lockForWrite(); // 修改 data... lock.unlock(); }
信号与槽的线程安全
Qt 的信号槽机制支持多种连接方式,可自动处理跨线程通信:
// 在不同线程间安全地发送信号 connect(sender, &Sender::dataReady, receiver, &Receiver::processData, Qt::QueuedConnection); // 使用自动连接方式(根据发送者和接收者所在线程自动选择) connect(sender, &Sender::dataReady, receiver, &Receiver::processData, Qt::AutoConnection);
为何不使用 C++ 原生库?
与 Qt 生态的集成:Qt 的多线程类能与信号槽、事件循环等其他 Qt 组件无缝协作。
更高级的抽象:像
QThread
和QtConcurrent
提供了比 C++ 标准库更易用的接口。跨平台一致性:无论在哪个平台上,Qt 的多线程 API 行为都是一致的,无需担心底层实现的差异。
额外功能:例如
QTimer
可以在线程中使用,以及QFuture
提供的异步操作接口。特殊场景下的选择
如果你有特殊需求,比如需要使用 C++20 的协程(coroutines),或者要与使用 C++ 标准库的代码库集成,也可以在 Qt 项目中使用 C++ 原生的多线程库(如
<thread>
、<mutex>
等)。不过,这种情况下就无法享受 Qt 多线程框架带来的便利了。
网络编程
Qt 网络模块为我们提供了编写 TCP / IP 客户端和服务器的类。它提供了较低级别的类,例如代表低级网络概念的 QTcpSocket,QTcpServer 和 QUdpSocket,以及诸如 QNetworkRequest,QNetworkReply 和 QNetworkAccessManager 之类的高级类来执行使用通用协议的网络操作。它还提供了诸如QNetworkConfiguration,QNetworkConfigurationManager和QNetworkSession等类实现承载管理。
想要在程序中使用 Qt 网络模块,我们需要在 pro 项目配置文件里增加下面的一条语句。
QT += network
QT的网络编程使用的是C++原生库吗?
Qt 的网络编程并非基于 C++ 原生库,而是借助自身强大的网络模块实现。该模块提供了高级的面向对象 API,能够简化网络编程的复杂度,并且具备跨平台的特性。下面为你介绍 Qt 中主要的网络编程类和方法:
Qt 网络模块的核心组件
QTcpSocket 和 QTcpServer(TCP 编程)
这两个类可用于实现基于 TCP 协议的客户端和服务器应用程序。示例如下:
TCP 客户端示例:
#include <QTcpSocket> #include <QDebug> void tcpClientExample() { QTcpSocket socket; socket.connectToHost("127.0.0.1", 1234); // 连接到服务器 if (socket.waitForConnected(3000)) { // 等待连接建立,超时时间 3 秒 qDebug() << "Connected to server!"; // 发送数据 socket.write("Hello, server!"); socket.waitForBytesWritten(); // 接收数据 socket.waitForReadyRead(3000); qDebug() << "Received:" << socket.readAll(); // 关闭连接 socket.close(); } else { qDebug() << "Connection failed:" << socket.errorString(); } }
TCP 服务器示例:
#include <QTcpServer> #include <QTcpSocket> #include <QDebug> class MyServer : public QTcpServer { Q_OBJECT protected: void incomingConnection(qintptr socketDescriptor) override { // 每当有新连接时会调用此函数 QTcpSocket* socket = new QTcpSocket(this); socket->setSocketDescriptor(socketDescriptor); // 连接信号槽处理数据接收 connect(socket, &QTcpSocket::readyRead, [socket]() { qDebug() << "Received from client:" << socket->readAll(); }); connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater); // 向客户端发送数据 socket->write("Welcome to the server!"); } }; // 在主函数中使用 MyServer server; if (server.listen(QHostAddress::Any, 1234)) { qDebug() << "Server started on port 1234"; } else { qDebug() << "Server could not start:" << server.errorString(); }
QUdpSocket(UDP 编程)
该类可用于实现基于 UDP 协议的通信。示例如下:
#include <QUdpSocket> #include <QDebug> // UDP 发送方 void udpSender() { QUdpSocket socket; QByteArray datagram = "Hello, UDP receiver!"; socket.writeDatagram(datagram, QHostAddress("127.0.0.1"), 5678); } // UDP 接收方 void udpReceiver() { QUdpSocket socket; socket.bind(QHostAddress::Any, 5678); connect(&socket, &QUdpSocket::readyRead, [&socket]() { while (socket.hasPendingDatagrams()) { QByteArray datagram; datagram.resize(socket.pendingDatagramSize()); QHostAddress sender; quint16 senderPort; socket.readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); qDebug() << "Received from" << sender.toString() << ":" << senderPort << "Data:" << datagram; } }); }
QNetworkAccessManager(HTTP 编程)
这个类可用于处理 HTTP 请求,支持 GET、POST 等常见操作。示例如下:
#include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QEventLoop> #include <QDebug> // 同步 HTTP GET 请求 QByteArray httpGet(const QString& url) { QNetworkAccessManager manager; QNetworkRequest request(QUrl(url)); QNetworkReply* reply = manager.get(request); // 使用事件循环等待请求完成(同步方式) QEventLoop loop; QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); QByteArray data; if (reply->error() == QNetworkReply::NoError) { data = reply->readAll(); } else { qDebug() << "HTTP error:" << reply->errorString(); } reply->deleteLater(); return data; } // 异步 HTTP POST 请求 void httpPostAsync(const QString& url, const QByteArray& data) { QNetworkAccessManager* manager = new QNetworkAccessManager(); QNetworkRequest request(QUrl(url)); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QNetworkReply* reply = manager->post(request, data); QObject::connect(reply, &QNetworkReply::finished, [reply, manager]() { if (reply->error() == QNetworkReply::NoError) { qDebug() << "POST response:" << reply->readAll(); } else { qDebug() << "POST error:" << reply->errorString(); } reply->deleteLater(); manager->deleteLater(); }); }
高级网络功能
QWebSocket(WebSocket 支持)
该类可用于实现 WebSocket 客户端和服务器。示例如下:
#include <QWebSocket> #include <QWebSocketServer> #include <QDebug> // WebSocket 客户端 void webSocketClient() { QWebSocket socket; socket.open(QUrl("ws://echo.websocket.org")); QObject::connect(&socket, &QWebSocket::connected, [&socket]() { qDebug() << "WebSocket connected"; socket.sendTextMessage("Hello, WebSocket server!"); }); QObject::connect(&socket, &QWebSocket::textMessageReceived, [](const QString& message) { qDebug() << "Received message:" << message; }); } // WebSocket 服务器 void webSocketServer() { QWebSocketServer server("My WebSocket Server", QWebSocketServer::NonSecureMode); if (server.listen(QHostAddress::Any, 12345)) { qDebug() << "WebSocket server started on port 12345"; QObject::connect(&server, &QWebSocketServer::newConnection, [&server]() { QWebSocket* socket = server.nextPendingConnection(); qDebug() << "New WebSocket connection"; QObject::connect(socket, &QWebSocket::textMessageReceived, [socket](const QString& message) { socket->sendTextMessage("Server received: " + message); }); QObject::connect(socket, &QWebSocket::disconnected, socket, &QObject::deleteLater); }); } }
QNetworkConfigurationManager(网络配置管理)
该类可用于检测网络状态变化,例如 WiFi、移动数据的连接和断开。示例如下:
#include <QNetworkConfigurationManager> #include <QDebug> void networkStatusMonitor() { QNetworkConfigurationManager manager; // 检查当前是否有可用网络 bool isOnline = manager.isOnline(); qDebug() << "Network available:" << isOnline; // 监听网络状态变化 QObject::connect(&manager, &QNetworkConfigurationManager::onlineStateChanged, [](bool isOnline) { qDebug() << "Network state changed. Online:" << isOnline; }); }
为何不使用 C++ 原生库?
跨平台性:Qt 的网络类能够自动处理不同操作系统在网络编程方面的差异,像 Windows 和 Unix 系统的套接字 API 就有所不同。
高级抽象:Qt 提供了比 C++ 标准库(如
<boost/asio>
或原生套接字 API)更高层次的抽象,降低了网络编程的难度。集成性:这些类可以与 Qt 的信号槽机制、事件循环等其他组件无缝集成,便于实现异步编程。
安全性:例如
QNetworkAccessManager
支持 SSL/TLS,能简化安全通信的实现过程。特殊场景下的选择
要是你有特殊需求,比如需要进行底层的网络编程,或者要与使用 C++ 原生库的代码集成,也可以在 Qt 项目中使用 C++ 标准库(如
<boost/asio>
)或者操作系统原生的网络 API(如 Windows 的 Winsock 或 Unix 的 Berkeley 套接字)。不过,这种做法会丧失 Qt 网络模块带来的跨平台和高级抽象的优势。由此可见,QT在这些功能上有自己的一套API,以保证他的可移植性。
多媒体
Qt 的多媒体模块提供了音频、视频、录音、摄像头拍照和录像等功能。
Qt 从 4.4 版本开始提供的一套多媒体框架,提供多媒体回放的功能。在 Qt 4.6 中实现多媒体播放图形界面主要依赖 phonon 框架。phonon 最初是 一个 源于 KDE 的项目,为使用音频和视频的应用程序开发提供的一个框架。应用程序不用去管多媒体播放是通过什么实现的(如gstreamer、xine),只需调用相应的接口就行,但这中间需要一个中转,被称为 backend。Qt 也是通过 phonon 来实现跨平台的多媒体播放。
从 Qt5 开始,Qt 就弃用了 phonon,直接使用 Qt Multimedia 模块。我们可以 Qt Multimedia模块来提供的类实现跨平台的多媒体播放了。使用 Qt Multimedia 就不需要中转了,但是底层还是需要多媒体插件实现的。Qt 只是提供多媒体接口,播放多媒体实际上是通过多媒体插件实现的,我们不需要管这些插件是什么,Qt 在不同平台使用的多媒体插件不同。本章将会介绍如何在 Windows 和 Linux 安装多媒体插件,Mac 系统不考虑,笔者条件有限!
Qt 多媒体模块提供了很多类,主要有 QMediaPlayer,QSound、QSoundEffect、QAudioOutput、QAudioInput、QAudioRecorder、QVideoWidget 等等。类太多了不一一作解释,可以直接复制名字到 Qt 的帮助文档里查看该解释。可以从名称大概了解它们是什么意思,具体类的使用直接看本章的例子。
想要在 Qt 里使用使用 Qt 多媒体模块,需要在 pro 项目文件里添加如下语句。
QT += multimedia
注意:Qt 中的音乐播放器与视频播放器需要在 Ubuntu 里安装媒体解码器才能实现播放。
Ubuntu16 / Ubuntu18,需要安装以下插件。播放音乐需要安装 Gst 解码插件。需要在终端输入如下指令,注意不要复制错误了,下面指令已经在 Ubuntu16/Ubuntu18 测试成功,如果读者 Ubuntu 没有配置网络与源服务器,这些导致安装不成功与本教程无关,确实需要读者好好打下 Ubuntu 操作的基础了!
sudo apt-get install gstreamer1.0-plugins-base gstreamer1.0-plugins-bad gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly gstreamer1.0-pulseaudio gstreamer1.0-libav
Windows 需要安装如 LAVFilters 解码器,只需要百度 LAVFilters,找到 LAVFilters 官网下载此软件即可,当然本教程的资料会提供一份 LAVFilters 的安装包。点击页脚下方的程序下载链接跳转到下载本教程所有资料下载地址处,在顶层目录下。
Qt 多媒体模块提供了丰富的 API,用于处理音频、视频、相机和收音机等多媒体功能。该模块支持跨平台运行,可在桌面、移动和嵌入式设备上使用。下面介绍其核心组件和使用方法:
一、Qt 多媒体核心组件
音频播放
使用
QMediaPlayer
和QAudioOutput
播放音频文件或流媒体:#include <QMediaPlayer> #include <QAudioOutput> // 初始化播放器 QMediaPlayer* player = new QMediaPlayer; QAudioOutput* audioOutput = new QAudioOutput; player->setAudioOutput(audioOutput); // 设置媒体源 player->setSource(QUrl::fromLocalFile("/path/to/music.mp3")); // 或播放网络流媒体 // player->setSource(QUrl("https://example.com/stream.mp3")); // 控制播放 audioOutput->setVolume(75); // 音量 75% player->play(); // 开始播放 // 监听播放状态 connect(player, &QMediaPlayer::playbackStateChanged, [](QMediaPlayer::PlaybackState state) { if (state == QMediaPlayer::PlayingState) { qDebug() << "正在播放"; } else if (state == QMediaPlayer::PausedState) { qDebug() << "已暂停"; } else if (state == QMediaPlayer::StoppedState) { qDebug() << "已停止"; } }); // 错误处理 connect(player, &QMediaPlayer::errorChanged, [](QMediaPlayer::Error error) { if (error != QMediaPlayer::NoError) { qDebug() << "播放错误:" << player->errorString(); } });
视频播放
结合
QMediaPlayer
和QVideoWidget
或QGraphicsVideoItem
播放视频:#include <QMediaPlayer> #include <QVideoWidget> // 创建视频播放器 QMediaPlayer* player = new QMediaPlayer; QVideoWidget* videoWidget = new QVideoWidget; player->setVideoOutput(videoWidget); // 设置视频源并播放 player->setSource(QUrl::fromLocalFile("/path/to/video.mp4")); videoWidget->show(); player->play();
相机功能
使用
QCamera
、QCameraViewfinder
和QImageCapture
访问摄像头:#include <QCamera> #include <QCameraViewfinder> #include <QImageCapture> // 初始化相机 QCamera* camera = new QCamera; QCameraViewfinder* viewfinder = new QCameraViewfinder; QImageCapture* imageCapture = new QImageCapture(camera); // 设置视图和开启相机 camera->setViewfinder(viewfinder); viewfinder->show(); camera->start(); // 拍照功能 connect(ui->captureButton, &QPushButton::clicked, [imageCapture]() { imageCapture->capture("/path/to/save/image.jpg"); }); // 处理拍摄的图像 connect(imageCapture, &QImageCapture::imageCaptured, [](int id, const QImage &preview) { qDebug() << "图像捕获成功,ID:" << id; // 可以在这里显示预览或保存图像 });
录音功能
使用
QAudioRecorder
录制音频:#include <QAudioRecorder> // 初始化录音器 QAudioRecorder* recorder = new QAudioRecorder; // 设置录音质量 QAudioEncoderSettings audioSettings; audioSettings.setQuality(QMultimedia::HighQuality); recorder->setEncodingSettings(audioSettings); // 设置保存位置 recorder->setOutputLocation(QUrl::fromLocalFile("/path/to/record.wav")); // 控制录音 recorder->record(); // 开始录音 // recorder->pause(); // 暂停录音 // recorder->stop(); // 停止录音
二、高级多媒体功能
媒体播放列表
使用
QMediaPlaylist
管理多个媒体文件:QMediaPlaylist* playlist = new QMediaPlaylist; playlist->addMedia(QUrl::fromLocalFile("/path/to/song1.mp3")); playlist->addMedia(QUrl::fromLocalFile("/path/to/song2.mp3")); playlist->addMedia(QUrl::fromLocalFile("/path/to/song3.mp3")); playlist->setPlaybackMode(QMediaPlaylist::Loop); // 循环播放 QMediaPlayer* player = new QMediaPlayer; player->setPlaylist(playlist); player->play();
媒体元数据
读取媒体文件的元数据(如歌曲的标题、艺术家、专辑等):
QMediaPlayer* player = new QMediaPlayer; player->setSource(QUrl::fromLocalFile("/path/to/music.mp3")); // 等待元数据加载完成 connect(player, &QMediaPlayer::metaDataChanged, [player]() { if (player->hasMetaData()) { qDebug() << "标题:" << player->metaData(QMediaMetaData::Title).toString(); qDebug() << "艺术家:" << player->metaData(QMediaMetaData::Artist).toString(); qDebug() << "专辑:" << player->metaData(QMediaMetaData::AlbumTitle).toString(); qDebug() << "时长:" << player->metaData(QMediaMetaData::Duration).toInt() / 1000 << "秒"; } });
视频帧处理
通过
QAbstractVideoSurface
自定义视频帧处理:class CustomVideoSurface : public QAbstractVideoSurface { Q_OBJECT public: CustomVideoSurface(QObject* parent = nullptr) : QAbstractVideoSurface(parent) {} QList<QVideoFrame::PixelFormat> supportedPixelFormats( QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const override { Q_UNUSED(handleType); return QList<QVideoFrame::PixelFormat>() << QVideoFrame::Format_RGB32 << QVideoFrame::Format_ARGB32; } bool present(const QVideoFrame &frame) override { if (!frame.isValid()) return false; QVideoFrame cloneFrame = frame; cloneFrame.map(QAbstractVideoBuffer::ReadOnly); // 在这里处理视频帧数据 QImage image(cloneFrame.bits(), cloneFrame.width(), cloneFrame.height(), cloneFrame.bytesPerLine(), QVideoFrame::imageFormatFromPixelFormat(cloneFrame.pixelFormat())); // 例如:显示或保存图像 emit frameAvailable(image); cloneFrame.unmap(); return true; } signals: void frameAvailable(const QImage &image); }; // 使用自定义视频表面 CustomVideoSurface* surface = new CustomVideoSurface; QMediaPlayer* player = new QMediaPlayer; player->setVideoOutput(surface);
三、平台兼容性注意事项
不同平台的支持差异:
Windows/macOS/Linux:支持大多数功能,包括音频、视频播放和摄像头。
Android/iOS:支持基本的音频、视频播放和相机功能,但可能需要额外权限。
嵌入式设备:功能依赖于底层硬件和驱动支持。
编解码器支持:
Qt 依赖系统安装的编解码器来处理多媒体格式。
某些格式(如 MP3、H.264)可能需要额外的许可证或插件。
权限要求:
在移动平台上访问相机或麦克风需要在应用清单中声明权限。
四、多媒体模块的替代方案
如果 Qt 多媒体模块不能满足需求,可以考虑以下替代方案:
GStreamer:跨平台多媒体框架,可通过 QtGStreamer 插件集成。
FFmpeg:强大的开源多媒体处理库,可通过
QProcess
调用或使用第三方包装库。WebEngine:使用 QtWebEngine 在应用中嵌入 HTML5 媒体播放器。
五、总结
Qt 多媒体模块提供了一套完整的工具,用于处理音频、视频和相机功能。其设计遵循 Qt 的跨平台理念,同时保持了 API 的简洁性和灵活性。在实际开发中,建议根据目标平台和具体需求选择合适的 API 和编解码器,必要时考虑使用第三方库来扩展功能。
数据库
想要在项目中使用 Qt SQL 模块,需要在项目配置文件里添加如下语句。
QT += core gui sql
Qt SQL 模块为数据库提供了编程支持,Qt 支持很多种常见的数据库,如 MySQL、Oracle、MS SQL Server、SQLite 等。Qt SQL 模块里包含了很多个类,可以轻松实现数据库的连接、执行 SQL 语句,获取数据库里的数据与界面显示等功能,一般数据与界面之间会采用 Model/View架构,从而很方便的显示数据界面和操作数据库。
在嵌入式里,一般常用的数据库就是 Sqlite3。SQLite 是非常小的,是轻量级的,完全配置时小于 400KiB,省略可选功能配置时小于 250KiB。SQLite 是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库不一样,您不需要在系统中配置。就像其他数据库,SQLite 引擎不是一个独立的进程,可以按应用程序需求进行静态或动态连接。SQLite 可以直接访问其存储文件。
本章主要对 Sqlite3 进行实验。需要用其他数据库的请自行学习,在我们正点原子里 Linux开发板里就是用 sqlite3,文件系统里不提供其他数据库类型。嵌入式一般是用 sqlite3,如需要其他类型数据库,请自行移植与学习!
Qt 提供了 QSqlDatabase 类用于建立数据库的连接,往往以指定加载的数据库驱动,然后设置数据库的登录参数,如主机地址,用户名、登录密码等。这些都是服务器类型的数据库所需要做的操作。恰好单机型(本地数据库类型)的Sqlite3 数据库不需要设置登录参数就可以方便的打开数据库进行操作了。在 QSqlDatabase 连接数据库后,用 QSqlTableModel 从数据库里读取出表格模型,然后通过 Qt 的 QTableView 类显示数据库的内容在我们面前。需要对数据库的数据进行修改可以使用 QSqlQuery,或者直接修改QSqlTableModel 对象,修改里面的模型数据即可!Qt 对数据库的基本操作流程大概是这样子,当然 Qt 提供了很多操作数据库的类,我们只讲解基本的与常用的就已经足够了。
Qt 提供了强大且统一的数据库操作 API,支持多种数据库系统(如 SQLite、MySQL、PostgreSQL 等)。下面介绍其核心组件和使用方法:
一、Qt 数据库模块核心组件
数据库驱动
Qt 通过插件式数据库驱动支持不同的数据库系统:
QSQLITE:SQLite 数据库(轻量级嵌入式数据库)
QMYSQL:MySQL 数据库
QPSQL:PostgreSQL 数据库
QODBC:ODBC 接口(可连接 Access、SQL Server 等)
主要类
QSqlDatabase:表示数据库连接
QSqlQuery:执行 SQL 查询
QSqlTableModel:操作单个数据库表的高级接口
QSqlRelationalTableModel:支持外键关联的表模型
QSqlError:表示数据库错误信息
二、基本数据库操作示例
连接数据库(以 SQLite 为例)
#include <QSqlDatabase> #include <QSqlError> #include <QSqlQuery> #include <QDebug> bool connectToDatabase() { // 添加 SQLite 数据库驱动 QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName("mydatabase.db"); // 数据库文件名 if (!db.open()) { qDebug() << "无法连接数据库:" << db.lastError().text(); return false; } qDebug() << "数据库连接成功"; return true; }
创建表
void createTable() { QSqlQuery query; // 创建一个简单的用户表 bool success = query.exec( "CREATE TABLE IF NOT EXISTS users (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "name TEXT NOT NULL, " "age INTEGER, " "email TEXT UNIQUE)" ); if (!success) { qDebug() << "创建表格失败:" << query.lastError().text(); } }
插入数据
void insertData() { QSqlQuery query; // 方法1:直接执行 SQL query.exec("INSERT INTO users (name, age, email) VALUES ('张三', 25, 'zhangsan@example.com')"); // 方法2:使用预编译语句(更安全,防止 SQL 注入) query.prepare("INSERT INTO users (name, age, email) VALUES (:name, :age, :email)"); query.bindValue(":name", "李四"); query.bindValue(":age", 30); query.bindValue(":email", "lisi@example.com"); if (!query.exec()) { qDebug() << "插入数据失败:" << query.lastError().text(); } }
查询数据
void queryData() { QSqlQuery query("SELECT * FROM users"); while (query.next()) { int id = query.value(0).toInt(); QString name = query.value(1).toString(); int age = query.value(2).toInt(); QString email = query.value(3).toString(); qDebug() << "ID:" << id << "姓名:" << name << "年龄:" << age << "邮箱:" << email; } }
更新数据
void updateData() { QSqlQuery query; query.prepare("UPDATE users SET age = :age WHERE name = :name"); query.bindValue(":age", 26); query.bindValue(":name", "张三"); if (!query.exec()) { qDebug() << "更新数据失败:" << query.lastError().text(); } }
删除数据
void deleteData() { QSqlQuery query; query.prepare("DELETE FROM users WHERE id = :id"); query.bindValue(":id", 2); if (!query.exec()) { qDebug() << "删除数据失败:" << query.lastError().text(); } }
三、使用高级模型类
QSqlTableModel(单表操作)
#include <QSqlTableModel> #include <QTableView> void useTableModel(QTableView* tableView) { QSqlTableModel* model = new QSqlTableModel; model->setTable("users"); model->select(); // 查询表中所有数据 // 设置列标题 model->setHeaderData(0, Qt::Horizontal, "ID"); model->setHeaderData(1, Qt::Horizontal, "姓名"); model->setHeaderData(2, Qt::Horizontal, "年龄"); model->setHeaderData(3, Qt::Horizontal, "邮箱"); // 在表格视图中显示数据 tableView->setModel(model); tableView->show(); // 修改数据示例 model->setData(model->index(0, 2), 27); // 修改第一行的年龄为 27 model->submitAll(); // 提交所有更改到数据库 }
QSqlRelationalTableModel(外键关联)
假设有两个表:
employees
和departments
,通过department_id
关联。#include <QSqlRelationalTableModel> void useRelationalModel(QTableView* tableView) { QSqlRelationalTableModel* model = new QSqlRelationalTableModel; model->setTable("employees"); // 设置外键关联:将 department_id 映射到 departments 表的 name 字段 model->setRelation(2, QSqlRelation("departments", "id", "name")); model->select(); // 设置列标题 model->setHeaderData(0, Qt::Horizontal, "ID"); model->setHeaderData(1, Qt::Horizontal, "姓名"); model->setHeaderData(2, Qt::Horizontal, "部门"); model->setHeaderData(3, Qt::Horizontal, "职位"); tableView->setModel(model); tableView->show(); }
四、事务处理
void transactionExample() { QSqlDatabase db = QSqlDatabase::database(); if (db.transaction()) { // 开始事务 QSqlQuery query; // 执行一系列操作 bool success = query.exec("INSERT INTO users (name, age, email) VALUES ('王五', 35, 'wangwu@example.com')"); if (success) { success = query.exec("UPDATE users SET age = 40 WHERE name = '李四'"); } if (success) { db.commit(); // 提交事务 qDebug() << "事务执行成功"; } else { db.rollback(); // 回滚事务 qDebug() << "事务执行失败,已回滚:" << query.lastError().text(); } } }
五、数据库连接池
在多线程应用中,使用连接池管理数据库连接:
#include <QSqlDatabase> #include <QQueue> #include <QMutex> #include <QMutexLocker> class DatabasePool { public: static DatabasePool& instance() { static DatabasePool pool; return pool; } QSqlDatabase acquireConnection() { QMutexLocker locker(&mutex); if (!connections.isEmpty()) { return connections.dequeue(); } // 创建新连接 QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", QString("connection_%1").arg(++counter)); db.setDatabaseName("mydatabase.db"); db.open(); return db; } void releaseConnection(const QSqlDatabase& db) { QMutexLocker locker(&mutex); connections.enqueue(db); } private: DatabasePool() = default; ~DatabasePool() = default; DatabasePool(const DatabasePool&) = delete; DatabasePool& operator=(const DatabasePool&) = delete; QQueue<QSqlDatabase> connections; QMutex mutex; int counter = 0; };
六、注意事项
错误处理:
所有数据库操作都可能失败,应检查
QSqlQuery::lastError()
或QSqlDatabase::lastError()
。SQL 注入防护:
始终使用预编译语句(
prepare()
和bindValue()
),避免直接拼接 SQL 字符串。多线程应用:
每个线程应使用独立的数据库连接(
QSqlDatabase
不是线程安全的)。考虑使用数据库连接池管理连接。
性能优化:
批量操作时使用事务提高性能。
对经常查询的字段创建索引。
不同数据库的差异:
SQL 语法可能因数据库系统而异(如自增字段、日期函数等)。
某些功能可能只在特定数据库中支持。
七、总结
Qt 数据库模块提供了一套完整的工具,用于连接、操作和管理多种数据库系统。其设计遵循 Qt 的跨平台理念,同时保持了 API 的简洁性和灵活性。无论是简单的嵌入式应用还是复杂的企业级系统,Qt 都能满足你的数据库需求。