目录
- 前言
- 一、Label
- [二、LCD Number](#二、LCD Number)
- 三、ProgressBar
- [四、Calendar Widget](#四、Calendar Widget)
前言
书接上文【Qt】常用控件----按钮类控件,今天继续介绍【Qt】常用控件----显示类控件(显示类控件都是继承自QWidget,QWidget涉及的属性、函数、使用方法,在下面介绍的控件中,都是能够使用的)
一、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,那么该QCheckBox就会被选中 |
显示不同格式的文本(textFormat)
- 在界面上创建三个QLabel
- 设置label的文本格式
cpp
ui->label->setTextFormat(Qt::PlainText);
ui->label->setText("这是一段纯文本");
ui->label_2->setTextFormat(Qt::RichText);
ui->label_2->setText("<b>这是一段富文本</b>");
ui->label_3->setTextFormat(Qt::MarkdownText);
ui->label_3->setText("## 这是一段markdown文本");
- < b > :表示文本加粗,## :在markdown中解析为二级标题。在纯本文中这两个符号只会倍当成单纯的< b > 和 ##
显示图片(pixmap)
虽然QPushButton也可以通过设置图标的方式设置图片,但是并非是一个好的选择。更多的时候还是希望通过QLabel 来作为一个更单纯的显示图片的方式
- 在界面上创建一个QLabel,并将QLabel铺满整个窗口
cpp
// 让label铺满整个窗口
QRect rect = this->geometry();
ui->label->setGeometry(0, 0, rect.width(), rect.height());
- 将图片导入(qrc)
- QLabel设置图片
cpp
QPixmap pixmap(":/Qt.png");
ui->label->setPixmap(pixmap);
- 从运行结果可以看到,图片并没有占满整个窗口,原因是图片并不是窗口大小(800*600)尺寸
scaledContents
如果想要图片占满label(窗口),可以使用scaledContents来自动填充
cpp
// 让label铺满整个窗口
QRect rect = this->geometry();
ui->label->setGeometry(0, 0, rect.width(), rect.height());
QPixmap pixmap(":/Qt.png");
ui->label->setPixmap(pixmap);
//启动自动拉伸
ui->label->setScaledContents(true);

- 此时如果拖动窗口大小,可以看到图片并不会随着窗口大小的改变而同步变化。原因是:label大小设置是在构造函数中,这个设置是一次性的,一旦程序运行起来,QLabel的尺寸就固定下来了。窗口尺寸发生变化,QLabel并不会随着窗口的尺寸变化而变化

- 为了让QLabel的尺寸随着窗口尺寸的变化而变化,我们这里需要了解事件
- 用户操作会对应一些信号(点击),Qt中,表示用户操作有两类概念:信号和事件
- 用户拖拽修改窗口大小会触发resize事件(resizeEvent) ,resize事件是连续变化的
- 可以让Widget窗口类重写父类(QWidget)的resizeEvent虚函数(在鼠标拖动窗口尺寸过程中,这个函数就会被反复调用执行,每触发一次resizeEvent事件,都会调用一次对应虚函数)
- 这个过程就是依赖C++中的多态来实现的。Qt框架内部管理着QWidget对象表示咱们的窗口。在窗口大小发生改变时,Qt就会自动调用resizeEvent 函数。但是由于实际上这个表示窗口的并非是QWidget,而是QWidget的子类,也就是自己的Widget。此时虽然是通过父类调用函数,但是实际上执行的是子类的函数(也就是我们重写后的resizeEvent )
void Widget::resizeEvent(QResizeEvent *event):这里的event参数非常重要,包含了触发这个resize事件这一时刻的窗口大小尺寸的数值
cpp
void Widget::resizeEvent(QResizeEvent *event)
{
ui->label->setGeometry(0, 0, event->size().width(), event->size().height());
//qDebug() << event->size();
}

文本对齐、自动换行、缩进、边距
- 创建四个label并且在QFrame 中设置frameShape 为Box (设置边框之后看起来会更清晰⼀些)

- 从属性框可以看出继承关系,从上图可以看出,QLabel继承QFrame,QFrame继承自QWidget,所以我们通过QFrame 设置frameShape 为Box

- 给4个label设置不同属性
- label设置本文对齐(居中等)
cpp
ui->label->setText("这是一个文本");
//ui->label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); //垂直水平居中
//ui->label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); //垂直居中,水平靠右
ui->label->setAlignment(Qt::AlignRight | Qt::AlignTop); //水平靠右,垂直靠上
- label_2设置自动换行
cpp
ui->label_2->setText("这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本。")
可以看到默认情况下,长文本并没有换行,也没有显示完整
cpp
ui->label_2->setText("这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本。");
ui->label_2->setWordWrap(true);
}

- label_3设置缩进
cpp
ui->label_3->setText("这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本。");
ui->label_3->setWordWrap(true);
ui->label_3->setIndent(50);
Qt中的缩进是每一行都有缩进
- label_4设置边距
cpp
ui->label_4->setText("这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本,这是一段很长的文本。");
ui->label_4->setWordWrap(true);
ui->label_4->setMargin(50);

QLabel伙伴设置
-
创建两个label和两个radioButton

-
设置伙伴关系
cpp
ui->label->setBuddy(ui->radioButton);
ui->label_2->setBuddy(ui->radioButton_2);
- 运行代码,我们发现点击radioButton可以选中选项,但是点击QLabel并不能选中对应的选项,使用alt + a 或者alt + b快捷键可以实现快捷键选中对应选项的操作
- Qt中,QLabel中写的文本时可以指定快捷键的,此处快捷键的规则功能上要比QPushbutton弱很多。
- 在文本中使用& 跟上一个字符来表示快捷键(比如& A:表示 alt + a)
- 绑定了伙伴关系之后,就可以通过快捷键就可以选中对应的单选按钮/复选按钮
二、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 | 设置比较小的小数点 |
倒计时
- 使用QLCDNumber显示一个初始的数值(10),每隔一秒,数字-1,一直到0
- 对于每隔一秒数字-1的实现,在Qt中封装了对应的定时器(结合了信号槽机制)QTimer类创建出对象,产生一个timeout信号,通过start方法来开启定时器,并且参数中设置触发timeout信号的周期
- 结合connect,把这个timeout信号绑定到需要的槽函数上,就可以执行逻辑,修改LCDNumber中的数字了
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QTimer>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置初始值
ui->lcdNumber->display("10");
//创建QTimer示例
timer = new QTimer(this);
//把 QTimer的timeout信号和自己的槽函数进行连接
connect(timer, &QTimer::timeout, this, &Widget::handle);
//启动定时器,参数是触发timeout的周期,单位是:ms
timer->start(1000);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handle()
{
//拿到LCDNumber中的数字
int value = ui->lcdNumber->intValue();
if(value <= 0)
{
timer->stop();
return;
}
else
{
ui->lcdNumber->display(value - 1);
}
}
- 通过一个循环+sleep的方式实现
- 下面代码无法正常显示,循环会使Widget的构造函数无法执行完毕,当构造函数构造完成后,才是
w.show()显示窗口,但是此时倒计时已经结束
cpp
//main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
//widget.cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置初始值
ui->lcdNumber->display("10");
int value = ui->lcdNumber->intValue();
while(true)
{
//先休眠1s
std::this_thread::sleep_for(std::chrono::seconds(1));
if(value <= 0)
break;
ui->lcdNumber->display(--value);
}
}
- 如果是在Widget构造函数中另起⼀个线程,在新线程中完成循环+sleep是否可以呢?从下面运行结果可以看到是不可以的。Qt中规定,任何对于GUI上内容的操作,必须在主线程中完成
- Widget构造函数以及connect连接的slot函数都是在主线程中调用的。而我们自己创建的线程则不是
综上所述,使用定时器是实现上述功能的最合理方案
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置初始值
ui->lcdNumber->display("10");
int value = ui->lcdNumber->intValue();
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);
}
});
}

