【QT】常用控件(按钮类控件、显示类控件、输入类控件、多元素控件、容器类控件、布局管理器)

【QT】常用控件(按钮类控件、显示类控件、输入类控件、多元素控件、容器类控件、布局管理器)

一、按钮类控件

1、Push Button

使用 QPushButton 表示一个按钮,这也是当前我们最熟悉的一个控件了。QPushButton 继承自 QAbstractButton,这个类是一个抽象类,是其他按钮的父类。

Qt Designer 中也能够看到这里的继承关系:

QAbstractButton 中,和 QPushButton 相关性较大的属性:

属性 说明
text 按钮中的文本
icon 按钮中的图标
iconSize 按钮中图标的尺寸
shortCut 按钮对应的快捷键
autoRepeat 按钮是否会重复触发。按住鼠标左键不放时:若为 true,则持续产生点击事件;若为 false,则需松开并再次点击才会触发。
autoRepeatDelay 重复触发的延时时间。按住按钮多久后开始重复触发。
autoRepeatInterval 重复触发的周期。
  1. QAbstractButton 作为 QWidget 的子类,当然也继承了 QWidget 的属性。上面介绍的 QWidget 里的各种属性用法,对于 QAbstractButton 同样适勇。因此表格仅列出 QAbstractButton 独有的属性。
  2. Qt 的 api 设计风格是非常清晰的,此处列出的属性都是可以获取和设置的。例如,使用 text() 获取按钮文本,使用 setText() 设置文本。

事实上,QPushButton 的核心功能都是 QAbstractButton 提供的,自身提供的属性都比较简单。其中,default 和 audoDefault 影响的是按下 enter 时自动点击哪个按钮的行为,flat 把按钮设置为扁平的样式。


代码示例:带有图标的按钮

通过qrc的方式设置图标,这里就不过多说明了。

修改 widget.cpp,给按钮设置图标


运行效果


代码示例:带有快捷键的按钮

(1)在界面中拖一个按钮

(2)修改 widget.cpp,设置图快捷键

  • 使用 setShortcut 给按钮设置快捷键,参数是⼀个 QKeySequence 对象,表示一个按键序列,支持持组合键(Ctrl + C 这种)。
  • QKeySequence 的构造函数参数,可以直接使用 "Ctrl + C" 这样的按键名字符串表示,也可以使用预定义好的常量(形如 Qt::CTRL + Qt::Key_C)表示。

运行效果

当使用 快捷键/鼠标 点击的时候按钮向上移动


2、Radio Buttion

QRadioButton 是单选按钮,可以让我们在多个选项中选择一个。

作为 QAbstractButton 和 QWidget 的子类,上面介绍的属性和用法,对于 QRadioButton 同样适用。

QAbstractButton 中和 QRadioButton 关系较大的属性:

属性 说明
checkable 是否能选中
checked 是否已经被选中。checkablechecked 的前提条件。
autoExclusive 是否排他。选中一个按钮后是否会取消其他按钮的选中。对于 QRadioButton 来说默认是排他的。

代码示例:选择性别

(1)在界面上创建⼀个 label 和 3 个单选按钮

设置的文本如下图,3 个单选按钮的 objectName 分别为:radioButton_male、radioButton_female、radioButton_other


(2)修改 widget.cpp,编辑三个 QRadioButton 的 slot 函数


运行效果

随着选择不同的单选按钮,label 中的提示文字就会随之变化:


代码示例:单选框分组

(1)在界面上创建 9 个单选框,用来模拟麦当劳点餐界面。

objectName 分别为 radioButton 到 radioButton_9

此时直接运行程序可以看到,这九个 QRadioButton 之间都是排他的。我们希望每一组内部来控制排他,但是组和组之间不能排他。


(2)引入 QButtonGroup 进行分组

再次执行程序可以看到可以按照正确的分组方式来完成排他了。


click、press、release、toggled 的区别

  • clicked 表示⼀次 "点击"
  • pressed 表示鼠标 "按下"
  • released 表示鼠标 "释放"
  • toggled 表示按钮状态切换

(1)在界面上创建四个单选按钮

objectName 分别为 radioButton、radioButton_2、radioButton_3、radioButton_4


(2)给 1 创建 clicked 槽函数,给 2 创建 pressed 槽函数,给 3 创建 released 槽函数,给 4 创建 toggled 槽函数


(3)运行程序

  • clicked 是⼀次鼠标按下 + 鼠标标释放触发的
  • pressed 是鼠标按下触发的
  • released 是鼠标释放触发的
  • toggled 是 checked 属性改变时触发的

总的来说,toggled 是最适合 QRadioButton 的。


3、Check Box

QCheckBox 表示复选按钮,可以允许选中多个。和 QCheckBox 最相关的属性也是 checkable 和 checked,都是继承自 QAbstractButton。

至于 QCheckBox 独有的属性 tristate 用来实现 "三态复选框",这个东西比较冷门,这里暂时不讲述。

(1)在界面上创建三个复选按钮和一个普通按钮

objectName 分别为 checkBox_study、checkBox_game、checkBox_work 以及 pushButton

(2)给 pushButton 添加 slot 函数


运行效果

可以看到点击确认按钮时,就会在控制台中输出选中的内容:


4、Tool Button

QToolButton 的大部分功能和 QPushButton 是一致的,但 QToolButton 主要应用在工具栏、菜单等场景。


二、显示类控件

1、Label

QLabel 可以用来显示文本和图片。

核心属性如下:

