回顾常用控件文章Qt常用控件- 1、概述、QWidget核心属性-CSDN博客一、按钮类控件1、Push Button使用QPushButton表示一个按钮这也是当前我们最熟悉的一个控件了。QPushButton继承自QABstractButton这个类是一个抽象类是其他按钮的父类在Qt Designer中也能够看到这里的继承关系QAbstractButton中和QPushButton相关性较大的属性1、QAbstractButton作为QWidget的子类当然也继承了QWidget的属性。上面介绍的QWIdget里的各种属性用法对于QAbstractButton同样适用因表格仅列出QAbstractButton独有属性2、Qt的api设计风格是非常清晰的此处列出的属性都是可以获取和设置的。例如使用text()获取按钮文本使用setText()设置文本事实上QPushButton的核心功能都是QAbstractButton提供的自身提供的属性都比较简单。其中default和audoDefault影响的是按下enter时点击那个按钮的行为flat把按钮设置为扁平的样式带有图标的按钮1创建 resource.qrc 文件并导入图片具体操作步骤参见 上一篇的QWidget 中的windowIcon部分2在界面上创建一个按钮3修改 widget.cpp给按钮设置图标4执行程序观察结果2、Radio Button带有快捷键的按钮1在界面中拖五个按钮五个按钮的 objectName 分别为 pushButton_target、pushButton_up、pushButton_down、pushButton_left、pushButton_right。2创建 resource.qrc并导入 5 个图片3修改 widget.cpp设置图标资源和快捷键使用 setShortcut 给按钮设置快捷键参数是⼀个 QKeySequence 对象表示一个按键序列支持持组合键Ctrl C 这种。QKeySequence 的构造函数参数可以直接使用 Ctrl C 这样的按键名字符串表示也可以使用预定义好的常量形如 Qt::CTRL Qt::Key_C表示。4修改 widget.cpp设置四个方向键的 slot 函数5运行程序此时点击按钮或者使用 wasd 均可让 “柯南” 移动按钮的重复触发在上述案例中按住快捷键是可以进行重复触发的但是鼠标点击则不能。1修改 widget.cpp在构造函数中开启重复触发此时按住鼠标时即可让 “柯南” 连续移动。3、Check BoxQCheckBox 表示复选按钮可以允许选中多个。和 QCheckBox 最相关的属性也是 checkable 和 checked都是继承自 QAbstractButton。至于 QCheckBox 独有的属性 tristate 用来实现 “三态复选框”这个东西比较冷门这里暂时不讲述。获取复选按钮的取值1在界面上创建三个复选按钮和一个普通按钮objectName 分别为 checkBox_study、checkBox_game、checkBox_work 以及 pushButton2给 pushButton 添加 slot 函数3运行程序可以看到点击确认按钮时就会在控制台中输出选中的内容4、Tool ButtonQToolButton 的大部分功能和 QPushButton 是一致的但 QToolButton 主要应用在工具栏、菜单等场景。二、显示类控件1、LabelQLabel 可以用来显示文本和图片。核心属性如下显示不同格式的文本1在界面上创建三个 QLabel尺存放大一些objectName 分别为 label、label_2、label_32修改 widget.cpp设置三个 label 的属性3运行程序显示图片虽然 QPushButton 也可以通过设置图标的方式设置图片但是并非是一个好的选择更多的时候还是希望通过 QLabel 来作为一个更单纯的显示图片的方式。1在界面上创建一个 QLabel、objectName 为 label2创建 resource.qrc 文件并把图片导入到 qrc 中3修改 widget.cpp给 QLabel 设置图片这个图片本身的尺寸是 1280 * 150超出了窗口的大小800 * 800。执行程序观察结果4修改代码设置 scaledContents 属性再次运行程观察效果可以看到图片已经被拉伸可以把窗口填满了此时如果拖动窗口大小可以看到图片并不会随着窗口大小的改变而同步变化。为了解决这个问题可以在 Widget 中重写resizeEvent函数执行程序此时改变窗口大小图片也会随之变化注意这里的 resizeEvent 函数我们没有手动调用但是能在窗口大小变化时被自动调用这个过程就是依赖 C 中的多态来实现的。Qt 框架内部管理着 QWidget 对象表示我们的窗口在窗口大小发生改变时Qt 就会自动调用 resizeEvent 函数。但是由于实际上这个表示窗口的并非是 QWidget而是 QWidget 的子类也就是我们自己写的 Widget。此时虽然是通过父类调用函数但是实际上执行的是子类的函数也就是我们重写后的 resizeEvent。此处属于是多态机制的⼀种经典用法。通过上述过程就可以把自定义的代码插入到框架内部执行相当于 “注册回调函数”。文本对齐、自动换行、缩进、边距1创建四个 labelobjectName 分别是 label 到 label_4并且在 QFrame 中设置 frameShape 为 Box设置边框之后看起来会更清晰一些QFrame 是 QLabel 的父类其中 frameShape 属性用来设置边框性质QFrame::Box矩形边框QFrame::Panel带有可点击区域的面板边框QFrame::WinPanelWindows 风格的边框QFrame::HLine水平线边框QFrame::VLine垂直线边框QFrame::StyledPanel带有可点击区域的面板边框但样式取决于窗口主题。2编写 widget.cpp给这四个 label 设置属性3运行程序设置伙伴1创建两个 label 和 两个 radioButtonobjectName 分别为 label、label_2、radioButton、radioButton_2此处把 label 中的文本设置为 “快捷键 A” 这样的形式。其中 后面跟着的字符就是快捷键可以通过 alt A 的方式来触发该快捷键。但是注意这里的快捷键和 QPushButton 的不同需要搭配 alt 和 单个字母的方式才能触发。绑定了伙伴关系之后通过快捷键就可以选中对应的单选按钮 / 复选按钮。2编写 widget.cpp设置 buddy 属性这里也可以使用Qt Designer直接设置3运行程序可以看到按下快捷键 alt a 或者 alt b即可选中对应的选项2、LCD NumberQLCDNumer是一个专门用来显示数字的控件类似于 “老式计算器” 的效果。核心属性倒计时1在界面上创建⼀个 QLCDNumber初始值设为 10objectName 为 lcdNumber2修改 widget.h 代码创建一个 QTimer 成员和一个 handle 函数3修改 widget.cpp在构造函数中初始化 QTimerQTimer 表示定时器通过 start 方法启动定时器之后就会每隔一定周期触发一次 QTimer::timeout 信号使用 connect 把 QTimer::timeout 信号和 Widget::updateTime 连接起来意味着每次触发 QTimer::timeout 都会执行 Widget::updateTime4修改 widget.cpp实现 handle通过 intValue 获取到 QLCDNumber 内部的数值。如果 value 的值归 0 了就停止 QTimer接下来 QTimer 也就不会触发 timeout 信号了。5执行程序可以看到每隔一秒钟显示的数字就减少 16针对上述代码存在两个问题A. 上述代码如果直接在 Widget 构造函数中通过一个循环 sleep 的方式是否可以呢显然上面这段代码是不行的循环会使 Widget 的构造函数无法执行完毕此时界面是不能正确构造和显示的。B. 上述代码如果是在 Widget 构造函数中另起一个线程在新线程中完成循环 sleep 是否可以呢这个代码同样是不行的。Qt 中规定任何对于 GUI 上内容的操作必须在主线程中完成。像 Widget 构造函数以及 connect 连接的 slot 函数都是在主线程中调用的。而我们自己创建的线程则不是当我们自己的线程中尝试对界面元素进行修改时Qt 程序往往会直接崩溃。这样的约定主要是因为 GUI 中的状态往往是牵一发动全身的修改一个地方就需要同步的对其他内容进行调整。比如调整了某个元素的尺存就可能影响到内部的文字位置或者其他元素的位置。这里一连串的修改都是需要按照一定的顺序来完成的。由于多线程执行的顺序无法保障因此 Qt 从根本上禁止了其他线程修改 GUI 状态避免后续的一系列问题。对于 Qt 的槽函数来说默认情况下槽函数都是由主线程调用到在槽函数中修改界面是没有任何问题的。综上所述使用定时器是实现上述功能的最合理方案。后续如果也有类似的需要 “周期性修改界面状态” 的需求也需要优先考虑使用定时器。3、ProgressBar使用QProgressBar表示一个进度条。核心属性设置进度条按时间增长1在界面上创建进度条objectName 为 progressBar其中最小值设为 0最大值设为 100当前值设为 02修改 widget.h创建 QTimer 和 handle 函数虽然在 widget.h 中用到了 QTimer但是却没在 widget.h 文件中包含 QTimer 头文件为什么这个代码编译没有出错呢上述问题其实是通过 Qt 内部提供的一个特殊技巧来实现的。在 Qt 中有一个专门的头文件#include QWidget这个头文件中包含了 Qt 中所有类的 “前置声明”class QWidgetclass QPushButtonclass QTimer。这个头文件我们一般不会直接接触到但是包含其它的 Qt 的头文件都会间接的包含到这个头文件。如果 Widget 类的前面以及提供了 QTimer 类的声明的话此时就可以在 Widget 中声明 QTimer 的指针 / 引用类型的成员。后续如果要真正使用 QTimer 的头文件包括创建实例使用里面的成员仍然要包含 QTimer 的头文件包含了 QTimer 的详细的类的定义。Qt 为什么要使用上述技巧呢上述技巧能解决什么问题有什么提升呢主要解决的是编译速度的问题。C/C 代码编译速度在其他语言横向对比中是非常慢的。C 编译速度慢和 #include 头文件有直接关系。由于 include 关系错综复杂所以尽可能减少 include 头文件的个数就可以有效地减少编译时间。Qt 中就使用 class 前置声明的方式来尽量减少头文件的包含。通过前置声明的方式Qt 中每个头文件包含的其它头文件数量都能得到一定的降低。3修改 widget.cpp初始化 QTimer此处设置 100ms 触发一次 timeout 信号也就是⼀秒钟触发 10 次4修改 widget.cpp实现 handle5) 运行程序可以看到进度条中的进度在快速增长在实际开发中进度条的取值往往是根据当前任务的实际进度来进行设置的。比如需要读取一个很大的文件就可以获取文件的总的大小和当前读取完毕的大小来设置进度条的比例。由于前面我们介绍了 Qt 禁止在其他线程修改界面因此进度条的更新往往也是需要搭配定时器来完成的。通过定时器周期触发信号主线程调用对应的 slot 函数再在 slot 函数中对当前的任务进度进行计算并更新进度条的界面效果。创建一个红色的进度条上述的进度条是用绿⾊表示的但是考虑到有些人可能不喜欢绿⾊因此我们改成一个红色的进度条。QProgressBar 同样也是 QWidget 的子类因此我们可以使用 styleSheet 通过样式来修改进度条的颜色。1在界面上创建一个进度条2在 Qt Designer 右侧的属性编辑器中找到 QWidget 的 styleSheet 属性编辑内容其中的 chunk 是选中进度条中的每个 “块”使用 QProgressBar::text 则可以选中文本。同时把 QProcessBar 的 alignment 属性设置为垂直水平居中此处如果不设置 alignment进度条中的数字会跑到左上角。3执行程序可以看到如下效果就得到了一个红色的进度条通过上述方式也可以修改文字的颜色字体大小等样式。4、Calendar WidgetQCalendarWidget表示⼀个 “日历”形如核心属性重要信号获取选中的日期1在界面上创建一个 QCalendarWidget 和一个 labelobjectName 为 calendarWidgetlabel2给 QCalendarWidget 添加 slot 函数3执行程序可以看到当选择不同的日期时label 中的内容就会随之改变