『 QT 』显示类控件 (一)
个人博客地址
| 个人博客: 花开富贵 |
文章目录
- 个人博客地址
- 1 什么是显示类控件
- 2 QLabel
- 2.1 QLabel 的文本格式
- 2.2 QLabel 设置图片
- 2.3 设置内容自动填充
- 2.4 resizeEvent 事件
- 2.5 设置 Label 的边框
- 2.6 QLabel 设置文本对齐
- 2.7 QLabel 的自动换行, 边距设置与缩进
- 2.8 设置 QLabel 伙伴
- 3 QLCDNumber 控件
- 3.1 倒计时小工具
- 3.2 能否使用 sleep 完成这个功能
1 什么是显示类控件
显示类控件通常为显示内容的一类控件, 这些控件通常可以用于显示文本, 图片, 以及其他内容从而丰富或者完善项目的整体显示效果;
2 QLabel
QLabel通常用于显示文本与图片, 其常用属性通常为如下:
| 属性 | 说明 |
|---|---|
text | QLabel中的文本 |
textFormat | 文本格式: - Qt::PlainText (纯文本)- Qt::RichText(富文本 - 支持html标签)- Qt::MarkdownText(Markdown 格式文本)- Qt::AutoText(根据文本内容自动决定文本格式) |
pixmap | QLabel内部包含的图片, 可通过QPixmap对象进行设置 |
scaledContents | 两种状态 true OR false:true - 内部内容自动拉伸填充QLabelfalse - 不会自动拉伸 |
alignment | 对齐方式: 水平方向对齐方式 垂直方向对齐方式 |
wordWrap | 两种状态 true OR false:true - 内部文本自动换行false - 不会自动换行 |
indent | 设置文本缩进(水平与垂直都能生效, 且非首行缩进) |
margin | 内部内容与边框之间的边距, 不同于indent, margin的上下左右四个方向都同时有效, indent最多只是两个方向有效(具体哪两个方向取决于alignment); |
openExternalLinks | 是否允许打开一个外部链接 当 QLabel中包含url时涉及到 |
buddy | 给QLabel关联一个==“伙伴控件”==, 点击QLabel将能激活对应的伙伴;如果伙伴是一个 QCheckBox, 那么该QCheckBox会被选中, 类似于一个组; |
同时, QLabel控件的继承顺序为如下:
QObject -> QWidget -> QFrame -> QLabel

2.1 QLabel 的文本格式
文本格式主要分为三种(排除AutoText的自动选择):
-
Qt::PlainText(纯文本) -
Qt::RichText(富文本 - 支持html标签) -
Qt::MarkdownText(Markdown格式文本)
创建三个QLabel控件并设置不同的文本格式, 并运行程序:

-
设置不同文本格式并输出相同格式的文本
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);ui->label->setText("# <b>这是一段普通文本</b>");ui->label->setTextFormat(Qt::PlainText);ui->label_2->setText("# <b>这是一段富文本</b>");ui->label_2->setTextFormat(Qt::RichText);ui->label_3->setText("# <b>这是一段Markdown文本</b>");ui->label_3->setTextFormat(Qt::MarkdownText); }如代码所示, 三种不同的文本形式使用同一种格式的文本;
普通文本不会进行任何渲染, 因此在
PlainText的Label中将会原模原样进行显示;富文本可以解析并渲染
html标签, 但无法识别#符号, 因此在RichText中将会显示#符号并加粗字体;Markdown文本既兼容html标签, 同时也有自己的一套语法体系, 其中#将会被解析为一级标题;
运行程序的结果为:

2.2 QLabel 设置图片
QLabel可通过传入一个QPixmax对象来设置图片, 其中设置图片的方法为:
setPixmax();
首先创建一个QLabel对象;

并通过QRC来管理一张图片资源;


