C++客户端Qt开发——常用控件(显示类控件)

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

QLCDNumber显示的数字值(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

设置比较小的小数点

倒计时

用到“定时器”,在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

文本在进度条中的对齐方式

  • Qt::AlignLeft:左对齐
  • Qt:AlignRight:右对齐
  • Qt::AlignCenter:居中对齐
  • Qt::AlignJustify:两端对齐

textVisible

进度条的数字是否可见

orientation

进度条的方向是水平还是垂直

invertAppearance

是否是朝反方向增长进度

textDirection

文本的朝向

format

展示的数字格式

%p:表示进度的百分比(0-100)

%v:表示进度的数值(0-100)

%m:表示剩余时间(以毫秒为单位)

%t:表示总时间(以毫秒为单位)

设置进度条按时间增长

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());
}
上一篇:无人机反制:车载侦测干扰一体设备技术详解


下一篇:数据类型与结构设计:Rust 语言的深度探索