Qt学习记录
以下示例如无特别说明,均在Clion,CMake,Qt6环境下测试
Qt6读取GBK文件
在Qt5中,有QTextCodec模块,支持各种编码设置。
// Qt5
QCoreApplication a(argc, argv);
auto desk = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
QFile file(QDir(desk).filePath("test.txt"));
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){
qDebug() << file.errorString() << Qt::endl;
return a.exec();
}
QTextStream in(&file);
in.setCodec(QTextCodec::codecForName("GBK"));
while (!in.atEnd()){
const QString &line = in.readLine();
qDebug() << line;
}
return a.exec();
Qt6后,删除了QTextCodec模块,引入了QStringConverter,其中有几个常用的编码,其他编码都不支持,目前只能结合libconv等其他库实现编码转换。但对于windows默认的GBK编码,可以换种方式实现。
// Qt6
QCoreApplication a(argc, argv);
auto desk = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
QFile file(QDir(desk).filePath("test.txt"));
if (!file.open(QIODevice::ReadOnly)){
qDebug() << file.errorString() << Qt::endl;
return a.exec();
}
QByteArray arr = file.readAll();
auto s = QString::fromLocal8Bit(arr);
qDebug() << s;
return a.exec();
Qt6组件语言设置
部分组件默认语言是英文,使用下面的代码语言改为跟随系统。例如QMessageBox弹框中的Ok会变成确认,Cancle会变成取消等。
QApplication app(argc, argv);
QTranslator translator;
const QString &locale = QLocale::system().name();
const QString &path = QLibraryInfo::path(QLibraryInfo::TranslationsPath);
if (!translator.load(QString("qt_%1").arg(locale), path)) {
qDebug() << "load qt_" + locale + ".qm failed";
} else {
QApplication::installTranslator(&translator);
}
Widget w;
w.show();
return QApplication::exec();
Qt CMake设置应用程序图标等信息
Windows .rc 文件(资源脚本文件)是用来定义和描述程序中的各种资源的文本文件。这些资源可以包括图标、光标、对话框、菜单、字符串等。.rc 文件是 Windows 应用程序开发中的重要组成部分,通常与 C 或 C++ 代码一起使用,通过资源编译器将其编译成二进制 .res 文件,并在程序中加载这些资源。
- 首先准备一个ico文件,用作应用程序图标。
- 新建c++头文件version.h,宏定义应用程序一些信息。
//
// Created by chunf on 2024/12/26.
//
#ifndef VERSION_H
#define VERSION_H
#define PRODUCT_ICON "resources/app.ico" // 图标,项目根目录/resources/app.ico
#define FILE_VERSION 1,0,0,0 // 文件版本
#define FILE_VERSION_STR "1.0.0.0"
#define PRODUCT_VERSION 1,0,0,0 // 产品版本
#define PRODUCT_VERSION_STR "1.0.0.0"
#define COMPANY_NAME "公司名称"
#define INTERNAL_NAME "zhd.exe"
#define FILE_DESCRIPTION "职代会评测统计程序" // 文件说明
#define LEGAL_COPYRIGHT "Copyright 2024 The xxxx Company. All rights reserved." // 版权
#define ORIGINAL_FILE_NAME "demo.exe" // 原始文件名
#define PRODUCT_NAME "zdh" // 产品名称
#define ORGANIZATION_DOMAIN "https://www.qt.io/" // 域名
#endif // VERSION_H
- 新建一个.rc文件
#include "winres.h" // 引入 Windows 资源文件头,包含常见的资源类型定义
#include "version.h" // 引入自定义的版本信息头文件,定义了版本、公司等常量
// 图标资源定义
IDI_ICON1 ICON PRODUCT_ICON // 定义一个图标资源,使用 'PRODUCT_ICON' 作为图标的文件名
// 版本信息资源定义
VS_VERSION_INFO VERSIONINFO // 定义版本信息资源块
FILEVERSION FILE_VERSION // 文件版本号,引用 FILE_VERSION 宏
PRODUCTVERSION PRODUCT_VERSION // 产品版本号,引用 PRODUCT_VERSION 宏
FILEFLAGSMASK 0x3fL // 允许的文件标志位掩码,0x3fL 表示保留所有标志
#ifdef _DEBUG // 如果是在调试模式下
FILEFLAGS 0x1L // 设置调试标志位,0x1L 表示调试版本
#else // 如果是发布模式
FILEFLAGS 0x0L // 设置文件标志位为 0,表示发布版本
#endif
FILEOS 0x40004L // 操作系统类型,0x40004L 通常表示 Windows 32 位操作系统
FILETYPE 0x1L // 文件类型,0x1L 表示可执行文件
FILESUBTYPE 0x0L // 文件子类型,0x0L 通常表示通用子类型
BEGIN
BLOCK "StringFileInfo" // 定义包含文件信息字符串的块
BEGIN
BLOCK "080404b0" // 语言/区域标识符,"080404b0" 是简体中文(中国)的标识符
BEGIN
VALUE "CompanyName", COMPANY_NAME // 公司名称,引用 COMPANY_NAME 宏
VALUE "FileDescription", "职代会评测统计程序/[email protected]" // 文件描述,描述程序用途
VALUE "FileVersion", FILE_VERSION_STR // 文件版本字符串,引用 FILE_VERSION_STR 宏
VALUE "InternalName", INTERNAL_NAME // 内部文件名称,引用 INTERNAL_NAME 宏
VALUE "LegalCopyright", LEGAL_COPYRIGHT // 法律版权信息,引用 LEGAL_COPYRIGHT 宏
VALUE "OriginalFilename", ORIGINAL_FILE_NAME // 原始文件名称,引用 ORIGINAL_FILE_NAME 宏
VALUE "ProductName", PRODUCT_NAME // 产品名称,引用 PRODUCT_NAME 宏
VALUE "ProductVersion", PRODUCT_VERSION_STR // 产品版本字符串,引用 PRODUCT_VERSION_STR 宏
END
END
BLOCK "VarFileInfo" // 版本信息中的可变数据块
BEGIN
VALUE "Translation", 0x804, 1200 // 翻译信息,0x804 表示简体中文(中国),1200 为编码(通常是 UTF-8)
END
END
- CMake中加入编译
add_executable(zdh
main.cpp
utils.h
utils.cpp
widget.h
widget.cpp
widget.ui
cardconfig.h
res.qrc
version.h
zdh.rc
)
- 编译并查看结果
这里附上一些常用的配置项说明,更多使用可以查看微软的文档。
QXlsx使用
QXlsx 是一个跨平台的非官方Qt库,允许开发者在不依赖 Microsoft Excel 或 COM 自动化的情况下读取和写入 Excel 文件。
- 下载QXlsx项目源代码到项目目录。
- CMake中加入
add_subdirectory(QXlsx-master/QXlsx)
target_link_libraries(qxlsx_test PUBLIC Qt::Core
Qt::Gui
Qt::Widgets)
target_link_libraries(qxlsx_test PRIVATE QXlsx::QXlsx)
经测试发现,QXlsx依赖于Qt Gui,未加入此库程序会直接奔溃
3. 基本操作
#include <QCoreApplication>
#include <QStandardPaths>
#include <QDir>
#include "xlsxdocument.h"
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
const QString &desktopPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
const QString &xlsxPath = QDir(desktopPath).filePath("test.xlsx");
// 这种读取方式不能获取失败信息
// QXlsx::Document document(xlsxPath);
// if (!document.load()) {
// qDebug() << "Error loading file";
// return 0;
// }
// 这种方式获取详细的读取失败信息
QFile file(xlsxPath);
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << "Error opening file";
qDebug() << file.errorString();
qDebug() << file.error();
return 0;
}
QXlsx::Document document(&file);
if (!document.load()) {
qDebug() << "Error loading file";
return 0;
}
qDebug() << document.sheetNames();
QXlsx::AbstractSheet *curSheet = document.currentSheet();
if (curSheet != nullptr) {
qDebug() << curSheet->sheetName();
auto *workSheet = dynamic_cast<QXlsx::Worksheet *>(curSheet);
const QXlsx::CellRange &range = workSheet->dimension();
qDebug() << range.firstRow() << range.lastRow() << range.firstColumn() << range.lastColumn();
qDebug() << range.topLeft().toString() << range.bottomRight().toString() << range.toString();
}
const QXlsx::CellRange &range = document.dimension();
qDebug() << range.firstRow() << range.lastRow() << range.firstColumn() << range.lastColumn();
qDebug() << range.topLeft().toString() << range.bottomRight().toString() << range.toString();
QXlsx::Worksheet *sheet = dynamic_cast<QXlsx::Worksheet *>(document.sheet("班子总打分"));
if (sheet == nullptr) {
qDebug() << "找不到工作表";
return 0;
}
sheet->write(50, 50, "Hello World!");
sheet->write("G50", "Hello World!");
// 保存和读取一样,也有多种方式
if (!document.saveAs(xlsxPath)) {
qDebug() << "Error saving file";
return 0;
}
return 0;
}
API见名知意,使用简单方便。
QXlsx复制工作表
bool copyWorkSheet(QXlsx::Document &document, const QString &srcSheetName, const QString &destSheetName,
bool replaceIfExists = false) {
auto *srcSheet = dynamic_cast<QXlsx::Worksheet *>(document.sheet(srcSheetName));
if (srcSheet == nullptr) {
throw std::runtime_error(QString("%1 not found").arg(srcSheetName).toStdString());
}
auto *testSheet = document.sheet(destSheetName);
if (testSheet != nullptr && !replaceIfExists) {
return false;
}
if (!document.copySheet(srcSheetName, destSheetName)) {
return false;
}
auto *destSheet = dynamic_cast<QXlsx::Worksheet *>(document.sheet(destSheetName));
const QXlsx::CellRange &range = srcSheet->dimension();
const int rowCount = range.rowCount();
const int colCount = range.columnCount();
for (int i = 1; i <= rowCount; ++i) {
destSheet->setRowHeight(i, i, srcSheet->rowHeight(i));
}
for (int i = 1; i < colCount; ++i) {
destSheet->setColumnWidth(i, i, srcSheet->columnWidth(i));
}
return true;
}
Qt多线程
- 编写Worker类继承QObject,在Controller中moveToThread(推荐)。
- 继承QThread重写run方法。
- GUI线程与子线程之间通过信号和槽进行通信并控制行为,不能直接在子线程中操作UI。
- 不同线程之间的信号和槽连接方式为异步,虽然如此,以局部变量的引用作为信号函数和槽函数的参数时,Qt会对其进行拷贝,无需考虑访问非法内存问题。
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
QItemSelectionModel中clearSelection, clearCurrentIndex和clear区别
- clearSelection(): 清除所有已选择的项时使用,但保持当前项不变。
- clearCurrentIndex(): 该方法清除当前项(即当前索引),但不会影响选中的项。这意味着当前项的显示或激活状态将被清除,但其他选项的状态保持不变。
适用场景:如果你希望取消当前项的激活状态,但不想清除选中的项目时使用。 - clear():这个方法会清除选区(选择的项)和当前项。相当于同时调用 clearSelection() 和 clearCurrentIndex(),即清除所有选择项和当前项。
适用场景:如果你希望同时取消所有选中的项并清除当前项时使用。
总结:
- clearSelection():只清除选择的项。
- clearCurrentIndex():只清除当前项。
- clear():清除选择的项和当前项。
QCoreApplication sendEvent和sendPostedEvent
- sendEvent():立即发送事件并调用目标对象的事件处理函数,同步阻塞式。
- sendPostedEvent():将事件添加到事件队列中,并在适当的时候异步处理该事件,异步非阻塞式。
QListView和QListWidget
特性 | QListView | QListWidget |
---|---|---|
模型-视图架构 | 需要外部模型与视图分离 | 内部自带默认模型 |
灵活性 | 高,可自定义模型和代理 | 较低,适用于简单列表显示 |
适用复杂数据结构 | 适合复杂的数据和自定义的交互方式 | 适合简单的、静态的数据 |
易用性 | 需要更多的代码来管理模型和视图 | 简单易用,直接操作项 |
功能 | 支持更丰富的自定义,例如代理、排序等 | 提供基础的添加、删除、选择等功能 |
Qt中QMouseEvent,pos, position, scenePosition和globalPosition
- pos():
返回相对于事件接收者的坐标。也就是说,返回的是鼠标点击位置在接收该事件的控件(例如 QWidget)的坐标系中的位置。这个位置是相对于该控件的左上角的。
示例:如果你在一个 QWidget 内点击位置 (10, 20),pos() 将返回 (10, 20)。 - position():
从 Qt 5.15 开始,position() 是 QMouseEvent 的新方法,返回鼠标事件的精确坐标,通常使用浮点数而不是整数。它与 pos() 类似,但精度更高。position() 返回的坐标也通常是相对于事件接收者的坐标系。
示例:如果鼠标在位置 (10.5, 20.5) 点击,position() 将返回 (10.5, 20.5)。 - scenePosition():
返回相对于 QGraphicsScene 的坐标。如果你在一个 QGraphicsView 中处理鼠标事件,这个方法特别有用,因为它给出的坐标是相对于整个 QGraphicsScene 的,而不是视图或控件的。
示例:在 QGraphicsView 内,鼠标点击时 scenePosition() 返回的坐标会相对于 QGraphicsScene 的原点,而不是视图或窗口的原点。 - globalPosition():
返回的是鼠标在屏幕上的全局坐标,通常是相对于显示设备的左上角。这个方法非常有用,特别是当你想获取鼠标位置与其他窗口或屏幕的关系时。
示例:如果鼠标点击位置在屏幕的 (100, 200) 处,globalPosition() 将返回 (100, 200)。
总结:
pos() 和 position():相对于事件接收者的坐标系,position() 提供更精确的浮点数值。
scenePosition():相对于 QGraphicsScene 的坐标系,通常用于图形视图场景中。
globalPosition():相对于屏幕的全局坐标。
Qt Enter和Hover事件
- QEvent::Enter,QEvent::Leave,当鼠标指针进入和离开一个控件的区域时触发。
- QEvent::HoverEnter,QEvent::HoverMove,QEvent::HoverLeave 当鼠标指针进入控件,在控件上方移动,离开控件时触发。当鼠标在控件的区域内移动时,会多次触发HoverMove。HoverEnter和HoverMove需要设置setAttribute(Qt::WA_Hover, true),HoverMove需要setMouseTracking(true)。
QImage,QPixmap和QPicture
-
QImage
用于处理图像数据的类,提供了对图像像素的直接访问和操作。
支持多种图像格式(如 PNG、JPEG、BMP 等)。
可以直接访问和修改像素数据。
适合用于图像处理、像素级操作和图像格式转换。
不依赖于底层平台的图形系统,因此可以在没有图形界面的环境下使用。
使用场景: 图像处理、图像格式转换、像素级操作等。 -
QPixmap
用于在屏幕上显示图像的类,通常用于绘制图像到窗口或控件上。
针对显示进行了优化,适合用于 GUI 应用程序中的图像显示。
依赖于底层平台的图形系统(如 X11、Windows GDI 等),因此在没有图形界面的环境下可能无法使用。
不支持直接访问像素数据,但可以通过 QImage 进行转换。
使用场景: 在 GUI 应用程序中显示图像、图标、背景等。 -
QPicture
记录和回放绘图操作的类,可以看作是一个绘图命令的容器。
不存储像素数据,而是存储绘图命令(如绘制线条、矩形、文本等)。
可以保存到文件或从文件加载,适合用于存储和重现绘图操作。
与 QImage 和 QPixmap 不同,QPicture 不直接处理图像数据,而是记录绘图指令。
使用场景: 记录和回放绘图操作、保存和加载绘图命令等。
如果需要处理图像数据,使用 QImage;如果需要显示图像,使用 QPixmap;如果需要记录绘图操作,使用 QPicture。