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
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
每隔一秒钟会执行一个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信号了.
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构造完毕,然后才能执行后续的显示操作~~
循环会使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);}});
}
这个代码同样是不⾏的.
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
其中最⼩值设为0,最⼤值设为100.当前值设为0.
创建一个进度条,
让这个进度条的进度跟随时间增长
(可以假设,每隔100ms,让进度条数值+1)
设置初始值
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);
}
在实际开发中,进度条的取值,往往是根据当前任务的实际进度来进⾏设置的.
⽐如需要读取⼀个很⼤的⽂件,就可以获取⽂件的总的⼤⼩,和当前读取完毕的⼤⼩,来设置进度条的⽐例.
由于上⾯我们介绍了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 通过样式来修改进度条的颜⾊.
QProgressBar::chunk {background-color: red;}
QProgressBar::chunk:选择器,设置的样式要针对哪个控件生效
chunk就是原绿色的部分
点击ok,变为红色
同时把 QProcessBar 的 alignment 属性设置为垂直⽔平居中.
此处如果不设置 alignment ,进度条中的数字会跑到左上⻆.这个怀疑是Qt本⾝的bug,暂时只能先使⽤alignment来⼿动调整下.
Calendar Widget
QCalendarWidget 表⽰⼀个"⽇历",形如
属性 | 说明 |
---|---|
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
2)给 QCalendarWidget 添加slot函数
void Widget::on_calendarWidget_selectionChanged()
{QDate date = ui->calendarWidget->selectedDate();qDebug() << date;
}
void Widget::on_calendarWidget_selectionChanged()
{QDate date = ui->calendarWidget->selectedDate();qDebug() << date;ui->label->setText(date.toString());
}