QT框架笔记

VS上使用QT

环境搭建

C++实践之Qt学习(扩展):Visual Studio中Qt开发环境的搭建_visual studio 与 qt-CSDN博客

配置下32,64位的生成配置

【QT】visual studio QT 32位和64位生成配置_qt生成32位程序-CSDN博客

显示中文,防止乱码:

1
#pragma execution_character_set("utf-8")

Qt 在跨平台开发中广泛支持 UTF-8 , 它告诉编译器将源代码中的字符串文字编码为 UTF-8,而不是默认的系统编码(例如GBK)。

关于父类的说明

QMainWindow

可以包含菜单栏、工具栏、状态栏、标题栏等,是最常见的窗口形式,可以作为桌面应用的主窗口

QWidget

是所有用户界面对象的基类,其他的窗口和控件都是直接或间接地集成自QWidget,一般创建桌面应用程序时需要创建一个窗口,此时选择QMainWindow或者QDialog即可,QMainWindow是主窗口,QDialog表示对话框。而如果不确定是否作为一个顶级窗口或者嵌入到其他窗口中,可以使用QWidget,同时它也是实现自定义部件的基类。

QDialog

表示对话框,主要用来进行短期交互,可以设置成模态的。一般没有菜单栏、工具栏、状态栏等。

1731162783725

QT信号与槽机制

简单介绍

在 Qt 框架中,信号和槽(Signal and Slot)是一种用于对象之间通信的机制,尤其适用于事件驱动的 GUI 应用程序。信号和槽是 Qt 中的一种扩展机制,专门为简化和增强组件间的交互设计的。

信号:类似于一种“广播”机制,表明发生了某些事件。信号本身不包含处理逻辑,只是单纯的事件通知。

:槽函数用于接收信号,槽函数会定义如何处理事件。槽函数可以是任何可调用的成员函数。

类似于之前学过的MFC框架中的消息映射

QT中信号和槽的特点

(为什么信号和槽不写成统一的普通函数)

1. 松耦合(Loose Coupling)

信号和槽的机制通过解耦事件的发送者(信号)和接收者(槽)之间的关系,提供了松耦合的编程方式。松耦合是指发送信号的对象并不需要知道任何关于接收信号的对象(即槽)的细节,反之亦然。

  • 传统的函数调用:通常在 C++ 中,一个对象直接调用另一个对象的函数,这需要对象彼此了解,并且依赖于函数的实现。如果你想更改接收者对象或者函数的实现,必须更改调用者代码。这种方式很紧耦合,修改和维护变得困难。
  • 信号与槽:使用信号和槽机制时,发送信号的对象根本不需要知道谁在接收信号,它只是通过 emit 关键字发出信号。接收信号的槽函数可以在其他任何地方实现并连接到信号。这使得对象间的关系非常灵活,减少了代码之间的依赖。

2. 事件驱动机制

Qt 是一个事件驱动框架,这意味着程序的行为是由事件触发的,而不是由方法调用的。信号和槽机制就是实现事件驱动模型的核心之一。

  • 传统的调用:在传统 C++ 中,你通常会通过直接调用函数来触发某些行为,这种行为是同步的,即调用者必须等待返回。
  • 信号和槽:信号和槽机制允许事件的异步处理。例如,某个对象可以发出信号(事件),而不关心谁处理它,接收者(槽)可以在合适的时机处理这个信号,而不必与发送者直接交互。Qt 框架也支持这种信号异步传递的机制,可以在后台线程中处理信号,从而避免 UI 阻塞。

3. 自动连接机制

Qt 的信号和槽机制具有自动连接功能。通常,Qt 会根据信号和槽的名称、参数匹配情况,自动连接信号与槽。你无需手动管理函数指针或事件传递。

  • 传统的函数指针或回调:你需要显式地将回调函数与事件绑定。例如,在某些情况下,你可能需要手动管理一个函数指针或回调对象。这种方式不如信号和槽机制直观和灵活。
  • 信号与槽的自动连接:通过 QObject::connect 函数,信号和槽可以自动绑定。如果信号发射时参数类型与槽匹配,Qt 会自动调用相应的槽函数。你只需要专注于信号的触发和槽的实现,不必担心对象间如何互相调用。

4. 多个槽的连接(多重响应)

使用信号和槽机制,一个信号可以连接到多个槽函数,这意味着一个事件可以触发多个不同的行为。这种特性使得信号和槽非常适合多任务或并发编程。

  • 传统的函数调用:你只能调用一个函数。
  • 信号与槽:一个信号可以连接到多个槽,这使得你可以根据不同的需求实现不同的行为。例如,多个组件都可以监听同一个信号,而各自作出不同的反应。

5. 线程安全

Qt 的信号和槽机制本身设计时考虑了多线程环境,在多线程的情况下,信号和槽之间的调用是线程安全的。Qt 内部自动处理了信号和槽的线程间通信。

  • 传统的线程通信:在传统的 C++ 中,你需要使用显式的线程同步机制(如互斥锁、条件变量等)来保证线程安全。
  • 信号与槽的线程安全:Qt 会自动处理跨线程的信号和槽连接,例如在不同线程之间传递信号时,它会将信号的执行放到目标线程的事件队列中,这样即使在多线程环境下,信号与槽的通信也能保持安全。

6. 灵活性

信号和槽提供了非常灵活的方式来处理事件。你可以通过编程动态地添加和移除槽,甚至可以在运行时动态连接信号和槽。这种灵活性非常适用于复杂的 GUI 应用程序或需要高度自定义行为的程序。

  • 传统的函数调用:通常是固定的,函数调用的绑定在编译时就已经确定了,缺乏动态调整的能力。
  • 信号和槽:通过 QObject::connectQObject::disconnect,你可以在运行时灵活地连接和断开信号与槽的关系,极大地增加了代码的灵活性和扩展性。

参数类型、参数数量和顺序都一致 那么信号和槽就会匹配。

QObject::connect()

在 Qt 中,信号和槽是通过 QObject::connect() 函数连接的:

1
2
3
4
5
6
7
QObject::connect(
const QObject *sender,
PointerToMemberFunction signal,
const QObject *receiver,
PointerToMemberFunction slot,
Qt::ConnectionType type = Qt::AutoConnection
);

sender (发送者对象指针)

  • const QObject *sender:表示信号的发送者对象,即哪个对象发出信号。
  • 一般是类的对象指针,如 &myButtonthis

signal (信号函数指针)

  • PointerToMemberFunction signal:这是指向信号函数的成员函数指针,指定要发出的信号。
  • 格式是 &ClassName::signalName,需要明确指出信号所在类的作用域。
  • 示例&QPushButton::clicked 表示 QPushButton 类中的 clicked() 信号。

receiver (接收者对象指针)

  • const QObject *receiver:表示信号的接收者对象,即哪个对象接收信号并执行相应槽函数。
  • 一般是类的对象指针,比如 &objthis

slot (槽函数指针)

  • PointerToMemberFunction slot:这是指向槽函数的成员函数指针,表示当信号触发时要调用的槽函数。
  • 格式是 &ClassName::slotName,也需要明确槽所在类的作用域。
  • 示例&MyClass::onButtonClicked 表示 MyClass 类中的 onButtonClicked() 槽函数。

**type**(连接类型,可选参数)

  • Qt::ConnectionType type = Qt::AutoConnection:指定连接类型,用来决定信号和槽的执行方式。默认为 Qt::AutoConnection,它会自动根据线程情况选择合适的连接类型。

  • 其他可选值包括:

    • Qt::DirectConnection:槽在发送信号的线程中立即执行。
    • Qt::QueuedConnection:槽在接收者所在的线程中异步执行。
    • Qt::BlockingQueuedConnection:阻塞发送者线程,直到槽函数完成(仅在多线程环境中使用)。
    • Qt::UniqueConnection:确保同一对信号和槽只连接一次。

函数返回值

返回一个 QMetaObject::Connection 类型的值,用于断开连接。如果连接成功,返回一个有效的连接对象;如果连接失败(例如信号与槽的参数不匹配),则返回一个无效的连接。

