当前位置: 首页 > news >正文

QT跨平台应用程序开发框架(6)—— 常用显示类控件

目录

一,Label

1.1 主要属性

1.2 文本格式

1.3 设置图片

1.4 其它常用属性

1.5 设置伙伴

二,LCD Number

2.1 主要属性

2.2 实现倒计时

​2.3 两个问题

三,ProgressBar

3.1 主要属性

3.2 进度条按时间增长

3.3 改变样式

3.4 一个问题

四,Calendar Widget

4.1 主要属性

4.2 获取选中的日期 

一,Label

1.1 主要属性

QLabel 可以用来显示文本或图片,主要属性如下:

属性说明
textQLabel 中的文本
textFormat

文本的格式:

  • Qt::PlainText:纯文本
  • Qt::RichText:富文本(⽀持html标签)
  • Qt::MarkdownText:markdown格式
  • Qt::AutoText:根据文本内容自动决定文本格式
pixmapQlabel 内部包含的图片
scaledContents设为 true表示图片自动拉伸填充满 QLabel,false就不会
alignment对齐方式,可设置水平和垂直方向如何对齐
wordWrap

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

QLabel 不会提供滚动条,别的控件才有,比如 QtextEdit(多行编辑框)

indent设置文本缩进,水平和垂直方向都生效
margin内部文本和边框之间的边距
openExternalLinks是否允许打开一个外部的链接(QLabel 文本内容包含 URL 时涉及)
buddy

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

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

 下面我们来搞几个具体的例子,演示下上表格的部分属性

1.2 文本格式

先创建三个label用于展示不同的格式,然后可以在构造函数里设置不同文本格式:

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->label->setTextFormat(Qt::PlainText);ui->label->setText("这是一段纯⽂本");ui->label_2->setTextFormat(Qt::RichText);ui->label_2->setText("<b> 这是一段富⽂本 </b>"); //b标签表示加粗ui->label_3->setTextFormat(Qt::MarkdownText);ui->label_3->setText("## 这是一段 markdown ⽂本"); // ##表示二级标题
}

 

1.3 设置图片

首先用 qrc 准备一张图片,然后就可以对 Label 设置图片:

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//先把整个 Label 铺满窗口,然后让 Label 的左上角设置到窗口的左上角ui->label->setGeometry(0, 0, this->width(), this->height());QPixmap pixmap(":/deepseek.png");ui->label->setPixmap(pixmap);ui->label->setScaledContents(true); //有时候图片尺寸会小于窗口,那么这个就可以让图片拉伸
}

但是上面我们对 Label 尺寸的设置是“一次性”的,就上面的程序而言,只要我们扩大或缩小窗口大小,里面的 Label控件大小是不会变的,所以下面我们让 Label 的大小随着窗口大小实时发生改变

原理

  • 用户的绝大部分操作,会对应一些事件
  • 但 Qt 中,除了信号,还有一个“事件”也用来表示用户的操作
  • 鼠标拖拽窗口大小时,会让 Qt 触发一个 resize 事件(resizeEvent),而且我们改变窗口尺寸的过程中,会触发一系列的 resize事件
  • 此时就可以借助 resizeEvent 来完成功能,重写父类 QWidget 的 resizeEvent虚函数,而且在鼠标拖动窗口尺寸过程中,会持续调用这个虚函数进而调用子类的函数

先在头文件里声明函数:

然后实现该函数即可:

//此处的形参 event 包含了触发 resize 事件时,窗口尺寸的数值
void Widget::resizeEvent(QResizeEvent *event)
{//qDebug() << event->size();ui->label->setGeometry(0, 0, event->size().width(), event->size().height());
}

 效果如下:

1.4 其它常用属性