属性 说明
text QLabel 中的文本
textFormat 文本的格式: • Qt::PlainText 纯文本 • Qt::RichText 富文本(支持 HTML 标签) • Qt::MarkdownText Markdown 格式 • Qt::AutoText 自动判断文本格式
pixmap QLabel 内部包含的图片
scaledContents true 时内容自动拉伸以填充 QLabel;为 false 时不拉伸
alignment 对齐方式,可设置水平和垂直方向如何对齐
wordWrap true 时文本会自动换行;为 false 时不会自动换行
indent 文本缩进,对水平垂直方向都生效
margin 文本与边框之间的边距(四个方向都有效)。不同于 indent,indent 最多只对两个方向生效(由 alignment 决定)
openExternalLinks 是否允许打开外部链接(当 QLabel 文本包含 URL 时适用)
buddy 给 QLabel 关联一个"伙伴控件",点击 QLabel 时可激活该伙伴。例如伙伴是 QCheckBox 时,点击 QLabel 会使其被选中

代码示例:显示不同格式的文本

(1)在界面上创建三个 QLabel

尺存放大一些,objectName 分别为 label、label_2、label_3


(2)修改 widget.cpp,设置三个 label 的属性


运行效果


代码示例:显示图片

虽然 QPushButton 也可以通过设置图标的方式设置图片,但是并非是一个好的选择,更多的时候还是希望通过 QLabel 来作为一个更单纯的显示图片的方式。

(1)在界面上创建一个 QLabel、objectName 为 label


(2)创建 resource.qrc 文件,并把图片导入到 qrc 中

(3)修改 widget.cpp,给 QLabel 设置图片

注意:

  • 这里的 resizeEvent 函数我们没有手动调用,但是能在窗口大小变化时被自动调用,这个过程就是依赖 C++ 中的多态来实现的。

  • Qt 框架内部管理着 QWidget 对象表示我们的窗口,在窗口大小发生改变时,Qt 就会自动调用 resizeEvent 函数。但是由于实际上这个表示窗口的并非是 QWidget,而是 QWidget 的子类,也就是我们自己写的 Widget。此时虽然是通过父类调用函数,但是实际上执行的是子类的函数(也就是我们重写后的 resizeEvent)。

  • 此处属于是多态机制的⼀种经典用法。通过上述过程就可以把自定义的代码插入到框架内部执行,相当于 "注册回调函数"。


运行效果


代码示例:文本对齐、自动换行、缩进、边距

(1)创建四个 label,objectName 分别是 label 到 label_4,并且在 QFrame 中设置 frameShape 为 Box(设置边框之后看起来会更清晰一些)


QFrame 是 QLabel 的父类,其中 frameShape 属性用来设置边框性质:

  • QFrame::Box:矩形边框
  • QFrame::Panel:带有可点击区域的面板边框
  • QFrame::WinPanel:Windows 风格的边框
  • QFrame::HLine:水平线边框
  • QFrame::VLine:垂直线边框
  • QFrame::StyledPanel:带有可点击区域的面板边框,但样式取决于窗口主题。

(2)编写 widget.cpp,给这四个 label 设置属性


运行效果


代码示例:设置伙伴

(1)创建两个 label 和 两个 radioButton

objectName 分别为 label、label_2、radioButton、radioButton_2

此处把 label 中的文本设置为 "快捷键 &A" 这样的形式。其中 & 后面跟着的字符就是快捷键,可以通过 alt + A 的方式来触发该快捷键。但是注意,这里的快捷键和 QPushButton 的不同需要搭配 alt 和 单个字母的方式才能触发。

绑定了伙伴关系之后,通过快捷键就可以选中对应的单选按钮 / 复选按钮。


(2)编写 widget.cpp,设置 buddy 属性

这里也可以使用 Qt Designer 直接设置:


运行效果


2、LCD Number

QLCDNumer 是一个专门用来显示数字的控件,类似于 "老式计算器" 的效果。

核心属性:

属性 说明
intValue QLCDNumber 显示的整数值(int)。与 value 联动。
value QLCDNumber 显示的数值(double)。与 intValue 联动,例如设为 1.5 时,intValue 会变为 2。 设置方法使用 display()
digitCount 显示的位数。
mode 数字显示形式: 1. QLCDNumber::Dec 十进制(唯一能显示小数点) 2. QLCDNumber::Hex 十六进制 3. QLCDNumber::Bin 二进制 4. QLCDNumber::Oct 八进制
segmentStyle 数字的显示风格: 1. QLCDNumber::Flat 平面风格 2. QLCDNumber::Outline 轮廓风格 3. QLCDNumber::Filled 填充风格
smallDecimalPoint 是否使用更小的"小数点"。

代码示例:倒计时

(1)在界面上创建⼀个 QLCDNumber,初始值设为 10

objectName 为 lcdNumber


(2)修改 widget.h 代码,创建一个 QTimer 成员和一个 handle 函数


(3)修改 widget.cpp,在构造函数中初始化 QTimer

  • QTimer 表示定时器,通过 start 方法启动定时器之后,就会每隔一定周期触发一次 QTimer::timeout 信号
  • 使用 connect 把 QTimer::timeout 信号和 Widget::handle 连接起来,意味着每次触发 QTimer::timeout 都会执行 Widget::handle

(4)修改 widget.cpp,实现 handle

  • 通过 intValue 获取到 QLCDNumber 内部的数值。
  • 如果 value 的值归 0 了,就停止 QTimer,接下来 QTimer 也就不会触发 timeout 信号了。

