0%

qt_5_常见技巧

简介

  • Qt常见技巧

C++ std::vector与QVector互相转换

在开发过程中,C++ 标准库的 std::vector 和 Qt 的 QVector 是两种常用的动态数组容器。在某些情况下,需要将这两种容器互相转换。下面是具体的转换方法:

1. 从 std::vector 转换为 QVector

可以使用 QVector 的构造函数或 QVectoroperator= 重载来完成。

使用构造函数

1
2
3
4
5
#include <vector>
#include <QVector>

std::vector<int> stdVector = {1, 2, 3, 4, 5};
QVector<int> qVector(stdVector.begin(), stdVector.end());

使用赋值运算符

1
2
3
4
5
6
#include <vector>
#include <QVector>

std::vector<int> stdVector = {1, 2, 3, 4, 5};
QVector<int> qVector;
qVector = QVector<int>::fromStdVector(stdVector);

2. 从 QVector 转换为 std::vector

可以使用 QVectortoStdVector() 方法。

1
2
3
4
5
#include <vector>
#include <QVector>

QVector<int> qVector = {1, 2, 3, 4, 5};
std::vector<int> stdVector = qVector.toStdVector();

注意事项

  1. 类型兼容性
    确保 std::vectorQVector 中存储的类型一致,例如两者都是 intdouble 等。

  2. 效率问题
    转换的效率取决于容器的大小。两种容器在底层内存布局上可能存在差异,因此需要完整的元素复制。

  3. 扩展用法
    如果存储的元素是复杂的类对象,确保拷贝构造函数或赋值运算符的正确性。

通过上述方法,可以轻松实现 std::vectorQVector 之间的互相转换,满足 Qt 与标准库协同工作的需求。

C++ Qt std::string传给QString中文乱码

好的!如果你的 std::string 使用的是其他编码(比如 GBK),你需要先将它转换为 UTF-8。可以使用 QTextCodec 来完成这个转换。以下是一个例子:

1
2
3
4
5
6
7
8
9
10
#include <QString>
#include <QTextCodec>
#include <string>

std::string str = "你的字符串"; // 假设这里是 GBK 编码的字符串

// 将 std::string 转换为 QString
QTextCodec *codec = QTextCodec::codecForName("GBK"); // 使用 GBK 编码
QString qstr = codec->toUnicode(str.c_str());

这样就可以将 std::string 中的 GBK 编码字符串正确转换为 QString 了。如果你知道你的字符串使用的是哪种编码,只需替换 "GBK" 为相应的编码名称。还有什么其他问题吗?

Qt5 QWidget类对象主动关闭窗口

在 Qt5 中,可以让一个 QWidget 类对象主动关闭窗口。一般来说,可以调用 close() 方法来关闭窗口。close()QWidget 类的一个公共成员函数,用于请求关闭窗口。这个方法会发送一个 closeEvent 事件,从而触发窗口的关闭。

如果想要让窗口在特定条件下自动关闭,可以在窗口的逻辑中调用 close() 方法。例如,我们可以在按钮点击事件中或者条件满足时让窗口关闭。

示例代码

以下代码演示了一个自定义的 MyWidget 类,包含一个按钮,点击该按钮后窗口会自动关闭。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>

class MyWidget : public QWidget {
Q_OBJECT

public:
MyWidget(QWidget *parent = nullptr) : QWidget(parent) {
// 设置窗口标题
setWindowTitle("Qt5 主动关闭窗口示例");

// 创建按钮
QPushButton *closeButton = new QPushButton("关闭窗口");

// 布局
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(closeButton);
setLayout(layout);

// 连接按钮点击信号到关闭窗口的槽
connect(closeButton, &QPushButton::clicked, this, &MyWidget::closeWindow);
}

private slots:
void closeWindow() {
// 主动关闭窗口
this->close();
}
};

int main(int argc, char *argv[]) {
QApplication app(argc, argv);

MyWidget window;
window.show();

return app.exec();
}

代码说明

  1. MyWidget:继承自 QWidget,并创建了一个按钮用于关闭窗口。

    • closeButton:点击按钮后会触发 closeWindow() 槽函数。
  2. closeWindow() 槽函数:调用 this->close() 来请求关闭当前窗口。

  3. close() 函数QWidget 类的内置函数,调用后会关闭窗口并触发 closeEvent