代码示例

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
41
42
43
44
45
46
47
48
49
//Calculate_Volume.h
#pragma once
#pragma execution_character_set("utf-8")
#include <QtWidgets/QMainWindow>
#include "ui_Calculate_Volume.h"
#include <QMainWindow>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QString>
#include <cmath> // 用于计算球的体积
#include <windows.h>
#include <QMessageBox>
#define M_PI 3.1415926535


class Calculate_Volume : public QMainWindow
{
Q_OBJECT

public:
Calculate_Volume(QWidget *parent = nullptr);
~Calculate_Volume();

private:
Ui::Calculate_VolumeClass ui;
QLabel* label; // 标签类
QLineEdit* radiusInput; // 半径输入文本框
QLineEdit* volumeOutput; // 体积输出文本框
QPushButton* calculateButton; // 计算按钮

public slots:
void onButtonClicked()
{
bool ok;
double radius = radiusInput->text().toDouble(&ok);
if (!ok || radius <= 0) {
QMessageBox::warning(this, "输入错误", "请输入一个有效的正数作为半径!");
return;
}

// 计算球的体积 V = (4/3) * π * r^3
double volume = (4.0 / 3.0) * M_PI * std::pow(radius, 3);
volumeOutput->setText(QString::number(volume));
}

};
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//Calculate_Volume.cpp
#include "Calculate_Volume.h"
#include <QVBoxLayout>
#include <QDebug>
Calculate_Volume::Calculate_Volume(QWidget* parent)
: QMainWindow(parent),
label(new QLabel("请输入球的半径:", this)),
radiusInput(new QLineEdit(this)),
volumeOutput(new QLineEdit(this)),
calculateButton(new QPushButton("计算体积", this))
{
ui.setupUi(this);
volumeOutput->setReadOnly(true); // 体积输出框只读

// 创建布局
QVBoxLayout* mainLayout = new QVBoxLayout;
QHBoxLayout* firstRowLayout = new QHBoxLayout;
QHBoxLayout* secondRowLayout = new QHBoxLayout;

// 第一个行:输入提示 + 半径文本框
firstRowLayout->addWidget(label);
firstRowLayout->addWidget(radiusInput);

// 第二个行:体积输出框 + 计算按钮
secondRowLayout->addWidget(volumeOutput);
secondRowLayout->addWidget(calculateButton);

// 将行布局添加到主布局
mainLayout->addLayout(firstRowLayout);
mainLayout->addLayout(secondRowLayout);

// 创建一个中央窗口部件,并设置布局
QWidget* centralWidget = new QWidget(this);
centralWidget->setLayout(mainLayout);
setCentralWidget(centralWidget);

// 连接信号和槽
connect(calculateButton, &QPushButton::clicked, this, &Calculate_Volume::onButtonClicked);
}

Calculate_Volume::~Calculate_Volume()
{}




//main.cpp
#include "Calculate_Volume.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Calculate_Volume w;
w.show();
return a.exec();
}

效果展示:

1731206890639

一些Qt的方法类

QString类:

QString 是 Qt 中用于处理字符串的类,提供了丰富的字符串操作功能。以下是 QString 中一些常用的方法:

  1. 基础操作
  • QString::isEmpty()
    检查字符串是否为空,返回 true 表示为空。
  • QString::isNull()
    检查字符串是否为 null,与 isEmpty() 不同的是,null 的字符串没有分配任何内存,而空字符串分配了内存但没有内容。
  • QString::length()QString::size()
    返回字符串的长度(字符个数)。
  • QString::clear()
    清空字符串内容。
  1. 字符串拼接
  • QString::append(const QString &str)
    在字符串末尾追加另一个字符串。
  • operator+
    使用 + 操作符拼接两个字符串。例如:str1 + str2
  1. 查找与替换
  • QString::contains(const QString &str)
    检查字符串是否包含指定子串,返回布尔值。
  • QString::indexOf(const QString &str)
    返回子串首次出现的位置,如果未找到返回 -1
  • QString::lastIndexOf(const QString &str)
    返回子串最后一次出现的位置。
  • QString::replace(const QString &before, const QString &after)
    将指定子串替换为另一个字符串。
  1. 大小写转换
  • QString::toLower()
    将字符串转换为小写。
  • QString::toUpper()
    将字符串转换为大写。
  1. 子串与截取
  • QString::left(int n)
    返回字符串左边的 n 个字符。
  • QString::right(int n)
    返回字符串右边的 n 个字符。
  • QString::mid(int position, int n = -1)
    从指定位置开始,提取 n 个字符的子串,若 n-1 则提取到字符串末尾。
  1. 格式化输出
  • QString::arg()
    格式化字符串,将指定的参数值插入到占位符(如 %1)中。可以链式调用,插入多个参数。

    1
    QString message = QString("Hello, %1! Your score is %2.").arg("Alice").arg(90);
  • QString::number()
    将数字转换为字符串,支持整数、浮点数等。

    1
    QString str = QString::number(123.45, 'f', 2); // 123.45
  1. 转换与编码
  • QString::toStdString()
    QString 转换为标准 C++ 字符串 std::string
  • QString::toUtf8()
    QString 转换为 QByteArray,编码为 UTF-8。
  • QString::fromUtf8(const char \*str)
    将 UTF-8 编码的 char 数组转换为 QString
  • QString::fromStdString(const std::string &str)
    将标准 C++ 字符串转换为 QString
  1. 去空格
  • QString::trimmed()
    去除字符串两端的空格字符。
  • QString::simplified()
    去除字符串开头和结尾的空格,并将连续空格缩减为单个空格。
  1. 分割与连接
  • QString::split(const QString &sep)
    按照指定分隔符将字符串分割成一个字符串列表,返回 QStringList
  • QStringList::join(const QString &sep)
    QStringList 中的元素用指定字符串 sep 拼接成一个 QString
1
2
3
4
5
6
QString str = "  Hello World!  ";
qDebug() << str.trimmed(); // 输出 "Hello World!"
qDebug() << str.toLower(); // 输出 " hello world! "
qDebug() << str.mid(2, 5); // 输出 "Hello"
qDebug() << str.contains("World"); // 输出 true
qDebug() << str.replace("World", "Qt"); // 输出 " Hello Qt! "

总之,要用查AI就行,没必要细究

QMap,QHash,QVector类

QMapQHash 是 Qt 中的键值对容器类,类似于标准库中的 std::mapstd::unordered_map,它们用于存储键值对并支持快速查找。

QMap

  • 定义QMap 是一个基于二叉树实现的键值对容器,键值对按键自动排序。
  • 特点:查找、插入、删除操作的时间复杂度为 O(log⁡n)
  • 适用场景:适合需要按顺序遍历键值对,或对键进行排序的场景。

常用方法

  • **insert(key, value)**:插入键值对。
  • **value(key)**:通过键查找值。
  • **contains(key)**:检查键是否存在。
  • **remove(key)**:移除指定键的键值对。
  • keys() 和 **values()**:分别获取所有键和所有值的列表

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <QMap>
#include <QDebug>

void exampleQMap() {
QMap<QString, int> map;
map.insert("Alice", 25);
map.insert("Charlie", 28);
map.insert("Bob", 30);


qDebug() << "Keys in sorted order:";
for (auto it = map.begin(); it != map.end(); ++it) {
qDebug() << it.key() << ":" << it.value();
}

if (map.contains("Alice")) {
qDebug() << "Alice's age:" << map.value("Alice");
}
}

QMap 中,会自动按字典序(或称为”字母顺序”)排序,这种排序是基于键的自然顺序实现的。也就是说,当插入键值对时,QMap 会自动将键按顺序排列,而不考虑插入顺序。

1731208247189

QHash

定义QHash 是一个基于哈希表实现的键值对容器,键值对的顺序不固定。

特点:查找、插入、删除的平均时间复杂度为 O(1)O(1)O(1),速度通常快于 QMap

适用场景:适合需要快速查找、插入和删除键值对,但不需要顺序或排序的场景。

常用方法

**insert(key, value)**:插入键值对。

**value(key)**:通过键查找值。

**contains(key)**:检查键是否存在。

**remove(key)**:移除指定键的键值对。

keys() 和 **values()**:分别获取所有键和所有值的列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <QHash>
#include <QDebug>