运行效果


针对上述代码,存在两个问题

①. 上述代码如果直接在 Widget 构造函数中,通过一个循环 + sleep 的方式是否可以呢?


显然,上面这段代码是不行的,循环会使 Widget 的构造函数无法执行完毕,此时界面是不能正确构造和显示的。


②. 上述代码如果是在 Widget 构造函数中,另起一个线程,在新线程中完成循环 + sleep 是否可以呢?

这个代码同样是不行的。Qt 中规定:任何对于 GUI 上内容的操作必须在主线程中完成。像 Widget 构造函数,以及 connect 连接的 slot 函数,都是在主线程中调用的。而我们自己创建的线程则不是,当我们自己的线程中尝试对界面元素进行修改时,Qt 程序往往会直接崩溃。

✍为什么这样约定?

  • 这样的约定主要是因为 GUI 中的状态往往是牵一发动全身的,修改一个地方,就需要同步的对其他内容进行调整。比如调整了某个元素的尺存,就可能影响到内部的文字位置,或者其他元素的位置。这里一连串的修改都是需要按照一定的顺序来完成的。

  • 由于多线程执行的顺序无法保障,因此 Qt 从根本上禁止了其他线程修改 GUI 状态,避免后续的一系列问题。

  • 对于 Qt 的槽函数来说,默认情况下,槽函数都是由主线程调用到,在槽函数中修改界面是没有任何问题的。

综上所述,使用定时器是实现上述功能的最合理方案。后续如果也有类似的需要 "周期性修改界面状态" 的需求也需要优先考虑使用定时器。


3、ProgressBar

使用 QProgressBar 表示一个进度条。

核心属性:

属性 说明
minimum 进度条最小值
maximum 进度条最大值
value 进度条当前值
alignment 文本在进度条中的对齐方式: • Qt::AlignLeft:左对齐 • Qt::AlignRight:右对齐 • Qt::AlignCenter:居中 • Qt::AlignJustify:两端对齐
textVisible 进度条的数字是否可见
orientation 进度条的方向(水平或垂直)
invertAppearance 是否反向增长进度
textDirection 文本的朝向
format 展示的数字格式: • %p:进度百分比(0-100) • %v:进度数值(0-100) • %m:剩余时间(毫秒) • %t:总时间(毫秒)

代码示例:设置进度条按时间增长

(1)在界面上创建进度条,objectName 为 progressBar

其中最小值设为 0,最大值设为 100,当前值设为 0:


(2)修改 widget.h,创建 QTimer 和 handle 函数

