目录
1 -> 概览
在构建现代软件界面时,开发者不仅追求功能强大,也日益注重视觉表现和用户体验。有时,为了契合特定主题(如工业控制、音频播放器、科学实验模拟或经典游戏),我们需要一种能够瞬间唤起用户时代感或专业感的界面元素。Qt 框架中的 QLCDNumber
控件,正是为此而生的经典组件。
QLCDNumber
是一个用于显示数字的小部件。其核心设计理念是模拟老式的七段数码管(Seven-segment display)或点阵式液晶显示(LCD)效果。这种显示方式在计算器、电子钟表、仪器仪表等领域极为常见。它不像普通的标签(QLabel
)那样直接渲染字体,而是通过点亮特定的“段”来组合成数字或少量字母,从而呈现出一种独特的、带有浓厚技术复古风的视觉效果。
2 -> 核心属性
属性 | 说明 |
intValue | QLCDNumber 显示的数字值(int) |
value | QLCDNumber 显示的数字值(double) 和 intValue 是联动的 例如给 value 设为 1.5,intValue 的值就是 2 另外,设置 value 和 intValue 的方法名字为 display,而不是 setValue 或者 setIntValue |
digitCount | 显示几位数字 |
mode | 数字显示形式
只有十进制的时候才能显示小数点后的内容 |
segmentStyle | 设置显示风格
|
smallDecimalPoint | 设置比较小的小数点 |
3 -> 核心功能与特性
1. 数字显示
QLCDNumber
最主要的功能是清晰地显示整型或浮点型数字。它可以自动处理数字的对齐方式(通常为右对齐),并支持显示一个额外的小数点。
2. 数码位数设置
开发者可以预先设定控件能够显示的数码位数。当数字的整数部分超出设定位数时,控件会显示溢出(通常为一连串的“E”或类似提示);当数字位数不足时,则会在左侧以零或空格进行填充,这非常符合传统电子设备的显示习惯。
3. 多种显示模式
控件提供了几种经典的显示模式,允许开发者根据整体UI风格进行选择:
十六进制(Hex):可以显示数字0-9和字母A-F,适用于需要显示内存地址等信息的场景。
十进制(Dec):最常用的模式,用于显示常规数字。
八进制(Oct):显示八进制数字。
二进制(Bin):以二进制形式显示数字,每一位都清晰可见。
4. 视觉风格定制
段样式(Segment Style):
QLCDNumber
允许选择数码管的视觉风格,例如“轮廓(Outlined)”、“填充(Filled)”和“扁平(Flat)”,以适应不同深浅的背景色,确保显示清晰易读。颜色:虽然传统LCD是单色的(通常是深色背景上的亮色数字),但Qt允许你设置任意颜色作为数字(“段”)的颜色和背景色,从而创造出诸如暗黑模式下的绿色数字、复古的琥珀色或现代感的蓝色效果。
5. 溢出与特殊值处理
当显示的数字超出控件范围或为非数值(NaN)时,控件会有一个明确的视觉指示(如显示“E”),这为调试和用户提示提供了便利。
4 -> 经典应用场景
计时器/秒表:显示经过的时间或剩余时间,是
QLCDNumber
最经典的应用。媒体播放器:显示当前播放进度和总时长,完美复刻了传统音响设备的UI。
计算器应用:作为计算结果的显示区域,是其最原始、最自然的用途。
工业控制与仪表盘模拟:显示温度、转速、压力等实时数据,营造出专业的工业HMI(人机交互界面)氛围。
游戏界面:在赛车游戏中显示速度,在模拟游戏中显示分数或资源数量,能有效增强游戏的特定时代感或科技感。
系统监控工具:显示CPU占用率、内存使用量等数值信息。
5 -> 优点和局限性
优点:
风格独特:能快速为应用界面确立特定的视觉基调,这是使用标准字体无法轻易实现的。
清晰可读:在高亮或大尺寸显示下,LCD风格的数字非常醒目,易于远距离或快速识别。
轻量级:作为Qt内置控件,它非常高效,无需依赖外部资源。
局限性:
显示内容有限:基本上只能用于显示数字和极有限的字母(A-F, ‘E’等),无法显示普通文本或复杂符号。
风格固定:其外观是高度风格化的,如果与应用的整体现代扁平设计风格不搭,则会显得格格不入。它更适合作为“点睛之笔”而非主要信息展示控件。
6 -> 代码示例:倒计时
1. 在界面上创建一个 QLCDNumber,初始值设为 10
objectName 设为默认
2. 修改 widget.h 代码,创建一个 QTimer 成员,和一个 handle 函数
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void handle();
private:
Ui::Widget *ui;
QTimer* timer;
};
#endif // WIDGET_H
3. 修改 widget.cpp,在构造函数中初始化 QTimer
- QTimer 表示定时器。通过 start 方法启动定时器之后,就会每隔一定周期,触发一次 QTimer::timeout 信号。
- 使用 connect 把 QTimer::timeout 信号和 Widget::handle 连接起来,意味着每次触发 QTimer::timeout 都会执行 Widget::handle。
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QTimer>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置初始值
ui->lcdNumber->display(20);
// 创建一个 QTimer 实例
timer = new QTimer(this);
// 把 QTimer 的 timeout 信号和咱们自己的槽函数进行连接
connect(timer, &QTimer::timeout, this, &Widget::handle);
// 启动定时器, 参数是触发 timeout 的周期. 单位是 ms
timer->start(1000);
}
Widget::~Widget()
{
delete ui;
}
4. 修改 widget.cpp,实现 handle
- 通过 intValue 获取到 QLCDNumber 内部的数值。
- 如果 value 的值归 0 了,就停止 QTimer。接下来 QTimer 也就不会触发 timeout 信号了。
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QTimer>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置初始值
ui->lcdNumber->display(20);
// 创建一个 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()
{
// 测试
// qDebug() << "handle";
int value = ui->lcdNumber->intValue();
if (value <= 0)
{
timer->stop();
return;
}
ui->lcdNumber->display(value - 1);
}
5. 执行程序,可以看到每隔一秒钟,显示的数字就减少 1
针对上述代码,存在两个问题:
1. 上述代码如果直接在 Widget 构造函数中,通过一个循环 + sleep 的方式是否可以呢?
int value = ui->lcdNumber->intValue();
while (true)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
if (value <= 0)
{
break;
}
ui->lcdNumber->display(value - 1);
}
显然,这个代码是不行的。循环会使 Widget 的构造函数无法执行完毕,此时的界面是不能正确构造和显示的。
2. 上述代码如果是在 Widget 构造函数中,另起一个线程,在新线程中完成 循环 + sleep 是否可以呢?
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;
}
this->ui->lcdNumber->display(value - 1);
}
});
这个代码同样是不行的。Qt 中规定,任何对于 GUI 上内容的操作,必须在 主线程 中完成。像 Widget 构造函数,以及 connect 连接的 slot 函数,都是在主线程中调用的。而自己创建的线程则不是。
当我们自己的线程中尝试对界面元素进行修改时,Qt 程序往往会直接崩溃。
这样的约定主要是因为 GUI 中的状态往往是牵一发动全身的,修改一个地方,就需要同步的对其他内容进行调整。
比如调整了某个元素的尺寸,就可能影响到内部的文字位置,或者其他元素的位置。这里一连串的修改,都是需要按照一定的顺序来完成的。
由于多线程执行的顺序无法保障,因此,Qt 从根本上禁止了其他线程修改 GUI 状态,避免后续的一系列问题。
综上所述,使用定时器,是实现上述功能的最合理方案。
7 -> 总结
QLCDNumber
是Qt工具箱中一个极具特色的控件。它远不止是一个简单的数字显示器,更是一个强大的 “氛围营造者”。当你需要为用户界面添加一丝复古科技感、专业仪器感或清晰的数字焦点时,QLCDNumber
是一个简单而高效的选择。它省去了开发者用图像自己绘制数字的麻烦,通过简单的属性设置就能达到出色的视觉效果,是 Qt 为丰富开发者界面表达能力所提供的一件精致而实用的工具。
感谢各位大佬支持!!!
互三啦!!!