void exampleQHash() {
QHash<QString, int> hash;
hash.insert("Alice", 25);
hash.insert("Charlie", 28);
hash.insert("Bob", 30);


qDebug() << "Keys in arbitrary order:";
for (auto it = hash.begin(); it != hash.end(); ++it) {
qDebug() << it.key() << ":" << it.value();
}

if (hash.contains("Bob")) {
qDebug() << "Bob's age:" << hash.value("Bob");
}
}

此时就是没有顺序了

1731208388403

QMap vs QHash

  • 顺序QMap 自动排序键,QHash 没有顺序。
  • 性能QMap 的查找速度较 QHash 慢,但在需要排序时是理想选择。QHash 在查找、插入和删除方面更快。

选择建议

  • QMap:需要排序或按顺序遍历键值对时。
  • QHash:仅需快速查找时,且不关心键的顺序时。

QVector

QVector(Qt库的Vector类) 和C++标准库里面的Vector类很相近

常用的方法有

1. appendprepend

  • append 用于在向量末尾追加元素。
  • prepend 用于在向量头部添加元素。

2. atoperator[]

  • at 用于访问指定索引的元素,带边界检查。
  • operator[] 直接访问元素,不检查越界。

3. sizeisEmpty

  • size 返回元素数量。
  • isEmpty 判断是否为空。

4. insertremove

  • insert 在指定位置插入元素。
  • remove 删除指定位置的元素。

5. containsindexOf

  • contains 判断是否包含某个值。
  • indexOf 查找元素的索引,未找到返回-1。

6. fillresize

  • fill 用指定值填充整个向量。
  • resize 调整向量大小,可能会丢失或补充数据。

7. toListfromList

  • toList 转换为 QList
  • fromListQList 转换为 QVector

示例代码:

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
#include <QVector>
#include <QDebug>

int main()
{
QVector<int> vec;

// 1. 添加元素
vec.append(10); // 在末尾添加 10
vec.append(20); // 在末尾添加 20
vec.prepend(5); // 在头部添加 5

// 2. 访问元素
qDebug() << "First element:" << vec.at(0); // 输出 5
qDebug() << "Second element:" << vec[1]; // 输出 10
qDebug() << "Vector size:" << vec.size(); // 输出 3

// 3. 判断和查找
if (vec.contains(20)) {
qDebug() << "Contains 20 at index:" << vec.indexOf(20); // 输出索引
}

// 4. 插入和删除
vec.insert(1, 15); // 在索引 1 处插入 15 原来索引1处及其后续的元素会向后移动一个位置,以腾出空间放入新元素。
vec.remove(2); // 删除索引 2 的元素

// 5. 修改大小
vec.resize(5); // 调整大小为 5,新增元素初始化为 0

// 6. 转换
QList<int> list = vec.toList(); // 转为 QList

// 输出所有元素
for (int i : vec) {
qDebug() << i;
}

return 0;
}

QList和QLinkedList类

在 Qt 中,QListQLinkedList 是两种常用的容器类,分别适用于不同的使用场景。它们都是存储一组元素的线性容器,但在内存布局和性能上有所区别。

QList

QList 存储结构体时,会选择存储指向结构体的指针,以减少内存开销,尤其是在隐式共享(copy-on-write)机制下,不同的 QList 可以共享结构体数据。然而,在 插入删除 操作时,它的效率相对较低

常用方法

  • append(const T &value): 在列表末尾添加元素。
  • prepend(const T &value): 在列表开头添加元素。
  • insert(int index, const T &value): 在指定索引位置插入元素。
  • removeAt(int index): 删除指定索引的元素。
  • at(int index): 获取指定索引处的元素(只读)。
  • contains(const T &value): 检查列表中是否包含某个元素。
  • size(): 返回列表中的元素数量。
  • isEmpty(): 检查列表是否为空。
  • clear(): 清空列表中的所有元素。

示例代码:

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
#include <QList>
#include <QDebug>

int main() {
QList<int> list;
list.append(10);
list.append(20);
list.prepend(5);

qDebug() << "List elements:";
for (int i = 0; i < list.size(); ++i) {
qDebug() << list.at(i);
}

if (list.contains(10)) {
qDebug() << "List contains 10.";
}

list.removeAt(1); // Removes element at index 1

qDebug() << "After removing element at index 1:";
for (int val : list) {
qDebug() << val;
}

return 0;
}

QLinkedList

QLinkedList 是基于链表实现的容器类,适合需要频繁在列表头部或中间插入或删除元素的场景。由于链表不支持随机访问,因此获取指定位置的元素会比较慢。

常用方法

  • append(const T &value): 在链表末尾添加元素。
  • prepend(const T &value): 在链表开头添加元素。
  • insert(iterator pos, const T &value): 在指定位置的迭代器处插入元素。
  • removeFirst(): 删除第一个元素。
  • removeLast(): 删除最后一个元素。
  • size(): 返回链表中的元素数量。
  • isEmpty(): 检查链表是否为空。
  • clear(): 清空链表中的所有元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <QLinkedList>
#include <QDebug>

int main() {
QLinkedList<int> linkedList;
linkedList.append(10);
linkedList.append(20);
linkedList.prepend(5);

qDebug() << "LinkedList elements:";
for (int val : linkedList) {
qDebug() << val;
}

linkedList.removeFirst(); // Removes the first element

qDebug() << "After removing the first element:";
for (int val : linkedList) {
qDebug() << val;
}

return 0;
}

使用场景总结

  • QList:适用于需要频繁访问元素的场景,且数据的增删操作主要集中在末尾。
  • QLinkedList:适用于需要频繁在头部或中间插入或删除元素的场景。
  • QMap:适合需要按顺序存储键值对,或在键的基础上进行排序的场景。
  • QHash:适合不关心元素顺序且需要快速查找的场景。

如果你存储的是结构体对象,并且这些结构体通常很大,那么 QList 存储结构体时的内存占用相对 QVector 更小 ,毕竟QList存的是指针

QVariant类

QVariant 是 Qt 中一个非常重要的类,它是一个通用容器,能够存储多种类型的数据。QVariant 可以用来在不同类型之间进行转换,尤其在 Qt 中的数据模型和视图类(如 QTableView, QListView)中非常常见。它提供了一种标准化的方式来存储不同类型的数据,同时也支持类型安全的类型转换。

在 Qt 中,QVariant 常用于存储如字符串、数字、日期、时间等多种不同类型的数据,并且支持这些数据类型之间的互相转换。

方法介绍:

1. 构造函数

  • QVariant():创建一个空的 QVariant
  • QVariant(int)QVariant(double) 等:直接用基本数据类型来构造 QVariant
1
2
3
4
QVariant intVariant(42);           // 存储整数
QVariant doubleVariant(3.14); // 存储浮点数
QVariant stringVariant("Hello"); // 存储字符串
QVariant emptyVariant; // 创建空的 QVariant

2. isValid()

判断 QVariant 是否包含有效数据。

1
2
3
4
5
QVariant v;
qDebug() << v.isValid(); // 输出:false

v = 100;
qDebug() << v.isValid(); // 输出:true

3. isNull()

检查 QVariant 是否为空(空指针或未初始化的值)。

1
2
3
4
5
QVariant nullVariant;
qDebug() << nullVariant.isNull(); // 输出:true

QVariant intVariant(10);
qDebug() << intVariant.isNull(); // 输出:false

4. clear()

清除 QVariant 的值,使其变成空。

1
2
3
4
QVariant v(123);
qDebug() << v; // 输出:QVariant(int, 123)
v.clear();
qDebug() << v.isNull(); // 输出:true

5. typeName()

返回 QVariant 中存储数据的类型名称。

1
2
3
4
5
cpp复制代码QVariant intVariant(42);
qDebug() << intVariant.typeName(); // 输出:int

QVariant stringVariant("Hello");
qDebug() << stringVariant.typeName(); // 输出:QString

6. canConvert()

检查 QVariant 是否可以转换为指定类型。

1
2
3
QVariant v(42);
qDebug() << v.canConvert<QString>(); // 输出:true
qDebug() << v.canConvert<QDate>(); // 输出:false

7. convert()

QVariant 转换为指定类型,如果转换成功则返回 true

1
2
3
QVariant v(42);
v.convert(QVariant::String);
qDebug() << v; // 输出:QVariant(QString, "42")

8. toInt()、toDouble()、toString() 等

QVariant 转换为不同的数据类型。常用的转换方法包括 toInt()toDouble()toString()toBool() 等。