widget.h 文件没有包含 < QTimer > 头文件,为什么这个代码编译没有出错呢?

  • 上述问题其实是通过 Qt 内部提供的一个特殊技巧来实现的。在 Qt 中有一个专门的头文件(#include ),这个头文件中包含了 Qt 中所有类的 "前置声明"(class QWidget,class QPushButton,class QTimer)。这个头文件我们一般不会直接接触到,但是包含其它的 Qt 的头文件,都会间接的包含到这个头文件。

  • 如果 Widget 类的前面以及提供了 QTimer 类的声明的话,此时就可以在 Widget 中声明 QTimer 的指针 / 引用类型的成员。后续如果要真正使用 QTimer 的头文件(包括创建实例,使用里面的成员),仍然要包含 QTimer 的头文件(包含了 QTimer 的详细的类的定义)。

Qt 为什么要使用上述技巧呢?上述技巧能解决什么问题?有什么提升呢?

  • 主要解决的是编译速度的问题。C/C++ 代码,编译速度在其他语言横向对比中是非常慢的。C++ 编译速度慢和 #include 头文件有直接关系。由于 include 关系错综复杂,所以尽可能减少 include 头文件的个数就可以有效地减少编译时间。

  • Qt 中就使用 class 前置声明的方式来尽量减少头文件的包含。通过前置声明的方式,Qt 中每个头文件包含的其它头文件数量都能得到一定的降低。


(3)修改 widget.cpp


运行效果

在实际开发中,进度条的取值往往是根据当前任务的实际进度来进行设置的。比如需要读取一个很大的文件,就可以获取文件的总的大小和当前读取完毕的大小,来设置进度条的比例。

由于前面我们介绍了 Qt 禁止在其他线程修改界面,因此进度条的更新往往也是需要搭配定时器来完成的。

通过定时器周期触发信号,主线程调用对应的 slot 函数,再在 slot 函数中对当前的任务进度进行计算,并更新进度条的界面效果。


代码示例:创建一个红色的进度条

上述的进度条是用绿色表示的,但是考虑到有些人可能不喜欢绿色,因此我们改成一个红色的进度条。

QProgressBar 同样也是 QWidget 的子类,因此我们可以使用 styleSheet 通过样式来修改进度条的颜色。

(1)在界面上创建一个进度条


(2)在 Qt Designer 右侧的属性编辑器中找到 QWidget 的 styleSheet 属性

编辑内容:其中的 chunk 是选中进度条中的每个 "块",使用 QProgressBar::text 则可以选中文本。

同时把 QProcessBar 的 alignment 属性设置为垂直水平居中:

此处如果不设置 alignment,进度条中的数字会跑到左上角(可能是QT的bug)。


运行效果


4、Calendar Widget

QCalendarWidget 表示⼀个 "日历",形如:

核心属性:

属性 说明
selectDate 当前选中的日期
minimumDate 最小日期
maximumDate 最大日期
firstDayOfWeek 每周的第一天(即日历第一列是星期几)
gridVisible 是否显示日历表格边框
selectionMode 是否允许选择日期
navigationBarVisible 日历上方导航栏(标题)是否显示
horizontalHeaderFormat 日历上方标题显示的日期格式
verticalHeaderFormat 日历左侧第一列显示的内容格式
dateEditEnabled 是否允许日期被编辑

重要信号:

信号 说明
selectionChanged(const QDate&) 当选中的日期发生改变时发出,参数为当前选中的 QDate
activated(const QDate&) 当双击一个有效日期或按下回车键时发出,参数为被激活的 QDate
currentPageChanged(int, int) 当年份或月份发生改变时发出,参数分别为新的年份和新的月份

代码示例:获取选中的日期

(1)在界面上创建一个 QCalendarWidget 和一个 label

objectName 为 calendarWidget,label


(2)给 QCalendarWidget 添加 slot 函数


运行效果


三、输入类控件

1、Line Edit

QLineEdit 用来表示单行输入框,可以输入一段文本,但是不能换行。

核心属性:

属性名 说明
text 输入框中的文本
inputMask 输入内容格式约束
maxLength 最大长度
frame 是否添加边框
echoMode 显示方式: • QLineEdit::Normal :这是默认值,文本框会显示输入的文本。 • QLineEdit::Password :在这种模式下,输入的字符会被隐藏,通常用星号(*)或等号(=)代替。 • QLineEdit::NoEcho:在这种模式下,文本框不会显示任何输入的字符。
cursorPosition 光标所在位置
alignment 文字对齐方式,设置水平和垂直方向的对齐
dragEnabled 是否允许拖拽
readOnly 是否是只读的(不允许修改)
placeHolderText 当输入框内容为空的时候,显示什么样的提示信息
clearButtonEnabled 是否会自动显示出"清除按钮"

核心信号:


代码示例:录入个人信息

(1)在界面上创建三个输入框和两个单选按钮,一个普通按钮

三个输入框的 objectName 为 lineEdit_name、lineEdit_password、lineEdit_phone

两个单选按钮的 objectName 为 radioButton_male、radioButton_female

按钮的 objectName 为 pushButton


(2)编写 widget.cpp,在构造函数中编写初始化代码


(3)继续修改 widget.cpp,给按钮添加 slot 函数


(4)执行程序

可以看到,随着用户输⼊内容之后,点击 "提交" 按钮就能打印到输入的信息:

inputMask 只能进行简单的输入格式校验。实际开发中,基于正则表达式的方式是更核心的方法。


关于正则表达式

正则表达式是一种在计算机中常用的,使用特殊字符描述一个字符串的特征的机制,在进行字符串匹配时非常有用。

正则表达式的语法还比较复杂,一般都是随用随查,不需要背下来。

可参考:

正则表达式文档:正则表达式语法 | Microsoft Learn

正则表达式在线工具:正则表达式语法测试工具 - 在线工具 (buyaocha.com)


代码示例:使用正则表达式验证输入框的数据

此处要求在输入框中输入一个合法的电话号码(1 开头,11 位,全都是数字)。如果验证不通过,则确定按钮无法点击。

(1)在界面上创建输入框和一个按钮

此处的规则:输入框要检查输入的内容是否为合法的手机号码。

  • 如果是,则按钮设为可用状态。
  • 如果不是,则按钮设为禁用状态。

(2)编写 widget.cpp,把按钮初始 enabled 设为 false,给输入框添加验证器

  • 使用 QRegExp 创建一个正则表达式对象,"^1\\d{10}$" 表示" 表示以 xxx 开头,后面跟着的 1 表示以 1 开头,\d 表示数字,为了在 C++ 字符串中使用,需要写作 \d,{10} 表示前面的内容重复出现 10 次,\d 数字要重复出现 10 次,$ 表示结尾。
  • 使用 QRegExpValidator 创建一个验证器对象,Qt 中内置了四个主要的验证器对象。

QRegularExpressionValidator 在匹配性能上做出了一定优化。但是从使用角度讲,和 QRegExpValidator 差别不大,我们使用 QRegExpValidator 即可。


(3)编写 widget.cpp,给 lineEdit 添加 textEdited 信号的 slot 函数

  • on_lineEdit_textEdited 的参数是当前输入框的内容
  • 通过 lineEdit->validator() 获取到内置的验证器
  • 通过 validate 方法验证文本是否符合要求
    • 第一个参数填写的是要验证的字符串。由于参数要求是 QString& 而不是 const QString&,需要把这个变量复制一下。
    • 第二个参数是⼀个 int&,是输出型参数。当验证的字符串不匹配时,返回这个字符串的长度(没有什么实质作用)。
    • 返回值是⼀个枚举。 QValidator::Acceptable 表示 验证通过,QValidator::Invalid 表示验证不通过。

(4)执行程序

观察效果,可以看到此时尝试输入字母是无法输入的,并且只有当输入的内容符合要求,确定按钮才能被使用


代码示例:验证两次输入的密码一致

(1)在界面上创建两个输入框和一个 label


(2)编写代码,设置两个输入框的 echoMode 为 Password


(3)给两个输入框设置 textEdited slot 函数

由于两个槽函数内容一致,我们可以选择封装一个函数,然后在槽函数内直接调用即可。


(4)执行程序

可以看到当两个输入框内的密码相同时,就会提示密码相同:


代码示例:切换显示密码

(1)创建一个输入框和一个复选按钮


(2)修改 widget.cpp,设置输入框的 echoMode 为 Password


(3)修改 widget.cpp,给 checkBox 添加 slot 函数


(4)执行程序

可以看到切换复选框的状态,就可以控制输入框显示密码:


2、TextEdit

QTextEdit 表示多行输入框,也是一个富文本 & markdown 编辑器,并且能在内容超出编辑框范围时自动提供滚动条。

核心属性:

核心信号:


代码示例:获取多行输入框的内容

(1)创建一个多行输入框和一个 label


(2)给多行输入框添加 slot 函数,处理 textChanged 信号

  • 通过 toPlainText 方法获取到内部的文本
  • 类似的,QTextEdit 还提供了 toMarkdown 和 toHtml,根据需要我们调整不同的获取方式

(3)执行程序

可以看到当输入框中的内容发生变化时,label 中的内容同步发生改变:


代码示例:验证输入框的各种信号

(1)创建多行输入框


(2)给输入框添加以下几个 slot 函数

QTextEdit 中包含了一个 QTextCursor 对象,通过这个对象可以获取到当前光标位置和选中的内容。


(3)执行程序

A. 编写内容时,textChanged 和 cursorPositionChanged 会触发


B. 选中一段文本时,cursorPositionChanged、selectChanged、copyAvailable 会触发


C. 按下 Ctrl + Z 时,textChanged、undoAvailable、redoAvailable、cursorPositionChanged 会触发


D. 按下 Ctrl + Y,textChanged、undoAvailable、redoAvailable、cursorPositionChanged 会触发


3、Combo Box

QComboBox 表示下拉框。

核心属性:

核心方法:

核心信号:


代码示例:使用下拉框模拟麦当劳点餐

(1)在界面上创建三个下拉框和一个按钮


(2)编写 widget.cpp,初始化三个下拉框的内容

也可以选择直接在 ui 界面添加选项内容:


(3)编写 widget.cpp,给按钮添加 slot 函数


(4)执行程序

在点击确定按钮时,就能获取到当前下拉框中选中的内容:


代码示例:从文件中加载下拉框的选项

很多时候下拉框的选项并非是固定的,而是通过读取文件 / 读取网络获取到的。

(1)在界面上创建一个下拉框


(2)创建文件 d:/config.txt,编写选项,每个选项占一行


(3)修改 widget.cpp,从文件中读取选项

  • 使用 ifstream 打开文件
  • 使用 getline 读取每一行
  • 使用 QString::fromStdString 把 std::string 转成 QString

(4)执行程序

可以看到文件内容已经被加载到下拉框中:

Qt 中也提供了 QFile 实现读写文件的功能,当然使用 C++ 标准库的 std::fstream 也是完全可以的。之所以存在两套,是因为 Qt 诞生较早(1991 年左右),此时 C++ 还没有完成 "标准化" 的工作,C++ 标准库这样的概念自然也没有诞生。

因此 Qt 就自己打造了一套库,实现了字符串、容器、文件操作、多线程、网络操作、定时器、正则表达式等内容。(由于 C++ 标准委员会的不作为,至今仍然有些 Qt 提供的功能,是标准库不具备的)


4、Spin Box

使用 QSpinBox 或者 QDoubleSpinBox 表示 "微调框",它是带有按钮的输入框,可以用来输入整数 / 浮点数,通过点击按钮来修改数值大小。

由于 SpinBox 和 QDoubleSpinBox 用法基本相同,就只介绍 SpinBox 的使用了。

Spin 英文原意为 "旋转",此处引申成 "微调"。

事实上很多术语在翻译的时候,不一定非要按照原始的翻译来表示,更追求的是 "信达雅"。举例:地铁上的 "Priority Seat" 会翻译成 "爱心专座",而不是 "优先座位"。


核心属性:

核心信号:


代码示例:调整麦当劳购物车中的份数

(1)在界面上创建下列内容

  • 三个下拉框:objectName 为 comboBox 到 comboBox_3
  • 三个微调框:objectName 为 spinBox 到 spinBox_3
  • 一个按钮:objectName 为 pushButton

(2)编写代码,修改 widget.cpp,给下拉框设置初始值


(3)编写代码,给按钮添加 slot 函数


(4)执行程序

可以看到当用户选择不同的内容时,点击按钮就能获取到对应的结果,同时我们也无法输入一些超出范围的非法值。


5、Date Edit & Time Edit

使用 QDateEdit 作为日期的微调框:

使用 QTimeEdit 作为时间的微调框:

使用 QDateTimeEdit 作为时间日期的微调框:

这几个控件用法非常相似,这里以 QDateTimeEdit 为例进行介绍。


QDateTimeEdit 核心属性:

核心信号:


关于本地时间(LocalTime)和协调世界时(UTC)

UTC 时间是一个基于原子钟的标准时间,不受地球的自转周期影响,和格林威治时间(GMT)是非常接近的,科学家会通过精密的设备来测量并维护。

我们的计算机内部使用的时间就是基于 UTC 时间。

本地时间则是基于不同的时区,对 UTC 时间做出了一些调整。比如咱们使用的北京时间,位于 "东八区",就需要在 UTC 时间基础上 +8 个小时的时差。


代码示例:实现日期计算器

(1)在界面上创建两个 QDateTimeEdit 和一个按钮,一个 label

QDateTimeEdit 的 objectName 为 dateTimeEdit_old 和 dateTimeEdit_new


(2)编写计算按钮的 slot 函数

  • 使用 daysTo 函数可以计算两个日期的天数
  • 使用 secsTo 函数可以计算两个时间的秒数
  • 通过(秒数 / 3600)换算成小时数,再余上 24 得到零几个小时
  • 使用 QString::number 把整数转成 QString 进行拼接

(3)执行程序


6、Dial

使用 QDial 表示一个旋钮。

有些程序通过鼠标拖动旋钮旋转,即可完成一些相关的设置。

核心属性:

核心信号:


代码示例:调整窗口透明度

(1)在界面上创建一个旋钮,并对其数值进行初始化


(2)编写 widget.cpp,设置旋钮的 valueChanged slot 函数


(3)运行程序

观察效果,可以看到随着拖动旋钮旋转,不透明度发生明显变化:


7、Slider

使用 QSlider 表示一个滑动条:

QSlider 和 QDial 都是继承自 QAbstractSlider,因此用法上基本相同。

核心属性:

核心信号:


代码示例:调整窗口大小

(1)在界面上创建两个滑动条,分别是水平和垂直滑动条

objectName 分别为 horizontalSlider 和 verticalSlider


(2)编写代码初始化滑动条


(3)编写滑动条的 valueChanged slot 函数


(4)执行程序

可以看到调整滑动条,窗口大小就会随之改变:


代码示例:通过自定义快捷键调整滑动条位置

设置 - 减小 value,设置 = 增加 value

默认情况下滑动条可以通过方向键或者 pageUp / pageDown 调整大小。

(1)在界面上创建滑动条和 label


(2)创建 valueChanged 的 slot 函数


(3)修改 widget.cpp 构造函数,增加快捷键

  • 使用 QShortCut 类设置快捷键
  • 快捷键触发时,会发出 QShortcut::activated 信号,我们连接到⾃⼰写的 slot 函数

(4)编写自定义 slot 函数


(5)执行程序

观察效果,可以看到此时按下 - 和 = 就可以调整 value 的值了:


四、多元素控件

Qt 中提供的多元素控件有:

  • QListWidget
  • QListView
  • QTableWidget
  • QTableView
  • QTreeWidget
  • QTreeView

1、xxWidget 和 xxView 之间的区别

以 QTableWidget 和 QTableView 为例。

  • QTableView 是基于 MVC 设计的控件。QTableView 自身不持有数据,使用 QTableView 的时候需要用户创建一个 Model 对象(比如 QStandardModel),并且把 Model 和 QTableView 关联起来。后续修改 Model 中的数据就会影响 QTableView 的显示,修改 QTableView 的显示也会影响到 Model 中的数据(双向绑定)。
  • QTableWidget 则是 QTableView 的子类,对 Model 进行了封装,不需要用户手动创建 Model 对象,直接就可以往 QTableWidget 中添加数据了。

2、List Widget

使用 QListWidget 能够显示一个纵向的列表。形如:

每个选项都可以被选中。

核心属性:

核心方法:

核心信号:

在上述介绍中涉及到⼀个关键的类:QListWidgetItem,这个类表示 QListWidget 中的一个元素。

核心方法如下,本质上就是⼀个 "文本+图标" 构成的。


代码示例:使用 ListWidget

(1)在界面上创建一个 ListView,右键 => 变形为 => ListWidget,再创建一个 lineEdit 和两个按钮

注意:ListWidget 是 ListView 的子类,功能比 ListView 更丰富。我们使用 ListWidget 即可。


(2)编写 widget.cpp,在构造函数中添加初始元素

或者直接在图形化界面选择 "编辑项目":


(3)编写 listWidget 的 slot 函数

此处需要判定 current 和 previous 非空,初始情况下是没有元素选中的,就导致这两个指针可能是 NULL。

这⾥需要给 widget.h 前⾯加上 #include 。


(4)编写按钮的 slot 函数


(5)执行程序

可以新增元素,选中元素,删除元素


3、Table Widget

使用 QTableWidget 表示一个表格控件。一个表格中包含若干行,每一行又包含若干列。表格中的每个单元格,是一个 QTableWidgetItem 对象。

QTableWidget 核心方法:

QTableWidgetItem 核心信号:

QTableWidgetItem 核心方法:


代码示例:使用 QTableWidget

(1)在界面上创建 QTableWidget 和 三个按钮,一个输入框

注意:QTableWidget 是 QTableView 的子类,功能比 QTableView 更丰富。我们使用 QTableWidget 即可。


(2)编写 widget.cpp 构造函数,构造表格中的初始数据


(3)编写按钮的 slot 函数


(4)执行程序,即可完成表格的基本操作

默认情况下,单元格中的内容直接就是可编辑的。

如果不想让用户编辑,可以设置 ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);