Qt5 保存数据到excel表格文件

在Qt5中将数据保存为Excel表格文件,可以使用以下几种方法:

1. 使用 QtXlsxWriter 库

QtXlsxWriter 是一个专为 Qt 设计的库,能够直接处理 .xlsx 文件格式。它可以帮助您轻松地创建、编辑、保存 Excel 表格。

安装 QtXlsxWriter

  1. 克隆库:
    1
    git clone https://github.com/dbzhang800/QtXlsxWriter.git
  2. 在项目中添加库:
    将库文件夹中的 src 目录直接添加到您的项目中,并在 .pro 文件中包含:
    1
    include(./QtXlsxWriter/src/xlsx/qtxlsx.pri)

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <QCoreApplication>
#include "xlsxdocument.h" // QtXlsxWriter 头文件

int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);

QXlsx::Document xlsx;
xlsx.write("A1", "姓名"); // 写入表头
xlsx.write("B1", "年龄");

xlsx.write("A2", "张三");
xlsx.write("B2", 25);

xlsx.write("A3", "李四");
xlsx.write("B3", 30);

xlsx.saveAs("output.xlsx"); // 保存文件

return a.exec();
}

2. 使用 QAxObject(仅适用于 Windows)

如果在 Windows 系统中使用 Qt,可以通过 QAxObject 调用 COM 接口直接控制 Microsoft Excel 应用程序。这种方法适合于有 Excel 安装的 Windows 环境。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <QCoreApplication>
#include <QAxObject>

int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);

QAxObject *excel = new QAxObject("Excel.Application"); // 打开Excel应用
excel->setProperty("Visible", false); // 设置为不可见
QAxObject *workbooks = excel->querySubObject("Workbooks");
QAxObject *workbook = workbooks->querySubObject("Add"); // 新建工作簿

QAxObject *sheet = workbook->querySubObject("Sheets(int)", 1); // 获取第一个工作表
sheet->setProperty("Name", "Sheet1");

// 填写数据
sheet->querySubObject("Cells(int,int)", 1, 1)->setProperty("Value", "姓名");
sheet->querySubObject("Cells(int,int)", 1, 2)->setProperty("Value", "年龄");

sheet->querySubObject("Cells(int,int)", 2, 1)->setProperty("Value", "张三");
sheet->querySubObject("Cells(int,int)", 2, 2)->setProperty("Value", 25);

sheet->querySubObject("Cells(int,int)", 3, 1)->setProperty("Value", "李四");
sheet->querySubObject("Cells(int,int)", 3, 2)->setProperty("Value", 30);

// 保存文件并关闭
workbook->dynamicCall("SaveAs(const QString&)", QDir::currentPath() + "/output.xlsx");
workbook->dynamicCall("Close()");
excel->dynamicCall("Quit()");

delete excel;

return a.exec();
}

3. 使用 CSV 文件格式

如果您不要求严格的 Excel 格式(.xlsx/.xls),可以将数据保存为 CSV 文件。这种方式简单并且跨平台支持。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <QCoreApplication>
#include <QFile>
#include <QTextStream>

int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);

QFile file("output.csv");
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file);

out << "姓名,年龄\n";
out << "张三,25\n";
out << "李四,30\n";

file.close();
}

return a.exec();
}

方法总结

  • QtXlsxWriter:适合跨平台、直接生成 .xlsx 文件。
  • QAxObject:适合 Windows 系统,适用于复杂的 Excel 操作。
  • CSV 文件:适用于简单的数据保存,跨平台,但不支持 Excel 的高级功能(如格式化)。

Qt QVector和C风格数组互相转换

在 Qt 中,QVector 和 C 风格数组(如 int[])之间的互相转换是常见的需求。下面介绍如何在这两者之间进行转换。

1. C 风格数组转 QVector

要将一个 C 风格数组转换为 QVector,可以使用 QVector 的构造函数或者通过 append() 方法将数组中的元素逐个添加到 QVector 中。

方法 1:使用 QVector 构造函数

如果你有一个 C 风格数组,并且知道它的大小,可以直接通过 QVector 的构造函数将其转换为 QVector

1
2
3
4
int arr[] = {1, 2, 3, 4, 5};
int size = sizeof(arr) / sizeof(arr[0]);