1
2
3
4
5
6
7
8
9
QVariant v("123");

int intValue = v.toInt(); // 转换为 int
double doubleValue = v.toDouble(); // 转换为 double
QString stringValue = v.toString(); // 转换为 QString

qDebug() << intValue; // 输出:123
qDebug() << doubleValue; // 输出:123.0
qDebug() << stringValue; // 输出:"123"

9. setValue()

使用模板方法存储任意类型的数据。

1
2
3
QVariant v;
v.setValue<QString>("Hello");
qDebug() << v.toString(); // 输出:"Hello"

10. value()

QVariant 中提取特定类型的数据。这个方法使用模板类型 T 来指定需要提取的类型。

1
2
3
QVariant v = QVariant::fromValue(3.14);  // 存储一个 double
double d = v.value<double>(); // 提取 double 值
qDebug() << d; // 输出:3.14

11. fromValue()

静态模板方法,用于将任意类型的数据转换为 QVariant

1
2
QVariant variant = QVariant::fromValue<QString>("Hello World");
qDebug() << variant.toString(); // 输出:"Hello World"

12. type()

返回 QVariant::Type 类型的枚举值,表示当前存储的数据类型。

1
2
3
4
QVariant v(100);
if (v.type() == QVariant::Int) {
qDebug() << "It's an int";
}

13. isDetached()

判断 QVariant 是否已从其共享数据中分离(浅拷贝时用于检测)。

1
2
3
4
5
6
QVariant v(42);
QVariant vCopy = v;
qDebug() << v.isDetached(); // 输出:false

v = 100;
qDebug() << v.isDetached(); // 输出:true

14. swap()

交换两个 QVariant 的值。

1
2
3
4
5
6
QVariant v1(42);
QVariant v2("Hello");

v1.swap(v2);
qDebug() << v1.toString(); // 输出:"Hello"
qDebug() << v2.toInt(); // 输出:42

使用代码例子:

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <QtCore/QCoreApplication>
#include <QVariant>
#include <QDebug>
#include <QMap>

struct Student
{
int iNo;
QString strName;
int score;
};

Q_DECLARE_METATYPE(Student);


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


QVariant qv1(298);
qDebug() << "qv1" << qv1.toInt() << endl;

QVariant qv2("this is string");
qDebug() << "qv2" << qv2.toString() << endl;


QMap<QString, QVariant> qmap;

qmap["int"] = 2000; //整型
qmap["double"] = 2000.0; //浮点型
qmap["string"] = "20000"; //字符型

//输出:转换函数来处理
qDebug() << qmap["int"] << qmap["int"].toInt();
qDebug() << qmap["double"] << qmap["double"].toInt();
qDebug() << qmap["string"] << qmap["string"].toString();




//创建一个字符串列表:QStringList
qDebug() << endl;
QStringList qsl;//QStringList是QList<QString>的别名
qsl << "A" << "B" << "C" << "D" << "E" << "F" << "G";

QVariant qvsl(qsl); //将列表存在一个QVariant变量里面
if (qvsl.type() == QVariant::StringList)
{
QStringList qlist = qvsl.toStringList();
for (int i = 0; i < qlist.size(); i++)
{
qDebug() << qlist.at(i);
}
}





qDebug() << endl;
Student stu;
stu.iNo = 202201;
stu.score = 715;
stu.strName = "sunny";


//使用静态的方法保存数据
QVariant Qvstu = QVariant::fromValue(stu);
if (Qvstu.canConvert<Student>())
{
Student temp_stu = Qvstu.value<Student>();//获得数据
Student qtemp = qvariant_cast<Student>(Qvstu);//获取数据
/*qvariant_cast<Student>(Qvstu):这是一个独立的模板函数,对 QVariant 进行安全转换。
如果类型不匹配,它会返回一个默认构造的对象(如 int 类型返回 0,类对象返回默认构造的实例)。
比直接调用 value<T>() 更安全,可以避免在类型不匹配时引发错误。*/

qDebug() << temp_stu.iNo << temp_stu.score << temp_stu.strName;

qDebug() << qtemp.iNo << qtemp.score << qtemp.strName;
}



system("pause");

return a.exec();
}

运行结果:

1731228124532

QT开发常见控件

Layouts&Spacers

手动操作

首先我们创建一个Qt Widgets Application (qt窗体程序)

1731376698193

进来之后,解决方案资源管理器有一个ui文件

1731376779502

双击这个.ui文件,即可进入Qt的设计板块

1731376805940

其中,这里有Layouts(布局)和Spacers(间隔)

  • Vertical Layout 垂直布局
  • Horizontal Layout 横向(水平)布局
  • Grid Layout 网格(栅格)布局
  • Form Layout 表单布局
  • Horizontal Spacer 水平弹簧
  • Vertical Spacer 垂直弹簧

菜单栏有布局的快捷键:

例如下图显示的垂直布局

1731377196766

先选中这三个控件,然后点击一下垂直布局
1731377247782

就会自动帮我们布局

1731377267567

  • Vertical Layout 垂直布局

包含对象都垂直排列开

  • Horizontal Layout 横向(水平)布局

包含的对象都横向排列开

  • Grid Layout 网格(栅格)布局

将控件放置到网格中布局,它本身会从父窗口或父布局中占据尽可能多的界面空间,然后把自己的空间划分为行和列,再把每个控件塞到设置好的一个或多个单元格中。通常情况下 QGridLayout不需要自己添加空白条 QSpacerltem,因为其他功能控件把各自的单元格占据之后,剩下没控件占据的单元格自然就是空的,空的格子默认里面什么都没有,也没有空白条。

  • Form Layout 表单布局

专门用于管理输入控件和与之相关的标签等表单布局,QFormLavout固定为两列布局,并针对表单做了建模,配套了一堆方便使用的函数。网格布局器的基本单元是单元格,而表单布局器的基本单元是行。表单布局器是高度建模并封装的,它没有 addWidget0和 addLavout0)之类的函数,它只有addRow()函数。表单布局器中一行的空间可以由多个控件占据,也可以由一个控件占据。

弹簧:

  • Horizontal Spacer 水平弹簧

  • Vertical Spacer 垂直弹簧

用于在布局中创建灵活的空白空间。弹簧的主要作用是控制控件之间的间距,提供空间的拉伸或压缩效果,以实现布局中的自适应。

在Qt中,给控件设置布局和给顶层窗口设置布局是两个不同层次的事情,目的是为了实现自适应布局效果和确保整个界面按期望排布。

顶层窗口(如QMainWindowQWidget)需要有一个布局来管理其内部的控件或子布局。如果不为顶层窗口设置布局,虽然可以在其中手动放置控件,但它们不会自动调整位置和大小,不具备自适应能力。

为顶层窗口设置布局后,Qt会自动管理窗口内部的空间分配,确保在窗口大小变化时,布局中的控件能够合理调整,达到更好的用户体验。

就像这样,这样是没有设置顶层布局,即使运行起来了,弹簧也不会起作用

1731410143158

所以我们要在空白处右键,点击布局,选择一个布局,例如这里选择了栅格布局,ctrl+s保存之后,运行,这时候弹簧就可以进行伸缩了

代码实现:

栅格布局:

我们先实现往栅格布局上面放一个按钮,注释都标注好了

1
2
3
4
5
6
7
8
9
10
11
12
13
Grid_Layout::Grid_Layout(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);//this 是当前对象,表示setupUi会把设计的UI加载到这个对象上。((通常由 Qt Designer 生成的 .ui 文件编译而成))
button1 = new QPushButton(this);//创建一个新的按钮控件 button1,并将其父对象设置为当前窗口或小部件 (this)。在 Qt 中,设置父对象后,父对象负责管理子对象的内存,即在父对象销毁时会自动销毁子对象。
button1->setText("第一区:顶不菜单栏选项");//设置 button1 按钮的文本内容为 "第一区:顶不菜单栏选项"。这段文本将会显示在按钮上。
button1->setFixedHeight(40);//设置 button1 按钮的固定高度为 40 像素,使其在布局中不受拉伸影响。
button1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);//设置 button1 的尺寸策略,将水平方向和垂直方向的尺寸策略都设置为 Expanding。Expanding 表示按钮在布局中会尽可能扩展以占满可用空间(受限于布局约束),而不是保持固定大小。
pGrid_layouts = new QGridLayout();//创建一个新的网格布局对象 pGrid_layouts。
pGrid_layouts->setContentsMargins(0, 0, 0, 0);//设置 pGrid_layouts 布局的内容边距为 0,使布局的控件紧贴窗口边缘。参数 (0, 0, 0, 0) 依次表示左、上、右、下的边距为 0。
pGrid_layouts->addWidget(button1, 0, 0);//将 button1 添加到 pGrid_layouts 的第 0 行、第 1 列(从 0 开始计数)的单元格中。在网格布局中,可以通过指定行和列的位置来组织控件的位置。
setLayout(pGrid_layouts);//将 pGrid_layouts 设置为当前窗口或小部件的布局。一旦设置了布局,布局管理器将会负责自动调整和排列该窗口中的所有控件。
}

