Qt常用控件之显示类控件

目录

QLabel

文本格式

设置图片

文本对齐/自动换行/边距/缩进

设置伙伴

QLCDNumber

倒计时功能

QProgressBar

进度条

QCalendarWidget


QLabel

QLabel 同样是 QWidget 的子类,所以前面博客中 QWidget 中的属性方法也是适用的

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

核心属性如下:

属性 说明
text QLabel中的文本
textFormat 文本的格式 Qt::PlainText 纯文本 (使用最多) Qt::RichText 富文本(支持 html 标签) Ot::MarkdownText markdown 格式 Qt::AutoText 根据文本内容自动决定文本格式
pixmap QLabel 内部包含的图片
scaledContents 设为 true 表示内容自动拉伸填充 QLabel 设为 false 则不会自动拉伸
alignment 对齐方式,可以设置水平和垂直方向如何对齐
wordWrap 设为 true 内部的文本会自动换行 设为 false 则内部文本不会自动换行 (QLabel不提供滚动条,QTestEdit会提供)
indent 设置文本缩进,水平和垂直方向都生效
margin 内部文本和边框之间的边距 不同于于 indent,但是是上下左右四个方向都同时有效 而 indent 最多只是两个方向有效(具体哪两个方向有效取决于 alignment)
openExternalLinks 是否允许打开一个外部的链接 (当 OLabel文本内容包含 url 的时候涉及到)
buddy 给 OLabel关联一个 "伙伴",这样点击 OLabel时就能激活对应的伙伴 例如伙伴如果是一个 QCheckBox,那么该 QCheckBox 就会被选中

文本格式

下面演示 QLabel 的 textFormat 属性,先创建三个 Label 标签:

再将这三个文本分别设置为纯文本、富文本、markdown文本:

可以看到,给这三个不同 textFormat 属性的文本的 Test,分别加入 html 标签、markdown格式,运行程序,结果如下:

所以得出结论,纯文本不会识别 html 标签或是 markdown格式,只会将他们当做文本打印出来


设置图片

想要设置图片,当然要使用 qrc机制,这里就不重复步骤了:

接着图形化界面的方式,拖动一个 Label,并编写构造函数:

此时运行程序:

我们发现图片并没有按照我们代码所编写的那样,让整个 Label 都是这个图片,是因为我们在 qrc 中传入的图片,可能原本的尺寸就是比较小的,像上述所插入的 dog.png 原本尺寸就是 200 * 200 的,而 Widget 窗口是 800* 600 的,所以不能够显示整个屏幕

而我们想要让整个 Widget 窗口都显示图片,就需要将 scaledContents 属性设置为 true,自动拉伸图片:

此时运行程序,dog.png 就能够填满整个屏幕了:

在上面代码中是在构造函数里,进行的这样的尺寸设置这个设置相当于是一次性的

一旦程序运行起来之后,QLabel 的尺寸就固定下来了,如果窗口发生改变,QLabel 是不会变化的

如果想要 QLabel 随着窗口大小实时发送改变,就需要了解 事件

在前面的学习中,我们了解了用户的操作,会对应一些信号

在 Qt 中,表示用户的操作,有两类概念,一个是信号,另一个是事件

当用户拖拽修改窗口大小的时候,就会触发 resize 事件(resizeEvent)

像 resize 这样的事件,是连续变化的,把窗口尺寸从 A 拖到 B 这个过程中,会触发出一系列的 resizeEvent

此时就可以借助 resizeEvent 来完成上述的功能

可以让 Widget 窗囗类,重写父类(QWidget)的 resizeEvent 虚函数,在鼠标拖动窗口尺寸的过程中,这个函数就会被反复调用执行,每次触发一个 resizeEvent 事件,都会调用一次对应的虚函数

由于此处进行了函数重写,调用父类的虚函数就会实际调用到子类的对应的函数(多态)

下面先在 widget.h 中,声明需要重写的 resizeEvent 函数:

这里的形参 event 是非常有用的,这里就包含了触发这个 resize 事件这一时刻,窗口的尺寸的数值