4、Tree Widget

使用 QTreeWidget 表示一个树形控件,里面的每个元素都是一个 QTreeWidgetItem,每个 QTreeWidgetItem 可以包含多个文本和图标,每个文本 / 图标为一个列。

可以给 QTreeWidget 设置顶层节点(顶层节点可以有多个),然后再给顶层节点添加子节点,从而构成树形结构。

QTreeWidget 核心方法:

QTreeWidget 核心信号:

QTreeWidgetItem 核心属性:

QTreeWidgetItem 核心方法:


代码示例:使用 QTreeWidget

(1)在界面上创建一个 TreeView,右键 => 变形为 => TreeWidget,再创建一个 lineEdit 和两个按钮

注意:TreeWidget 是 TreeView 的子类,功能比 TreeView 更丰富。我们使用 TreeWidget 即可。


(2)编写代码,构造初始数据


(3)编写代码,实现按钮的 slot 函数


(4)执行程序,可以针对树形框进行编辑


五、容器类控件

1、Group Box

使用 QGroupBox 实现一个带有标题的分组框,可以把其他的控件放到里面作为一组,这样看起来能更好看一点。

注意:不要把 QGroupBox 和 QButtonGroup 混淆(之前在介绍 QRadionButton 的时候提到了 QButtonGroup)。