以上代码有一些疑问:

1.固定高度和 Expanding 尺寸策略的

固定高度优先于 Expanding,导致按钮高度固定,只有水平方向会尝试扩展。所以这两者并不冲突,固定高度在垂直方向优先,Expanding 只在水平方向有效。

2. 网格布局的行列数

QGridLayout 的行列数是动态的,由添加的控件位置决定。

行列数是根据 addWidget 方法中给出的行和列参数动态生成的。没有预先设置网格大小,布局会根据所添加的控件自动计算需要的行和列数。

例如

1
2
3
pGrid_layouts->addWidget(button1, 0, 1);
pGrid_layouts->addWidget(button2, 1, 0);
pGrid_layouts->addWidget(button3, 1, 1);

pGrid_layouts 会自动创建一个 2 行 × 2 列的网格布局。

再介绍下addWidget

指定控件的对齐方式和弹性策略:

1
2
3
4
5
6
7
addWidget(
QWidget *widget,
int row, int column, int rowSpan, int columnSpan,
Qt::Alignment alignment,
QSizePolicy::Policy horizontalPolicy,
QSizePolicy::Policy verticalPolicy
)

参数

widget:要添加的控件。

row:控件所在的起始行。

column:控件所在的起始列。

rowSpan:控件跨越的行数。

columnSpan:控件跨越的列数。

alignment:控件的对齐方式(如 Qt::AlignCenterQt::AlignLeft)。

horizontalPolicy:控件在水平方向上的尺寸策略(如 QSizePolicy::Expanding,表示水平方向上可以扩展)。

verticalPolicy:控件在垂直方向上的尺寸策略(如 QSizePolicy::Fixed,表示垂直方向上的尺寸固定)。

作用:允许更精确地控制控件的对齐方式和尺寸策略。尺寸策略用于指定控件如何在布局中调整自己的大小。

表单布局

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
Form_Layout::Form_Layout(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);

// 创建表单布局指针
QFormLayout* qLayout = new QFormLayout(this);



le1 = new QLineEdit(); // 输入学号
le2 = new QLineEdit(); // 输入姓名
le3 = new QLineEdit(); // 输入学校

// 添加行:第一列是标签,第二列是QLineEdit
qLayout->addRow("学号", le1);
qLayout->addRow("姓名", le2);
qLayout->addRow("学校", le3);

qLayout->setSpacing(8);

qLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
qLayout->setLabelAlignment(Qt::AlignLeft);//设置标签对其方式

setWindowTitle("表单布局测试案例");

// 设置当前窗口的布局为 qLayout
setLayout(qLayout);
}

其中

QFormLayout::setRowWrapPolicy 方法用于设置行的换行策略,即当空间不足以显示一行时,控件在布局中的排列方式。

1
void QFormLayout::setRowWrapPolicy(QFormLayout::RowWrapPolicy policy);

参数:RowWrapPolicy

RowWrapPolicy 是一个枚举类型,提供了三种策略:

  • **QFormLayout::DontWrapRows**:不换行。当空间不足时,控件会被截断,但不会自动换行。
  • **QFormLayout::WrapLongRows**:仅在行的内容过长时换行。此策略在行内容(标签和控件)超过窗口宽度时,才会将控件换到下一行。
  • **QFormLayout::WrapAllRows**:总是换行。标签在一行,控件自动放置在下一行,形成两行布局(标签一行,控件一行)。

setLabelAlignment 用于设置标签的对齐方式,即标签在控件旁边的布局位置(如左对齐或右对齐)。

方法定义

1
void QFormLayout::setLabelAlignment(Qt::Alignment alignment);

参数 :

alignment 使用 Qt::Alignment 枚举类进行设置。常用的值包括:

  • **Qt::AlignLeft**:左对齐标签。
  • **Qt::AlignRight**:右对齐标签,常用于在窗口宽度较大时使标签在布局中右对齐。
  • **Qt::AlignHCenter**:水平居中对齐标签。
  • **Qt::AlignJustify**:标签的对齐方式为两端对齐。

Button控件

控件介绍

Push Button (QPushButton)

这是最常见的按钮,用于触发简单的命令或操作,比如“确定”或“取消”。

Tool Button (QToolButton)

通常用于工具栏或小型控件,可以显示一个图标(或者文本)并触发特定的操作。

Radio Button (QRadioButton)

用于创建一组互斥的选项(只能选择一个)。

Check Box (QCheckBox)

允许用户选择多个选项或一个布尔值。

Command Link Button (QCommandLinkButton)

这是一个扩展了 QPushButton 的按钮,通常用于对话框中提供详细的操作说明。

Dialog Button Box (QDialogButtonBox)

功能:
用于对话框中管理多个标准按钮(如“确定”、“取消”等)。
特点:

  • 提供了标准按钮(如 QDialogButtonBox::Ok)。
  • 布局自动调整,不需要手动管理按钮的位置。
  • 方便处理对话框的信号和槽。

代码示例

做一个按钮的集合,并结合上一期学到的布局

头文件:

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
41
42
43
44
45
46
#pragma once
#include <QMainWindow>
#include <QPushButton>
#include <QToolButton>
#include <QRadioButton>
#include <QCheckBox>
#include <QCommandLinkButton>
#include <QDialogButtonBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QtWidgets/QMainWindow>
#include "ui_Button.h"

class Button : public QMainWindow
{
Q_OBJECT

public:
Button(QWidget *parent = nullptr);
~Button();

private:
Ui::ButtonClass ui;


private:
QPushButton* pb1;
QToolButton* tb1;
QRadioButton* rb1;
QRadioButton* rb2;
QCheckBox* cb1;
QCheckBox* cb2;
QCommandLinkButton* clb1;
QDialogButtonBox* dbb1;


private slots:
void pushbutton_clicked(); //QPushButton
void toolbutton_clicked(); // QToolButton
void radiobutton1_clicked(); // QRadioButton 1
void radiobutton2_clicked(); // QRadioButton 2
void checkbox1_clicked(); // QCheckBox 1
void checkbox2_clicked(); // QCheckBox 2
void commandlinkbutton_clicked(); // QCommandLinkButton
void dialogbuttonbox_clicked(QAbstractButton* button); // QDialogButtonBox
};

cpp文件:

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include "Button.h"

#include <QDebug>
Button::Button(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);


// 创建中央窗口
QWidget* centralWidget = new QWidget(this);
setCentralWidget(centralWidget);

// 创建垂直布局
QVBoxLayout* mainLayout = new QVBoxLayout();

// 1. QPushButton
pb1 = new QPushButton("Push Button", this);
mainLayout->addWidget(pb1);


// 2. QToolButton
tb1 = new QToolButton(this);
tb1->setText("Tool Button");
tb1->setIcon(QIcon(":/icons/tool.png"));
tb1->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
mainLayout->addWidget(tb1);



// 3. QRadioButton
rb1 = new QRadioButton("Radio Button 1", this);
rb2 = new QRadioButton("Radio Button 2", this);
QHBoxLayout* radioLayout = new QHBoxLayout();
radioLayout->addWidget(rb1);
radioLayout->addWidget(rb2);
mainLayout->addLayout(radioLayout);

// 4. QCheckBox
cb1 = new QCheckBox("Check Box 1", this);
cb2 = new QCheckBox("Check Box 2", this);
QHBoxLayout* checkBoxLayout = new QHBoxLayout();
checkBoxLayout->addWidget(cb1);
checkBoxLayout->addWidget(cb2);
mainLayout->addLayout(checkBoxLayout);

