显示类控件

目录

1.Label

代码示例: 显示不同格式的文本

代码示例: 显示图片

代码示例: 文本对齐, 自动换行, 缩进, 边距

代码示例: 设置伙伴

2.LCD Number

代码示例: 倒计时

3.ProgressBar

代码示例: 设置进度条按时间增长

代码示例: 创建一个红色的进度条

4.Calendar Widget

代码示例: 获取选中的日期


1.Label

QLabel 可以用来显示文本图片.

核心属性如下:

属性说明
textQLabel 中的文本
textFormat

文本的格式.

• Qt::PlainText 纯文本

• Qt::RichText 富文本(支持 html 标签) 

• Qt::MarkdownText markdown 格式

• Qt::AutoText 根据文本内容自动决定文本格式.

pixmapQLabel 内部包含的图片.
scaledContents

设为 true 表示内容自动拉伸填充 QLabel

设为 false 则不会自动拉伸

alignment

对齐方式.

可以设置水平和垂直方向如何对齐.

wordWrap

设为 true 内部的文本会自动换行.

设为 false 则内部文本不会自动换行.

indent设置文本缩进. 水平和垂直方向都生效.
margin

内部文本和边框之间的边距.

不同于于 indent, 但是是上下左右四个方向都同时有效.

而 indent 最多只是两个方向有效(具体哪两个方向有效取决于 alignment )

openExternalLinks

是否允许打开⼀个外部的链接.

(当 QLabel 文本内容包含 url 的时候涉及到)

buddy

给 QLabel 关联⼀个 "伙伴" , 这样点击 QLabel 时就能激活对应的伙伴.

例如伙伴如果是⼀个 QCheckBox, 那么该 QCheckBox 就会被选中.


代码示例: 显示不同格式的文本

🌴1) 在界面上创建三个 QLabel

尺寸放大一些. objectName 分别为 label, label_2, label_3

🌴2) 修改 widget.cpp, 设置三个 label 的属性

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 把第一个 label 设置成显示纯文本
    ui->label->setTextFormat(Qt::PlainText);
    ui->label->setText("这是一段纯文本");

    // 把第二个 label 设置成显示富文本
    ui->label_2->setTextFormat(Qt::RichText);
    ui->label_2->setText("<b> 这是一段富文本 </b>");

    // 把第三个 label 设置成显示 markdown
    ui->label_3->setTextFormat(Qt::MarkdownText);
    ui->label_3->setText("## 这是 markdown 文本");
}

🌴3) 运行程序, 观察效果

  • 在富文本中, <b> 标签表示文本加粗。而将 <b> 标签加到纯文本中,只是被当成了单纯的文本,没有进行任何的渲染操作。
  • 在markdown中 ## 表示二级标题。而在纯文本中,## 只是被单纯的当成了 “文本”。 

代码示例: 显示图片

        虽然 QPushButton 也可以通过设置图标的方式设置图片, 但是并非是⼀个好的选择. 更多的时候还是希望通过 QLabel 来作为一个更单纯的显示图片的方式.

🌵1) 在界面上创建一个 QLabel, objectName 为 label

 

🌵2) 创建 resource.qrc 文件, 并把图片导入到 qrc 中.

🌵3) 修改 widget.cpp, 给 QLabel 设置图片

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 先把 QLbel 设置成和窗口一样大,并且把这个 QLabel 左上角设置到窗口的左上角这里
    // 让整个 QLabel 铺满整个窗口
    QRect windowRect = this->geometry();
    ui->label->setGeometry(0, 0, windowRect.width(), windowRect.height());

    QPixmap pixmap(":/meng.png");
    ui->label->setPixmap(pixmap);
}

执行程序, 观察效果:

这个图片本身的尺寸是 200* 200, 并没有把 QLabel 填充满.

🌵4) 修改代码, 设置 scaledContents 属性

    // 启动自动拉伸,此时图片就能够填充满整个窗口了
    ui->label->setScaledContents(true);

再次运行, 观察效果, 可以看到图片已经被拉伸, 可以把窗口填满了.

🌵5) 此时, 如果拖动窗口大小, 可以看到图片并不会随着窗口大小的改变而同步变化.

为了解决这个问题, 可以在 Widget 中重写 resizeEvent 函数.

// 重写 resizeEvent,这个函数会在窗口大小发生改变时被自动调用
// 此处的形参 event 是非常有用的,这里就包含了触发这个 resize 事件这一时刻,窗口的尺寸的数值。
void Widget::resizeEvent(QResizeEvent *event)
{
    // 可以直接通过 this->width() 和 this->height() 设置 label 新的尺⼨, 也可以通过event 参数拿到新的尺⼨.
    // ui->label->setGeometry(0, 0, this->width(), this->height());
    ui->label->setGeometry(0, 0, event->size().width(), event->size().height());

    qDebug() << event->size();
}

执行程序, 此时改变窗口大小, 图片也会随之变化.