核心属性: '

分组框只是一个用来 "美化界面" 这样的组件,并不涉及到用户交互和业务逻辑,属于 "锦上添花"。


代码示例:给麦当劳案例加上分组框

(1)在界面上创建三个分组框,并且在分组框内部创建下拉框和微调框

注意:在复制粘贴控件的时候,一定要先选中对应的父控件,再粘贴。


2、Tab Widget

使用 QTabWidget 实现一个带有标签页的控件,可以往里面添加一些 widget,进一步的就可以通过标签页来切换。

核心属性:

核心信号:


代码示例:使用标签页管理多组控件

(1)在界面上创建一个 QTabWidget 和两个按钮

注意 :

  • QTabWidget 中的每个标签页都是⼀个 QWidget
  • 点击标签页就可以直接切换
  • 右键 QTabWidget,可以添加标签页或者删除标签页

(2)编写 widget.cpp,进行初始化,给标签页中放个简单的 label

注意新创建的 label 的父元素,是 ui->tab 和 ui->tab_2。Qt 中使用父子关系决定该控件 "在哪里"。


(3)编写按钮的 slot 函数

  • 使用 count() 获取到标签页的个数
  • 使用 addTab 新增标签页
  • 使用 removeTab 删除标签页
  • 使用 currentIndex 获取到当前标签页的下标
  • 使用 setCurrentIndex 切换当前标签页