// 5. QCommandLinkButton
clb1 = new QCommandLinkButton("Command Link Button", "Description goes here", this);
clb1->setIcon(QIcon(":/icons/info.png"));
mainLayout->addWidget(clb1);

// 6. QDialogButtonBox
dbb1 = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
mainLayout->addWidget(dbb1);


// 将布局应用到中央窗口
centralWidget->setLayout(mainLayout);

// 设置窗口标题
setWindowTitle("Qt Button Controls Example");
resize(400, 300);



connect(pb1, &QPushButton::clicked, this, &Button::pushbutton_clicked);
connect(tb1, &QToolButton::clicked, this, &Button::toolbutton_clicked);
connect(rb1, &QRadioButton::clicked, this, &Button::radiobutton1_clicked);
connect(rb2, &QRadioButton::clicked, this, &Button::radiobutton2_clicked);
connect(cb1, &QCheckBox::clicked, this, &Button::checkbox1_clicked);
connect(cb2, &QCheckBox::clicked, this, &Button::checkbox2_clicked);
connect(clb1, &QCommandLinkButton::clicked, this, &Button::commandlinkbutton_clicked);
connect(dbb1, &QDialogButtonBox::clicked, this, &Button::dialogbuttonbox_clicked);
}

Button::~Button()
{}



void Button::pushbutton_clicked() // QPushButton
{
qDebug() << "QPushButton";
}


void Button::toolbutton_clicked() // QToolButton
{
qDebug() << "QToolButton";
}


void Button::radiobutton1_clicked() // QRadioButton 1
{
qDebug() << "QRadioButton 1";
}


void Button::radiobutton2_clicked() // QRadioButton 2
{
qDebug() << "QRadioButton 2";
}


void Button::checkbox1_clicked() // QCheckBox 1
{
qDebug() << "QCheckBox 1";
}


void Button::checkbox2_clicked() // QCheckBox 2
{
qDebug() << "QCheckBox 2";
}


void Button::commandlinkbutton_clicked() // QCommandLinkButton
{
qDebug() << "QCommandLinkButton";
}


void Button::dialogbuttonbox_clicked(QAbstractButton* button) // QDialogButtonBox
{
qDebug() << "QDialogButtonBox";
}

效果图:

1731983793959

Item Views&Item Widgets

Item Views

Item Views 是基于 Model/View 架构的视图组件,提供了一种高效且灵活的方式来显示和编辑数据。常用的包括:

  • QListView:显示垂直列表。
  • QTableView:显示表格。
  • QTreeView:显示树状结构。

Model/View 架构

Model(数据模型):负责管理数据的存储和组织,例如 QStandardItemModel 或自定义的模型。

View(视图):负责渲染数据,例如 QTableView

Delegate(委托):负责数据的显示和编辑,例如 QStyledItemDelegate

头文件:

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
#pragma once

#include <QtWidgets/QMainWindow>
#include "ui_ItemViews.h"
#include <QTableView>
#include <QStandardItemModel>
#include <QHeaderView>
#include <QListView>
#include <QStringListModel>
#include <QTreeView>
#include <QStandardItemModel>
class ItemViews : public QMainWindow
{
Q_OBJECT

public:
ItemViews(QWidget *parent = nullptr);
~ItemViews();

private:
Ui::ItemViewsClass* ui;


private:
QListView* listView; // 列表视图
QStringListModel* listModel; // 数据模型

QTableView* tableView; // 视图
QStandardItemModel* tablemodel; // 数据模型


QTreeView* treeView; // 树状视图
QStandardItemModel* treeModel; // 数据模型

void setuptableViewModel(); // 初始化模型和视图
void setuplistViewModel(); // 初始化模型和视图
void setupTreeViewModel(); // 初始化模型和视图
};

QListView:显示垂直列表。

cpp文件为:

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
#include "ItemViews.h"


ItemViews::ItemViews(QWidget* parent)
: QMainWindow(parent),
listView(new QListView(this)), // 创建视图
listModel(new QStringListModel(this))// 创建数据模型
{
// 设置窗口标题
setWindowTitle("Item Views Example");

// 初始化模型和视图
setuplistViewModel();

// 设置模型到视图
listView->setModel(listModel);

// 设置视图为窗口的中心部件
setCentralWidget(listView);
}

ItemViews::~ItemViews()
{
// tableView 和 model 由 Qt 的 parent-child 机制自动释放,无需手动 delete
}


void ItemViews::setuplistViewModel()
{
// 设置数据列表
QStringList data;
data << "Alice" << "Bob" << "Charlie" << "David" << "Eve";

// 将数据加载到模型
listModel->setStringList(data);
}

效果图:

1732025673933

QTableView:

cpp

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
41
42
43
44
45
46
47
48
49
50
51
#include "ItemViews.h"


ItemViews::ItemViews(QWidget* parent)
: QMainWindow(parent),
tableView(new QTableView(this)), // 创建视图
model(new QStandardItemModel(this)) // 创建数据模型
{
// 设置窗口标题
setWindowTitle("Item Views Example");

// 初始化模型和视图
setupModel();

// 设置视图
tableView->setModel(model); // 绑定模型
tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); // 列宽自适应
tableView->setSelectionBehavior(QAbstractItemView::SelectRows); // 点击选择整行
tableView->setEditTriggers(QAbstractItemView::DoubleClicked); // 双击单元格编辑
setCentralWidget(tableView); // 将 tableView 设置为主窗口中心部件
}

ItemViews::~ItemViews()
{
// tableView 和 model 由 Qt 的 parent-child 机制自动释放,无需手动 delete
}

void ItemViews::setupModel()
{
// 设置模型的行列数
model->setRowCount(3);
model->setColumnCount(3);

// 设置列标题
model->setHeaderData(0, Qt::Horizontal, "Name");
model->setHeaderData(1, Qt::Horizontal, "Age");
model->setHeaderData(2, Qt::Horizontal, "Country");

// 填充数据
model->setItem(0, 0, new QStandardItem("Alice"));
model->setItem(0, 1, new QStandardItem("25"));
model->setItem(0, 2, new QStandardItem("USA"));

model->setItem(1, 0, new QStandardItem("Bob"));
model->setItem(1, 1, new QStandardItem("30"));
model->setItem(1, 2, new QStandardItem("UK"));

model->setItem(2, 0, new QStandardItem("Charlie"));
model->setItem(2, 1, new QStandardItem("28"));
model->setItem(2, 2, new QStandardItem("Canada"));
}

效果如下:

1732024657348

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include "ItemViews.h"


ItemViews::ItemViews(QWidget* parent)
: QMainWindow(parent),
tableView(new QTableView(this)),
listView(new QListView(this)), // 创建视图
tablemodel(new QStandardItemModel(this)), // 创建数据模型
listModel(new QStringListModel(this)),// 创建数据模型
treeView(new QTreeView(this)), // 创建树状视图
treeModel(new QStandardItemModel(this)) // 创建数据模型
{
// 设置窗口标题
setWindowTitle("Item Views Example");

// 初始化模型和视图
setupTreeViewModel();

// 将模型绑定到视图
treeView->setModel(treeModel);

// 设置视图的显示属性
treeView->setHeaderHidden(false); // 显示表头
treeView->setSelectionBehavior(QAbstractItemView::SelectRows); // 整行选择
treeView->expandAll(); // 默认展开所有节点

// 将视图设置为主窗口的中心控件
setCentralWidget(treeView);

}

ItemViews::~ItemViews()
{
// tableView 和 model 由 Qt 的 parent-child 机制自动释放,无需手动 delete
}


