10.QT-显示类控件|LCD Number|ProgressBar|Calendar Widget(C++)
LCD Number
QLCDNumer 是⼀个专⻔⽤来显⽰数字的控件.类似于"⽼式计算器"的效果
| 属性 | 说明 |
|---|---|
| intValue | QLCDNumber 显⽰的数字值(int). |
| value | QLCDNumber 显⽰的数字值(double). 和intValue是联动的. 例如给value设为1.5,intValue的值就是2. 另外,设置value和intValue的⽅法名字为 display ,⽽不是 setValue 或者 setIntValue . |
| 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
![![[Pasted image 20250420134015.png]]](https://i-blog.csdnimg.cn/direct/e3ed4e958479453a9aef9649d96c9073.png)
2)修改widget.h代码,创建⼀个 QTimer 成员,和⼀个 updateTime 函数
使用QLCDNumber显示一个初始的数值,比如10
每隔一秒钟,数字就-1 一直到0,就停止了.
此处关键要点是要实现“每秒钟-1”这个效果
周期性的执行某个逻辑~~“定时器"
C++标准库中,没有提供定时器的实现.Boost里面提供了对应的功能
Qt中也封装了对应的定时器~~(结合了信号槽机制的) QTimer
通过这个类创建出来的对象,就会产生一个timeout这样的信号
可以通过start方法来开启定时器,并且参数中设定触发timeout信号的周期
#include "widget.h"
#include "ui_widget.h"
#include <QTimer>
#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->lcdNumber->display("10");//创建一个QTimer实例QTimer* timer = new QTimer(this);//把QTimer的timeout信号和自己的槽函数进行连接connect(timer, &QTimer::timeout, this, &Widget::handle);// 启动 QTimer, 并且规定每隔 1000ms 触发⼀次 timeout 信号timer->start(1000);
}Widget::~Widget()
{delete ui;
}void Widget::handle()
{qDebug() << "handle";
}
QTimer 表⽰定时器.通过 start ⽅法启动定时器之后,就会每隔⼀定周期,触发⼀次QTimer::timeout 信号.
使⽤ connect 把 QTimer::timeout 信号和 Widget::handle 连接起来,意味着每次触发 QTimer::timeout 都会执⾏ Widget::handle
![![[Pasted image 20250420135546.png]]](https://i-blog.csdnimg.cn/direct/683004034031470b867ea1a9a7f97ecc.png)
每隔一秒钟会执行一个handle
void Widget::handle()
{//先拿到lcdnumber中的数字int value = ui->lcdNumber->intValue();if (value <= 0){//数字减到0了,停止计时器timer->stop();return;}ui->lcdNumber->display(value - 1);
}
- 通过 intValue 获取到 QLCDNumber 内部的数值.
- 如果value的值归0了,就停⽌ QTimer .接下来 QTimer 也就不会触发timeout信号了.
![![[Pasted image 20250420135930.png]]](https://i-blog.csdnimg.cn/direct/ea2d3467948c4eed981210af9f05943b.png)
1)上述代码如果直接在Widget构造函数中,通过⼀个循环+sleep的⽅式是否可以呢?
Sleep=> Windows 的 api,需要包含"Windows.h"头文件才能使用的
C++ 11 标准库中引l入了 sleep 操作. sleep_for
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);int value = ui->lcdNumber->intValue();while (true) {//先休眠一秒std::this_thread::sleep_for(std::chrono::seconds(1));if (value <= 0) {break;}ui->lcdNumber->display(--value);}
}
Widget的构造函数
需要把Widget构造完毕,然后才能执行后续的显示操作~~
![![[Pasted image 20250420142528.png]]](https://i-blog.csdnimg.cn/direct/bdc54cc0370e43c481bfb11ead82a571.png)
循环会使Widget的构造函数⽆法执⾏完毕,此时界⾯是不能正确构造和显⽰的.
2)上述代码如果是在Widget构造函数中,另起⼀个线程,在新线程中完成循环+sleep是否可以呢?
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);std::thread t([this](){int value = this->ui->lcdNumber->intValue();while (true) {std::this_thread::sleep_for(std::chrono::seconds(1));if (value <= 0) {break;}ui->lcdNumber->display(--value);}});
}
![![[Pasted image 20250420143719.png]]](https://i-blog.csdnimg.cn/direct/3cbd696fd483406ebe01bcf3208d8c8d.png)
这个代码同样是不⾏的.
Qt中规定,任何对于GUI上内容的操作,必须在主线程中完成.像Widget构造函数,以及connect连接的slot函数,都是在主线程中调⽤的.⽽我们⾃⼰创建的线程则不是.
当我们⾃⼰的线程中尝试对界⾯元素进⾏修改时,Qt程序往往会直接崩溃.
这样的约定主要是因为GUI中的状态往往是牵⼀发动全⾝的,修改⼀个地⽅,就需要同步的对其他内容进⾏调整.
⽐如调整了某个元素的尺⼨,就可能影响到内部的⽂字位置,或者其他元素的位置.这⾥⼀连串的修改,都是需要按照⼀定的顺序来完成的.
由于多线程执⾏的顺序⽆法保障,因此Qt从根本上禁⽌了其他线程修改GUI状态,避免后续的⼀系列问题.
综上所述,使⽤定时器,是实现上述功能的最合理⽅案
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
![![[Pasted image 20250420150204.png]]](https://i-blog.csdnimg.cn/direct/66a75877b5684011864dffc6f428de57.png)
其中最⼩值设为0,最⼤值设为100.当前值设为0.
创建一个进度条,
让这个进度条的进度跟随时间增长
(可以假设,每隔100ms,让进度条数值+1)
设置初始值
![![[Pasted image 20250420151719.png]]](https://i-blog.csdnimg.cn/direct/ca082db5eb09473ab422fa50bd2d8740.png)
2)修改widget.h,创建 QTimer 和 updateProgressBar 函数.
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTimer>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void handle();private:Ui::Widget *ui;QTimer* timer;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &Widget::handle);//启动操作要在槽函数之后timer->start(100);
}Widget::~Widget()
{delete ui;
}void Widget::handle()
{//获取当前数值int value = ui->progressBar->value();if (value >= 100) {//进度条满了,就可以停止计时器timer->stop();return;}ui->progressBar->setValue(value + 1);
}
![![[Pasted image 20250420152215.png]]](https://i-blog.csdnimg.cn/direct/1e9bc29f08474e09aa5aeb925a29c03b.png)
在实际开发中,进度条的取值,往往是根据当前任务的实际进度来进⾏设置的.
⽐如需要读取⼀个很⼤的⽂件,就可以获取⽂件的总的⼤⼩,和当前读取完毕的⼤⼩,来设置进度条的⽐例.
由于上⾯我们介绍了Qt禁⽌在其他线程修改界⾯,因此进度条的更新往往也是需要搭配定时器来完成的.
通过定时器周期触发信号,主线程调⽤对应的slot函数.再在slot函数中对当前的任务进度进⾏计算,并更新进度条的界⾯效果.
虽然在widget.h中用到了QTimer,但是却没在.h文件中包含<QTimer>头文件
为啥这个代码编译不会出错?
为啥此处的QTimer就不会提示“找不到定义”之类的
上述问题其实是通过Qt内部提供的一个特殊技巧来实现的~~
在Qt中,有一个专门的头文件这个头文件中包含了Qt中所有类的“前置声明
这个头文件,一般不会直接接触到,但是包含其他的Qt的头文件,都会间接的包含到这个头文件
Widget类的前面已经提供了QTimer类的声名的话
此时就可以在Widget中声明QTimer的指针/引I用类型的成员~~ 后续如果要真正使用QTimer(包括创建实例,使用里面的成员…)
仍然需要包含QTimer的头文件(包含了QTimer的详细的类的定义)
Qt为啥要使用上述的技巧,上述技巧能解决什么问题?有啥提升呢?主要解决的是编译速度的问题
C/C++的代码,编译速度在其他语言横向对比中,是非常慢的
对于一个大规模的项目,编译速度可能非常慢!!!俺在华为的时候,我们哪怕只是给代码中添加一个 printf,编译消耗的时间,就是1个小时左右
C++编译速度慢,和 #include 头文件,有直接关系的,由于include关系错综复杂
因此,尽可能减少 include 头文件的个数,就可以有效的减少编译时间
Qt中就使用class前置声明的方式,来尽量减少头文件的包含
通过前置声明的方式,Qt中的头文件,每个头文件包含的其他头文件数量都能得到一定的降低
所以在C++20 标准开始,就引l入了"模块”module 来替代 #include
代码⽰例:创建⼀个红⾊的进度条
QProgressBar 同样也是 QWidget 的⼦类,因此我们可以使⽤ styleSheet 通过样式来修改进度条的颜⾊.
![![[Pasted image 20250420160038.png]]](https://i-blog.csdnimg.cn/direct/a072b04e0f344a8e8d9dc53920b1014a.png)
QProgressBar::chunk {background-color: red;}
QProgressBar::chunk:选择器,设置的样式要针对哪个控件生效
chunk就是原绿色的部分
点击ok,变为红色
![![[Pasted image 20250420160357.png]]](https://i-blog.csdnimg.cn/direct/7489fa07085441cf99ef5ad455a05dfb.png)
同时把 QProcessBar 的 alignment 属性设置为垂直⽔平居中.
![![[Pasted image 20250420160617.png]]](https://i-blog.csdnimg.cn/direct/202bc47842aa4d42bfc8d7ee5b5e19f6.png)
此处如果不设置 alignment ,进度条中的数字会跑到左上⻆.这个怀疑是Qt本⾝的bug,暂时只能先使⽤alignment来⼿动调整下.
![![[Pasted image 20250420160712.png]]](https://i-blog.csdnimg.cn/direct/4368e15e5b2b46799f6d723eb03adf74.png)
Calendar Widget
QCalendarWidget 表⽰⼀个"⽇历",形如
![![[Pasted image 20250420161602.png]]](https://i-blog.csdnimg.cn/direct/46e92f94f7fa4d9aac57f33b1d56a5aa.png)
| 属性 | 说明 |
|---|---|
| selectDate | 当前选中的⽇期 |
| minimumDate | 最⼩⽇期 |
| maximumDate | 最⼤⽇期 |
| firstDayOfWeek | 每周的第⼀天(也就是⽇历的第⼀列)是周⼏. |
| gridVisible | 是否显⽰表格的边框 |
| selectionMode | 是否允许选择⽇期 |
| navigationBarVisible | ⽇历上⽅标题是否显⽰ |
| horizontalHeaderFormat | ⽇历上⽅标题显⽰的⽇期格式 |
| verticalHeaderFormat | ⽇历第⼀列显⽰的内容格式 |
| dateEditEnabled | 是否允许⽇期被编辑 |
| 信号 | 说明 |
|---|---|
| selectionChanged(const QDate&) | 当选中的⽇期发⽣改变时发出 |
| activated(const QDate&) | 当双击⼀个有效的⽇期或者按下回⻋键时发出,形参是⼀个QDate类型,保存了选中的⽇期 |
| currentPageChanged(int, int) | 当年份⽉份改变时发出,形参表⽰改变后的新年份和⽉份 |
代码示例:获取选中的⽇期
1)在界⾯上创建⼀个 QCalendarWidget 和⼀个label
objectName 为 calendarWidget , label
![![[Pasted image 20250420162916.png]]](https://i-blog.csdnimg.cn/direct/1a7c496e5ea242bebe5349064a320c70.png)
2)给 QCalendarWidget 添加slot函数
![![[Pasted image 20250420162845.png]]](https://i-blog.csdnimg.cn/direct/af9e67cf94694775bf5168fb823c7b87.png)
void Widget::on_calendarWidget_selectionChanged()
{QDate date = ui->calendarWidget->selectedDate();qDebug() << date;
}
![![[Pasted image 20250420163114.png]]](https://i-blog.csdnimg.cn/direct/4a8c6ec64d6b44648987c810a4cfc309.png)
void Widget::on_calendarWidget_selectionChanged()
{QDate date = ui->calendarWidget->selectedDate();qDebug() << date;ui->label->setText(date.toString());
}
![![[Pasted image 20250420163222.png]]](https://i-blog.csdnimg.cn/direct/67337fcd17ea4be1a9a44f62caf8b459.png)