(4)编写 QTabWidget 的 currentChanged 函数


(5)运行程序

  • 点击新建标签页,可以创建出新的标签
  • 点击删除当前标签页,可以删除标签
  • 切换标签页时,可以看到 qDebug 打印出的标签页编号

六、布局管理器

之前使用 Qt 在界面上创建的控件都是通过 "绝对定位" 的方式来设定的,也就是每个控件所在的位置都需要计算坐标,最终通过 setGeometry 或者 move 方式摆放过去。

这种设定方式其实并不方便,尤其是界面如果内容比较多,不好计算,而且一个窗口大小往往是可以调整的,按照绝对定位的方式,也无法自适应窗口大小。因此 Qt 引入 "布局管理器"(Layout)机制来解决上述问题。

当然,布局管理器并非 Qt 独有。其他的 GUI 开发框架,像 Android、前端等也有类似的机制。

Layout 只是用于界面布局,并没有提供信号。


1、QVBoxLayout

使用 QVBoxLayout 表示垂直的布局管理器,V 是 vertical 的缩写。

核心属性:


代码示例:使用 QVBoxLayout 管理多个控件

(1)编写代码,创建布局管理器和三个按钮,并且把按钮添加到布局管理器中

  • 使用 addWidget 把控件添加到布局管理器中
  • 使用 setLayout 设置该布局管理器到 widget 中

(2)运行程序

可以看到此时界面上的按钮就存在于布局管理器中,随着窗口尺存变化而发生改变。此时的三个按钮的尺存和位置都是自动计算出来的。

通过上述代码的方式,只能给这个 widget 设定一个布局管理器。实际上也可以通过 Qt Design 在一个窗口中创建多个布局管理器。


代码示例:创建两个 QVBoxLayout

(1)在界面上创建两个 QVBoxLayout,每个 QVBoxLayout 各放三个按钮


(2)运行程序

可以看到这些按钮已经自动排列好,只不过当前这些按钮的位置不能随着窗口大小自动变化


通过 Qt Designer 创建的布局管理,其实是先创建了一个 widget,设置过 geometry 属性的,再把这个 layout 设置到这个 widget 中。

实际上,一个 widget 只能包含一个 layout。打开 ui 文件的原始 xml,可以看到其中的端倪。这种情况下 layout 并非是窗口 widget 的布局管理器,因此不会随着窗口大小改变。


2、QHBoxLayout

使用 QHBoxLayout 表示垂直的布局管理器,H 是 horizontal 的缩写。

核心属性 (和 QVBoxLayout 属性是一致的)


代码示例:使用 QHBoxLayout 管理控件

(1)编写代码,创建布局管理器和三个按钮,并且把按钮添加到布局管理器中


(2)运行程序

可以看到此时界面上的按钮就存在于布局管理器中,随着窗口尺寸变化而发生改变。此时的三个按钮的尺存和位置都是自动计算出来的。

Layout 里面可以再嵌套上其他的 layout,从而达到更复杂的布局效果。

代码示例:嵌套的 layout

(1)在代码中创建以下内容

使用 addLayout 给 layout 中添加子 layout:


(2)执行程序

结合 QHBoxLayout 和 QVBoxLayout, 就可以做出各种复杂的界面了。


3、QGridLayout

Qt 中还提供了 QGridLayout 用来实现网格布局的效果,可以达到 M * N 的这种网格的效果。

核心属性:

整体和 QVBoxLayout 以及 QHBoxLayout 相似,但是设置 spacing 的时候是按照垂直水平两个方向来设置的。


代码示例:使用 QGridLayout 管理元素