void ItemViews::setupTreeViewModel()
{
// 设置列标题
treeModel->setHorizontalHeaderLabels(QStringList() << "Category" << "Details");

// 创建根节点
QStandardItem* rootNode = treeModel->invisibleRootItem();

// 添加分类1
QStandardItem* category1 = new QStandardItem("Fruits");
QStandardItem* detail1 = new QStandardItem("Delicious and healthy");
category1->appendRow(QList<QStandardItem*>() << new QStandardItem("Apple") << new QStandardItem("Red or green"));
category1->appendRow(QList<QStandardItem*>() << new QStandardItem("Banana") << new QStandardItem("Yellow"));
rootNode->appendRow(QList<QStandardItem*>() << category1 << detail1);

// 添加分类2
QStandardItem* category2 = new QStandardItem("Animals");
QStandardItem* detail2 = new QStandardItem("Different types");
category2->appendRow(QList<QStandardItem*>() << new QStandardItem("Cat") << new QStandardItem("Cute pet"));
category2->appendRow(QList<QStandardItem*>() << new QStandardItem("Dog") << new QStandardItem("Loyal friend"));
rootNode->appendRow(QList<QStandardItem*>() << category2 << detail2);

// 添加分类3
QStandardItem* category3 = new QStandardItem("Vehicles");
QStandardItem* detail3 = new QStandardItem("Used for transport");
category3->appendRow(QList<QStandardItem*>() << new QStandardItem("Car") << new QStandardItem("Fast and private"));
category3->appendRow(QList<QStandardItem*>() << new QStandardItem("Bus") << new QStandardItem("Public transport"));
rootNode->appendRow(QList<QStandardItem*>() << category3 << detail3);
}

效果图:

1732026092852

ItemWidgets

Item Widgets 是基于 QWidget 的组件,用于在表格或列表中插入小部件,例如按钮、复选框等。它们通常用于需要显示复杂内容的场景。常见类包括:

  • QTableWidget:用于表格。
  • QTreeWidget:用于树状结构。
  • QListWidget:用于列表。

工作机制

  • 基于 QWidget 的传统方式,直接在单元格中添加小部件(如按钮或复选框)。
  • 典型用法是通过 QTableWidget::setCellWidget()QTreeWidget::setItemWidget() 方法为某个单元格插入一个小部件。

TreeWidgetItem

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
41
42
43
44
45
46
47
ItemWidgets::ItemWidgets(QWidget *parent)
: QMainWindow(parent), tableWidget(new QTableWidget(this)), // 创建表格控件
treeWidget(new QTreeWidget(this)) // 创建树形控件
{
ui.setupUi(this);
setWindowTitle("QTableWidget Example"); // 设置窗口标题

// 初始化表格
setupTree();

// 将表格控件设置为主窗口的中心部件
setCentralWidget(treeWidget);
}

ItemWidgets::~ItemWidgets()
{}



void ItemWidgets::setupTree()
{
// 创建根节点
QTreeWidgetItem* root = new QTreeWidgetItem(treeWidget);
root->setText(0, "Root");
root->setText(1, "Root Node");

// 添加子节点到根节点
QTreeWidgetItem* child1 = new QTreeWidgetItem(root);
child1->setText(0, "Child 1");
child1->setText(1, "This is child 1");

QTreeWidgetItem* child2 = new QTreeWidgetItem(root);
child2->setText(0, "Child 2");
child2->setText(1, "This is child 2");

// 为子节点添加孙子节点
QTreeWidgetItem* grandchild1 = new QTreeWidgetItem(child1);
grandchild1->setText(0, "Grandchild 1");
grandchild1->setText(1, "This is grandchild 1");

QTreeWidgetItem* grandchild2 = new QTreeWidgetItem(child2);
grandchild2->setText(0, "Grandchild 2");
grandchild2->setText(1, "This is grandchild 2");

// 展开根节点
treeWidget->expandAll();
}

tableWidget

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
41
42
43
44
45
#include "ItemWidgets.h"

ItemWidgets::ItemWidgets(QWidget *parent)
: QMainWindow(parent), tableWidget(new QTableWidget(this)) // 创建表格控件
{
ui.setupUi(this);
setWindowTitle("QTableWidget Example"); // 设置窗口标题

// 初始化表格
setupTable();

// 将表格控件设置为主窗口的中心部件
setCentralWidget(tableWidget);
}

ItemWidgets::~ItemWidgets()
{}



void ItemWidgets::setupTable()
{
// 设置表格的行和列
tableWidget->setRowCount(3); // 设置行数
tableWidget->setColumnCount(2); // 设置列数

// 设置表头
tableWidget->setHorizontalHeaderLabels(QStringList() << "Name" << "Age");

// 填充数据
tableWidget->setItem(0, 0, new QTableWidgetItem("Alice"));
tableWidget->setItem(0, 1, new QTableWidgetItem("25"));

tableWidget->setItem(1, 0, new QTableWidgetItem("Bob"));
tableWidget->setItem(1, 1, new QTableWidgetItem("30"));

tableWidget->setItem(2, 0, new QTableWidgetItem("Charlie"));
tableWidget->setItem(2, 1, new QTableWidgetItem("35"));

// 设置列宽自适应
tableWidget->resizeColumnsToContents();

// 设置单元格编辑模式
tableWidget->setEditTriggers(QAbstractItemView::DoubleClicked); // 双击编辑单元格
}

listWidget

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include "ItemWidgets.h"
#include <QDebug>
ItemWidgets::ItemWidgets(QWidget *parent)
: QMainWindow(parent), tableWidget(new QTableWidget(this)), // 创建表格控件
treeWidget(new QTreeWidget(this)), // 创建树形控件,
listWidget(new QListWidget(this)), // 创建列表控件
addButton(new QPushButton("Add Item", this)), // 创建添加按钮
removeButton(new QPushButton("Remove Item", this)) // 创建删除按钮
{
ui.setupUi(this);
setWindowTitle("QTableWidget Example"); // 设置窗口标题

// 设置按钮的布局
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(listWidget);
layout->addWidget(addButton);
layout->addWidget(removeButton);

QWidget* container = new QWidget();
container->setLayout(layout);

setCentralWidget(container); // 设置中央控件为容器

// 设置初始列表
setupList();

// 连接按钮的信号和槽
connect(addButton, &QPushButton::clicked, [this]() {
// 点击“添加”按钮,添加一个新项
listWidget->addItem("New Item");
});

connect(removeButton, &QPushButton::clicked, [this]() {
// 点击“删除”按钮,删除选中的项
delete listWidget->currentItem();
});

// 连接项点击事件
connect(listWidget, &QListWidget::itemClicked, [this](QListWidgetItem* item) {
qDebug() << "Item clicked: " << item->text();
});
}

ItemWidgets::~ItemWidgets()
{}

void ItemWidgets::setupList()
{
// 向列表中添加初始项
listWidget->addItem("Item 1");
listWidget->addItem("Item 2");
listWidget->addItem("Item 3");
listWidget->addItem("Item 4");
}

区别:

特点 Item Views Item Widgets
架构 基于 Model/View 架构 基于 QWidget 的传统方式
性能 更高效,适合处理大量数据 数据量大时性能差
灵活性 借助委托实现自定义渲染和编辑 可直接插入复杂小部件,灵活但不高效
开发复杂性 需要实现数据模型,稍复杂 使用传统方法开发,简单直观
适用场景 数据量大,需求偏重于数据展示 小数据量,需求偏重于复杂交互

Input Widgets&Display Widgets

Input Widgets

全家福

1732193961776

部分控件的实现:

Combo Box,Font Combo Box ,Text Edit , Horizontal Scroll Bar

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
41
42
43
44
45
46
47
48
49
50
51
52
#pragma once

#include <QtWidgets/QMainWindow>
#include "ui_Input_Widgets.h"
#include <QComboBox>
#include <QLabel>
#include <QVBoxLayout>
#include <QPalette>
#include <QFontComboBox>
#include <QTextEdit>
#include <QPushButton>
#include <QFileDialog>
#include <QTextStream>
#include <QMessageBox>
#include <QSlider>
#pragma execution_character_set("utf-8")
class Input_Widgets : public QMainWindow
{
Q_OBJECT

public:
Input_Widgets(QWidget *parent = nullptr);
~Input_Widgets();

private:
Ui::Input_WidgetsClass ui;


private:
QComboBox* comboBox;
QLabel* label;
QVBoxLayout* layout;
QFontComboBox* fontComboBox; // 字体选择框
QTextEdit* textEdit; // 文本编辑框
QPushButton* saveButton; // 保存按钮
QWidget* centralWidget;
QSlider* slider; // 水平滑动条
QLabel* slider_label; // 显示滑动条当前值的标签

void Init_ConboBox();
void Init_FontComboBox();
void Init_TextEdit();
void Init_Slider();

private slots:

void ConboBox_Slot(int index);
void FontComboBox_Slot(const QFont& font);
void TextEdit_Slot();
void Slider_Slot(int value);

};

