【QT】常用控件(概述、QWidget核心属性)
- 一、控件概述
- [二、QWidget 核心属性](#二、QWidget 核心属性)
-
- 1、核心属性概览
- 2、enabled
-
- [代码示例:通过按钮 2 切换按钮 1 的禁用状态](#代码示例:通过按钮 2 切换按钮 1 的禁用状态)
- 3、geometry
-
- 代码示例:控制按钮的位置
- [geometry 和 frameGeometry 的区别](#geometry 和 frameGeometry 的区别)
- 4、windowTitle
- 5、windowIcon
-
- 代码示例:设置窗口图标
- [通过 qrc 管理图片作为图标](#通过 qrc 管理图片作为图标)
- 6、windowOpacity
- 7、cursor
-
- [代码示例:在 Qt Designer 中设置按钮的光标](#代码示例:在 Qt Designer 中设置按钮的光标)
- 代码示例:通过代码设置按钮的光标
- 代码示例:自定义鼠标光标
- 8、font
-
- [代码示例:在 Qt Designer 中设置字体属性](#代码示例:在 Qt Designer 中设置字体属性)
- 9、toolTip
-
- [代码示例:设置按钮的 toolTip](#代码示例:设置按钮的 toolTip)
- 10、focusPolicy
-
- [理解不同的 focusPolicy](#理解不同的 focusPolicy)
- 11、styleSheet
一、控件概述
Widget 是 Qt 中的核心概念,英文原义是 "小部件",此处也把它翻译为 "控件"。控件是构成一个图形化界面的基本要素。

像上述示例中的按钮、列表视图、树形视图、单行输入框、多行输入框、滚动条、下拉框都可以称为 "控件"。
Qt 作为一个成熟的 GUI 开发框架,内置了大量的常用控件。这一点在 Qt Designer 中就可以看出来,并且 Qt 也提供了 "自定义控件" 的能力,可以让我们在现有控件不能满足需求的时候,对现有控件做出扩展,或者手搓出新的控件。
学习 Qt 其中一个很重要的任务就是熟悉并掌握 Qt 内置的常用控件,这些控件对于我们快速开发出符合需求的界面是至关重要的。
1、关于控件体系的发展
控件是 GUI 开发中的通用概念,不仅仅局限在 Qt 中。
(1)第一阶段
完全没有控件。此时需要通过一些绘图 API 手动的绘制出按钮或者输入框等内容,代码编写繁琐。(例如文曲星的 Lava 平台开发)

(2)第二阶段
只包含粗略的控件。只是提供了按钮、输入框、单选框、复选框等最常用的控件。(例如 html 的原生控件)

(3)第三阶段
更完整的控件体系,基本可以覆盖到 GUI 开发中的大部分场景。(例如早期的 MFC、VB、C++ Builder、Qt、Delphi、后来的 Android SDK、Java FX、前端的各种 UI 库等)

上图是前端中的 Element-ui 中的控件概览,无论是丰富程度还是颜值,都比 Qt 自带的控件更胜一筹。
二、QWidget 核心属性
在 Qt 中,使用 QWidget 类表示 "控件",像按钮、视图、输入框、滚动条等具体的控件类,都是继承自 QWidget。QWidget 中包含了 Qt 整个控件体系中通用的部分。
在 Qt Designer 中,随便拖一个控件过来,选中该控件,即可在右下方可以看到 QWidget 中的属性。

这些属性既可以通过 QtDesigner 直接修改,也可以通过代码的方式修改。这些属性的具体含义在 Qt Assistant 中均有详细介绍。
在 Qt Assistant 中搜索 QWidget,即可找到对应的文档说明(或者在 Qt Creator 代码中选中 QWidget,按 F1(+Fn) 也可)
1、核心属性概览
下列表格列出了 QWidget 中的属性及其作用:
| 属性 | 说明 |
|---|---|
| enabled | 是否启用控件。true 可交互,false 变灰不可用 |
| geometry | 位置和尺寸,包含 x, y, width, height(以父控件为参考坐标) |
| windowTitle | 窗口标题栏显示的文字 |
| windowIcon | 窗口左上角图标 |
| windowOpacity | 窗口透明度(0.0~1.0) |
| cursor | 鼠标悬停时显示的光标形状(如箭头、手型、I形光标等) |
| font | 控件文字字体、大小、粗细、斜体等 |
| toolTip | 鼠标悬停时弹出的提示文字 |
| toolTipDuration | toolTip 显示的持续时间(毫秒) |
| statusTip | 鼠标悬停时在状态栏显示的提示信息 |
| whatsThis | "这是什么?"帮助,按 Alt+F1 或点击问号按钮时显示 |
| styleSheet | 用类似 CSS 的方式设置控件样式(Qt 支持非常丰富) |
| focusPolicy | 焦点获取策略: • NoFocus • TabFocus • ClickFocus • StrongFocus • WheelFocus |
| contextMenuPolicy | 右键菜单策略: • DefaultContextMenu(默认) • NoContextMenu • PreventContextMenu • ActionsContextMenu • CustomContextMenu |
| locale | 设置语言和国家地区(如 zh_CN、en_US) |
| acceptDrops | 是否接受拖拽放入。 true:可接收拖放,触发 dropEvent false:拒绝(默认) |
| minimumSize | 控件最小尺寸(最小宽度 + 最小高度) |
| maximumSize | 控件最大尺寸(最大宽度 + 最大高度) |
| sizePolicy | 在布局中的伸缩策略(Fixed、Minimum、Maximum、Expanding 等) |
| windowModality | 窗口模态性(NonModal、WindowModal、ApplicationModal) |
| sizeIncrement | 拖拽改变窗口大小时的步进值 |
| baseSize | 基础尺寸,配合 sizeIncrement 计算合适尺寸 |
| palette | 调色板,可自定义前景色、背景色、按钮色等 |
| mouseTracking | 是否持续跟踪鼠标移动。 true:即使没按下也能收到 mouseMoveEvent false:只有按下时才收到(默认) |
| tabletTracking | 是否跟踪手写板/触摸屏移动事件(Qt5.9+ 新增) |
| layoutDirection | 布局方向: • LeftToRight(默认) • RightToLeft • LayoutDirectionAuto(根据 locale 自动) |
| autoFillBackground | 是否自动填充背景颜色 |
| windowFilePath | 窗口标题栏显示的文件路径(常用于文档类程序) |
| accessibleName | 无障碍阅读器读取的控件名称 |
| accessibleDescription | 无障碍阅读器读取的详细描述 |
| inputMethodHints | 输入法提示,如禁止自动大写、输入密码、数字键盘等(常用于 QLineEdit) |
| Qt::CustomContextMenu | 使用自定义右键菜单(配合 customContextMenuRequested 信号使用) |
下面我会介绍上面列出的其中一些比较重要和常用的属性。
2、enabled
| API | 说明 |
|---|---|
isEnabled() |
获取控件当前是否可用 |
setEnabled(bool) |
设置控件是否可用:true 表示可用,false 表示禁用 |
所谓 "禁用" 指的是该控件不能接收任何用户的输入事件,并且外观上往往是灰色的。如果一个 widget 被禁用,则该 widget 的子元素也被禁用。
使用代码创建一个禁用状态的按钮

运行程序,可以看到按钮处于灰色状态,无法被点击:

代码示例:通过按钮 2 切换按钮 1 的禁用状态
① 使用 Qt Designer 拖两个按钮到 Widget 中

两个按钮的 objectName 分别为 pushButton 和 pushButton_enbale。
QObject 的 objectName 属性介绍
QObject 是 QWidget 的父类,里面最重要的属性就是 objectName 。在一个 Qt 程序中,objectName 相当于对象的身份证,彼此之间不能重复。
在使用 Qt Designer 时,尤其是界面上存在多个 widget 的时候,可以通过 objectName 获取到指定的 widget 对象。
Qt Designer 生成的 .ui 文件,本质是 xml 格式的,qmake 会把这个 xml 文件转换成 C++ 的 .h 文件(这个文件生成在 build 目录中),构成一个 ui_widget 类。
每个 widget 的 objectName 最终就会成为 ui_widget 类的属性名字。最终这个类的实例就是:
Ui::Widget *ui,因此就可以通过形如ui->pushButton或者ui->pushButton_enbale这样的代码获取到界面上的 widget 对象了。
② 生成两个按钮的 slot 函数
- 使用 isEnabled 获取当前按钮的可用状态。
- 使用 setEnabled 修改按钮的可用状态,此处是直接针对原来的可用状态进行取反后设置。

在 Qt Designer 中创建按钮的时候可以设置按钮的初始状态是 "可用" 还是 "禁用"。如果把 enabled 这一列的对钩去掉,则按钮的初始状态就是 "禁用" 状态。

3、geometry
位置和尺寸是四个属性的统称:
- x 横坐标
- y 纵坐标
- width 宽度
- height 高度

但是实际开发中并不会直接使用这几个属性,而是通过一系列封装的方法来获取 / 修改。
对于 Qt 的坐标系,不要忘记是一个 "左手坐标系",其中坐标系的原点是当前元素的父元素的左上角。

| API | 说明 |
|---|---|
geometry() |
获取控件的位置和尺寸,返回 QRect,包含 x、y、width、height,其中 x、y 为左上角坐标 |
setGeometry(QRect) |
通过传入一个 QRect 设置控件的位置与尺寸 |
setGeometry(int x, int y, int width, int height) |
通过四个参数分别设置控件的 x、y、width、height |
代码示例:控制按钮的位置
五个按钮的 objectName 分别为:pushButton_target、pushButton_up、pushButton_down、pushButton_left、pushButton_right(五个按钮的初始位置和大小都随意)

在 widget.cpp 中编写四个按钮的 slot 函数

运行程序可以看到:按下下方的四个按钮就会控制 target 的左上角的位置,对应的按钮整个尺寸也会发生改变。
上述代码是直接设置的 QRect 中的 x 和 y。实际上,QRect 内部是存储了左上和右下两个点的坐标,再通过这两个点的坐标差值计算长宽。单纯修改左上坐标就会引起整个矩形的长宽发生改变。

如果想让整个按钮都移动(宽度和高度不发生改变),可以不再修改 QRect,而是通过 QRect 基于 setGeometry 第二个版本的函数重新设置位置即可:

上述代码使用 move 方法也是可以的。
geometry 和 frameGeometry 的区别
① 在按钮的 slot 函数中编写代码 & 在构造函数中也添加同样的代码

可以看到:在构造函数中打印出的 geometry 和 frameGeometry 是相同的。但是在点击按钮时,打印的 geometry 和 frameGeometry 则存在差异。

注意:
-
在构造方法中,Widget 刚刚创建出来,还没有加入到对象树中,此时也就不具备 Window frame。
-
在按钮的 slot 函数中,由于用户点击的时候,对象树已经构造好了,此时 Widget 已经具备了 Window frame,因此在位置和尺寸上均出现了差异。
-
如果把上述代码修改成打印 pushButton 的 geometry 和 frameGeometry,结果就是完全相同的。因为 pushButton 并非是一个窗口。
4、windowTitle
| API | 说明 |
|---|---|
windowTitle() |
获取控件的窗口标题 |
setWindowTitle(const QString& title) |
设置控件的窗口标题 |
注意 :上述设置操作针对不同的 widget 可能会有不同的行为。
- 如果是顶层 widget(独立窗口),这个操作才会有效。
- 如果是子 widget,这个操作无任何效果。
代码示例:设置窗口标题

5、windowIcon
| API | 说明 |
|---|---|
windowIcon() |
获取控件的窗口图标,返回 QIcon 对象 |
setWindowIcon(const QIcon& icon) |
设置控件的窗口图标 |
同 windowTitle,上述操作仅针对顶层 widget 有效。
代码示例:设置窗口图标
前面推荐使用堆来创建对象,主要是因为要确保当前控件的生命周期是足够的,要通过 Qt 对象树来释放对象。
QIcon 自身是一个比较小的对象。创建出来之后,就是要设置到某个 QWidget 里面,QIcon 对象本身是否释放并不影响图标最终的显示。
QIcon 也不支持对象树,无法给它执行父对象。

Windows 下路径的分隔符可以使用 / 也可以使用 \,但是如果在字符串中使用 \,需要写作转义字符的形式 \\,因此还是更推荐使用 /。
运行程序可以看到窗口图标已经成为上述图片:

实际开发中,一般不会在代码中通过绝对路径引入图片。因为我们无法保证程序发布后,用户的电脑上也有同样的路径。如果使用相对路径,则需要确保代码中的相对路径写法和图片实际所在的路径匹配(比如代码中写作 "./image/Q&A.jpg",就需要在当前工作目录中创建 image 目录,并把 Q&A.jpg 放进去)。
- 绝对路径:以盘符(windows)或者以 /(Linux)开头的路径。
- 相对路径:以 .(表示当前路径)或者 以 ...(表示当前路径上级路径)开头的路径。其中,经常也会省略,相对路径的前提是需要明确 "当前工作目录"。
对于 Qt 程序来说,当前工作目录可能是变化的。比如通过 Qt Creator 运行的程序,当前工作目录是项目的构建目录,直接双击 exe 运行,工作目录则是 exe 所在目录。
所谓构建目录,是和 Qt 项目并列的,专门用来放生成的临时文件和最终 exe 的目录。

Qt 使用 qrc 机制帮我们自动完成了上述工作,更方便的来管理项目依赖的静态资源。这个机制可以从根本上解决以下两个问题:
- 确保我们的图片所在路径在目标用户机器上存在。
- 确保我们的图片不会被用户搞没了。
qrc 文件是一种 XML 格式的资源配置文件,它用 XML 记录硬盘上的文件和对应的随意指定的资源名称,应用程序通过资源名称来访问这些资源。在 Qt 开发中,可以通过将资源文件添加到项目中来方便地访问和管理这些资源,这些资源文件可以位于 qrc 文件所在目录的同级或其子目录下。
在构建程序的过程中,Qt 会把资源文件的二进制数据转成 cpp 代码,编译到 exe 中,从而使依赖的资源变得 "路径无关"。
这种资源管理机制并非 Qt 独有,很多开发框架都有类似的机制。例如:Android 的 Resources 和 AssetManager 也是类似的效果。
qrc 的缺点:无法导入太大的资源文件。
通过 qrc 管理图片作为图标
① 右键项目,创建一个 Qt Resource File(qrc 文件)

② 在 qrc 编辑器中添加前缀

此处我们前缀设置成 / 即可。所谓的前缀,可以理解成 "虚拟的目录",这个目录在我们的电脑中并不是真实存在的,是 Qt 自己抽象出来的,它决定了后续我们如何在代码中访问资源。
③ 在资源编辑器中,点击 add Files 添加资源文件
此处我们需要添加的是:Q&A.jpg:

注意 :
- 添加的文件必须是在 qrc 文件的同级目录或者同级目录的子目录中,因此我们需要把之前 D 盘中的 Q&A.jpg 复制到上述目录中。
④ 在代码中使用 Q&A.jpg

代码中需要访问 qrc 中管理的文件时,就需要在路径上带有 : 前缀。创建的前缀叫什么名字,代码中就写什么名字:前缀 + 文件名。
注意上述路径的访问规则:
- 使用 : 作为开头,表示从 qrc 中读取资源
- / 是上面配置的前缀
- rose.jpg 是资源的名称
需要确保代码中编写的路径和添加到 qrc 中资源的路径匹配,否则资源无法被访问(同时也不会有报错提示)。
⑤ 运行效果
可以看到图标已经能正确设置:

我们可以进入到项目的构建目录中可以看到:目录中多了一个 qrc_resource.cpp 文件,直接打开这个文件可以看到:
qrc 中导入的图片资源会被转成这个 qrc_resource.cpp(自动生成)这个 c++ 代码


上述代码其实就是通过 unsigned char 数组,把 rose.jpg 中的每个字节都记录下来。这些代码会被编译到 exe 中,后续无论 exe 被复制到哪个目录下都确保能够访问到该图片资源。
上述 qrc 这一套资源管理方案的优点和缺点都很明显:
- 优点:确保了图片、字体、剩余等资源能够真正做到 "目录无关",无论如何都不会出现资源丢失的情况。
- 缺点:不适合管理体积大的资源。如果资源比较大(比如是几个 MB 的文件),或者资源特别多,生成的最终的 exe 体积就会比较大,程序运行消耗的内存也会增大,程序编译的时间也会显著增加。
6、windowOpacity
| API | 说明 |
|---|---|
windowOpacity() |
获取控件透明度,返回 float,范围 0.0 ~ 1.0(0.0 = 全透明,1.0 = 完全不透明) |
setWindowOpacity(float n) |
设置控件透明度,取值范围同上 |
代码示例:调整窗口透明度
在界面上拖放两个按钮,分别用来增加不透明度和减少不透明度
objectName 分别为 pushButton_add 和 pushButton_sub
编写 wdiget.cpp, 编写两个按钮的 slot 函数
- 点击 pushButton_sub 会减少不透明度,也就是窗口越来越透明
- 点击 pushButton_add 会增加不透明度,窗口会逐渐恢复

执行效果

注意 :
- C++ 中 float 类型遵守 IEEE 754 标准,因此在进行运算的时候会有一定的精度误差,因此 1 - 0.1 的数值并非是 0.9。
7、cursor
| API | 说明 |
|---|---|
cursor() |
获取当前 widget 的光标,返回 QCursor;鼠标悬停在该 widget 上时会显示对应形状 |
setCursor(const QCursor& cursor) |
设置当前 widget 的光标形状,仅在鼠标停留于该 widget 上时生效 |
QGuiApplication::setOverrideCursor(const QCursor& cursor) |
设置全局光标形状,对整个程序所有 widget 生效,会覆盖所有 setCursor() 的设置 |
代码示例:在 Qt Designer 中设置按钮的光标

代码示例:通过代码设置按钮的光标

系统内置的光标形状如下:
Ctrl + 左键点击 Qt::WaitCursor 跳转到源码即可看到:

代码示例:自定义鼠标光标
通过上面qrc的方式自定义图标,详细可以看前面。

8、font
| API | 说明 |
|---|---|
font() |
获取当前 widget 的字体信息,返回 QFont |
setFont(const QFont& font) |
设置当前 widget 的字体信息 |
关于 QFont:
| 属性 | 说明 |
|---|---|
family |
字体家族,如 "楷体"、"宋体"、"微软雅黑" 等 |
pointSize |
字体大小 |
weight |
字体粗细,范围 [0, 99],数值越大越粗 |
bold |
是否加粗:true ≈ weight=75,false ≈ weight=50 |
italic |
是否倾斜 |
underline |
是否带下划线 |
strikeOut |
是否带删除线 |
代码示例:在 Qt Designer 中设置字体属性

代码示例:在代码中设置字体属性

在实际开发中,字体属性如何选择是一个 "审美问题",而不是 "技术问题",往往需要有一定的艺术细胞。幸运的是,公司中往往有专业的 "美工" / "设计" 这样的岗位,去做这方面的工作,我们程序员只要按照人家给的设计稿,把代码写出来即可,不必过多考虑怎样搭配才好看的问题。
9、toolTip
| API | 说明 |
|---|---|
setToolTip(const QString &text) |
设置 tooltip,当鼠标悬停在 widget 上时显示提示信息 |
setToolTipDuration(int ms) |
设置 tooltip 的显示时长,单位为毫秒(ms),时间到后 tooltip 自动消失 |
toolTip 只是给用户看的,在代码中一般不需要获取到 toolTip。
代码示例:设置按钮的 toolTip

运行效果

10、focusPolicy
设置控件获取到焦点的策略,比如某个控件能否用鼠标选中或者能否通过 tab 键选中。
所谓 "焦点",指的就是能选中这个元素。接下来的操作(比如键盘操作),就都是针对该焦点元素进行的了。这个对于输入框、单选框、复选框等控件非常有用的。
这个事情就和 war3 或者 sc2 中,先选中单位,再下达命令是一样的。

| API | 说明 |
|---|---|
focusPolicy() |
获取 widget 的焦点策略,返回 Qt::FocusPolicy |
setFocusPolicy(Qt::FocusPolicy policy) |
设置 widget 的焦点策略 |
Qt::FocusPolicy 是一 个 枚举 类型,取值如下:
- Qt::NoFocus:控件不会接收键盘焦点
- Qt::TabFocus:控件可以通过 Tab 键接收焦点
- Qt::ClickFocus:控件在鼠标点击时接收焦点
- Qt::StrongFocus:控件可以通过 Tab 键和鼠标点击接收焦点(默认值)
- Qt::WheelFocus:类似于 Qt::StrongFocus,同时控件也通过⿏标滚轮获取到焦点(新增的选项, ⼀般很少使用)
理解不同的 focusPolicy
(1)在界面上创建四个单行输入框(Line Edit)

(2)修改四个输入框的 focusPolicy 属性为 Qt::StrongFocus(默认取值,一般不用额外修改)

此时运行程序可以看到,使用鼠标单击 / tab,就可以移动光标所在输入框,从而接下来的输入就是针对这个获取焦点的输⼊框展开的了。
(3)修改第二个输入框的 focusPolicy 为 Qt::NoFocus,则第二个输入框不会被 tab / 鼠标左键选中

此时这个输入框也就无法输入内容了。
(4)修改第二个输入框 focusPolicy 为 Qt::TabFocus,则只能通过 tab 选中,无法通过鼠标选中

(5)修改第二个输入框 focusPolicy 为 Qt::ClickFocus,则只能通过 tab 选中,无法通过鼠标选中

11、styleSheet
通过 CSS 设置 widget 的样式。
CSS(Cascading Style Sheets 层叠样式表)本身属于网页前端技术,主要就是用来描述界面的样式。所谓 "样式",包括不限于大小、位置、颜色、间距、字体、背景、边框等。我们平时看到的丰富多彩的网页就都会用到大量的 CSS。
Qt 虽然是做 GUI 开发,但实际上和网页前端有很多异曲同工之处,因此 Qt 也引入了对于 CSS 的支持。CSS 中可以设置的样式属性非常多,基于这些属性 Qt 只能支持其中一部分,称为 QSS (Qt Style Sheet)。具体的支持情况可以参考 Qt 文档中 "Qt Style Sheets Reference" 章节。
代码示例:设置文本样式
(1)在界面上创建 label

(2)编辑右侧的 styleSheet 属性,设置样式


此处的语法格式同 CSS,使用键值对的方式设置样式。其中键和值之间使用 : 分割.,键值对之间使用 ; 分割。
另外,Qt Designer 只能对样式的基本格式进行校验,不能检测出哪些样式不被 Qt ⽀持,比如 text-align: center 这样的文本居中操作,就无法支持。
编辑完成样式之后,可以看到在 Qt Designer 中能够实时预览出效果:

代码示例:实现切换夜间模式
- 日间模式:文字是黑色的,背景是白色的
- 夜间模式:文字是白色的,背景是黑色的
(1)在界面上创建一个多行输入框(Text Edit)和两个按钮:objectName 分别为 pushButton_light 和 pushButton_dark

(2)编写按钮的 slot 函数
- #fff 是纯白色
- #000 是纯黑色
使用在线调色版或者画图板都可以看到数字对应的颜色,参考:在线调色板

运行效果

关于计算机中的颜色表示
计算机中使用 "像素" 表示屏幕上的一个基本单位(也就是一个发亮的光点)。每个光点都使用三个字节表示颜色,分别是 R(red)、G(green)、B(blue)一个字节表示(取值范围是 0-255,或者 0x00-0xFF)。
混合三种不同颜色的数值比例就能搭配出千千万万的颜色出来。
- rgb(255, 0, 0) 或者 #FF0000 或者 #F00 表示纯红色
- rgb(0, 255, 0) 或者 #00FF00 或者 #0F0 表示纯绿色
- rgb(0, 0, 255) 或者 #0000FF 或者 #00F 表示纯蓝色
- rgb(255, 255, 255) 或者 #FFFFFF 或者 #FFF 表示纯白色
- rgb(0, 0, 0) 或者 #000000 或者 #000 表示纯黑色
当然,上述规则只是针对⼀般的程序而言是这么设定的。实际的显示器可能有 8bit 色深或者 10bit 色深等,实际情况会更加复杂。