(1)代码中创建 QGridLayout 和 4 个按钮

使用 addWidget 添加控件到布局管理器中,但是添加的同时会指定两个坐标,表示放在第几行,第几列。


(2)执行代码

可以看到当前的这几个按钮是按照 2 行 2 列的方式排列的:


(3)如果调整行列坐标为下列代码

执行代码可以看到这几个按钮都在同一行了,相当于 QHBoxLayout:


(4)如果调整行列坐标为下列代码

执行代码可以看到这几个按钮都在同一列了,相当于 QVBoxLayout:


(5)任意调整行列,即可看到不同的效果


(6)编写代码形如

注意 :

  • 设置行和列的时候,如果设置的是一个很大的值,但是这个值和上一个值之间并没有其他的元素,那么并不会在中间腾出额外的空间。
  • 虽然把 button4 设置在第 100 行,但是由于 3-99 行没有元素,因此 button4 仍然会紧挨在 button3 下方看起来和上面的 0 1 2 3 的情况是相同的。

代码示例:设置 QGridLayout 中元素的大小比例

(1)创建 6 个按钮,按照 2 行 3 列的方式排列

使用 setColumnStretch 设置每一列的拉伸系数:


(2)执行程序

可以看到每一列的宽度是不同的,并且随着窗口调整动态变化:

另外,QGridLayout 也提供了 setRowStretch 设置行之间的拉伸系数。

上述案例中,直接设置 setRowStretch 效果不明显,因为每个按钮的高度是固定的。需要把按钮的垂直方向的 sizePolicy 属性设置为 QSizePolicy::Expanding 尽可能填充满布局管理器,才能看到效果。

如果拉伸系数设为 0,意思是不参与拉伸,此时按钮的宽度是固定值。


代码示例:设置垂直方向的拉伸系数

(1)编写代码, 创建 6 个按钮, 按照 3 行 2 列方式排列.

使用 setSizePolicy 设置按钮的尺寸策略,可选的值如下:

  • QSizePolicy::Ignored:忽略控件的尺寸,不对布局产生影响。
  • QSizePolicy::Minimum:控件的最小尺寸为固定值,布局时不会超过该值。
  • QSizePolicy::Maximum:控件的最大尺寸为固定值,布局时不会小于该值。
  • QSizePolicy::Preferred:控件的理想尺寸为固定值,布局时会尽量接近该值。
  • QSizePolicy::Expanding:控件的尺寸可以根据空间调整,尽可能占据更多空间。
  • QSizePolicy::Shrinking:控件的尺寸可以根据空间调整,尽可能缩小以适应空间。

(2)执行代码

此时的按钮垂直方向都舒展开了,并且调整窗口尺寸,也会按照设定的比例同步变化。

总的来说,使用 QGridLayout 能够代替很多 QHBoxLayout 和 QVBoxLayout 嵌套的场景。毕竟嵌套的代码写起来是比较麻烦的。另外不要忘了,QGridLayout 里面也能嵌套 QHBoxLayout 和 QVBoxLayout,QHBoxLayout 和 QVBoxLayout 里面也能嵌套 QGridLayout。灵活使用上述布局管理器就可以实现出任意的复杂界面。

4、QFormLayout

除了上述的布局管理器之外,Qt 还提供了 QFormLayout,属于是 QGridLayout 的特殊情况 ,专门用于实现两列表单的布局

这种表单布局多用于让用户填写信息的场景,左侧列为提示,右侧列为输入框。


代码示例:使用 QFormLayout 创建表单

(1)编写代码,创建 QFormLayout,以及三个 label 和三个 lineEdit

  • 使用 addRow 方法来添加一行,每行包含两个控件。第一个控件固定是 QLabel / 文本,第二个控件则可以是任意控件。
  • 如果把第⼀个参数填写为 NULL,则什么都不显示。

(2)执行程序


5、Spacer

使用布局管理器的时候,可能需要在控件之间添加一段空白,就可以使用 QSpacerItem 来表示。

核心属性:

上述属性在构造函数设置即可。


代码示例:创建一组左右排列的按钮

(1)在界面上创建一个 QVBoxLayout ,并添加两个按钮


(2)直接运行程序,可以看到两个按钮是紧挨着的


(3)在两个按钮中间添加一个 spacer


(4)运行程序

可以看到两个按钮之间已经存在了间隔了,调整 QSpacerItem 不同的尺寸,即可看到不同的间距:

在 Qt Designer 中,也可以直接给界面上添加 spacer:

相关推荐
Zach_yuan2 小时前
自定义协议:实现网络计算器
linux·服务器·开发语言·网络
云姜.2 小时前
java多态
java·开发语言·c++
CoderCodingNo2 小时前
【GESP】C++五级练习题 luogu-P1865 A % B Problem
开发语言·c++·算法
陳10302 小时前
C++:红黑树
开发语言·c++
一切尽在,你来2 小时前
C++ 零基础教程 - 第 6 讲 常用运算符教程
开发语言·c++
泉-java2 小时前
第56条:为所有导出的API元素编写文档注释 《Effective Java》
java·开发语言
weixin_499771552 小时前
C++中的组合模式
开发语言·c++·算法
初级代码游戏2 小时前
套路化编程 C# winform 自适应缩放布局
开发语言·c#·winform·自动布局·自动缩放
_waylau3 小时前
鸿蒙架构师修炼之道-架构师的职责是什么?
开发语言·华为·harmonyos·鸿蒙