Qt开发基础
开发概览:Qt类图专业技术宝典
可参考我的开源代码配合博客记录的内容查看:Qt Dev Essential
Tips1:很多设置项,都可以在Qt Creator中搜索文档看到具体说明,不论是不是初学都该在实际开发时翻阅文档确定内容。
Tips2:所有 UI 操作必须在主线程,Qt 严禁在子线程操作窗口。
Qt,肤浅地理解为一个做GUI的玩意儿,用C++进行开发任务,实际开发感觉不如叫Q++,C++常用的东西基本被它包了个遍。以往只有本地零散的记录,在此打算系统记录一下一些概念和设计,整理出来到博客上,有些东西搞忘记了开发的时候可能会一个头两个大,实用的内容该记还是得记。
PS:有部分内容是与AI问答,自己加以修改补充的,多是使用场景一类。
元对象系统(Meta-Object System)
这是对C++标准的一个扩展,为Qt提供了信号槽机制,实时类型信息,动态属性系统。
使用元对象系统有三个基本条件:类必须继承自QObject,类中声明Q_OBJECT宏(默认私有),元对象编译器moc(Meta_Object Compiler)。
在 Qt Creator 软件中,左下角🔎查找类信息,比如输入? QDialog,就可以查看QDialog的文档,可以看到他继承自哪儿。
翻看QObject文档内容,开头第一句是:
The QObject class is the base class of all Qt objects.
这句话让我不得不把元对象系统放第一个标题,描述中它有种不怒自威,傲视群雄的感觉。
元对象系统提供的最实用的功能特性:
信号与槽:这是 Qt 最核心的特性。传统的 C++ 回调函数难以保证类型安全且容易产生耦合,而信号槽利用元对象系统提供的类型信息,实现了对象之间的松耦合通信。在运行时,moc 生成的代码维护了信号与槽的连接表,让调用变得安全。
属性系统:通过
Q_PROPERTY宏,可以在运行时动态地读写对象的属性。这是 Qt 中 QML 与 C++ 交互的基石,也是 Qt Designer 能够动态调整控件属性的原理所在。运行时类型信息:标准 C++ 虽然有 typeid,但功能有限。Qt 提供了:
inherits():判断一个对象是否属于某个特定类或继承链。qobject_cast():类似于 C++ 的 dynamic_cast,但不需要开启 RTTI(运行时类型信息),而且速度更快,专门用于 QObject 的转换。
动态翻译:
tr()函数之所以能够动态切换界面语言,是因为元对象系统在运行时记录了字符串的上下文。
注意一些实际开发中的问题:
信号和槽的语法演进:
Qt5 之前:必须使用
SIGNAL()和SLOT()宏,属于字符串匹配,运行时才报错。Qt5 及以后:推荐使用函数指针语法(如
&MyClass::signalName),编译期检查,性能更好,也更符合现代 C++ 风格。
moc 的局限性:
模板类问题:QObject 不能作为模板类的基类。因为 moc 不支持模板预处理,如果你的类既是模板又继承了 QObject,moc 无法正确生成元对象代码。
多重继承:如果必须多重继承,QObject 必须出现在继承列表的第一个位置。这是因为 moc 生成的代码假设 this 指针与 QObject 子对象的偏移量为 0。
编译链接问题:如果忘记写
Q_OBJECT宏,信号槽连接可能会失败,或者编译时出现“undefined reference to vtable”的错误。解决办法是:确保头文件中有 Q_OBJECT,并清理项目重新编译(因为 moc 可能未重新生成)。
元对象编译器MOC
前面说了,Qt设计者设计了元对象系统给C++加了一套动态特性,用的时候要在类中写上 Q_OBJECT 宏,所以在编译前,编译器要能够扫描源码中标记了 Q_OBJECT 的类,自动生成额外的 C++ 代码以实现元对象系统,之后将生成的代码和用户的代码一起编译成可执行文件,这显然只靠 C++ 编译器办不到。
这就是 MOC 出现的原因,MOC 将 Qt 特有的语法(如信号、槽、属性等)转换成标准 C++ 代码,它为每个使用了 Q_OBJECT 的类构建了一张“元数据表”,这张表在运行时可以被查询和调用。
| 功能 | MOC 生成的代码负责 |
|---|---|
| 信号 | 为每个信号生成一个函数,该函数调用 qt_metacall 来激活所有连接到该信号的槽。 |
| 槽 | 在元对象表中注册槽函数的索引和签名,使得 qt_metacall 可以根据索引调用正确的槽函数。 |
| 属性系统 | 生成 qt_metacall 中对 Q_PROPERTY 的读/写处理,支持动态获取和设置属性。 |
| 运行时类型信息 | 生成 staticMetaObject 静态对象,以及 metaObject()、inherits()、qobject_cast 所需的类型比较数据。 |
| 动态翻译 | 标记 tr() 中的字符串上下文,使翻译系统能准确定位。 |
MOC工作流程,其中生成的 moc_xxx.cpp 代码可以在 build 目录里找到,里面有很多”元调用”(上面表格记录的调用函数):
graph TD
A[项目源文件] --> B{头文件含有 Q_OBJECT?}
B -->|是| C[MOC 处理]
B -->|否| D[常规编译]
C --> E[生成 moc_xxx.cpp]
E --> F[与原始源文件一起编译]
D --> F
F --> G[链接生成可执行文件]
当然,作为一个编译器,我们开发时对它的存在是“无感”的,只是了解一下可以对 Q_OBJECT 扩展出来的奇奇怪怪的东西获得一分通透感。
信号与槽
信号与槽(Signals & Slots)的灵活性,概括起来就九个字:松耦合,一对多,多对一;下面把它和传统回调对比同时感受一下灵活性。
信号与槽相比传统回调(如函数指针、观察者模式),主要有以下优势:
- 类型安全:编译期检查参数类型,避免回调签名不匹配导致的崩溃。
- 松耦合:发送者无需知道接收者是谁,只需发射信号;接收者只需连接信号,无需修改发送者代码。
- 多对多通信:一个信号可连接多个槽,一个槽可接收多个信号,自动管理连接列表。
- 自动生命周期管理:当发送者或接收者被销毁时,连接自动断开,避免悬空指针。
对比场景实例:按钮点击时,同时更新日志和播放声音。
传统回调(函数指针)方式
1 | // 回调函数类型 |
缺点:类型不安全(可传入任何返回void无参的函数),无法传递上下文,手动管理回调列表易出错。
Qt 信号槽方式
1 | class Button : public QObject { |
优点:类型安全(连接时签名必须匹配),发送者与接收者完全解耦,断开连接自动处理。
例子中信号槽的连接关系
graph LR
subgraph 发送者
B[Button]
end
subgraph 接收者
A[App]
end
B -- clicked() --> A
A -->|调用 playSound 和 updateLog| A
图中可见一个信号可以触发多个槽,且信号与槽之间没有直接依赖。
connect 和 disconnect
上面我们看到了信号槽的松耦合和一对多特性,多对一也是类似,设置好 connect 就行了,需要注意的是,一对多 connect 时槽方法参数要匹配。
而且,信号还能触发信号(信号 connect 信号),这更加验证了信号槽的灵活性。
断开信号与槽连接使用 disconnect,可以指定需要操作的对象、信号和槽,特定参数位为0则表示:所有对象,所有信号,所有槽。
比如,断开A对象和B对象的信号槽连接:
1 | disconnect(&A, &A::signal, &B, &B::slot); |
断开A对象信号和B对象所有槽的连接:
1 | disconnect(&A, &A::signal, &B, 0); |
断开A对象所有信号和所有对象所有槽的连接:
1 | disconnect(&A, 0, 0, 0); |
断开所有对象信号和槽的连接:
1 | disconnect(0, 0, 0, 0); |
属性系统(Q_PROPERTY)
属性系统(Q_PROPERTY)是 Qt 元对象系统的重要组成部分,它让你能够在运行时动态地读取、写入对象的属性,并且支持属性变化通知。这在自定义控件、QML 与 C++ 交互、Qt Designer 可调属性等场景中极其常用。
简单来说,它把一个 C++ 类的成员变量或方法“暴露”给 Qt 的元对象系统,让外部可以:
- 用
QObject::property()和setProperty()动态读写 - 在
Qt Designer中直接修改(用于自定义控件) - 在
QML中绑定和自动更新 - 配合信号实现属性变化的自动通知
Q_PROPERTY 基本语法
1 | class MyWidget : public QWidget { |
关键部分:
- READ:必选,提供一个 const 的 getter 方法。
- WRITE:可选,提供一个 setter 方法。如果没有,属性就是只读的。
- NOTIFY:可选,当属性值改变时发射的信号。强烈建议加上,这样外部才能监听到变化(比如 QML 自动刷新)。
- 其他可选关键字:
DESIGNABLE(是否在 Designer 中可见)、STORED(是否持久化)等,但工作中最常用的就是 READ、WRITE、NOTIFY。
常用场景
- 自定义控件 + Qt Designer
如果你写一个自定义控件,想让用户在设计器里调整它的外观属性,就用 Q_PROPERTY 声明,并加上 DESIGNABLE true(默认就是 true)。这样 Designer 会自动生成属性编辑器。
- QML 与 C++ 交互
将 C++ 对象注册到 QML 后,QML 可以直接读写该对象的 Q_PROPERTY 属性,并且当 NOTIFY 信号触发时,QML 中绑定的界面会自动更新。
- 动态保存/恢复状态
用 QObject::property() 和 setProperty() 可以在不修改类的情况下临时存储一些额外数据。比如给一个按钮附加一个 id 值:
1 | button->setProperty("value", 100); |
这在一些动态界面、数据传递时很实用。
注意事项
- 属性名不要和已有的方法名冲突,通常用小写开头。
- NOTIFY 信号尽量在属性真正变化时才发射,避免在 setter 中无脑发射(先比较新旧值)。
- 属性类型可以是 int、bool、QString、枚举(需用
Q_ENUM注册)等。如果是自定义类型,需要用Q_DECLARE_METATYPE注册。 - 如果你只是临时想给一个对象附加点数据,不需要专门定义子类,直接用
setProperty动态添加即可,元对象系统会帮你在运行时管理这些动态属性。
对象树
每个QObject内部有一个子对象列表,父对象析构时自动删除所有子对象,层层传递,最终由顶层对象释放整棵树。
核心要点
- 父子关系通过构造函数参数或
setParent()建立:new QObject(parent)。 - 有父对象,绝不手动
delete,否则会导致双重释放。 - 所有
QObject子类都支持对象树(不需要Q_OBJECT宏,但你要用信号槽就得加)。 - 顶层窗口(如
QMainWindow)通常没有父对象,由QApplication间接管理,一般不需要手动删除。 - 可以通过
findChild<T>()按类型/名字查找子对象。
常见错误
- 在栈上创建
QObject子类(如QPushButton btn(this);)→ 双重释放。 - 给有父对象的子对象手动
delete→ 崩溃。 - 忘记给布局或控件指定父对象 → 内存泄漏。
示例
添加析构函数声明(在 public 区域):
1 | // colorcircle.h |
添加析构函数实现,并包含QDebug:
1 | // colorcircle.cpp |
关闭主窗口将会看到这个打印,实际上我们new QObject[Son] {this}的地方都会被对象树管理。
组件基类 QWidget
如果是非自定义控件,布局和样式还可以在设计模式中通过属性表配置。
窗体和控件风格
只需掌握 最常用的几种设置 和 样式表(QSS)基础,就能应对大部分界面美化需求。
窗口标志(Qt::WindowFlags)—— 控制窗口的“长相”和“行为”
窗口标志决定了窗口是否有标题栏、能否最大化、是否为工具窗等。常用组合:
| 效果 | 代码(在构造或 setWindowFlags 中使用) |
|---|---|
| 普通窗口(默认) | Qt::Widget |
| 顶级窗口(带标题栏、关闭按钮) | Qt::Window |
| 对话框(带问号?可设置) | Qt::Dialog |
| 无边框窗口 | Qt::FramelessWindowHint |
| 始终置顶 | Qt::WindowStaysOnTopHint |
| 工具窗口(窄标题栏,不在任务栏显示) | Qt::Tool |
实际工作中最常用的是 无边框窗口(自定义标题栏和拖动)和 置顶窗口(如悬浮工具)。
比如我们可以这样设置主窗口无边框:
1 | // 在 MainWindow 构造函数中 |
如果同时需要多个标志,用 | 组合:
1 | setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); |
注意:改变窗口标志后,通常需要调用
show()才能生效(或在构造函数末尾调用setWindowFlags后调用show())。如果窗口已显示,可以先hide()再show()。
样式表(QSS)—— 让控件变漂亮
Qt 样式表语法类似 CSS,可以快速改变控件的外观(背景色、边框、圆角、字体等)。
最常用的一些样式设置
给整个窗口设置背景色和圆角:
1 | setStyleSheet("QWidget { background-color: #2c3e50; border-radius: 10px; }"); |
给按钮设置样式:
1 | QPushButton { |
给一个特定控件单独设置样式(用对象名):
1 | button->setObjectName("myButton"); |
实用技巧
- 全局样式:通常在
main()中给QApplication设置样式表,所有控件都会继承。 - 边框圆角:
border-radius: 5px;需要配合border使用,否则不生效。 - 背景透明:
background-color: transparent;或background: transparent;。 - 无边框窗口的阴影:无边框窗口默认没有阴影,但可以用
QGraphicsDropShadowEffect模拟。
无边框窗口的拖动实现
无边框窗口没有标题栏,无法拖动。你需要自己实现鼠标拖动逻辑:
在 MainWindow 中添加成员变量:
1 | private: |
重写鼠标事件(在 MainWindow 中):
1 |
|
半透明窗口
1 | setWindowOpacity(0.8); // 0.0 完全透明,1.0 不透明 |
注意:无边框窗口开启半透明后,可能需要设置 setAttribute(Qt::WA_TranslucentBackground) 来实现真正的透明背景。
样式设置 api 概览
| 需求 | 代码 |
|---|---|
| 无边框 | setWindowFlags(Qt::FramelessWindowHint); |
| 置顶 | setWindowFlags(Qt::WindowStaysOnTopHint); |
| 全局样式 | qApp->setStyleSheet("..."); |
| 单控件样式 | widget->setStyleSheet("..."); |
| 无边框拖动 | 重写 mousePressEvent / mouseMoveEvent |
| 半透明 | setWindowOpacity(0.8); |
窗口和控件尺寸和尺寸策略
三种基础尺寸设置方法
| 方法 | 作用 | 常用场景 |
|---|---|---|
setFixedSize(w, h) |
固定大小,用户不能拖动改变 | 按钮、图标、固定大小的控件 |
setMinimumSize(w, h) |
设置最小尺寸(可以更大) | 窗口、可缩放的控件 |
setMaximumSize(w, h) |
设置最大尺寸 | 限制控件过大 |
resize(w, h) |
设置当前尺寸(用户仍可改变) | 窗口初始大小 |
示例:固定按钮大小
1 | QPushButton *btn = new QPushButton("确定"); |
示例:让窗口最小 400x300,最大 800x600
1 | setMinimumSize(400, 300); |
布局中的尺寸策略(sizePolicy)
当你使用布局管理器(QVBoxLayout 等)时,控件的大小由布局和控件的 尺寸策略 共同决定。
常用策略(QSizePolicy::Policy)
| 策略 | 含义 | 常见控件默认策略 |
|---|---|---|
Fixed |
大小固定,不拉伸 | 按钮、标签(水平方向) |
Minimum |
可以扩展,但不能小于 sizeHint() |
输入框 |
Expanding |
优先扩展,占据多余空间 | 文本编辑、列表框 |
Preferred |
按 sizeHint(),可以扩展或收缩 |
大多数控件 |
Maximum |
可以收缩,但不能大于 sizeHint() |
不常用 |
让按钮水平方向自动拉伸
1 | button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); |
第一个参数是水平策略,第二个是垂直策略。
重要概念:sizeHint() 和 minimumSizeHint()
sizeHint():控件的“建议大小”,基于内容(如按钮文字、图片)。minimumSizeHint():控件能正常显示的最小大小。
你很少需要重写这些函数,但要理解布局会参考它们。
让控件随窗口自动缩放(最常用)
当你把控件放入布局(QVBoxLayout、QHBoxLayout、QGridLayout)后,布局会自动调整控件大小。想让某个控件在窗口放大时“抢”到更多空间,有两个办法:
方法1:设置 sizePolicy 为 Expanding
1 | widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); |
方法2:设置布局的拉伸因子(stretch)
在 addWidget 时指定拉伸系数:
1 | layout->addWidget(widget1, 1); // 拉伸因子1 |
尺寸设置 api 概览
| 需求 | 代码 |
|---|---|
| 固定大小 | setFixedSize(w, h) |
| 最小/最大限制 | setMinimumSize(w, h) / setMaximumSize(w, h) |
| 布局中让控件可扩展 | setSizePolicy(Expanding, Expanding) |
| 布局中按比例分配空间 | addWidget(widget, stretch) |
| 窗口初始大小 | resize(w, h) |
常用控件 QDialog
只需要掌握 模态 vs 非模态、标准对话框 和 如何做自定义对话框,就能应对大部分场景。
QDialog 是什么?
QDialog 是 QWidget 的子类,专门用来做 临时交互窗口(比如提示、设置、选择)。它的核心特点是 可以以“模态”方式运行,阻塞其他窗口直到对话框关闭。
模态 vs 非模态(最重要)
| 类型 | 效果 | 常用场景 |
|---|---|---|
| 模态(Modal) | 弹出后,用户必须关闭对话框才能操作其他窗口 | 文件选择、确认删除、登录框 |
| 非模态(Modeless) | 弹出后,可以同时操作主窗口 | 查找替换、属性面板、工具窗口 |
代码写法
模态(阻塞):
1 | QDialog dialog(this); |
非模态(不阻塞):
1 | QDialog *dialog = new QDialog(this); |
工作中 模态对话框更常用,因为它简单且符合大多数用户预期。
在 Qt 中,设置对话框的模态/非模态行为有多种方式。下表总结了最常用的方法及其效果:
| 方式 | 代码示例 | 模态/非模态 | 说明 |
|---|---|---|---|
exec() |
dialog.exec(); |
模态(阻塞) | 启动独立的事件循环,阻塞所有同应用窗口,直到对话框关闭。返回值 Accepted / Rejected。 |
setModal(true) + show() |
dialog.setModal(true); dialog.show(); |
模态(非阻塞) | 设置为模态后调用 show(),不阻塞代码执行,但对话框模态阻止其他窗口交互。 |
setModal(false) + show() |
dialog.setModal(false); dialog.show(); |
非模态 | 对话框不阻塞其他窗口,可同时操作。 |
open() |
dialog.open(); |
模态(非阻塞) | 相当于 setModal(true) + show(),且提供了 finished 信号(可连接槽接收结果)。不阻塞代码。 |
setWindowModality() |
dialog.setWindowModality(Qt::ApplicationModal); dialog.show(); |
按参数决定 | 细粒度控制:Qt::NonModal(非模态)、Qt::WindowModal(阻塞父窗口及祖先)、Qt::ApplicationModal(阻塞整个应用)。 |
show() 默认 |
如果父窗口存在且未设置 WA_ShowModal 属性 |
通常非模态 | 默认 QDialog 的 show() 是非模态的,除非设置了模态属性或调用了 exec()。 |
Qt 内置的标准对话框(拿来即用)
| 对话框 | 静态方法 | 用途 |
|---|---|---|
| 消息框 | QMessageBox::information(), warning(), critical(), question() |
提示信息、询问确认 |
| 文件对话框 | QFileDialog::getOpenFileName(), getSaveFileName() |
打开/保存文件 |
| 颜色对话框 | QColorDialog::getColor() |
选择颜色 |
| 字体对话框 | QFontDialog::getFont() |
选择字体 |
| 输入对话框 | QInputDialog::getText(), getInt(), getDouble() |
获取用户输入 |
示例:消息框询问
1 | QMessageBox::StandardButton reply = QMessageBox::question(this, "确认", "确定要退出吗?"); |
示例:获取颜色
1 | QColor color = QColorDialog::getColor(Qt::red, this, "选择颜色"); |
自定义对话框(最实用)
当标准对话框不能满足需求时,自己做一个对话框很简单:
1. 新建一个类继承 QDialog
myDialog.h
1 |
|
myDialog.cpp
1 | MyDialog::MyDialog(QWidget *parent) : QDialog(parent) { |
2. 使用自定义对话框
1 | MyDialog dlg(this); |
关键点:
- 使用
exec()模态运行。 - 点击确定调用
accept(),点击取消调用reject()。 - 通过
exec()的返回值判断用户操作。
作用机制
一、exec() 做了什么?
当你调用 dialog.exec() 时,Qt 会:
显示对话框(如果还没显示)。
启动一个新的事件循环(QEventLoop),这个循环会不断处理窗口消息(鼠标点击、键盘输入等)。
阻塞当前代码(模态),直到事件循环退出。
返回一个结果:QDialog::Accepted 或 QDialog::Rejected。
那事件循环什么时候退出?当对话框内部调用了 accept() 或 reject() 时。
二、accept() 和 reject() 的作用
accept():告诉对话框“用户确认了”,内部会设置结果为 Accepted,然后退出事件循环。
reject():告诉对话框“用户取消了”,内部设置结果为 Rejected,然后退出事件循环。
这两个函数是 QDialog 的槽,也是普通的成员函数。你可以随时调用它们。
所以我们需要事先connect信号槽(clicked和accept/reject),后面用 dialog.exec() 调用就可以生效了。
QDialog api 概览
| 需求 | 代码 |
|---|---|
| 模态对话框 | dlg.exec() |
| 非模态对话框 | dlg.show() + setAttribute(Qt::WA_DeleteOnClose) |
| 标准消息框 | QMessageBox::question(...) |
| 标准颜色对话框 | QColorDialog::getColor(...) |
| 自定义对话框 | 继承 QDialog,使用 accept() / reject() |
| 判断用户确认 | if (dlg.exec() == QDialog::Accepted) {...} |
常用控件 QLabel
QLabel 是最常用的显示控件之一,用来显示文本、数字、图片或富文本。它不能编辑,但可以响应点击等事件(需额外处理)。
基础用法:显示文本
1 | QLabel *label = new QLabel("Hello Qt", this); |
常用设置:
setText(QString):改变显示内容。text():获取当前文本。setAlignment(Qt::AlignCenter):设置对齐(居中、左、右等)。setWordWrap(bool):是否自动换行。
示例:创建并设置标签
1 | QLabel *info = new QLabel(this); |
对齐方式常用组合:
Qt::AlignLeft/AlignRight/AlignHCenter(水平)Qt::AlignTop/AlignBottom/AlignVCenter(垂直)- 可以用
|组合,如AlignCenter即水平垂直居中。
显示数字或其它类型
QLabel 只接受 QString,所以需要将数字转换:
1 | int count = 10; |
显示图片(QPixmap)
1 | QLabel *imageLabel = new QLabel(this); |
如果你想保持图片比例,可以调整标签大小或使用 QPixmap::scaled()。
富文本(HTML 子集)
QLabel 支持简单的 HTML 标签,如 <b>、<i>、<font color=...>、<img> 等。
1 | label->setText("<b>粗体</b> 和 <i>斜体</i> 以及 <font color=red>红色</font>"); |
处理链接点击:需要设置 setOpenExternalLinks(true) 才会自动打开浏览器。
1 | label->setText("<a href='https://www.qt.io'>Qt官网</a>"); |
常用信号(QLabel 本身信号很少)
QLabel 不直接提供点击信号。要检测鼠标点击,需要重写 mousePressEvent 或使用事件过滤器。但简单做法是用一个按钮替代,或者将 QLabel 放在一个 QPushButton 内部(不推荐)。
如果你需要可点击的文本,可以用 QPushButton 设置扁平样式,或用 QLabel + 事件过滤器。新手阶段可以先用按钮。
QLabel api 概览
| 功能 | 代码 |
|---|---|
| 设置文本 | setText(QString) |
| 获取文本 | text() |
| 设置对齐 | setAlignment(Qt::AlignCenter) |
| 显示数字 | setText(QString::number(num)) |
| 显示图片 | setPixmap(QPixmap(...)) |
| 缩放图片适应 | setScaledContents(true) |
| 支持HTML | 直接 setText("<b>bold</b>") |
| 自动打开链接 | setOpenExternalLinks(true) |
常用控件 Button
在 Qt 中,所有按钮都继承自 QAbstractButton,它提供了按钮的通用功能(点击、状态、文本等)。实际工作中最常用的按钮子类有四种:QPushButton(普通按钮)、QToolButton(工具按钮)、QRadioButton(单选按钮)、QCheckBox(复选框)。
按钮家族总览
| 按钮类型 | 典型用途 | 是否互斥 | 是否可选中 |
|---|---|---|---|
QPushButton |
执行命令(确定、取消、提交) | 否 | 通常不可选中(除非设置 checkable) |
QToolButton |
工具栏中的图标按钮 | 否 | 同左 |
QRadioButton |
单选选项(性别、模式) | 是(同一父控件内) | 是,选中后保持 |
QCheckBox |
多选选项(兴趣爱好) | 否 | 是,独立选中/取消 |
逐个击破(实用级)
1. QPushButton:最常用的按钮
1 | QPushButton *btn = new QPushButton("点击我", this); |
常用设置:
setEnabled(bool):禁用/启用(变灰)。setCheckable(true):变成开关按钮(按下/弹起状态)。setIcon(QIcon(":/icon.png")):添加图标。
工作中:QPushButton 占按钮使用的 80% 以上,记住它的点击信号就够了。
2. QToolButton:工具栏按钮(通常只显示图标)
1 | QToolButton *toolBtn = new QToolButton(this); |
区别:QToolButton 默认没有文字边框,适合放在工具栏;QPushButton 适合放在窗口主体。
3. QRadioButton:单选按钮
同一组内只能选一个。要自动互斥,需要将它们放在同一个父控件(如 QWidget)或使用 QButtonGroup。
1 | QRadioButton *male = new QRadioButton("男", this); |
获取选中状态:
1 | if (male->isChecked()) { /* 选中的是男 */ } |
注意:如果不希望它们互斥(比如两组单选按钮),用 QButtonGroup 分组。
4. QCheckBox:复选框
可以独立选中/取消,也支持“三态”(半选,用于树形节点)。
1 | QCheckBox *agree = new QCheckBox("我同意协议", this); |
常用信号:checkStateChanged 或 toggled。
Button 核心要点
- 90% 的按钮需求:
QPushButton+clicked信号。 - 开关效果:
setCheckable(true)+toggled信号。 - 单选按钮组:把几个
QRadioButton放在同一个父控件里就自动互斥。 - 复选框:
stateChanged信号,isChecked()判断。 - 工具按钮:通常用于工具栏,可以只放图标。
常用控件 QLineEdit
QLineEdit 是最常用的输入控件,用于让用户输入一行文本(用户名、密码、数字等)。工作中 90% 的单行输入场景都由它完成。
基础用法
1 | QLineEdit *lineEdit = new QLineEdit(this); |
常用设置(实用级)
| 功能 | 代码 |
|---|---|
| 清空按钮(右侧X) | lineEdit->setClearButtonEnabled(true); |
| 密码模式(显示圆点) | lineEdit->setEchoMode(QLineEdit::Password); |
| 只读模式 | lineEdit->setReadOnly(true); |
| 限制最大输入长度 | lineEdit->setMaxLength(16); |
| 输入掩码(如手机号) | lineEdit->setInputMask("999-9999-9999"); |
常用回声模式:
QLineEdit::Normal:正常显示QLineEdit::Password:显示密码圆点QLineEdit::NoEcho:完全不显示(密码类,防偷窥)
注:手机号、IP 地址、MAC 地址、日期格式、产品序列号等固定格式输入,掩码比较有用,需要用的时候查表编写代码就好。
QLineEdit设置掩码ip - FreeLikeTheWind.(CSDN)
常用信号
| 信号 | 触发时机 | 常用场景 |
|---|---|---|
textChanged(const QString &) |
内容改变(包括代码设置) | 实时搜索、输入统计 |
textEdited(const QString &) |
用户编辑触发(代码设置不触发) | 用户输入验证 |
returnPressed() |
按下回车键 | 登录、搜索提交 |
editingFinished() |
编辑完成(焦点离开或回车) | 保存数据 |
示例:实时显示输入长度
1 | connect(lineEdit, &QLineEdit::textChanged, this, [](const QString &text){ |
输入验证(常用)
- 用
QIntValidator限制整数范围:
1 | QLineEdit *ageEdit = new QLineEdit(this); |
- 用
QDoubleValidator限制浮点数:
1 | QLineEdit *doubleEdit = new QLineEdit(this); |
- 用
QRegularExpressionValidator自定义规则(如手机号):
1 | QRegularExpression regex("^1[3-9]\\d{9}$"); |
LineEdit 核心要点
- 获取文本:
text() - 设置提示:
setPlaceholderText() - 清空按钮:
setClearButtonEnabled(true) - 密码模式:
setEchoMode(QLineEdit::Password) - 输入限制:
setValidator() - 常用信号:
textChanged(实时)、returnPressed(回车)