创建一个QPixmap对象来管理这张图片, 并通过setPixmap将图片设置进QLabel对象中;
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPixmap pixmap(":/R.jpg"); // QPixmap对象管理图片资源ui->label->setPixmap(pixmap); // 将QPixmap图片设置进Label中
}

2.3 设置内容自动填充
在2.2中, 使用QPixmap设置了Label中的图片, 然而图片所显示并不全;
通常情况下, 可以使用setScaledContents()来设置内容自动填充;
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPixmap pixmap(":/R.jpg");ui->label->setPixmap(pixmap);ui->label->setScaledContents(true); // 设置自动填充
}
运行结果为:

除此之外也可以设置将Label的大小位置与Widget相同使得图片铺满整个Widget;
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->label->setGeometry(0, 0, this->geometry().width(),this->geometry().height()); // 设置Label的大小位置与Widget相同QPixmap pixmap(":/R.jpg");ui->label->setPixmap(pixmap);ui->label->setScaledContents(true); // 设置自动填充
}

2.4 resizeEvent 事件
在2.3中将Label设置为与Widget大小相同, 在scaledContents为true的加持下, 图片将会占满整个Label, 而Label将会与Widget大小相同, 变相的为图片与Widget大小相同;
但这种情况下并不是持续的, 当此时对Widget大小变化后, Label大小并不会跟着变化;

这种大小的变化将会触发一种事件, 事件名称为resizeEvent;
在拖动Widget大小的情况下, 这个事件是针对Widget的;
在Qt中, 存在两种概念:
- 事件
- 信号
通常情况下, 大多的默认信号是通过事件来设定的, 如当我们对程序进行点击时, 可能会出现一个点击事件, 放开时则会出现松开鼠标点击事件, 这些事件通常也与硬件有关;
总结来说, 事件偏向于底层, 而信号偏向于上层, 针对于槽而言, 信号是槽的触发者, 而信号通常又是事件的触发者;
同时, 事件与信号还有一点不同的是, 通常情况下, 事件是持续触发的, 而大多的信号是根据状态变化而触发;
在Qt中, 其封装了一些事件, 并通过继承的方式使得这些事件被继承为纯虚函数, 这表示用户可以手动重写纯虚函数来进行对事件有一个自定义的行为;
// widget.h
void resizeEvent(QResizeEvent* event)override;// widget.cpp
void Widget::resizeEvent(QResizeEvent *event)
{ui->label->setGeometry(0, 0, this->geometry().width(),this->geometry().height()); // 设置Label的大小位置与Widget相同qDebug()<<ui->label->geometry();
}
在这段代码中对resizeEvent事件函数进行重写, 当resizeEvent事件被触发时将会调用这个函数;
而函数内部设置了Label控件的大小为Widget大小相同, 并通过qDebug()打印对应geometry参数;
运行结果为:

在这个事件函数中可以看到存在一个* event参数, 实际上不同的事件函数中都会有不同的事件参数, 可以看到实际上这个参数的类型是非固定的, 由于当前函数为resizeEvent, 因此对应的event事件参数为QresizeEvent, 同时也可以通过该参数来获取当前窗口的size;
qDebug() << event->size();

从打印信息可以看出, 实际上其打印了对应的QSize信息;
我们可以理解这个事件函数为一个回调函数, 当触发对应的事件时, 这个事件的回调函数将会被自动调用;
2.5 设置 Label 的边框
通常情况下, 可以为显示类控件设置边框;
在QtDesigner右侧的属性中可以看到, 在QFrame中可以设置其的frameShape属性;