先创建几个带边框的 Label,如下图:

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

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_2->setText("Tcp全称“传输控制协议(Transmission Control Protocol)”,是当今互联网使用最广泛的传输层协议,因为它基于通信时保证可靠性,并且对于高效传输也有一定策略,是目前应用层底层使用的非常常见的网络协议");ui->label_2->setWordWrap(true); //表示开启自动换行//③设置缩进ui->label_3->setText("这是另一段文本");ui->label_3->setIndent(50); //表示在显示在程序上时给字体前面加上多少像素的空白//如果是label_2那样的长文本的话,会给所有的行都添加缩进//④设置边距ui->label_4->setText("Tcp的三次握手是验证双方通信信道的最小次数,能够快速建立连接;并且奇数次握手,可以确保一般情况下握手失败的连接成本是嫁接在客户端的,能保证服务器本身的稳定性");ui->label_4->setWordWrap(true); //开启自动换行ui->label_4->setMargin(50); //设置边距,表示文本内容的上下左右四个方向都要留出部分像素的空白
}

效果如下:

1.5 设置伙伴

这个和 HTML 前端中一样的,有时候按钮旁边会有一些字,但是为了方便用户点击,一般用户直接点击按钮的旁边的文字也可以选中按钮,所以这个设置伙伴就是将文本和按钮“绑定”

先创建两个单选框和两个文本框:

关于 QLabel 快捷键:

  • Qt 中 Label 的快捷键是在文本中使用符号 ‘ & ’ 跟上一个字符来表示快捷键
  • 比如 &A ,然后需要通过键盘上的 Alt + a 才能触发快捷键
  • 绑定了伙伴关系之后,就可以通过快捷键选中对应的单选或复选按钮了
  • 但是这个快捷键没有我们前面QPushButton的功能那么强大,所以了解即可

然后就是通过代码将下面的文本框添加“伙伴”,如下代码:

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//设置 label 和 radioButton 的伙伴关系ui->label->setBuddy(ui->radioButton);ui->label_2->setBuddy(ui->radioButton_2);
}

然后我们就可以通过 Alt + A 或 Alt + B 的快捷键方式来选中按钮了:

二,LCD Number

2.1 主要属性

QLCDNumber 是一个专门用来显示数字的控件,类似于“老式计算器”的效果,主要属性如下:

属性说明
intValue显示的数字值(int)
value

显示的数字值(double)

  • 和 intValue 是联动的,例如 value 设为1.5,那么intValue 的值就是2
  • 另外,设置 value 和 intValue 的方法名字为 display,而不是 setValue 或 setIntValue
digitCount显示几位数字
mode

数字显示形式

  • QLCDNumber::Dec:十进制模式,显示常规的十进制数字
  • QLCDNumber::Hex:十六进制模式,以十六进制格式显示数字
  • QLCDNumber::Bin:二进制模式,以二进制格式显示数字
  • QLCDNumber::Oct:八进制模式,以八进制格式显示数字

只有十进制才能显示小数点后的内容

segmentStyle

设置显示风格

  • QLCDNumber::Flat:平面的显示风格,数字呈现在一个平坦的表面上
  • QLCDNumber::Outline:轮廓显示风格,数字具有清晰的轮廓哦和阴影效果
  • QLCDNumber::Filled:填充显示风格,数字被填充颜色并与背景区分开
smallDecimalPoint设置比正常大小还小的小数点

下面通过例子来演示上述效果:

2.2 实现倒计时

我们先使用 QLCDNumber 显示一个初始的数值,比如4,然后程序启动后,每过一秒数字就 -1,直到 0 就结束

先创建一个LCD,如下:

然后我们现在的关键点就是要实现“每秒钟 -1” 这个效果,也就是周期性的执行某个逻辑

关于“定时器”功能

  • C++ 标准库里没有提供定时器的相关接口(Boost库里面有)
  • 但是Qt 中是封装了对应的定时器的,涉及到的类叫做 QTimer,创建出来的对象会产生 timeout 这样的信号
  • 我们可以通过 start 方法来开启定时器,并在参数中设定触发 timeout 信号的周期 
  • 然后结合 connect ,把这个 timeout 信号绑定到需要的槽函数中,就可以执行逻辑,修改 LCDNumber 中的数字了

我们先在头文件添加 定时器对象和槽函数的声明:

然后就是倒计时的逻辑了:

#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(4); //设置一个初始值timer = new  QTimer(this);connect(timer, &QTimer::timeout, this, &Widget::handle);//将 QTimer 的 timeout 信号和我们自己创建的槽函数进行关联timer->start(1000); //启动定时器,单位ms,1000表示每隔一秒触发一次信号
}Widget::~Widget()
{delete ui;
}void Widget::handle()
{int value = ui->lcdNumber->intValue();if(value <= 0){timer->stop();return;}ui->lcdNumber->display(value - 1);
}

效果如下:

2.3 两个问题

问题一:如果我不用计时器,直接在 Widget 的构造函数里,通过 “循环 + sleep(1)” 的方式是否可以呢

例如下列代码:

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 - 1);}
}

这个方法直接叉掉,在构造函数里搞循环,会导致构造函数无法退出,此时界面就无法显示任何控件

问题二:那么我是否可以另外创建一个线程,在新线程里完成 循环 + sleep(1) 可以吗?

如下代码:

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//创建线程t,并添加lamdba线程函数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 的界面只能由主线程去负责维护和更新的,并且 Qt 为了保证修改页面过程中不受影响,禁止了其他线程直接修改页面
  • 因为这种情况如果不加锁,不维护好线程安全,就可能有多个线程同时在修改页面,很容易导致页面显示混乱,这是无法容忍的
  • 所以Qt 为了主界面线程安全,直接要求所有对界面的修改操作,必须在主线程中完成
  • 槽函数就是由主线程调用的,所以可以修改

简单来说,界面就相当于共享资源,每个线程访问签都必须先加锁,但是 Qt 规定只能由主线程一个线程去修改 

解决办法也有,就是创建线程后,线程只负责每秒发 timeout 信号给槽函数再去修改,但这属于多此一举, 所以暂时不考虑 

三,ProgressBar

3.1 主要属性

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:表示剩余时间(单位ms)
  • %t:表示总时间(单位ms)

3.2 进度条按时间增长

我们之前是写过一个命令行版本的进度条的,可以参考:Linux实现简单进度条-CSDN博客

先创建一个进度条,如下图:

我们的期望是每隔100毫秒进度条就增加1,所以我们仍然可以通过定时器来实现周期行为

代码如下:

#include "widget.h"
#include "ui_widget.h"
#include<QTimer>
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(20); //启动计时器
}Widget::~Widget()
{delete ui;
}void Widget::handle()
{int value = ui->progressBar->value(); //获取进度条当前数值if(value >= 100){timer->stop();return;}ui->progressBar->setValue(value + 1);
}

步骤和前面的倒计时很像,都是计时器发信号然后触发槽函数然后修改,效果如下:

 在实际开发中,进度条的取值,往往是根据当前任务的实际进度来进行设置的

3.3 改变样式

QProGressBar 也是继承自 QWidget 的,所以也可以使用 styleSheet等方式来改变进度条的颜色等外表

假如我们要把上面的进度条颜色改为“红色”,如下:

 可以添加下列样式:

QProgressBar::chunk { background-color: red; }

上面两个冒号是“选择器”,对于什么是选择器,可以参考:前端学习(2)—— CSS详解与使用-CSDN博客 

 效果如下:

但是我们发现,我们的 100% 的数字跑到了左上角,这个忙猜是 Qt 的bug,毕竟我们没有改变其他的任何地方,所以我们只能先使用 alignment 来手动调整下,如下图:

效果如下:

3.4 一个问题

问题:我们上面的 Widget.h 头文件,添加QTimer* timer; 声明时,明明没有包含 对应头文件,为什么不会提示“QTimer 找不到定义”之类的呢?

如下图:

原因如下:

  • 在 Qt 中,有一个特殊的头文件,这个头文件里包含了 Qt 中所有类的“前置声明”,这个头文件一般不会直接接触到,但是包含其他的 Qt 的头文件,都会间接包含到这个文件
  • 比如上面的 Widget 类的前面已经提供了 QTimer 类的声明的话,此时就可以直接在 Widget 中使用 QTimer 的指针/引用类型的成员(这个是 C++ 中的特殊技巧,Qt 就把它充分发挥了)