再在 widget.cpp 中编写 resizeEvent 函数,观察拖动窗口时发送的情况:

运行程序:

此时拖动窗口,发现 Qt 会自动调用 resizeEvent 函数,所以我们这里重写虚函数,是指定了一个回调函数的逻辑:

在实际编程中,指定回调函数其实有很多种写法:

  • 设置函数指针
  • 设置仿函数(函数对象)
  • 设置 lambda
  • 通过重写父类虚函数(框架中拿着父类的指针调用这个函数,如果你创建了子类重写了这个函数此时在多态机制下,实际执行的就是子类的函数了)
  • Qt 的信号槽

上面只是单纯的打印出尺寸,下面完善代码,使得能够通过实时窗口大小的结果,来实时修改 QLabel 的尺寸:

运行程序,随意拖动窗口的大小,图片也会实时改变,满足了我们的需求:


文本对齐/自动换行/边距/缩进

文本对齐

我们创建一个 Label,并在右侧找到 QFrame,设置 Label 的边框,这样能够更加清楚的观察文本对齐的方式

我们将 第一个属性 frameShape 改为 box,此时就有边框了:

下面给第一个 Label 设置属性,将其设置为 水平居中(AlignHCenter)和垂直居中(AlignVCenter)

之所以中间使用 按位或 连接,因为这里的 AlignHCenter 和 AlignVCenter 都是枚举类型:

类似于位图一样的效果,想要某个效果,就将某个比特位置为1即可

此时运行程序:

如果水平和垂直方向不居中了,想让它靠右上角对齐,将 AlignHCenter 改为 AlignRight,AlignVCenter 改为 AlignTop 即可:


自动换行

给第二个 Label 设置一段很长的文本:

运行程序观察:

发现这段文本无法完全显示出来,所以给它添加上自动换行的属性:


缩进

缩进使用的属性是 indent:

此处设置的缩进即使文本换行了,后续的行也会产生缩进不仅仅是首行缩进:


边距

边距是会影响上下左右四个方向的,用到了 margin 属性:

可以看到设置完边距50,这段长文本有一部分被覆盖掉了,显示不太清晰,超出部分也不再显示了,这就是设置边距的效果


设置伙伴

可以将 QLabel 和 单选框/复选框 绑定一个伙伴关系,此时就能够通过 QLabel 触发 单选框/复选框 的选择操作

下面先创建两个 RadioButton,分别在这两个 RadioButton 后面跟上一个 Label:

此时在构造函数中,将 Label 与对应的 RadioButton 建立伙伴关系:

运行程序,不需要鼠标点击,使用 alt + a / alt + b 就能选中对应的 单选按钮/复选按钮:

Qt 中,QLabel 中写的文本,是可以指定快捷键的,此处快捷键的规则功能上要比 QPushButton 弱很多

是在文本中使用 & 跟上一个字符来表示快捷键

比如 &A => 通过键盘上的 alt +a来触发这个快捷键

&B =>通过键盘上的 alt +b 来触发

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


QLCDNumber

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

核心属性:

属性 说明
intValue QLCDNumber 显示的数字值(int)
value QLCDNumber 显示的数字值(double) 和 intValue 是联动的 例如给 value 设为 1.5,intValue 的值就是 2 另外,设置 value和 intValue 的方法名字为 display,而不是 setValue 或者 setIntValue
digitCount 显示几位数字
mode 数字显示形式 QLCDNumber::Dec:十进制模式,显示常规的十进制数字 QLCDNumber::Hex:十六进制模式,以十六进制格式显示数字 QLCDNumber::Bin:二进制模式,以二进制格式显示数字 QLCDNumber::0ct:八进制模式,以八进制格式显示数字 只有十进制的时候才能显示小数点后的内容
segmentStyle 设置显示风格 QLCDNumber::Flat:平面的显示风格,数字呈现在一个平坦的表面上QLCDNumber::0utline:轮廓显示风格,数字具有清晰的轮和阴影效果 5QLCDNumber::Filled:填充显示风格,数字被填充颜色并与背景区分开
smallDecimalPoint 设置比较小的小数点