QVector<int> vec(arr, arr + size);
  • arr + size 表示数组的结束位置。
  • QVector 构造函数接受两个迭代器或指针,用于定义要添加到向量的元素范围。

方法 2:使用 QVector::append()QVector::push_back()

你可以逐个将数组元素添加到 QVector 中:

1
2
3
4
5
6
7
int arr[] = {1, 2, 3, 4, 5};
int size = sizeof(arr) / sizeof(arr[0]);

QVector<int> vec;
for (int i = 0; i < size; ++i) {
vec.append(arr[i]);
}

2. QVector 转 C 风格数组

要将 QVector 转换为 C 风格数组,可以通过使用 QVector 提供的 data() 函数。data() 函数返回一个指向 QVector 内部存储区的指针,这个指针就是一个普通的 C 风格数组。

方法 1:通过 QVector::data()

1
2
3
4
5
6
7
QVector<int> vec = {1, 2, 3, 4, 5};
int* arr = vec.data(); // 获取 C 风格数组的指针

// 可以像操作普通数组一样使用 arr
for (int i = 0; i < vec.size(); ++i) {
qDebug() << arr[i];
}

注意QVector::data() 返回的指针指向 QVector 内部的数据区域,因此不能对该指针进行 delete[] 操作。它仅仅是一个数据访问的快捷方式,内存管理仍然由 QVector 负责。

方法 2:手动复制数据到一个新的 C 风格数组

如果你希望独立于 QVector 的生命周期使用 C 风格数组,可以手动分配内存并复制数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
QVector<int> vec = {1, 2, 3, 4, 5};
int size = vec.size();
int* arr = new int[size];

// 将 QVector 的数据复制到 C 风格数组
for (int i = 0; i < size; ++i) {
arr[i] = vec[i];
}

// 使用 arr...
for (int i = 0; i < size; ++i) {
qDebug() << arr[i];
}

// 记得释放手动分配的内存
delete[] arr;

3. 注意事项

  • QVector::data() 返回的指针与 QVector 共享内存,因此在 QVector 被修改或销毁后,这个指针可能会失效。
  • 当使用 new 分配的 C 风格数组时,必须手动释放内存,避免内存泄漏。

总结

  • **C 风格数组转 QVector**:可以使用构造函数或逐个添加元素。
  • QVector 转 C 风格数组:可以使用 data() 获得指向内部数组的指针,或者手动分配新数组并复制数据。

这两个容器之间的转换非常灵活,可以根据具体需求选择合适的转换方式。

QVector和std::vector的区别和联系

QVectorstd::vector 是 Qt 和 C++ 标准库中常用的动态数组容器,它们在功能上有许多相似之处,但在设计和使用上也有一些区别。下面是它们的区别和联系的详细说明。

1. 共同点

  • 动态数组:两者都是动态数组容器,支持按需自动扩展容量,并且提供随机访问功能(通过下标访问元素)。
  • 元素连续存储:无论是 QVector 还是 std::vector,它们都保证其元素在内存中是连续存储的。这使得它们能够很好地支持基于指针的算法,并与低级 C 风格数组兼容。
  • 模板类:两者都是模板类,可以存储任何类型的对象,只要该类型满足拷贝或移动要求。
  • 时间复杂度:两者在大多数操作上的时间复杂度是相同的,诸如:
    • 访问元素:O(1)
    • 添加/删除元素(尾部):均摊 O(1)
    • 插入/删除元素(中间):O(n)

2. 主要区别

2.1 库和平台依赖

  • **QVector**:是 Qt 框架的一部分,必须依赖 Qt 环境才能使用。QVector 提供了一些与 Qt 生态系统紧密集成的特性,适合在 Qt 应用中使用。
  • **std::vector**:属于 C++ 标准库的一部分,不依赖任何第三方库,适用于标准 C++ 开发。std::vector 通常更适合在不依赖 Qt 的纯 C++ 项目中使用。

