目录
一、控件概述
控件体系的发展阶段:
二、QWidget 核心属性
核心属性概览:
1、enabled
2、Geometry
实例 1: 控制按钮的位置
实例 2: 表白 程序
i、Window Frame 的影响
ii、API 设计理念
iii、Geometry 和 FrameGeometry 的区别
(1) 在按钮的 slot 函数中编写代码 & 在构造函数中也添加同样的代码
(2)执行程序
一、控件概述
- Widget:Qt 中的核心概念,指图形化界面的基本构成元素(如按钮、列表视图等),即“控件”。
- Qt 的优势:内置大量常用控件,并支持自定义控件,便于快速开发符合需求的界面。
像上述示例中的按钮、列表视图、树形视图、单行输入框、多行输入框、滚动条、下拉框都可以称为 “控件”。
- Qt 作为一个成熟的 GUI 开发框架,内置了大量的常用控件。
- 这一点在 Qt Designer 中就可以看出来,并且 Qt 也提供了 “自定义控件” 的能力,可以让我们在现有控件不能满足需求的时候,对现有控件做出扩展,或者手搓出新的控件。
所以,学习 Qt 其中一个很重要的任务就是熟悉并掌握 Qt 内置的常用控件,这些控件对于我们快速开发出符合需求的界面是至关重要的。
控件体系的发展阶段:
- 无控件阶段:完全没有控件。此时需要通过一些绘图 API 手动的绘制出按钮或者输入框等内容,代码编写繁琐。
(例如文曲星的 Lava 平台开发)
- 粗略控件阶段:提供基本控件,如按钮和输入框,简化了GUI开发。
(例如 html 的原生控件)
- 成熟控件体系:更完整的控件体系,基本可以覆盖到 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) 也可)
核心属性概览:
QWidget
属性及其作用:
属性 | 作⽤ |
enabled | 设置控件是否可使用。 |
geometry | 控制控件的位置和尺寸,包含 |
windowTitle | 设置 widget 的标题。 |
windowIcon | 设置 widget 的图标。 |
windowOpacity | 设置 widget 的透明度。 |
cursor | 设置鼠标悬停时显示的图标形状,如普通箭头、沙漏或十字等。可通过 Qt Designer 查看选项。 |
font | 控制字体相关属性,包括字体家族、大小、粗体、斜体、下划线等样式。 |
toolTip | 当鼠标悬停在 widget 上时,在状态栏中显示的提示信息。 |
toolTipDuration |
|
statusTip | 当 widget 状态发生改变时(如按钮被按下)显示的提示信息。 |
whatsThis | 当鼠标悬停并按下 |
styleSheet | 允许使用 CSS 来设置 widget 中的样式,支持丰富的样式,便于前端开发者上手。 |
focusPolicy | 定义 widget 如何获取焦点: |
contextMenuPolicy | 设置上下文菜单的显示策略: |
locale | 设置语言和国家地区。 |
acceptDrops | 设置该部件是否接受拖放操作。 |
minimumSize | 控件的最小尺寸,包含最小宽度和最小高度。 |
maximumSize | 控件的最大尺寸,包含最大宽度和最大高度。 |
sizePolicy | 设置控件在布局管理器中的缩放方式。 |
windowModality | 指定窗口是否具有 "模态" 行为。 |
sizeIncrement | 拖动窗口大小时的增量单位。 |
baseSize | 窗口的基础大小,用于配合 |
palette | 设置 widget 的颜色风格。 |
mouseTracking | 是否跟踪鼠标移动事件。 |
tabletTracking | 是否跟踪触摸屏的移动事件,类似于 |
layoutDirection | 设置布局方向: |
autoFillBackground | 是否自动填充背景颜色。 |
windowFilePath | 将 widget 和一个本地文件路径关联起来。 |
accessibleName | 设置 widget 的可访问名称,辅助技术(如屏幕阅读器)可以获取到这个名称。 |
accessibleDescription | 设置 widget 的详细描述,作用同 |
inputMethodHints | 针对输入框有效,用来提示用户当前能输入的合法数据格式,如只能输入数字、只能输入日期等。 |
下面我会介绍上面列出的其中一些比较重要和常用的属性。
1、enabled
- 所谓 “禁用” 指的是该控件不能接收任何用户的输入事件,并且外观上往往是灰色的。
- 如果一个 widget 被禁用,则该 widget 的子元素也被禁用。
使用代码创建一个禁用状态的按钮:
运行程序,可以看到按钮处于灰色状态,无法被点击:
通过按钮 2 切换按钮 1 的禁用状态
(1)使用 Qt Designer 拖两个按钮到 Widget 中
- 两个按钮的 objectName 分别为 pushButton 和 pushButton_2。
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_2 这样的代码获取到界面上的 widget 对象了。
- 当前自动生成的 objectName 是有规律的:控件的类型 + 下划线 + 数字。很明显,以数字的方式命名并不是一个好的编程习惯,这里我将它修改为如下所示:
(2)生成两个按钮的 slot 函数
- 使用 isEnabled 获取当前按钮的可用状态。
- 使用 setEnabled 修改按钮的可用状态,此处是直接针对原来的可用状态进行取反后设置。
运行程序可以看到:初始情况下,上面的按钮是可用状态。接着点击下方按钮,即可使上方按钮被禁用~
在 Qt Designer 中创建按钮的时候可以设置按钮的初始状态是 “可用” 还是 “禁用”。如果把 enabled 这一列的对钩去掉,则按钮的初始状态就是 “禁用” 状态。
2、Geometry
位置和尺寸是四个属性的统称:
- x 横坐标
- y 纵坐标
- width 宽度
- height 高度
在实际开发中,我们通常不会直接使用这四个属性来获取或修改控件的位置和大小。
Qt 提供了一系列封装的方法,这些方法更方便操作,并且考虑到了 Qt 的左手坐标系——其中原点位于父元素的左上角。
实例 1: 控制按钮的位置
创建界面布局:
- 在界面上拖拽五个按钮,分别命名为
pushButton_target
(目标按钮)、pushButton_up
(向上移动按钮)、pushButton_down
(向下移动按钮)、pushButton_left
(向左移动按钮)和pushButton_right
(向右移动按钮)。这些按钮的初始位置和大小可以随意设置。
编写槽函数:
-
- 在
widget.cpp
文件中为每个方向的按钮添加槽函数,用于改变pushButton_target
的位置。当点击相应的方向按钮时,会调整目标按钮的 x 和 y 坐标,从而实现位置变化。 - 注意,这样做会导致按钮的整个矩形区域发生位移,而不仅仅是其左上角。
- 在
优化移动逻辑:
-
- 如果希望按钮只移动而不改变尺寸,应该避免直接修改
QRect
对象中的 x 和 y 值。 - 相反,可以通过
setGeometry()
方法的第二个版本来重新设定按钮的位置,保持宽度和高度不变。
- 如果希望按钮只移动而不改变尺寸,应该避免直接修改
上述代码使用 move 方法也是可以的。
运行,发现设置成功~
实例 2: 表白 程序
设计界面:
-
- 向界面上添加两个按钮(接受
pushButton_accept
和拒绝pushButton_reject
)以及一个标签label
,用来显示文本信息。
- 向界面上添加两个按钮(接受
实现交互逻辑:
-
- 在
widget.cpp
中定义槽函数,使得当用户点击 "Sorry.." 拒绝按钮时,触发按钮逃跑的行为。 - 此行为可以通过监听
clicked
事件(即鼠标点击后释放)实现。
- 在
运行程序可以看到:当点击 "Sorry.." 时,按钮就跑了。
上述代码使用的是 clicked(一下一上是点击),如果使用 pressed(鼠标按下事件)。
如果使用 mouseMoveEvent,会更狠一些, 只要鼠标移动过来,按钮就跑了。
对应的代码更麻烦⼀些,需要使用到 Qt 的使用机制(需要自定义类继承自 QPushButton,重写 mouseMoveEvent 方法)这里就暂时不展开了。
i、Window Frame 的影响
当 widget 作为一个窗口时(例如带有标题栏等),计算尺寸和坐标有两种算法:
- 包含 window frame 的方式(如
x()
,y()
,frameGeometry()
,pos()
,move()
) - 不包含 window frame 的方式(如
geometry()
,width()
,height()
,rect()
,size()
)
对于非窗口的 widget,这两种计算方式的结果是一致的。
ii、API 设计理念
API | 说明 |
x() | 获取横坐标。计算时包含 window frame。 |
y() | 获取纵坐标。计算时包含 window frame。 |
pos() | 返回 |
frameSize() | 返回 |
frameGeometry() | 返回 |
width() | 获取宽度。计算时不包含 window frame。 |
height() | 获取高度。计算时不包含 window frame。 |
size() | 返回 |
rect() | 返回 |
geometry() | 返回 |
setGeometry() | 设置窗口的位置和尺寸,可以设置 x, y, width, height 或 |
认真观察上面的表格,可以看到,其实这里的 API 有 frameGeometry 和 geometry 两个就足够完成所有的需求了。
为什么要提供这么多功能重复的 API 呢?
这涉及到 Qt API 的设计理念:尽量符合人的直觉。例如,Qt 的
QVector
提供了多种尾插元素的方法:
push_back
append
+=
<<
上述方法的效果都是等价的,即使不翻阅文档,单纯的凭借直觉就能把代码写对。减少了记忆负担,使编程变得更加直观和友好。
iii、Geometry 和 FrameGeometry 的区别
(1) 在按钮的 slot 函数中编写代码 & 在构造函数中也添加同样的代码
(2)执行程序
- 可以看到:在构造函数中打印出的 geometry 和 frameGeometry 是相同的。
- 但是在点击按钮时,打印的 geometry 和 frameGeometry 则存在差异。
注意:
- 在构造方法中,Widget 刚刚创建出来,还没有加入到对象树中,此时也就不具备 Window frame。
- 在按钮的 slot 函数中,由于用户点击的时候,对象树已经构造好了,此时 Widget 已经具备了 Window frame,因此在位置和尺寸上均出现了差异。
- 如果把上述代码修改成打印 pushButton 的 geometry 和 frameGeometry,结果就是完全相同的。因为 pushButton 并非是一个窗口。