而在右侧的继承关系可以看到, 这个属性实际上是QFrame的属性;
在上文中我们提到了QLabel的继承关系:
QObject -> QWidget -> QFrame -> QLabel
子类继承父类时子类可以编辑或者获取其父类的public/protected成员属性;
因此在Label中设置frameShape属性实际是QFrame的属性;
2.6 QLabel 设置文本对齐
QLabel中, 通常使用setAlignment()来为文本内容设置对齐;
在这个函数中, 通常需要传入一个flag值;
然而, 在该函数中的flag值采用的类似一种位图的方式;
enum AlignmentFlag {AlignLeft = 0x0001,AlignLeading = AlignLeft,AlignRight = 0x0002,AlignTrailing = AlignRight,AlignHCenter = 0x0004,AlignJustify = 0x0008,AlignAbsolute = 0x0010,AlignHorizontal_Mask = AlignLeft | AlignRight | AlignHCenter | AlignJustify | AlignAbsolute,AlignTop = 0x0020,AlignBottom = 0x0040,AlignVCenter = 0x0080,AlignBaseline = 0x0100,AlignVertical_Mask = AlignTop | AlignBottom | AlignVCenter | AlignBaseline,AlignCenter = AlignVCenter | AlignHCenter};
通过按位或|的方式将多个选项组合在一起, 使得一个函数只能获取一个参数, 但通过按位的方式获取每个位的位置以此来判断其设置的属性;
假设文本需要居中, 那么居中通常有两种, 一种为垂直居中AlignVCenter, 另一种为水平居中AlignHCenter;
因此若要设置水平与垂直居中时可以传入:Qt::AlignVCenter | Qt::AlignHCenter;
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->label->setText("该段文本设置水平与垂直居中");ui->label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
}
除此之外在上文的枚举值中可以看到, Qt::AlignCenter实际上就是Qt::AlignHCenter | Qt::AlignVCenter, 因此这里的参数可以直接传入Qt::AlignHCenter;
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->label->setText("该段文本设置水平与垂直居中");// ui->label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);ui->label->setAlignment(Qt::AlignCenter);
}
运行结果为:

其余设置可以参考对应的Qt文档进行查看;
2.7 QLabel 的自动换行, 边距设置与缩进
-
自动换行
默认情况下,
QLabel内的文本不会自动换行;当文本内容超出
QLabel的大小时, 将会超出显示, 多余的文本内容将会被隐藏;ui->label_2->setText("这是一段超长的文本这是一段超长的文本这是一段超长的文本这是一段超长的文本这是一段超长的文本这是一段超长的文本这是一段超长的文本");运行结果为:

从结果可以看出, 多余(超出
QLabel范围)的文本内容被隐藏, 无法查看;在
QLabel中, 可以修改wordWrap参数从而对文本的自动换行状态进行设置, 该参数有两个状态,true Or false, 为true时表示自动换行,false不自动换行;ui->label_2->setText("这是一段超长的文本这是一段超长的文本这是一段超长的文本这是一段超长的文本这是一段超长的文本这是一段超长的文本这是一段超长的文本");ui->label_2->setWordWrap(true);运行结果为:

-
缩进
在
QLabel中, 参数indent用于设置QLabel的缩进;默认情况下直接调用
setIndent(int)时, 其缩进将默认由左往右;ui->label_3->setText("这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本");ui->label_3->setWordWrap(true);ui->label_3->setIndent(20);运行结果为:

除此之外, 其可以配合文本对齐方式来设置对应的缩进;
ui->label_3->setText("这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本");ui->label_3->setAlignment(Qt::AlignTop);ui->label_3->setWordWrap(true);ui->label_3->setIndent(20);在这里设置了对齐方式为顶部对齐
Qt::AlignTop;运行结果为:

同样的多个对齐方式配合
setIndent可以有更多的对齐效果;ui->label_3->setText("这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本");ui->label_3->setAlignment(Qt::AlignTop|Qt::AlignRight|Qt::AlignLeft);ui->label_3->setWordWrap(true);ui->label_3->setIndent(20);
同时, 从上文可以看出, 实际上对于
indent属性而言, 并没有首行缩进; -
设置边距
在
Qt中,margin属性可以用于设置边距;边距与缩进不同, 缩进用于一个或多个方向, 而边距
margin将应用于所有方向;ui->label_4->setText("这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本");ui->label_4->setMargin(20);ui->label_4->setWordWrap(true);运行结果为:

从结果可以看出, 当设置对应的
margin属性后, 四个方向(上下左右)都因边距的原因缩小了可视范围;
2.8 设置 QLabel 伙伴
在Qt中, 支持为QLabel设置一个伙伴控件, 当选择伙伴控件时, 该控件也会被选中;
假设一个QWidget中存在两个RadioButton与两个QLabel;

其次是, 当QLabel控件设置伙伴关系后, 其可以设置快捷键, 快捷键通常为'&' + Key;
通过setBuddy()设置对应的伙伴关系;
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->label->setBuddy(ui->radioButton);ui->label_2->setBuddy(ui->radioButton_2);
}
运行结果:

可以观察到, 当程序运行时, &被解析为绑定的符号, 因此不进行显示;
通过ALT + 快捷键的方式可选择对应的Label, 又因为其与对应的RadioButton为伙伴关系, 因此快捷键将默认的点击对应的RadioButton;

需要注意的是, QLabel伙伴关系的快捷键功能规则不如QPushButton;
3 QLCDNumber 控件
该控件是一个专门用来显示数字的控件, 其显示效果类似于计算器的效果;

其常用属性通常为如下:
| 属性 | 说明 |
|---|---|
intValue | 控件中所显示的数值(int) |
value | 控件中所显示的数值double与 intValue是联动的假设给 value设为1.5, 那么intValue值为2.同时设置数值的方法名为 display, 不为setXXXX |
digitCount | 显示几位数字 |
mode | 数字显示形式: - QLCDNumber::Dec: 十进制, 显示常规的十进制数字- QLCDNumber::Hex: 十六进制, 显示十六进制数字- QLCDNumber::Bin: 二进制, 显示二进制数字- QLCDNumber::Oct: 八进制, 显示八进制数字其中只有十进制才显示小数点后的内容 |
segmentStyle | 设置显示风格 - QLCDNumber::Flat: 平面的显示风格, 数字呈现平坦的表面- QLCDNumber::Outline: 轮廓显示风格, 数字具有清晰的轮廓和阴影效果- QLCDNumber::Filled: 填充显示风格, 数字被填充的颜色与背景分开 |
smallDecimalPoint | 设置比较小的小数点 |
3.1 倒计时小工具
可结合以往的控件实现一个倒计时的小工具;
这个工具的控件如下:
-
一个
QLCDNumber用于显示倒计时;
-
一个
QLineEdit用于获取用户预定的时间(单位
s); -
一个
QLabel用于提示
QLineEdit用于提示用户QLineEdit用于输入时间; -
两个
QPushButton- 获取
LineEdit中的内容并赋给QLCDNumber - 执行倒计时操作
- 获取
上述的控件进行组合, 基本的框架为如下:

-
获取时间并赋值
获取时间通常为
Confirm键, 当按下这个键时将会去执行它的槽函数, 对应的槽函数将会获取内部的QString;而在
QString中存在一个函数可以将字符串转为数字, 即QString::toInt(bool *, int), 其中第一个参数传入一个bool的指针(输出型参数), 第二个参数传入一个整数表示转化的数据表示形式, 如10即为十进制;void Widget::on_confirm_clicked() {QString num = ui->nums->text();bool flag;int tmp = num.toInt(&flag, 10);if(flag && tmp>0)ui->lcdNumber->display(tmp);elseqDebug()<< "This is not a positive integer."; }调用
LCDNumber的display来对转化后的数值进行赋值;判断值是否为正数且大于
0, 是则进行赋值, 否则打印错误; -
倒计时功能
在
Qt中存在一个类, 即QTimer;这个类是一个计时器功能的类, 其支持一个
QTimer对象通过其成员方法start(int)传入一个值作为时间(单位ms);每间隔该单位时间将会发出一个
QTimer::timeout()信号;既然是信号, 因此该信号同样需要有一个槽来处理信号;
这里的槽可以是对
QLCDNumber中的值进行-1, 并判断当前值是否为0, 为0则调用成员方法stop()停止计时并返回;void Widget::update_time() {int value = ui->lcdNumber->intValue();if(value<=0){_time->stop();return;}ui->lcdNumber->display(value-1); }同时将该信号与槽使用
connect()进行连接;同时, 由于该类是计时器的核心, 每次计时都需要有该对象, 因此将其设定为一个成员对象, 同时将其挂在对象树上, 让
Widget管理其生命周期;Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget), _time(new QTimer(this)) {ui->setupUi(this);connect(_time, &QTimer::timeout, this, &Widget::update_time); } -
开始计时
当一切就绪以后, 我们需要通过
Start按钮来执行计时, 因此对应的该PushButton的槽用于执行QTimer的start()函数, 并传参1000作为间隔时间;同时, 为了避免在计时器启动后对应的
Start按钮被不断点击导致的延迟, 选择在按下Start按钮后禁用其他控件, 即将其他的控件的Enable状态设置为false;void Widget::on_start_clicked() {_time->start(1000);ui->confirm->setEnabled(false);ui->start->setEnabled(false);ui->nums->setEnabled(false); }对应的, 在时间为
0时恢复这些控件的Enable状态为true;void Widget::update_time() {int value = ui->lcdNumber->intValue();if(value<=0){_time->stop();ui->confirm->setEnabled(true);ui->start->setEnabled(true);ui->nums->setEnabled(true);return;}ui->lcdNumber->display(value-1); }
完整的代码为:
#include "widget.h"
#include "ui_widget.h"
#include <QString>
#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget), _time(new QTimer(this))
{ui->setupUi(this);connect(_time, &QTimer::timeout, this, &Widget::update_time);
}Widget::~Widget()
{delete ui;
}void Widget::on_confirm_clicked()
{QString num = ui->nums->text();bool flag;int tmp = num.toInt(&flag, 10);if(flag && tmp>0)ui->lcdNumber->display(tmp);elseqDebug()<< "This is not a positive integer.";
}void Widget::on_start_clicked()
{_time->start(1000);ui->confirm->setEnabled(false);ui->start->setEnabled(false);ui->nums->setEnabled(false);
}void Widget::update_time()
{int value = ui->lcdNumber->intValue();if(value<=0){_time->stop();ui->confirm->setEnabled(true);ui->start->setEnabled(true);ui->nums->setEnabled(true);return;}ui->lcdNumber->display(value-1);
}
运行结果为:

3.2 能否使用 sleep 完成这个功能
这个问题的答案是否定的;
现在将代码进行修改, 为如下:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget), _time(new QTimer(this))
{ui->setupUi(this);
}Widget::~Widget()
{delete ui;
}void Widget::on_confirm_clicked()
{QString num = ui->nums->text();bool flag;int tmp = num.toInt(&flag, 10);if(flag && tmp>0)ui->lcdNumber->display(tmp);elseqDebug()<< "This is not a positive integer.";
}void Widget::on_start_clicked()
{int value = ui->lcdNumber->intValue();while(value){std::this_thread::sleep_for(std::chrono::seconds(1));ui->lcdNumber->display(--value);}
}
在这段代码中, 我们采用this_thread::sleep_for()的方式, 每隔一秒将对应的数值进行-1;
运行程序, 此处我们设置值为3, 结果为:

在这段代码的运行结果中可以看到, 这个进程似乎被卡住了, 直到最后倒计时结束进程才恢复;
本质上是因为sleep将会阻塞住主线程, 而主线程通常用来处理一切的信号与槽, 因此使用sleep时将会阻塞整个进程包括GUI线程而;
同时若是使用子线程来执行倒计时的逻辑, 子线程首主线程管理, 当主线程生命周期结束, 子线程同样会停止, 因此最有效的方法是采用非阻塞的QTimer;