2.2 浅拷贝(隐式共享)机制

  • **QVector**:支持隐式共享(copy-on-write,COW)。这意味着当你复制一个 QVector 时,Qt 实际上并不会立即复制其内部数据,而是与源对象共享数据,直到有一方修改容器中的内容时才会真正复制数据。这种设计在处理大量数据时可以节省内存和提高性能。

    1
    2
    3
    QVector<int> vec1 = {1, 2, 3};
    QVector<int> vec2 = vec1; // 浅拷贝,vec1 和 vec2 共享内存
    vec2[0] = 10; // 修改时才会进行深拷贝
  • **std::vector**:不支持隐式共享。当你复制一个 std::vector 时,会立即进行深拷贝,将所有元素复制到新的容器中。每次复制都需要消耗额外的内存和时间。

    1
    2
    std::vector<int> vec1 = {1, 2, 3};
    std::vector<int> vec2 = vec1; // 深拷贝,vec1 和 vec2 拥有独立的数据

2.3 性能和线程安全

  • **QVector**:由于 Qt 使用了隐式共享机制,某些操作(如拷贝和赋值)在表面上比 std::vector 更高效。但这也带来了一些额外的复杂性,尤其是在多线程环境下。如果多个线程共享一个 QVector,并且有写操作,必须确保数据的一致性,否则会引发线程安全问题。

  • **std::vector**:没有隐式共享,因此不需要在多线程环境下担心这种问题。只要保证不同线程不同时修改同一个 std::vector 实例,线程安全性可以更容易控制。

2.4 API 和接口差异

  • **QVector**:作为 Qt 的一部分,提供了一些与 Qt 生态系统紧密集成的接口和功能。例如,QVectortoList() 方法可以方便地将它转换为 QList。此外,QVector 支持 qDebug() 输出、QDataStream 序列化等 Qt 特有的特性。

    1
    2
    QVector<int> vec = {1, 2, 3};
    QList<int> list = vec.toList(); // 转换为 QList
  • **std::vector**:遵循标准 C++ API,没有与 Qt 特定的接口。例如,std::vector 没有类似的转换功能,但是它与标准库容器之间可以通过迭代器进行高效互操作。std::vector 还支持标准算法库 (<algorithm>) 中的许多算法,如 std::sort, std::find 等。

2.5 内存分配与管理

  • **QVector**:Qt 框架对内存分配做了许多优化,尤其是在管理动态对象的生命周期和复制时。QVector 的隐式共享机制减少了不必要的深拷贝,同时内存管理方式与 Qt 的其他容器(如 QList)保持一致。

  • **std::vector**:标准库的 std::vector 使用的是 C++ 标准库的内存分配器,通常为 std::allocator。开发者可以通过自定义内存分配器来精细控制内存分配行为。

2.6 反射和元对象系统

  • **QVector**:由于 Qt 支持元对象系统(Meta-Object System),QVector 可以与 Qt 的信号和槽、反射等机制配合使用,支持特定类型的序列化、GUI 绑定等。

  • **std::vector**:不支持 Qt 的元对象系统或信号槽机制。因此,如果你在使用 Qt 的信号槽机制或需要动态类型检查、反射等特性时,QVector 是更合适的选择。

3. 适用场景

  • **使用 QVector**:

    • 项目中广泛使用 Qt 框架。
    • 需要与 Qt 容器(如 QList, QSet)互操作,或依赖于 Qt 的元对象系统和信号槽机制。
    • 需要隐式共享以优化内存使用和提高性能(尤其是数据量较大时)。
  • **使用 std::vector**:

    • 项目不依赖 Qt,纯 C++ 开发。
    • 需要兼容 C++ 标准库的其他容器和算法。
    • 希望避免隐式共享带来的复杂性,尤其是在多线程环境中。

总结

特性 QVector std::vector
库依赖 Qt 框架 C++ 标准库
隐式共享 支持(浅拷贝,写时复制) 不支持(深拷贝)
线程安全 需要注意隐式共享导致的安全问题 非共享,线程安全性更容易控制
内存分配 使用 Qt 的内存管理 使用标准库分配器
与 Qt 集成 强(支持信号槽、Qt 容器转换等)
性能优化 提供浅拷贝优化 无隐式共享,复制开销较大
适用场景 Qt 项目,数据量大 纯 C++ 项目,多线程安全

两者的选择通常取决于项目的上下文以及你是否需要 Qt 提供的隐式共享和其他特性。如果你正在开发 Qt 应用程序,QVector 通常是更好的选择;如果是标准 C++ 项目,std::vector 更为适合。

感谢老板支持!敬礼(^^ゞ