追加问题:Qt 为什么要使用上面的技巧呢?

解答

  • C/C++ 的代码,编译速度在其他语言的横向对比中,其实是非常慢的,这个 #include 头文件有很大关系
  • 因为C/C++代码在编译期间,是直接把头文件展开,然后替换掉原来包含头文件的位置,相当于复制拷贝,而一个头文件会间接包含其他头文件,然后其他头文件也会包含其他头文件,这样一下来,就会造成很多不必要的头文件展开
  • 因此,尽可能减少 include 头文件的个数,就可以有效减少编译事件,Qt 就使用 class 前置声明的方式,尽量减少头文件的包含
  • 但是实际开发中,该包含就该包含,也可以引入更好的硬件资源来更高效的编译,一些互联网大肠,都有专门的“编译集群”(分布式编译),专门用来编译

四,Calendar Widget

4.1 主要属性

QCalendarWidget 表示一个“日历”,主要属性如下:

属性说明
selectDate当前选中的日期
minimumDate最小日期
maximumDate最大日期
firstDayOfWeek每周的第一天是周几(日历的第一列)
gridVisible是否显示表格的边框
selectionMode是否允许选择日期
navigationBarVisible日历上方标题是否显示
horizontalHeaderFormat日历上方标题显示的日期格式
verticalHeaderFormat日历第一列显示的内容格式
dateEditEnabled是否允许日期被编辑

也提供了一些信号,如下:

信号说明
selectionChanged(const QDate&)当选中的日期发生改变时发出
activated(const QDate&)当双击一个有效的日期或按下回车键时发出,形参是一个QDate类型,保存了选中的日期
currentPageChanged(int, int)当年份月份改变时发出,形参表示改变后的新年份个月份

4.2 获取选中的日期

先创建一个 label 和一个日历,然后右键转到槽,选择 selectionChange():

编写如下槽函数:

void Widget::on_calendarWidget_selectionChanged()
{QDate date = ui->calendarWidget->selectedDate();QString ret = "选择的日期是:";ret += date.toString();ui->label->setText(ret);
}

效果如下:

http://www.lryc.cn/news/586405.html

相关文章:

  • 使用FastAdmin框架开发
  • Java项目2——增强版飞机大战游戏
  • 【极客日常】后端任务动态注入执行策略的一种技术实现
  • R 语言绘制 10 种精美火山图:转录组差异基因可视化
  • 算法第三十一天:贪心算法part05(第八章)
  • CCF CSP第一轮认证一本通
  • 【理念●体系】模板规范篇:打造可标准化复用的 AI 项目骨架
  • 一分钟快速了解Apache
  • Redis集群会有写操作丢失吗?为什么?
  • 动态规划基本操作
  • 从LLM到VLM:视觉语言模型的核心技术与Python实现
  • FastAdmin项目开发三
  • (LeetCode 面试经典 150 题 )3. 无重复字符的最长子串 (哈希表+双指针)
  • 回归(多项式回归)
  • 算法练习6-大数乘法(高精度乘法)
  • Linux系统中部署Redis详解
  • (C++)STL:list认识与使用全解析
  • OpenEuler操作系统测试USB摄像头
  • The Black Heart
  • AOSP Settings模块问题初窥
  • day03-链表part1
  • C++类模版1
  • HTTP和HTTPS部分知识点
  • JAVA开发
  • 【数据结构初阶】--顺序表(三)
  • 广东省省考备考(第四十三天7.12)——数量(第四节课)
  • kettle从入门到精通 第101课 ETL之kettle DolphinScheduler调度kettle
  • 亚矩阵云手机:重构物流供应链,让跨境包裹“飞”得更快更准
  • 配置驱动开发:初探零代码构建嵌入式软件配置工具
  • ESP32使用freertos更新lvgl控件内容