三、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 :表示总时间(以毫秒为单位) |
设置进度条按时间增长
- 每隔100ms,进度条+1
- 将ProgressBar属性的minimum设置为0,maximum设置为100,初始时设置value为0

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()
{
//获取进度条value
int value = ui->progressBar->value();
if(value >= 100)
{
timer->stop();
return;
}
ui->progressBar->setValue(value + 1);
}
- 将上述绿色进度条的颜色改为红色,QProgressBar 同样也是QWidget 的子类,因此我们可以使用styleSheet 通过样式来修改进度条的颜色
cpp
QProgressBar::chunk{background-color: #FF0000;}
从下面可以看到,绿色进度条变成了红色了,但是进度条数据24%到左上角了
- 把QProcessBar 的alignment 属性设置为垂直水平居中
四、Calendar Widget
- QCalendarWidget 表示一个"日历"
核心属性:
| 属性 | 说明 |
|---|---|
| selectDate | 当前选中的日期 |
| minimumDate | 最小日期 |
| maximumDate | 最大日期 |
| firstDayOfWeek | 每周的第一天(也就是日历的第一列)是周几 |
| gridVisible | 是否显示表格的边框 |
| selectionMode | 是否允许选择日期 |
| navigationBarVisible | 日历上方标题是否显示 |
| horizontalHeaderFormat | 日历上方标题显示的日期格式 |
| verticalHeaderFormat | 日历第一列显示的内容格式 |
| dateEditEnabled | 是否允许日期被编辑 |
重要信号:
| 信号 | 说明 |
|---|---|
| selectionChanged(const QDate&) | 当选中的日期发生改变时发出 |
| activated(const QDate&) | 当双击一个有效的日期或者按下回车键时发出,形参是一个QDate类型,保存了选中的日期 |
| currentPageChanged(int, int) | 当年份月份改变时发出,形参表示改变后的新年份和月份 |
获取选中的日期
-
在界面上创建一个QCalendarWidget 和一个label

-
给QCalendarWidget 添加slot函数

-
QDate数据不能直接传入到setText中,因为setText需要传入QString类型的数据,所以使用toString将QDate数据转换成QString类型数据即可
cpp
void Widget::on_calendarWidget_selectionChanged()
{
QDate date = ui->calendarWidget->selectedDate();
qDebug() << date;
ui->label->setText(date.toString());
}






