从数据持久化到网络通信与OpenCV:Qt应用程序开发的深度探索与实战
文章目录
- 前言
- 一、QSettings:轻量级数据持久化方案
- 1.1 QSettings 主要特点
- 1.2 QSettings 常用函数整理
- 二、数据库
- 2.1 连接SQLite数据库
- 2.2 建表
- 2.3 增删改
- 三、网络编程
- 3.1 网络分层
- 3.2 IP地址
- 3.3 端口号
- 3.4 基于TCP的Socket通信
- 3.4 相关接口
- 3.4.1核心类
- 3.4.2 通信原理图
- 3.4.3 项目配置
- 3.4.4 常用函数与信号
- 3.4.4.1 QTcpServer (服务器端)
- 3.4.5 QTcpSocket (客户端和服务器端通信对象)
- 3.4.5.1 QIODevice (所有 IO 设备基类)
- 3.4.5.2 数据读写方式
- 3.4.5.3 QByteArray
- 3.4.5.4 QTextStream
- 3.5 代码示例
- 四、QT中的OpenCV
- 4.1 OpenCV简介
- 4.2 环境搭建
- 4.3 人脸检测
- 4.4 代码示例
- 五、Qt程序打包
- 5.1 设置应用程序图标
- 5.2 Debug版本与Release版本:构建应用程序的两种模式
- 5.2 打包
- 最终章-项目演练
- 项目要求
- 实现功能
- 项目演示
- 结语
前言
在Qt基础知识的扎实积累后,我们今天的重点将转向Qt应用程序中至关重要的一环——数据持久化。高效且安全地存储数据是任何复杂应用不可或缺的基础。我们将深入探讨Qt提供的数据存储机制,从简单的文本文件到结构化的二进制文件,再到强大的数据库集成,旨在为您构建稳定、可扩展的Qt应用程序奠定坚实基础。
一、QSettings:轻量级数据持久化方案
数据持久化 是指将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称。数据库是一种常见的数据持久化方式,但对于嵌入式系统等资源受限的环境,即使是 SQLite 这样的轻量级数据库也可能显得“重”。
QSettings 是 Qt 框架提供的一种比数据库更轻量级的数据持久化方式,特别适用于存储应用程序的配置信息、用户偏好设置等小型数据。
1.1 QSettings 主要特点
轻量级: 无需复杂的数据库管理系统,直接读写配置文件。
跨平台: QSettings 自动处理不同操作系统下配置文件的存储位置和格式(如 Windows 注册表、macOS Plist 文件、Unix/Linux INI 文件等)。
简单易用: 提供直观的 API 进行键值对的读写。
1.2 QSettings 常用函数整理
构造函数
QSettings::QSettings(const QString & fileName, Format format, QObject * parent = 0)
参数1 fileName:存储文件的名称。对于INI格式,这是文件的实际名称。对于其他格式(如Windows注册表),QSettings 会根据应用程序和组织名称自动确定路径。默认为构建目录下的一个文件。
参数2 format: 存储格式。QSettings支持多种格式,如 QSettings::IniFormat (INI文件)、QSettings::NativeFormat (操作系统原生格式,如注册表) 等。
参数3 parent:父对象。
设置INI文件编码
void QSettings::setIniCodec(const char * codecName)
用途: 建议在使用INI文件格式时调用此函数,将编码设置为 UTF-8,以避免乱码问题。
参数 codecName: 编码字符串,如 “UTF-8”。
数据写入函数
beginWriteArray:以数组方式存储(相同类型数据建议使用)
void QSettings::beginWriteArray(const QString & prefix)
用途: 当您有一组相同类型的数据需要存储时(例如,一系列窗口位置,或一组文件路径),beginWriteArray 提供了更结构化的方式。它会将数据存储为数组形式。
参数 prefix: 数组的名称。
beginGroup:以组方式存储(不同类型数据建议使用,相同类型也可以但性能不如数组方式)
void QSettings::beginGroup(const QString & prefix)
用途: 用于组织不同类型的数据,或者将相关的键值对分组。这有助于保持配置文件的整洁和可读性。
参数 prefix: 组的名称。
setValue:在组或数组中添加键值对
void QSettings::setValue(const QString & key, const QVariant & value)
用途: 这是实际写入数据的函数。它将一个键 (key) 与一个值 (value) 关联起来。
参数1 key: 数据的键名。
参数2 value: 数据的值。QVariant 类型允许存储多种数据类型(如 int, QString, bool 等)。
结束存储函数
endArray:结束数组的存储
void QSettings::endArray()
用途: 匹配 beginWriteArray,标志着当前数组写入的结束。
endGroup:结束组的存储
void QSettings::endGroup()
用途: 匹配 beginGroup,标志着当前组写入的结束。
数据读取函数
value:根据键获得值
QVariant QSettings::value(const QString & key, const QVariant & defaultValue = QVariant()) const
用途: 从配置文件中读取与指定键关联的值。
参数1 key: 要读取数据的键名。
参数2 defaultValue: 如果指定的键不存在,则返回此默认值。这可以避免读取失败导致程序崩溃。
返回值: 返回一个 QVariant 对象,您需要将其转换为所需的数据类型(例如,value(“myKey”).toInt() 或 value(“anotherKey”).toString())。
代码示例
代码包
二、数据库
Qt 提供了强大的机制来操作各种常见的数据库系统。
本次课程重点: 使用 SQLite 数据库。
原因: Qt 内置支持 SQLite,无需额外安装或配置。
Qt 项目配置:
在使用数据库功能之前,必须在您的 Qt 项目的 .pro 文件中添加 sql 模块:
QT += sql
主要数据库相关类:
QSqlDatabase: 数据库连接类。
负责建立、管理和断开与数据库的连接。
QSqlError: 数据库错误信息类。
用于获取和解析数据库操作过程中可能出现的错误信息。
QSqlQuery: 数据库操作类。
用于执行 SQL 查询语句(如 SELECT, INSERT, UPDATE, DELETE 等)。
Qt常见数据库:
数据库类型 | 驱动名 | 特点 |
---|---|---|
SQLite | QSQLITE | 轻量级、嵌入式、文件型数据库。非常适合桌面应用、移动应用、嵌入式系统,无需单独的服务器进程。Qt对其支持非常好,是许多Qt应用的首选。 |
MySQL | QMYSQL | 广泛使用的开源关系型数据库服务器。适用于中到大型Web应用、企业应用。 |
PostgreSQL | QPSQL | 功能强大、遵循SQL标准、开源的关系型数据库服务器。以其高级特性、数据完整性和可靠性而闻名。 |
ODBC (通用接口) | QODBC | 开放数据库连接 (Open Database Connectivity)。这是一个通用的API,允许应用程序通过ODBC驱动程序管理器连接到任何支持ODBC的数据库。这意味着如果你有数据库的ODBC驱动,Qt就可以通过QODBC连接它。通过ODBC可以连接以下数据库:SQL Server, Oracle, Access, DB2以及其他许多兼容ODBC的数据库。 |
Oracle | QOCI | 强大的企业级商业数据库。Qt提供了直接的OCI (Oracle Call Interface) 驱动,提供了比ODBC更原生和高效的连接方式。 |
IBM DB2 | QDB2 | IBM的商业关系型数据库系统。 |
Microsoft SQL Server | QODBC 或 QTDS (较少见或第三方实现) | 微软的商业关系型数据库服务器。通常通过QODBC连接最为常见。QTDS驱动如果存在,则可能提供更直接的连接,但不如QODBC通用。 |
2.1 连接SQLite数据库
在Qt中连接SQLite数据库主要涉及以下几个步骤和关键函数:
- 添加数据库驱动
首先,你需要使用QSqlDatabase::addDatabase()静态函数来添加一个数据库连接。对于SQLite数据库,你需要传入 “QSQLITE” 作为类型字符串。这个函数会返回一个QSqlDatabase对象,这个对象代表了你的数据库连接。
相关函数:
QSqlDatabase QSqlDatabase::addDatabase(const QString & type)
参数: type - 数据库类型字符串,对于SQLite,通常是 “QSQLITE”。
返回值: 一个 QSqlDatabase 对象,代表了数据库连接。
示例:
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
- 设置数据库名称/路径
接下来,你需要使用QSqlDatabase::setDatabaseName()函数来指定SQLite数据库文件的路径和名称。
如果指定的数据库文件不存在,SQLite会在程序尝试打开连接时自动创建这个文件。
如果文件路径是相对路径,它会相对于当前应用程序的执行目录。
成功连接后,你可以在构建目录(通常是你的项目编译输出目录)中找到生成的数据库存储文件。你可以使用SQLiteSpy等工具打开此文件进行查看和辅助开发。
相关函数:
void QSqlDatabase::setDatabaseName(const QString & name)
参数: name - 数据库文件的路径和名称。例如,“my_database.db” 或 “C:/path/to/my_database.db”。
示例:
db.setDatabaseName("my_database.db"); // 或者 db.setDatabaseName("/path/to/my_database.db");
- 打开数据库连接
使用QSqlDatabase::open()函数尝试打开数据库连接。这个函数会返回一个布尔值,指示连接是否成功。
相关函数:
bool QSqlDatabase::open()
返回值: true 表示连接成功,false 表示连接失败。
示例:
if (!db.open()) {// 处理连接失败的情况qDebug() << "Database connection failed!";// 可以通过 db.lastError().text() 获取详细错误信息
} else {qDebug() << "Database connected successfully!";
}
- 检查连接状态和处理错误
在打开连接之后,或者在进行其他数据库操作时,最好检查连接是否成功。
QSqlDatabase::isOpen() 可以检查当前连接是否处于打开状态。
QSqlDatabase::lastError() 返回一个 QSqlError 对象,其中包含了上一次数据库操作的错误信息。
QSqlError::text() 可以从 QSqlError 对象中提取可读的错误信息文本。
相关函数:
bool QSqlDatabase::isOpen() const
返回值: true 如果连接已打开,否则 false。
QSqlError QSqlDatabase::lastError() const
返回值: 一个 QSqlError 对象,包含上一次操作的错误信息。
QString QSqlError::text() const
返回值: 错误信息的文本描述。
示例:
if (!db.open()) {qDebug() << "Error: Failed to connect to database." << db.lastError().text();
}
// 在其他地方,你可以检查连接状态
if (db.isOpen()) {qDebug() << "Database is currently open.";
} else {qDebug() << "Database is currently closed.";
}
- 关闭数据库连接
当不再需要数据库连接时,使用QSqlDatabase::close()函数关闭连接。这会释放相关的资源。
相关函数:
void QSqlDatabase::close()
示例:
db.close();
qDebug() << "Database connection closed.";
总结连接流程
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); // 添加SQLite驱动
db.setDatabaseName("your_database.db"); // 设置数据库文件路径
if (!db.open()) { ... } // 尝试打开连接,并检查结果
db.close(); // 完成操作后关闭连接
通过以上步骤,你就可以在Qt应用程序中成功连接和管理SQLite数据库了!
2.2 建表
目标: 在数据库中创建一个新的表。
所需类和函数:
QSqlQuery: 用于执行SQL语句的核心类。
QSqlError: 用于获取SQL操作的错误信息。
核心函数回顾:
QSqlQuery::QSqlQuery() (构造函数)
作用: 创建一个QSqlQuery对象。默认情况下,它会关联到当前的默认数据库连接。如果你有多个数据库连接,可以使用QSqlQuery(QSqlDatabase db)构造函数指定。
用法: QSqlQuery query; 或 QSqlQuery query(myDatabaseConnection);
bool QSqlQuery::exec(const QString & query) (执行SQL语句)
作用: 执行传入的SQL语句。
参数: query - 一个QString类型的SQL语句。
返回值: true表示SQL语句执行成功,false表示执行失败。
用法: bool success = query.exec(“CREATE TABLE MyTable (id INTEGER PRIMARY KEY, name TEXT)”);
QSqlError QSqlQuery::lastError() const (返回上一次操作的错误信息)
作用: 当exec()返回false时,可以使用此函数获取详细的错误信息,以便诊断问题。
返回值: 一个QSqlError对象,包含了错误类型、错误文本等信息。
用法: QSqlError error = query.lastError();
打开db文件查看:
在你的项目构建目录(通常是build-yourprojectname-Desktop_Qt_x_x_x_MinGW_xx_bit或类似名称)中,你会找到一个名为 mydatabase.db 的文件。
你可以使用任何支持SQLite的数据库浏览器工具来打开这个 .db 文件,例如:
SQlitSpy 点击下载
DB Browser for SQLite (推荐,免费且功能强大)
SQLiteStudio
各种集成开发环境(如VS Code with SQLite Viewer插件)
打开后,你将能够看到新创建的 users 表及其定义的字段。
2.3 增删改
在使用 QSqlQuery 进行数据库的增删改(以及复杂的查询)操作时,推荐使用预处理语句配合参数绑定。这比直接拼接 SQL 字符串更加安全和高效。
核心函数介绍:
bool QSqlQuery::prepare(const QString & query)
功能:送入一个 SQL 预处理语句,以便于后续二次处理。这是使用预处理语句的第一步。
参数:
query (const QString &): 包含 SQL 语句的字符串。
特点:这个 SQL 语句中可以包含参数占位符。常见的占位符有:
命名占位符 (Named Placeholders):例如 :name, :id
位置占位符 (Positional Placeholders):例如 ? (对于大多数数据库,包括 SQLite, PostgreSQL, MySQL)
示例:
INSERT INTO users (name, age) VALUES (?, ?)
UPDATE products SET price = :newPrice WHERE id = :productId
返回值:bool。如果预处理成功 (即 SQL 语法无误,数据库驱动能解析),返回 true;否则返回 false。
作用:在内部,数据库驱动会解析这个 SQL 语句,并为后续的参数绑定和执行做好准备。
void QSqlQuery::addBindValue(const QVariant & val)
功能:绑定输入参数到预处理语句中的占位符。
参数:
val (const QVariant &): 要绑定的值。QVariant 是 Qt 的通用数据类型,可以兼容各种常见的 Qt 数据类型(如 int, QString, double, QDateTime 等),提供了极大的灵活性。
作用:当你调用 prepare() 后,SQL 语句中的占位符需要被实际的值填充。addBindValue() 就是用来提供这些实际值的。
绑定顺序/方式:
位置占位符 (?):按照你调用 addBindValue() 的顺序,依次绑定到 SQL 语句中的 ? 占位符。例如,第一个 addBindValue() 对应第一个 ?,第二个对应第二个 ?。
命名占位符 (:name):在某些数据库驱动中,addBindValue() 也可以用来绑定命名占位符,但更常见和推荐的方式是使用 QSqlQuery::bindValue(const QString & placeholder, const QVariant & val)。然而,如果数据库驱动支持,多次调用 addBindValue() 也可以按照命名占位符在 SQL 语句中的出现顺序进行绑定。
注意:每次执行预处理语句前,你需要为所有的占位符调用相应的 addBindValue() 或 bindValue()。
bool QSqlQuery::exec()
功能:执行已经预处理并绑定了参数的 SQL 语句。
参数:无。
返回值:bool。如果执行成功(例如 INSERT 成功,UPDATE 成功,即使没有行被更新也算成功),返回 true;否则返回 false。
作用:将准备好的 SQL 语句及其绑定好的参数发送到数据库执行。对于 INSERT, UPDATE, DELETE 语句,成功执行后可以通过 QSqlQuery::numRowsAffected() 获取受影响的行数。对于 SELECT 语句,成功执行后可以通过 QSqlQuery::next() 遍历查询结果。
三、网络编程
3.1 网络分层
为什么要分层
由于结点之间联系很复杂,在制定协议时,把复杂成份分解成
一些简单的成份,再将它
们复合起来。最常用的复合方式是层次方式,即同层间可以通信、上一层可以调用下一层
而与再下一层不发生关系。各层互不影响,利于系统的开发和扩展。
3.2 IP地址
定义: Internet Protocol (互联网协议) 的缩写。是网络中每个设备的通信地址,类似于门牌号,是网络通信的核心条件。
格式: 可以是 IPv4 或 IPv6 格式,其中 IPv4 更常用。
用途: 标识网络中的唯一设备。
操作:
在 Windows 命令行中使用 ipconfig 命令可以查看当前计算机在局域网中的 IP 地址。
可以使用 ping 命令测试网络连通性。
注意: Windows 默认开启防火墙以保障网络安全,进行局域网编程时可能需要关闭防火墙。
3.3 端口号
定义: 用于在一台设备上唯一标识一个程序。
用途:
在知道 IP 地址或域名的情况下,如果想与该计算机上的特定程序交换数据,还必须知道该程序使用的端口号。
同一台计算机中,不同的程序使用不同的端口号,以确保信息发送到各自对应的程序。
范围:
一台计算机上最多可以有 65536 个端口号(1-65535)。
重要提示: 1024 以下的端口号是预留给一些著名应用程序使用的,不建议占用,例如:
HTTP: 80
FTP: 21
SMTP: 25
POP3: 110
3.4 基于TCP的Socket通信
TCP (Transmission Control Protocol): 传输控制协议。
特性: 是一种基于连接的协议。
连接建立: 在正式收发数据之前,必须先与对方建立可靠的连接。这个连接建立过程需要经过“三次对话”,被称为三次握手。
三次握手步骤:
询问: (客户端发送连接请求)
确定: (服务器回应并确认收到请求)
连接: (客户端再次确认,连接正式建立)
3.4 相关接口
您提供的文本内容是对Qt网络编程中TCP相关类(QTcpServer和QTcpSocket)及其常用接口、通信原理和读写方式的详细说明。为了更好地理解和整理,我将对内容进行结构化和补充,并对关键点进行提炼。
3.4.1核心类
Qt 提供了两个主要的类用于 TCP 网络编程:
- QTcpServer:
- 作用: 基于 TCP 的服务器管理类。
- 特点: 自身不具备任何 IO (输入/输出) 功能,主要负责监听客户端连接请求。
- QTcpSocket:
- 作用: 基于 TCP 的通信连接类。
- 特点: 间接继承自
QIODevice
,因此具备读写数据的功能。它是客户端与服务器之间实际进行数据交换的载体。
3.4.2 通信原理图
+----------------+ +----------------+
| Server | | Client |
| (QTcpServer) | | (QTcpSocket) |
+----------------+ +----------------+| || 1. 监听端口 (listen) || ||<---------------------------------------| 2. 连接到服务器 (connectToHost)| (客户端发起连接请求) || || 3. newConnection() 信号发出 || || 4. nextPendingConnection() || (服务器“下蛋”得到一个QTcpSocket) || |
+-------V-------+ +----------------+
| QTcpSocket | <-------------------> | QTcpSocket |
| (服务器端) | 5. 建立连接 | (客户端) |
+---------------+ +----------------+| ||<--------------------------------------->| 6. 数据读写 (readAll, QTextStream, write)| (通过各自的QTcpSocket进行数据通信) |
3.4.3 项目配置
要在 Qt 项目中使用网络功能,需要在 .pro
文件中添加网络模块:
QT += network
3.4.4 常用函数与信号
3.4.4.1 QTcpServer (服务器端)
- 构造函数:
QTcpServer::QTcpServer(QObject * parent = 0)
- 监听:
bool QTcpServer::listen(const QHostAddress & address = QHostAddress::Any, quint16 port = 0)
- 功能: 开启服务器监听,等待客户端连接请求。
- 参数:
address
:QHostAddress
是 IP 地址的封装类。QHostAddress::Any
表示监听所有可用 IP 地址上的请求。port
: 服务器端口号。0
表示随机分配一个可用端口。
- 返回值:
true
表示监听成功,false
表示失败 (常见失败原因为端口被占用)。
- 判断是否监听:
bool QTcpServer::isListening() const
- 关闭服务器:
void QTcpServer::close()
- 信号:
void QTcpServer::newConnection()
: 当有新客户端连接建立时发出此信号。
- 获取待处理连接:
QTcpSocket * QTcpServer::nextPendingConnection()
- 功能: 在
newConnection()
信号发出后调用此函数,服务器会“下蛋”生成一个QTcpSocket
对象,用于与刚刚连接的客户端进行通信。这个QTcpSocket
对象是服务器与该客户端通信的“绿蛋”。
- 功能: 在
3.4.5 QTcpSocket (客户端和服务器端通信对象)
- 构造函数:
QTcpSocket::QTcpSocket(QObject * parent = 0)
- 连接到主机:
void QAbstractSocket::connectToHost(const QString & hostName, quint16 port, OpenMode openMode = ReadWrite)
- 功能: 客户端发起连接请求到指定服务器。
- 参数:
hostName
: 服务器的 IP 地址或域名。port
: 服务器的端口号。openMode
: IO 打开模式,默认为ReadWrite
(读写模式)。
- 判断 IO 流是否打开:
bool QIODevice::isOpen() const
- 获取对方 IP 地址:
QHostAddress QAbstractSocket::peerAddress() const
- 获取对方端口号:
quint16 QAbstractSocket::peerPort() const
- 获取 IP 地址字符串:
QString QHostAddress::toString() const
- 注意: 某些系统可能会返回多余字符,需要自行裁剪。
- 信号:
void QAbstractSocket::connected()
: 当成功连接到服务器时发出此信号 (客户端)。void QAbstractSocket::disconnected()
: 当连接断开时发出此信号 (客户端或服务器端)。
3.4.5.1 QIODevice (所有 IO 设备基类)
- 信号:
void QIODevice::readyRead()
: 当有新内容可读时发出此信号。
- 读取所有内容:
QByteArray QIODevice::readAll()
- 功能: 将当前可读的所有数据读取到一个
QByteArray
中。
- 功能: 将当前可读的所有数据读取到一个
3.4.5.2 数据读写方式
Qt 提供了两种主要的读写接口,适用于不同场景:
3.4.5.3 QByteArray
- 特点:
- 使用 8 位字节作为基础存储单位。
- 兼容性更好: 适用于 Qt 程序与其他非 Qt 程序(如 C++ 标准库、Java、Python 等)进行通信。
- 推荐场景: 跨语言、跨平台或协议定义为字节流的通信。
- 读写:
QByteArray QIODevice::readAll()
: 读取所有可用的字节数据。qint64 QIODevice::write(const QByteArray & data)
: 写入字节数据。
3.4.5.4 QTextStream
- 特点:
- 使用 16 位
QChar
作为基础存储单位。 - 使用更方便: 封装了文本格式化和解析功能,可以直接读写字符串、数字等。
- 推荐场景: 仅限于 Qt 程序之间进行文本通信,尤其是不涉及复杂二进制协议的简单文本数据交换。
- 使用 16 位
- 构造函数:
QTextStream::QTextStream(QIODevice * device)
- 功能: 将
QTextStream
绑定到一个QIODevice
(例如QTcpSocket
) 上。
- 功能: 将
- 读取所有文本:
QString QTextStream::readAll()
- 功能: 从关联的
QIODevice
中一口气读取所有可用的文本数据。
- 功能: 从关联的
- 其他读写操作: 支持
operator<<
和operator>>
进行格式化输入输出 (例如QTextStream << "Hello" << endl;
).
3.5 代码示例
服务器和客户端通讯代码示例包
四、QT中的OpenCV
4.1 OpenCV简介
OpenCV主要通过C++实现,Python接口是基于此开发的。
C++接口在官方文档中更靠前。
官方文档链接:官方文档
4.2 环境搭建
-
步骤1:下载OpenCV离线包
网盘链接:opencv -
步骤2:解压并拷贝
将解压后的文件拷贝到D盘下,注意文件夹层级,避免多一层目录。
-
步骤3:打开Windows环境变量配置。
-
步骤4:配置系统环境变量。
-
步骤5:重启电脑。
-
步骤6:新建Qt项目并配置.pro文件
Qt项目路径需为英文。
在.pro文件中添加以下OpenCV库路径:
INCLUDEPATH += D:/opencv/opencv3.4-install/install/include
INCLUDEPATH += D:/opencv/opencv3.4-install/install/include/opencv
INCLUDEPATH += D:/opencv/opencv3.4-install/install/include/opencv2
LIBS += D:/opencv/opencv3.4-install/install/x86/mingw/lib/libopencv_*.a
- 步骤7:在main.cpp中编写测试代码验证环境
#include "dialog.h"
#include <QDebug>
#include <opencv2/opencv.hpp> // 引入OpenCV头文件
using namespace cv; // 使用名字空间
int main(int argc, char *argv[])
{Mat src; // 矩阵存储图像,相当于ndarraysrc = imread("handsome.jpg"); // 根目录是构建目录if(!src.data){qDebug() << "读取失败!";return -1;}imshow("input", src);waitKey(0);return 0;
}
问题解决:
如果出现未响应情况,可:参考文章
4.3 人脸检测
原理:
通过定时器获取摄像头采集的图像。
使用OpenCV自带的人脸数据模型与图像进行比对,执行人脸检测。
在检测到的人脸周围画红框进行标注。
QImage构造函数:
QImage(
const uchar * data, // 图像数据
int width, // 宽度(列数)
int height, // 高度(行数)
int bytesPerLine, // 每一行的总通道数 = 宽度 * 通道数
Format format) // 图像格式
QImage转换为QPixmap:
QPixmap QPixmap::fromImage(const QImage & image) [static] // 参数为要转换的QImage图像
4.4 代码示例
人脸识别代码
五、Qt程序打包
5.1 设置应用程序图标
操作步骤如下:
第一步:准备图标文件 (.ico 格式)
- 选择图片: 找到一张你喜欢的图片作为应用程序图标。
- 转换格式: 借助在线网站将图片转换为
.ico
格式。- 推荐分辨率: 256 x 256 像素。
- 在线转换工具:
- https://cn.office-converter.com/png-to-ico
- https://convertio.co/zh/
重磅介绍本地转换工具:
ImageMagick
使用命令行cmd打开
运行指令:
magic xxx.png xxx.ico
该工具极其强大,还有其他很多专业作用,请参照官网ImageMagic使用指南
- 命名并放置: 将转换后的
.ico
文件正确命名(例如icon.ico
)并放置到你的 Qt 项目的工作目录中(通常是.pro
文件所在的目录)。
第二步:创建资源配置文件 (.rc)
-
在 Qt Creator 中:
- 选中你的项目名称(在左侧的项目视图中)。
- 鼠标右键,点击“添加新文件”。
-
选择文件类型:
- 在弹出的窗口中,选择
- 在弹出的窗口中,选择
-
命名配置文件:
- 输入图标配置文件的名称(例如
icon_config.rc
)。 - 重要: 务必加上文件的扩展名
.rc
。 - 点击“Next”。
- 输入图标配置文件的名称(例如
-
完成创建:
- 在项目管理界面,直接点击“完成”。
-
编辑 .rc 文件:
-
打开刚刚创建的
.rc
文件。 -
-
输入以下配置参数,将
icon.ico
替换为你的图标文件名称:IDI_ICON1 ICON DISCARDABLE "icon.ico"
-
第三步:配置项目文件 (.pro)
- 打开 .pro 文件: 在 Qt Creator 中找到并打开你的项目
.pro
文件。
2. 添加配置项: 在 .pro
文件中,增加下面的配置项,将 icon_config.rc
替换为你的 .rc
文件名称:
cpp //后面的参数就是rc文件的名称 RC_FILE += test.rc
第四步:编译运行
- 编译项目: 在 Qt Creator 中点击“构建”或“运行”按钮。
- 观察效果: 观察编译生成的应用程序的可执行文件图标是否已成功设置。
总结流程图:
5.2 Debug版本与Release版本:构建应用程序的两种模式
在软件开发过程中,我们通常会使用两种主要的构建版本来编译和打包我们的应用程序:Debug版本和Release版本。它们各自有不同的用途和特点。
1. Debug版本 (调试版本)
- 用途: 主要用于开发和调试阶段。
- 特点:
- 包含开发信息: 生成的可执行文件中会包含大量的调试信息,例如符号表、源码行号等。
- 性能较低: 由于包含额外信息且通常不做性能优化,程序运行速度相对较慢。
- 体积较大: 调试信息的加入会使生成的文件体积增大。
- 易于调试: 调试器可以利用这些信息方便地进行断点设置、变量查看、步进执行等操作,帮助开发者定位和修复错误。
2. Release版本 (发布版本)
- 用途: 最终用于发布给用户。
- 特点:
- 性能优化: 编译器会对程序进行高度优化,例如代码精简、内联函数、循环展开等,以提高运行速度和效率。
- 体积小巧: 移除了调试信息和未使用的代码,使生成的文件体积更小。
- 不便调试: 由于经过优化和移除了调试信息,这类程序在发布后不方便被调试。如果出现问题,定位起来会更加困难。
- 用户体验: 运行速度快、体积小,更适合最终用户使用。
Debug版本是开发者的工具,旨在帮助他们高效地发现和解决问题;Release版本则是面向最终用户的产品,追求最佳的性能和用户体验。我们使用Release版本打包。
主题:Qt 应用程序的动态链接库 (DLL) 打包
核心概念:
- 动态链接库 (DLL):Windows 系统下的动态链接库文件,包含可被多个应用程序共享的代码和资源。Qt 应用程序在运行时会调用 Qt 库中的接口,这些接口通常封装在 DLL 文件中。
- 构建目录:Qt 项目编译后生成可执行文件的目录,通常包含
debug
和release
子目录。
在没有 Qt 开发环境的计算机上双击运行 Qt 生成的 .exe
可执行文件时,通常会因为缺少必要的 Qt DLL 文件而报错。
解决方案:补充 EXE 所需的 DLL 文件
以下是打包 Qt 应用程序,使其能在无 Qt 环境计算机上运行的步骤:
-
准备打包根目录:
- 将 Release 模式下生成的
.exe
可执行文件单独复制到一个新的文件夹中。这个文件夹将作为最终软件打包的根目录。
- 将 Release 模式下生成的
-
启动带 Qt 环境的命令行:
- 在 Windows 搜索框中搜索并启动一个“带 Qt 环境的命令行”(例如 Qt Creator 自带的“Qt 5.xx for Desktop (MinGW / MSVC)”命令行)。
- 在 Windows 搜索框中搜索并启动一个“带 Qt 环境的命令行”(例如 Qt Creator 自带的“Qt 5.xx for Desktop (MinGW / MSVC)”命令行)。
-
切换命令行目录:
- 使用命令行命令
cd
切换到您在步骤 1 中创建的软件打包根目录。- 常用命令行命令提示:
cd <子目录>
:进入指定子目录。cd ..
:返回上级目录。dir
:显示当前目录内容。<盘符>:
(例如D:
):切换到指定盘符。
- 常用命令行命令提示:
- 使用命令行命令
-
执行 DLL 提取命令:
- 在命令行中,于软件打包根目录执行以下命令,提取所需的 DLL 文件:
windeployqt 可执行文件名称.exe
- 注意: 将
可执行文件名称.exe
替换为您的实际.exe
文件名。
- 注意: 将
- 在命令行中,于软件打包根目录执行以下命令,提取所需的 DLL 文件:
-
验证:
- 命令执行完成后,回到软件打包根目录,再次双击运行您的
.exe
文件,应该可以正常运行。
- 命令执行完成后,回到软件打包根目录,再次双击运行您的
-
重要提示:
- 最终的“软件根目录”(即打包后的文件夹)在功能上相当于开发阶段的“工作目录”和“构建目录”的结合体,包含了运行应用程序所需的所有文件。
总结流程图:
5.2 打包
打包软件:Inno Setup
使用方法:使用方法博文
最终章-项目演练
恭喜所有的小伙伴们!在经历了这么多篇博文的学习之后,我们终于迎来了Qt学习之旅的又一个里程碑——笔者的qt博文系列的结尾。这并非真正的终点,更像是一个小憩,让我们得以回顾和巩固所学。从最初的界面搭建到复杂的信号槽机制,从数据处理到文件操作,我们一同探索了Qt的奇妙世界。它强大的功能和优雅的C++接口,为我们的开发插上了翅膀。当然,Qt的广阔天地远不止于此,它还有无数的宝藏等待我们去发掘。希望这次旅程能激发大家对Qt更深层次学习的兴趣,未来继续在Qt的道路上,创造出更多精彩的应用。那么最后的最后,我们一起来自己动手做一个小项目吧!
项目要求
编写一个聊天室
实现功能
1. 实现注册登录
2. 实现聊天
3. 增加历史聊天查询功能
4. 美化界面
5. 打包成安装包
项目演示
[演示视频](【基于Qt的聊天室项目】 https://www.bilibili.com/video/BV1B4j3zHEoG/?share_source=copy_web&vd_source=28f8f5f13e40ab01eafa4c84779eb390)
结语
在Qt的广阔天地中,数据持久化、网络通信和图像处理是构建现代、复杂应用程序不可或缺的三大支柱。本系列文章从轻量级的QSettings到强大的数据库集成,从TCP/IP网络基础到QTcpServer/QTcpSocket的实战运用,再到Qt与OpenCV的无缝结合,最终以应用程序打包和项目实战为收尾,希望能为您打开Qt高级应用开发的大门。尽管我们探讨了诸多核心概念和技术细节,Qt的潜力远不止于此。愿您能将这些知识融会贯通,持续探索,在Qt的道路上创作出更多创新、高效且用户友好的软件。未来的开发之路充满挑战与机遇,愿我们携手共进,在Qt的精彩世界中不断成长!