倒计时功能

使用 QLCDNumber 显示一个初始的数值,比如 10

每隔一秒钟,数字就 -1,一直到 0,就停止了

先使用 图形化界面 的方式拖动一个 LCDNumber:

此处关键要点是要实现"每秒钟 -1"这个效果

周期性的执行某个逻辑,也就是"定时器"

C++标准库中,没有提供定时器的实现,Boost 里面提供了对应的功能

Qt 中也封装了对应的定时器(结合了信号槽机制的)

Qt 中是使用 QTimer类,通过这个类创建出来的对象,就会产生一个timeout这样的信号

可以通过 start 方法来开启定时器,并且参数中设定触发 timeout 信号的周期

再结合 connect,把这个 timeout 信号绑定到需要的槽函数中,就可以执行逻辑,修改 LCDNumber 中的数字了

先在 widget.h 中添加成员 timer 和 槽函数 handle:

再在 widget.cpp 中编写代码:

此时运行程序,完成了倒计时功能:


上面实现的 倒计时功能 是利用的 QTimer类, 那么如果不使用这个类,直接写一个循环,每个一秒减一,能够实现倒计时的功能吗,看下面的示例

我们直接在 ui界面 中,在右侧修改初始值:

之前学习 C++ 时使用 sleep函数 是 Windows 的 API,需要在 VS 中包含 Windows.h 的头文件才能使用,而 Qt 是无法直接使用 Windows.h 这个头文件的

所以下面采用 thread库 中的 sleep_for 来完成休眠一秒的操作

具体代码如下:

此时运行程序,发现10秒内并没有显示窗口,而是10秒后,才会显示下面的界面:

这是因为我们是在构造函数中编写的上述代码,而 main.cc 在执行的时候,构造函数执行完,才会执行 show函数,所以运行程序后,无法看到窗口,直到在构造函数中10秒执行完,才显示0秒的界面


下面我们就可以思考一下,另一个情况能够实现吗

假设在构造函数中,另外创建一个线程,在新的线程中,执行上述循环+更新操作,此时并不影响主线程的构造函数的执行

所以将代码改为:

此时运行程序,发现控制台上日志显示,程序终止,出现异常:

而之所以出现异常,原因是:

Qt 里,界面有一个专门的线程去负责维护更新的(主线程,也就是 main 函数所在的线程)

对于 GUI 来说,内部包含了很多的隐藏状态,Qt 为了保证修改界面的过程中,线程安全是不会受到影响的,所以 Qt 禁止了其他线程直接修改界面

而我们上面创建一个线程,在最后一行的 ui->lcdNumber->display(value); 就是在修改界面,所以会出现异常

因此 Qt 为了确保线程安全,直接要求所有的对界面的修改操作,必须在主线程中完成

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


QProgressBar

使用 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:表示总时间 (以毫秒为单位)

进度条

创建一个进度条,让这个进度条的进度跟随时间增长

(可以假设每隔 100ms,让进度条数值+1)

先拖拽一个 进度条:

通过右边的属性栏,将最小值/最大值设置为0/100,将当前值设置为0

下面就需要上面设计倒计时功能时,所用到的 QTimer类

同样在 widget.h 中新增成员 timer,新增槽函数 handle:

之所以需要定义一个成员 timer,而不是在构造函数中定义局部变量,是因为下面的槽函数 handle 还需要使用 timer

widget.cpp:

运行程序,进度条以 100ms 的速度每次 +1


这里有一个细节问题,我们所使用的 QTimer类,是需要包含头文件的,我们之前在写 C++代码 时一般是包含在 .h文件 中的,而在 Qt 中,既可以在 .h 中包含,也可以在 .cpp 中包含

之所以可以不在 .h 中包含,却能够在 .h 中声明 QTimer类,是因为:

在 Qt 中,有一个专门的头文件,这个头文件中包含了 Qt 中所有类的"前置声明",这个头文件一般不会直接接触到,但是包含其他的 Qt 的头文件,例如 #include<QWidget> 都会间接的包含到这个头文件

Widget 类的前面已经提供了 QTimer 类的声名的话,此时就可以在 Widget.h 中声明 QTimer 的指针/引用类型的成员

后续如果要真正使用 QTimer(包括创建实例,使用里面的成员),仍然需要包含 QTimer 的头文件(包含了 QTimer 的详细的类的定义)

Qt 使用上述的技巧,主要解决的是编译速度的问题

C++编译速度慢,和 #include 头文件有直接关系的

由于 include 关系错综复杂,因此尽可能减少 include 头文件的个数,就可以有效的减少编译时间

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

但是咱们实际开发中,还是要该包含就包含,与其通过特殊技巧来缩短编译时间,不如说引入更好的硬件资源,来更高效的编译

例如:一些互联网大厂,都有专门的"编译集群"(分布式编译)

正是因为 #include 编译速度慢的原因,所以在 C++ 20 标准开始,就引入了"模块" module来替代 #include


因为 QProgressBar 也是 QWidget 的子类,所以 QWidget 的 styleSheet 属性也就可以使用,所以我们可以将 进度条 的颜色改为红色:

先拖动一个进度条:

右键,点击改变样式表:

输入以下内容:

此时进度条颜色就变为红色了,但是我们发现 24% 原来是在右边,现在却在左上角了:

这种情况有可能是 Qt 的 bug,并不知道原因是什么,但是也没关系,我们也可以设置样式:

此时就变为 垂直/水平方向 都 居中对齐了:

进度条具体的进度如何设置,一般都是根据实际的任务类型来灵活设置的

例如:要读取一个很大的文件,就可以先获取到文件的总大小,每读取一部分数据(可以计算出读了多少数据的),更新一次进度条的数值

设置进度条的过程中,往往是要搭配 定时器 的


QCalendarWidget

QCalendarwidget 表示一个"日历",形如

核心属性:

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

重要信号

信号 说明
selectionChanged(const QDate&) 当选中的日期发生改变时发出
activated(const QDate&) 当双击⼀个有效的日期或者按下回车键时发出,形参是⼀个QDate类型,保存了选中的日期
currentPageChanged(int, int) 当年份月份改变时发出,形参表示改变后的新年份和月份

首先使用 图形化界面 的方式,拖动一个 QCalendarWidget 和一个 Label,用于显示我们选中的日期:

接着右键日历,选择 转到槽 的 selectionChanged:

下面编写 槽函数 :

运行程序,可以看到控制台会打印我们所点击的日期,上面的 Label 中也会显示所点击的日期:


Qt常用控件之显示类控件到此结束啦

相关推荐
【建模先锋】12 分钟前
Python轴承故障诊断 (21)基于VMD-CNN-BiTCN的创新诊断模型
开发语言·python·cnn·轴承故障诊断
xnuscd21 分钟前
Python websocket
开发语言·python·websocket
萧鼎23 分钟前
使用 Python 的 pdfplumber 库高效解析 PDF 文件
开发语言·python·pdf
hvinsion23 分钟前
Python PDF转JPG图片小工具
开发语言·python·pdf
Eiceblue27 分钟前
.NET平台用C#添加动作到PDF文档
开发语言·vscode·pdf·c#·.net
山河不见老36 分钟前
【tiler】一个数据可视化和地图处理切片的 Python 库
开发语言·python·信息可视化
秀儿y1 小时前
vue3-setup基本使用(非响应式数据)
开发语言·前端·javascript·vue.js
狐小粟同学1 小时前
JavaEE---计算机是如何工作的?
java·服务器·开发语言
余衫马1 小时前
基于 JNI + Rust 实现一种高性能 Excel 导出方案(上篇)
开发语言·rust·excel
小哈里2 小时前
【后端开发】Go语言编程实践,Goroutines和Channels,基于共享变量的并发,反射与底层编程
开发语言·后端·golang·编程·并发