3.显示类控件
①Label 显示文本和图片
QLabel可以用来显示文本和图片
属性 |
说明 |
text |
QLabel中的文本 |
textFormat |
文本的格式: Qt::PlainText 纯文本 Qt::RichText 富文本(支持html标签) Qt::MarkdownText markdown格式 Qt::AutoText 根据文本内容自动决定文本格式 |
pixmap |
QLabel内部包含的图片 |
scaledContents |
设为true表示内容自动拉伸填充QLabel 设为false则不会自动拉伸 |
alignment |
对齐方式 可以设置水平和垂直方向如何对齐 |
worldWrap |
设为true内部的文本会自动换行 设为false则内部文本不会自动换行 |
indent |
设置文本缩进.水平和垂直方向都生效 |
margin |
内部文本和边框之间的边距 不同于于indent,但是是上下左右四个方向都同时有效 而indent最多只是两个方向有效(具体哪两个方向有效取决于alignment) |
openExternalLinks |
是否允许打开一个外部的链接 (当QLabel文本内容包含url的时候涉及到) |
buddy |
给QLabel关联一个"伙伴",这样点击QLabel时就能激活对应的伙伴 例如伙伴如果是一个QCheckBox,那么该QCheckBox就会被选中 |
#include "widget.h"
#include "ui_widget.h"
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>");
ui->label_3->setTextFormat(Qt::MarkdownText);//文本的格式
ui->label_3->setText("# 这是一段Markdown文本");
}
Widget::~Widget()
{
delete ui;
}
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//先把QLabel设置成和窗口一样大,并把这个QLabel左上角设置成窗口左上角
QRect windowRect = this->geometry();
ui->label->setGeometry(0,0,windowRect.width(),windowRect.height());
QPixmap pixmap(":/r.png");
ui->label->setPixmap(pixmap);
//设置*拉伸
ui->label->setScaledContents(true);
}
Widget::~Widget()
{
delete ui;
}
ui->label->setGeometry(0,0,windowRect.width(),windowRect.height());
这个尺寸设置是在构造函数内,相当于一次性的,一旦程序运行起来后,QLabel尺寸就固定下来了,窗口大小发生改变的时候,QLabel是不会变化的
如果想要跟随窗口大小改变,当用户拖拽修改窗口大小的时候,会触发resize事件,像这样的事件,是连续变化的,把窗口尺寸从A拖拽到B这个过程中,会触发一系列的事件
所以可以借助事件resizeEvent来完成跟随窗口改变大小的功能
可以让Widget窗口类,重写父类(QWidget)的resizeEvent虚函数
上图可以看出一直在触发事件并打印size
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include<QResizeEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//先把QLabel设置成和窗口一样大,并把这个QLabel左上角设置成窗口左上角
QRect windowRect = this->geometry();
ui->label->setGeometry(0,0,windowRect.width(),windowRect.height());
QPixmap pixmap(":/r.png");
ui->label->setPixmap(pixmap);
//设置*拉伸
ui->label->setScaledContents(true);
}
Widget::~Widget()
{
delete ui;
}
// 形参enent包含了触发这个resize事件这一时刻窗口尺寸的数值
void Widget::resizeEvent(QResizeEvent *event)
{
qDebug() << event->size();
ui->label->setGeometry(0,0,event->size().width(),event->size().height());
}
注意:
此处的resizeEvent
函数我们没有手动调用,但是能在窗口大小变化时被自动调用,这个过程就是依赖C++中的多态来实现的.Qt框架内部管理着QWidget对象表示咱们的窗口。在窗口大小发生改变时,Qt就会自动调用resizeEvent函数
但是由于实际上这个表示窗口的并非是QWidget,而是QWidget的子类,也就是咱们自己写的Widget.此时虽然是通过父类调用函数,但是实际上执行的是子类的函数(也就是我们重写后的resizeEvent
)
此处属于是多态机制的一种经典用法.通过上述过程,就可以把自定义的代码,插入到框架内部执行,相当于"注册回调函数”
文本对齐方式
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 在构造函数中设置QLabel的属性
ui->label->setText("这是文本一");
ui->label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
ui->label_2->setText("这是文本二");
ui->label_2->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
ui->label_3->setText("这是文本三");
ui->label_3->setAlignment(Qt::AlignRight | Qt::AlignTop);
ui->label_4->setText("这是文本四");
}
Widget::~Widget()
{
delete ui;
}
自动换行、缩进、边距
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 在构造函数中设置QLabel的属性
// 对齐方式
ui->label->setText("这是文本一");
ui->label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
// 自动换行
ui->label_2->setText("这是一段很长的文本 这是一段很长的文本 这是一段很长的文本 这是一段很长的文本 这是一段很长的文本 这是一段很长的文本 这是一段很长的文本 这是一段很长的文本 ");
ui->label_2->setWordWrap(true);
// 缩进
ui->label_3->setText("这是一段很长的文本 这是一段很长的文本 这是一段很长的文本 这是一段很长的文本 这是一段很长的文本 这是一段很长的文本 这是一段很长的文本 ");
ui->label_3->setWordWrap(true);
ui->label_3->setIndent(50);
// 边距
ui->label_4->setText("这是一段很长的文本 ");
ui->label_4->setWordWrap(true);
ui->label_4->setMargin(30);
}
Widget::~Widget()
{
delete ui;
}
伙伴关系
Qt中,QLabel中写的文本,是可以指定"快捷键
此处快捷键的规则功能上要比QPushButton弱很多.
是在文本中使用&跟上一个字符来表示快捷键
比如&A=>通过键盘上的alt+a来触发这个快捷键
&B=>通过键盘上的alt+b来触发
绑定了伙伴关系之后,通过快捷键就可以选中对应的单选按钮/复选按钮~~
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置label和伙伴关系
ui->label->setBuddy(ui->radioButton);
ui->label_2->setBuddy(ui->radioButton_2);
}
Widget::~Widget()
{
delete ui;
}
②QLCD Number 显示数字
QLCDNumber
是一个专门用来显示数字的控件,类似于"老式计算器"的效果
属性 |
说明 |
intValue |
|
value |
例如给value设为1.5, |
digitCount |
显示几位数字 |
mode |
数字显示形式
|
segmentStyle |
设置显示风格 |
smallDecimalPoint |
设置比较小的小数点 |
倒计时
用到“定时器”,在C++标准库中,没有提供定时器的实现,Boost里面提供了对应的功能
Qt中也封装了对应的定时器,结合了信号槽机制
QTime通过这个类创建出来的对象,就会产生一个timeout的信号,用start方法来开启定时器,并且参数中设定触发timeout信号的周期
结合connect,把这个timeout信号绑定到需要的槽函数中,就可以执行逻辑,修改LCDNumber中的数字了
#include "widget.h"
#include "ui_widget.h"
#include<QTimer>
#include<QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置初始值
ui->lcdNumber->display("10");
//创建一个QTimer实例
QTimer* timer = new QTimer(this);
//把QTimer的timeout信号和自己的槽函数进行连接
connect(timer, &QTimer::timeout, this, &Widget::handle);
//启动定时器
timer->start(1000);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handle()
{
//先拿到LCDNumber中的数字
int value = ui->lcdNumber->intValue();
if(value<=0)
{
timer->stop();
return;
}
ui->lcdNumber->display(value-1);
}
#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
针对上述代码, 存在两个问题:
1) 上述代码如果直接在 Widget 构造函数中, 通过⼀个循环 + sleep 的⽅式是否可以呢?
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);
}
}
2) 上述代码如果是在 Widget 构造函数中, 另起⼀个线程, 在新线程中完成 循环 + sleep 是否可以呢?
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
std::thread t([this] (){
int value = ui->lcdNumber->intValue();
while(true)
{
//先休眠1s
std::this_thread::sleep_for(std::chrono::seconds(1));
if(value<=0)
{
break;
}
value -= 1;
ui->lcdNumber->display(value);
}
});
}
另起一个线程,在新线程中完成循环+sleep,这个代码是有错误的,对于GUI上内容的操作,必须在主线程中完成,像Widget构造函数,以及connect连接的slot函数,都是在主线程中调用的,而我们自己创建的线程则不是
这样的约定主要是因为 GUI 中的状态往往是牵⼀发动全身的, 修改⼀个地方, 就需要同步的对 其他内容进行调整.
比如调整了某个元素的尺寸, 就可能影响到内部的文字位置, 或者其他元素的位置. 这里⼀连串 的修改, 都是需要按照⼀定的顺序来完成的.
由于多线程执行的顺序无法保障, 因此 Qt 从根本上禁止了其他线程修改 GUI 状态, 避免后续的 ⼀系列问题.
对于GUI来说,内部包含了很多的隐藏状态,Qt为了保证修改界面的过程中,线程安全是不会受到影响的,Qt禁止了其他线程直接修改界面.
ui->lcdNumber->display(value);
形如这种操作,就是在修改界面~~
因此Qt为了确保线程安全,直接要求所有的对界面的修改操作,必须在主线程中完成
对于Qt的槽函数来说,默认情况下,槽函数都是由主线程调用的.
在槽函数中修改界面是没有任何问题的!
return a.exec();
a.exec就会使主线程进入"事件循环"
exec就会一直循环下去,每执行一次循环,都会有一些固定的事情要操作
综上所述,使用定时器是实现上述功能的最合理方案,后续如果有类似需要“周期性修改界面状态”的需求,也需要优先考虑使用定时器
③ProgressBar 进度条
属性 |
说明 |
minimum |
进度条最小值 |
maximum |
进度条最大值 |
value |
进度条当前值 |
alignment |
文本在进度条中的对齐方式
|
textVisible |
进度条的数字是否可见 |
orientation |
进度条的方向是水平还是垂直 |
invertAppearance |
是否是朝反方向增长进度 |
textDirection |
文本的朝向 |
format |
展示的数字格式
|
设置进度条按时间增长
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()
{
//获取到进度条的当前数据
int value = ui->progressBar->value();
if(value>=100)
{
timer->stop();
return;
}
ui->progressBar->setValue(value+1);
}
修改进度条颜色
具体进度根据实际的任务类型灵活设置的,例如,要读取一个很大的文件,就先获取到文件的总大小,每读取一部分,计算出读取了多少数据,更新一次进度条的数值,设置进度条的过程中,往往都需要搭配定时器
④Calender Widget 日历
属性 |
说明 |
selectDate |
当前选中的日期 |
minimumDate |
最小日期 |
maximumDate |
最大日期 |
firstDayOfWeek |
每周的第一天(日历的第一列是周几) |
gridVisible |
是否显示表格的边框 |
selectionMode |
是否允许选择日期 |
navigationBarVisible |
日历上方标题是否显示 |
horizontalHeaderFormat |
日历上方标题显示的日期格式(水平的) |
verticalHeaderFormat |
日历第一列显示的内容格式(垂直的) |
dataEditEnabled |
是否允许日期被编辑 |
重要信号
信号 |
说明 |
selectionChanged(const QDate&) |
当选中的日期发生改变时发出 |
activated(const QDate&) |
当双击一个有效的日期或者按下回车键时发出,形参是一个QDate类型,保存了选中的日期 |
currentPageChanged(int, int) |
当年份月份改变时发出,形参表示改变后的新年份和月份 |
void Widget::on_calendarWidget_selectionChanged()
{
QDate date = ui->calendarWidget->selectedDate();
qDebug()<<date;
ui->label->setText(date.toString());
}