与此同时, 在控制台里也能够看到尺寸变化的过程.

        Qt 中,表示用户的操作,有两类概念,一个是信号,另一个是事件。当用户拖拽修改窗口大小的时候,就会触发 resize 事件(resizeEvent)。像 resize 这样的事件,是连续变化的,把窗口尺寸从 A 拖到 B 这个过程中,会触发一系列的 resizeEvent。此时就可以借助 resizeEvent 来完成上述的功能。

        可以让 Widget 窗口类,重写父类(QWidget)的 resizeEvent 虚函数。 在鼠标拖动窗口尺寸的过程中,这个函数就会被反复调用执行,每次触发一个 resizeEvent 事件都会调用一次对应的虚函数。由于此处进行了函数重写,调用父类的虚函数就会实际调用到子类的对应的函数(多态)。

        在实际编程中,指定回调函数其实有很多种写法:

  1. 设置函数指针
  2. 设置仿函数(函数对象)
  3. 设置 lambda 
  4. 通过重写父类虚函数(框架中拿着父类的指针调用这个函数,如果你创键了子类重写了这个函数,此时在多态机制下,实际执行的就是子类的函数了)
  5. Qt 的信号槽

注意:

  1. 此处的 resizeEvent 函数我们没有手动调用, 但是能在窗口大小变化时被自动调用.
  2. 这个过程就是依赖 C++ 中的多态来实现的. Qt 框架内部管理着 QWidget 对象表示咱们的窗口. 在窗口大小发生改变时, Qt 就会自动调用 resizeEvent 函数.
  3. 但是由于实际上这个表示窗口的并非是 QWidget, 而是 QWidget 的子类, 也就是咱们自己写的 Widget. 此时虽然是通过父类调用函数, 但是实际上执行的是子类的函数(也就是我们重写后的 resizeEvent ).
  4. 此处属于是 多态 机制的一种经典用法. 通过上述过程, 就可以把自定义的代码, 插入到框架内部执行. 相当于 "注册回调函数" .

代码示例: 文本对齐, 自动换行, 缩进, 边距

🍒1) 创建四个 label, objectName 分别是 label ~ label_4

并且在 QFrame 中设置 frameShape Box (设置边框之后看起来会更清晰一些)

 

QFrame 是 QLabel 的父类. 其中 frameShape 属性用来设置边框性质.

  • QFrame::Box :矩形边框
  • QFrame::Panel :带有可点击区域的面板边框
  • QFrame::WinPanel :Windows风格的边框
  • QFrame::HLine :水平线边框
  • QFrame::VLine :垂直线边框
  • QFrame::StyledPanel :带有可点击区域的面板边框,但样式取决于窗口主题

🍒 2) 编写 widget.cpp, 给这四个 label 设置属性

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 在构造函数中,给这几个 label 设置不同的属性

    // 设置文字居中对齐
    ui->label->setText("雄关漫道真如铁,而今迈步从头越。");
    ui->label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);

    // 设置自动换行
    ui->label_2->setText("见说道、天涯芳草无归路。怨春不语。算只有殷勤,画檐蛛网,尽日惹风絮。长门事,准拟佳期又误。蛾眉曾有人妒。千金纵买相如赋,脉脉此情谁诉。君莫舞。君不见、玉环飞燕皆尘土。闲愁最苦。休去倚危栏,斜阳正在,烟柳断肠处。");
    ui->label_2->setAlignment(Qt::AlignTop | Qt::AlignLeft);
    ui->label_2->setWordWrap(true);

    //设置首行缩进
    ui->label_3->setText("予怀怆然,感慨今昔,因自度此曲。千岩老人以为有《黍离》之悲也。淮左名都,竹西佳处,解鞍少驻初程。过春风十里,尽荠麦青青。自胡马窥江去后,废池乔木,犹厌言兵。渐黄昏,清角吹寒,都在空城。");
    ui->label_3->setIndent(50);
    ui->label_3->setAlignment(Qt::AlignTop | Qt::AlignLeft);

    // 设置边距
    ui->label_4->setText("千古江山,英雄无觅,孙仲谋处。舞榭歌台,风流总被,雨打风吹去。斜阳草树,寻常巷陌,人道寄奴曾住。想当年,金戈铁马,气吞万里如虎。元嘉草草,封狼居胥,赢得仓皇北顾。四十三年,望中犹记,烽火扬州路。");
    ui->label_4->setMargin(30);
    ui->label_4->setAlignment(Qt::AlignTop | Qt::AlignLeft);
}

🍒 3) 运行程序, 可以看到如下效果

  • 第一个 label 垂直水平居中
  • 第二个 label 设置了 wordWrap, 能够自动换行
  • 第三个 label 设置了 Indent, 左侧和上方和边框有间距. 右侧则没有.
  • 第四个 label 设置了 margin, 四个方向均有间距。


代码示例: 设置伙伴

🍒1) 创建两个 label 和 两个 radioButton.

objectName 分别问 label , label_2 , radioButton , radioButton_2

  • 此处把 label 中的文本设置为 "快捷键 &A" 这样的形式.
  • 其中 & 后面跟着的字符, 就是快捷键.
  • 可以通过 alt + A 的方式来触发该快捷键.
  • 但是注意, 这里的快捷键和 QPushButton 的不同. 需要搭配 alt 和 单个字母的方式才能触发.