cpp的实现:

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include "Input_Widgets.h"

Input_Widgets::Input_Widgets(QWidget* parent)
: QMainWindow(parent),
comboBox(new QComboBox),
layout(new QVBoxLayout),
fontComboBox(new QFontComboBox),
label(new QLabel),
textEdit(new QTextEdit),
saveButton(new QPushButton),
slider(new QSlider(Qt::Horizontal, this)),
slider_label(new QLabel)
{
ui.setupUi(this);
layout->addWidget(label);
centralWidget = new QWidget(this);
centralWidget->setLayout(layout);
setCentralWidget(centralWidget);
//QComboBox::currentIndexChanged() 是 Qt 中 QComboBox 组件的一个信号,它在用户更改组合框选项时被触发。
//QOverload<int>:我们要指定的是带有 int 类型参数的重载版本,因为&QComboBox::currentIndexChanged有重载
Init_ConboBox();
Init_FontComboBox();
Init_TextEdit();
Init_Slider();
connect(comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Input_Widgets::ConboBox_Slot);

connect(fontComboBox, &QFontComboBox::currentFontChanged, this, &Input_Widgets::FontComboBox_Slot); // 连接字体选择框的信号到槽函数

connect(saveButton, &QPushButton::clicked, this, &Input_Widgets::TextEdit_Slot);

connect(slider, &QSlider::valueChanged, this, &Input_Widgets::Slider_Slot);
}





Input_Widgets::~Input_Widgets()
{}



void Input_Widgets::Init_Slider()
{
// 设置滑动条范围和初始值
slider->setRange(0, 100); // 范围为0到100
slider->setValue(0); // 初始值为0

// 将滑动条和标签添加到布局中
layout->addWidget(slider);
slider_label->setText("Value: 0");
layout->addWidget(slider_label);
centralWidget->setLayout(layout);
}


void Input_Widgets::Slider_Slot(int value)
{
slider_label->setText("Value: " + QString::number(value)); // 显示滑动条的当前值
}

void Input_Widgets::Input_Widgets::TextEdit_Slot()
{
// 弹出文件对话框,获取保存文件的路径
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "", tr("Text Files (*.txt);;All Files (*)"));

// 如果用户选择了文件路径
if (!fileName.isEmpty()) {
QFile file(fileName);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file);
// 将文本框中的内容写入到文件中
out << textEdit->toPlainText();
file.close();
QMessageBox::information(this, tr("Success"), tr("File saved successfully."));
}
else {
QMessageBox::warning(this, tr("Error"), tr("Could not save file."));
}
}
}


void Input_Widgets::Init_TextEdit()
{
// 设置文本编辑框的一些属性
textEdit->setPlainText("Hello, Qt!"); // 设置初始文本
layout->addWidget(textEdit);
saveButton->setText("保存");
layout->addWidget(saveButton);
centralWidget->setLayout(layout);
}


void Input_Widgets::FontComboBox_Slot(const QFont& font)
{
// 当选择的字体发生变化时,更新标签内容
label->setText("Selected font: " + font.family());

}


void Input_Widgets::Init_FontComboBox()
{
layout->addWidget(fontComboBox);//不用做其他的,直接塞到布局就行
setLayout(layout);
centralWidget->setLayout(layout);
}


void Input_Widgets::ConboBox_Slot(int index)
{
// 当选择发生变化时,更新标签内容
QString selectedColor = comboBox->itemText(index);
label->setText("Selected color: " + selectedColor);
centralWidget->setLayout(layout);
}

void Input_Widgets::Init_ConboBox()
{
// 添加颜色选项到下拉框
comboBox->addItem("Red");
comboBox->addItem("Green");
comboBox->addItem("Blue");
comboBox->addItem("Yellow");

// 创建一个垂直布局器,并将comboBox和label添加到布局器中

layout->addWidget(comboBox);
setLayout(layout);
}

效果图:
1732198137362

Display Widgets

QLabel

  • 功能:显示文本或图像,可支持富文本和对齐方式。
  • 典型应用:标签、提示信息、简单的图像展示。

QTextBrowser

  • 功能:显示超文本(HTML)内容,支持超链接点击。
  • 典型应用:帮助文档、带超链接的富文本显示。

QGraphicsView

  • 功能:用于管理和显示 2D 图形场景的视图。
  • 典型应用:复杂的图形或用户界面场景展示。

QCalendarWidget

  • 功能:用于显示和选择日期。
  • 典型应用:日期选择器。

QLCDNumber

  • 功能:显示数值,仿真数字显示屏效果。
  • 典型应用:计数器、计时器显示。

QProgressBar

  • 功能:显示任务进度。
  • 典型应用:文件下载、加载过程的进度指示。

QFrame (Horizontal Line / Vertical Line)

  • 功能:绘制简单的框架或分割线。
  • 典型应用:界面布局分割。

QOpenGLWidget

  • 功能:嵌入 OpenGL 图形渲染内容。
  • 典型应用:3D 图形渲染或图形处理。

QQuickWidget

  • 功能:嵌入 QML 内容(Qt Quick 的用户界面)。
  • 典型应用:混合 QML 与 Qt Widgets 的界面。

QWebEngineView

  • 功能:嵌入一个现代 Web 浏览器,支持 HTML5。
  • 典型应用:内嵌网页、显示在线内容或 Web 应用程序。

移植Qt Ui文件

我在写QT项目的时候,想拿之前一个项目的UI文件直接使用,但是当我直接把其他项目的UI文件导入我的项目的时候,发现并不能直接使用

求助ChatGPT,发现QT已经为我们想到了这一切

在QT的bin目录下,有一个叫uic.exe的应用程序,这个负责将ui文件转为头文件.h,直接导入这个头文件,我们就可以使用原来的ui界面了

1733835008955

头文件长这样:

其实就是以代码的形式展现

1733835146569

其中:

1
2
3
namespace Ui {
class ServerClass: public Ui_ServerClass {};
} // namespace Ui

解释一下就是:

Ui::ServerClass代替Ui_ServerClass

然后可以自己新建一个头文件:

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
#include "ZeroServer_ui.h"
#include <QMainWindow>
#include <QMenu>
#include <QMessageBox>
#pragma execution_character_set("utf-8")
class ServerWindow : public QMainWindow {
Q_OBJECT

public:
explicit ServerWindow(QWidget* parent = nullptr)
: QMainWindow(parent) {
ui.setupUi(this); // 初始化界面
Initialize_Interface();
}

private:
Ui::ServerClass ui; // 包含由 .ui 文件生成的 UI 类


private:
void Initialize_Interface(); //初始化界面
void Set_Right_click_options();//给表单结构的行设置右键选项

private slots:
void showContextMenu(const QPoint& pos);
};

例如这样,加了我之前项目的一些函数,具体实现直接复制过来就行

完美运行

1733835593767

windows QT项目移植Linux

https://blog.csdn.net/jolin678/article/details/110964681

1.创建一个目录,首先把windows vs工程里面的.cpp文件,.h文件,.qrc拷贝到这个文件夹,其他都不要

1734264116715

2.设置qmake的环境变量,除非你已经加在系统变量了,不然得自己手动加,这里的环境变量是一次性的,只在当前有效

例如我电脑里面的qt的程序都在这个目录

1734264350386

所以我就把这个目录添加到环境变量

1
export PATH=/opt/Qt5.12.10/5.12.10/gcc_64/bin/:$PATH

3.执行命令添加模块,这样会生成一个.pro文件

1
qmake -project QT+=widgets

1734264674552

当然,如果你的项目不止只有widgets这个模块,还有其他的,可以编辑这个.pro文件

例如这里添加一个network

1734264756391

然后运行指令:

1
qmake

执行完这一步在目录下会生出Makefile文件

1734264891678

然后再执行命令:

1
make

就可以编译链接生成可执行文件了

生成了一个可执行文件,打开就能用

1734265688123

当然你也可以用QtCreator,把.pro文件拖进来

然后选择编译器

1734265240188

构建,成功运行

1734265631709

QT打包发布

Vs+Qt项目打包发布

VS+QT发布程序详细步骤,以及遇到的问题-CSDN博客