【Qt】控件概述(3)—— 显示类控件

显示类控件

  • [1. QLabel------标签](#1. QLabel——标签)
    • [1.1 setPixmap设置图片](#1.1 setPixmap设置图片)
    • [1.2 setAlignment设置文本对齐方式](#1.2 setAlignment设置文本对齐方式)
    • [1.3 setWordWrap设置自动换行](#1.3 setWordWrap设置自动换行)
    • [1.4 setIndent设置缩进](#1.4 setIndent设置缩进)
    • [1.5 setMargin设置边距](#1.5 setMargin设置边距)
    • [1.6 body](#1.6 body)
  • [2. QLCDNumber](#2. QLCDNumber)
    • [2.1 使用QTimer实现一个倒计时效果](#2.1 使用QTimer实现一个倒计时效果)
    • [2.2 使用循环的方式实现倒计时](#2.2 使用循环的方式实现倒计时)
  • [3. QProgressBar------进度条](#3. QProgressBar——进度条)
  • [4. QCalendarWidget------日历](#4. QCalendarWidget——日历)

1. QLabel------标签

QLabel可以用来显示文本和图片:

属性 说明
text QLabel中的文本
textFormat 文本格式:1:Qt::PlaintText纯文本,2:RichText富文本,3:MarkDownText markdown格式,4:Qt:AutoText根据文本内容自动决定文本格式
pixmap QLabel内的图片
scaldContents 设置未true表示自动拉伸填充QLabel,设置为false则不会自动填充
alignment 表示对齐方式,可以设置为水平对齐和垂直对齐
wordWrap 自动换行设置,设置为true表示开启,设置为false表示不开启
indent 文本缩进,水平和垂直都可以生效
margin 表示文本和边框之间的距离,在上下左右四个方向生效
openExternalLinks 是否允许打开一个外部链接(当Qlabel涉及到url的时候)
buddy 给QLabel关联一个"伙伴",当点击QLabel时就会激活对应的"伙伴"

1.1 setPixmap设置图片

代码样例:Label显示图片

虽然前面讲的QPushButton也可以通过setIcon的方式进行添加图片,但是大多数时候我们更希望使用QLabel来进行添加一个更单纯的一个图片。

  1. 首先同样讲图片以qrc的方式添加到Qt中
  2. 创建Label,并可以通过pixmap进行添加图片
c 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QLabel* label = new QLabel(this);
    // 设置标签的大小和Widget窗口的大小是一样的
    QRect rect = this->geometry();
    label->setGeometry(0, 0, rect.width(), rect.height());
    // 插入图片并设置图片大小
    QPixmap pixmap(":/qt.jpg");
    pixmap = pixmap.scaled(rect.width(), rect.height());
    // 也可以直接使用自动填充,让图片自动贴合标签的大小
	// label->setScaledContents(true);
    label->setPixmap(pixmap);
}

但是这个时候我们是可以对Widget的窗口进行调整大小的,而在我们进行调整大小的时候,我们的标签是不会跟着改变的,所以这里我们需要提供一个事件。也就是说当我们在调整Widget的时候QWidget会触发一个事件resizeEvent事件,也就是说当Widget窗口大小发生变化的时候QWidget 会自动进行调用,而这个时候我们想要Widget进行调用,就可以在Widget中重写这个resizeEvent实现多态,这样只要窗口发生变化就会通过多态调用Widget中的resizeEvent(这个事件我们后面会详细进行讲解),所以我们就可以自定义函数来完成这一操作。

c 复制代码
// 在widget.h中添加函数声明
void resizeEvent(QResizeEvent *event); // 这个是QWidget中有的
c 复制代码
void Widget::resizeEvent(QResizeEvent *event)
{
    label->setGeometry(0, 0, event->size().width(), event->size().height());
    label->setScaledContents(true);
    qDebug() << event->size();
}

1.2 setAlignment设置文本对齐方式

首先为了我们方便观察,我么需要给label设置一个外边框,我们可以在ui界面中进行设置。

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

  • QFrame::Box :矩形边框
  • QFrame::Panel :带有可点击区域的⾯板边框
  • QFrame::WinPanel :Windows风格的边框
  • QFrame::HLine :水平线边框
  • QFrame::VLine :垂直线边框
  • QFrame::StyledPanel :带有可点击区域的⾯板边框,但样式取决于窗口主题
c 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->label->setText("这是一个水平居中和垂直居中");
    // 设置文本的对齐方式
    // 设置方式 水平方向 | 垂直方向
    ui->label->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
}

而设置垂直方向和水平方向的对齐方式其实就是宏。

c 复制代码
enum AlignmentFlag {
        AlignLeft = 0x0001, 
        AlignLeading = AlignLeft,
        AlignRight = 0x0002,
        AlignTrailing = AlignRight,
        AlignHCenter = 0x0004,
        AlignJustify = 0x0008,
        AlignAbsolute = 0x0010,
        AlignHorizontal_Mask = AlignLeft | AlignRight | AlignHCenter | AlignJustify | AlignAbsolute,

        AlignTop = 0x0020,
        AlignBottom = 0x0040,
        AlignVCenter = 0x0080,
        AlignBaseline = 0x0100,
        // Note that 0x100 will clash with Qt::TextSingleLine = 0x100 due to what the comment above
        // this enum declaration states. However, since Qt::AlignBaseline is only used by layouts,
        // it doesn't make sense to pass Qt::AlignBaseline to QPainter::drawText(), so there
        // shouldn't really be any ambiguity between the two overlapping enum values.
        AlignVertical_Mask = AlignTop | AlignBottom | AlignVCenter | AlignBaseline,

        AlignCenter = AlignVCenter | AlignHCenter
    };

1.3 setWordWrap设置自动换行

如果我们使用在label中输入了一行很长的文本而没有自动换行,就会导致部分文本被覆盖而无法显示。

c 复制代码
ui->label->setText("这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,");
c 复制代码
ui->label->setText("这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,");
ui->label->setWordWrap(true);

1.4 setIndent设置缩进

一般我们在书写word的时候使用的都是首行缩进,但是在Qt中,这里的缩进是对所有行都适用的,不单单是首行缩进

c 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->label->setText("这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,");
    ui->label->setWordWrap(true);
    ui->label->setIndent(50); // 单位像素
}

1.5 setMargin设置边距

我们先直接讲对齐方式改为水平左对齐,垂直上对其。

c 复制代码
ui->label->setText("这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,");
ui->label->setWordWrap(true);
ui->label->setAlignment(Qt::AlignLeft | Qt::AlignTop);

设置margin后

c 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->label->setText("这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,");
    ui->label->setWordWrap(true);
    ui->label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    ui->label->setMargin(20);
}

1.6 body

我们可以创建两个label和两个radioButton,将其两两关联。然后可以使用快捷键的方式进行选择。

c 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->label->setBuddy(ui->radioButton);
    ui->label_2->setBuddy(ui->radioButton_2);
}

这样就可以使用 alt + a选择1,alt+b选择2了。而如果想要做到这样的效果也需要遵循一些规则,也就是使用&A(& + 快捷键)这样的形式才可以做到。

2. QLCDNumber

QLCDNumber是一个专门用来显示数字的控件。

属性 说明
intValue QLCDNumber显示数字的值(int)
value QLCDNumber显示的数字值(double),和intValue是联动的,例如value为1.2,那么intValue就是1,另外value和intValue的方法名在位display,而不是setValue或者setValue了
digitCount 显示数字的位数
mode 显示数字的模式。1:QLCDNubmer::Dec,十进制。2:QLCDNubmer::Hex,十六进制。3:QLCDNubmer::Bin,二进制。4:QLCDNubmer::Oct,八进制。
segmentStyle 设置显示风格,1:QLCDNumber::Flat,平面显示风格,数字呈现在一个平坦的表面上。2:QLCDNumber::Outline,轮廓显示风格,数字具有轮廓和阴影效果。3:QLCDNumber::Filled,填充风格,数字可以填充颜色与背景进行区分。

2.1 使用QTimer实现一个倒计时效果

代码样式 :显示一个倒计时效果

在此之前介绍一个了QTimer类,这个类是Qt封装的一个定时器,可以通过start方法启动定时器,并通过传递参数的方式进行设定定时周期。而QTimer中有一个信号timeout,会根据周期定期的触发timeout信号,这样我们可以把周期设置为1秒,就可以首先一个简单的定时器了。

c 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 创建一个定时器类
    time = new QTimer(this);
    ui->lcdNumber->display(10);
    connect(time, &QTimer::timeout, this, &Widget::handle);
    // 启动定时器
    time->start(1000); // 单位毫秒
}

Widget::~Widget()
{
    delete ui;
}

void Widget::handle()
{
    // 获取当前lcdNumnber值
    int num = ui->lcdNumber->value();
    if (num <= 0){
        // 关闭定时器
        time->stop();
        return;
    }
    // 设置值
    ui->lcdNumber->display(num - 1);
}

2.2 使用循环的方式实现倒计时

针对上述存在两个问题。

  1. 我们先使用最为平常的for循环进行实现,使用Sleep的方式实现。而Sleep是windows的需要包含"windows.h",那是Qt并没有相关的api,但是C++11 中引路了sleep是thread库中的sleep_for
c 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->lcdNumber->display(10);
    int value = ui->lcdNumber->value();
    while(true)
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        if (value <= 0){
            break;
        }
        value -= 1;
        ui->lcdNumber->display(value);
    }
}

这样明显是没法显示出来的,因为whiel循环是在Widget的构造函数中实现的,而窗口的显示是在Widget类实例化后使用w.show()进行实现的,所以是等到while循环结束后,才会调用窗口显示函数进行窗口展示。

  1. 所以这里我们就想到使用子线程来进行修改
c 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->lcdNumber->display(10);
    std::thread t([this](){
        int value = ui->lcdNumber->value();
        while(true)
        {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            if (value <= 0){
                break;
            }
            value -= 1;
            ui->lcdNumber->display(value);
        }
    });
}

上述通过子线程的方式修改GUI上的内容同样是不行的,在Qt中规定,任何对GUI上内容的操作,必须是主线程中完成的。这样的约定主要是因为GUI中的状态往往是牵一发东全身,就需要同步的对其他内容进行调整。比如调整了某个元素的尺寸,就可能影响到内部的文字位置,或者其他元素的位置.这里⼀连串的修改,都是需要按照⼀定的顺序来完成的.由于多线程执行的顺序无法保障,因此Qt从根本上禁止了其他线程修改GUI状态,避免后续的⼀系列问题.

3. QProgressBar------进度条

核心属性:

属性 说明
minimum 进度条最小值
maximum 进度条最大值
value 进度条当前值,同上
alignment 文本在进度条中的对齐方式. 同上
textVisible 进度条的数字是否可见
orientation 进度条的方向是水平还是垂直
invertAppearance 是否是朝反方向增长进度
textDirection 文本的朝向.
format 展示的数字格式. 1:%p :表示进度的百分比(0-100),2:%v :表示进度的数值(0-100),3:%m :表示剩余时间(以毫秒为单位),4:%t :表示总时间(以毫秒为单位)

代码样例:实现一个进度条

c 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    time = new QTimer(this);
    connect(time, &QTimer::timeout, this, &Widget::handle);
    time->start(100);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::handle()
{
    int value = ui->progressBar->value();
    if (value >= 100){
        time->stop();
        return;
    }
    ui->progressBar->setValue(value + 1);
}

这里我们看到Widget.h这块

c 复制代码
#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* time;
};
#endif // WIDGET_H

从上述代码中我们可以发现一个小细节,就是Widget.h中明明使用了QTimer类,但是并没有包含QTimer头文件#include < QTimer >,而且还没有编译报错。这是应为Qt做了一些特殊处理,在Qt中专门有一个头文件,这个头文件包含了Qt中所有类的"前置声明"形如(class QTimer; class QWidget......),而这个头文件一般不会直接接触到,而是包含在其他的Qt的头文件中,所以在继承的时候就间接的包含这个头文件,所以就可以使用一些类在头文件中声明各种类的指针或者了引用。

而这样的做的目的就是减少编译时的运行时间,应为在C/C++编译期间是要讲头文件进行展开的,而对于一个大型的项目来讲,不乏会包含大量的头文件,而这也间接的增加了编译的时间,所以在头文件声明之处就可以使用这样的前置声明的技术来减少头文件的包含,也可以做到间接减少编译的时间。

但是在C++20后就开始使用module来代替#include,这样也是为了减少编译时的时间消耗。

同样进度条也是可以设置样式的。可以通过ui界面的熟悉面板进行设置,也可以使用代码进行设置。

4. QCalendarWidget------日历

核心属性

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

重要信号

信号 说明
selectionChanged(const QDate&) 当选中的日期发生改变时发出
activated(const QDate&) 当双击⼀个有效的日期或者按下回车键时发出,形参是⼀个QDate类型,保存了选中的日期
currentPageChanged(int, int) 当年份月份改变时发出,形参表示改变后的新年份和月份
c 复制代码
void Widget::on_calendarWidget_selectionChanged()
{
    QDate date = ui->calendarWidget->selectedDate();
    ui->label->setText(date.toString());
}
相关推荐
逢生博客11 分钟前
Rust 语言开发 ESP32C3 并在 Wokwi 电子模拟器上运行(esp-hal 非标准库、LCD1602、I2C)
开发语言·后端·嵌入式硬件·rust
杰哥在此31 分钟前
Python知识点:如何使用Hadoop与Python进行大数据处理
开发语言·hadoop·python·面试·编程
CocoaAndYy1 小时前
Java实现限流算法(四种)
java·开发语言·算法
所待.3831 小时前
设计循环队列
java·开发语言·数据结构
Geek之路1 小时前
Qt系统学习篇(6)-QMainWindow
数据库·qt·学习
平平无奇。。。1 小时前
C++之多态篇(超详细版)
c语言·开发语言·c++·visualstudio
脑瓜疼啊脑瓜疼1 小时前
Java中的自定义异常
java·开发语言
木子02041 小时前
SpringBoot线程问题
java·开发语言
技术无疆2 小时前
【Python】Uvicorn:Python 异步 ASGI 服务器详解
运维·服务器·开发语言·网络·python·pygame·python3.11
初阳7852 小时前
【Qt】控件概述(2)—— 按钮类控件
开发语言·qt