🍒2) 编写 widget.cpp, 设置 buddy 属性

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 设置 label 的伙伴 widget
    ui->label->setBuddy(ui->radioButton);
    ui->label_2->setBuddy(ui->radioButton_2);
}

🍒3) 运行程序, 可以看到, 按下快捷键 alt + a 或者 alt + b, 即可选中对应的选项.

2.LCD Number

QLCDNumer 是一个专门用来显示数字的控件. 类似于 "老式计算器" 的效果.

核心属性:

属性说明
intValueQLCDNumber 显⽰的数字值(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 函数

void updateTime();

QTimer* timer;

🍁3) 修改 widget.cpp, 在构造函数中初始化 QTimer

  • QTimer 表示定时器. 通过 start 方法启动定时器之后, 就会每隔一定周期, 触发一次QTimer::timeout 信号.
  • 使用 connect 把 QTimer::timeout 信号和 Widget::updateTime 连接起来, 意味着每次触发 QTimer::timeout 都会执行 Widget::updateTime,修改 LCDNumber 中的数字。
#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 信号,现在把 timeout 信号和 updateTime 连接起来
    // 此时意味着每次触发 timeout 信号都会伴随 updataTime 函数的执行
    connect(timer, &QTimer::timeout, this, &Widget::updateTime);
    // 启动 QTimer,并且规定每隔 1000ms 触发一次 timeout 信号
    timer->start(1000);
}

🍁4) 修改 widget.cpp, 实现 updateTime

  • 通过 intValue 获取到 QLCDNumber 内部的数值.
  • 如果 value 的值归 0 了, 就停止 QTimer . 接下来 QTimer 也就不会触发 timeout 信号了.
void Widget::updateTime()
{
    int value = ui->lcdNumber->intValue();
    if(value <= 0){
        // 如果时间到,就停止计时器
        timer->stop();
        return;
    }
    ui->lcdNumber->display(value - 1);
}

🍁5) 执行程序, 可以看到每隔一秒钟, 显示的数字就减少 1.

倒计时


针对上述代码, 存在两个问题:

1) 上述代码如果直接在 Widget 构造函数中, 通过一个循环 + sleep 的方式是否可以呢?

代码形如:

#include <thread>

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 的构造函数无法执行完毕, 此时界面是不能正确构造和显示的.

2) 上述代码如果是在 Widget 构造函数中, 另起一个线程, 在新线程中完成 循环 + sleep 是否可以呢?

代码形如:

#include <thread>

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 状态, 避免后续的一系列问题。

综上所述, 使用定时器, 是实现上述功能的最合理方案.

3.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.

🌵2) 修改 widget.h, 创建 QTimer 和 handle 函数.

QTimer* timer;

void handle ();

🌵3) 修改 widget.cpp, 初始化 QTimer

  • 此处设置 100ms 触发一次 timeout 信号. 也就是一秒钟触发 10 次.
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);
}

🌵4) 修改 widget.cpp, 实现 handle

void Widget::handle()
{
    // 获取到进度条的当前数值
    int value = ui->progressBar->value();
    if(value >= 100){
        timer->stop();
        return;
    }
    ui->progressBar->setValue(value + 1);
}

🌵5) 运行程序, 可以看到进度条中的进度在快速增长.

green 进度条

  • 在实际开发中, 进度条的取值, 往往是根据当前任务的实际进度来进行设置的.
  • 比如需要读取一个很大的文件, 就可以获取文件的总的大小, 和当前读取完毕的大小, 来设置进度条的比例.
  • 由于上面我们介绍了 Qt 禁止在其他线程修改界面, 因此进度条的更新往往也是需要搭配定时器来完成的.
  • 通过定时器周期触发信号, 主线程调用对应的 slot 函数. 再在 slot 函数中对当前的任务进度进行计算, 并更新进度条的界面效果.

代码示例: 创建一个红色的进度条

        上述的进度条是使用绿色表示的, 但是考虑到有些人可能不喜欢绿色, 因此我们基于上面的程序改成一个红色的进度条.

        不要忘了,QProgressBar 同样也是 QWidget 的子类, 因此我们可以使用 styleSheet 通过样式来修改进度条的颜色.

🌴1) 在 Qt Designer 右侧的属性编辑器中, 找到 QWidget 的 styleSheet 属性. 编辑如下内容:

  • 其中的 chunk 是选中进度条中的每个 "块" . 使用 QProgressBar::text 则可以选中文本.

同时把 QProcessBar 的 alignment 属性设置为垂直水平居中.

        此处如果不设置 alignment , 进度条中的数字会跑到左上角. 这个怀疑是 Qt 本身的 bug, 暂时只能先使用 alignment 来手动调整下.

🌴2) 执行程序, 可以看到如下效果. 我们就得到了一个红色的进度条.

red 进度条

4.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();
    ui->label->setText(date.toString());
}

🌻3) 执行程序, 可以看到当选择不同的日期时, label 中的内容就会随之改变.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南风与鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值