Qt开发⑤Qt常用控件_中_显示类控件+输入类控件

目录

1. 显示类控件

1.1 Label 文本和图片

1.2 LCD Number 数字

1.3 ProgressBar 进度条

1.4 Calendar Widget 日历

2. 输入类控件

2.1 Line Edit 单行输入框

2.2 Text Edit 多行输入框

2.3 Combo Box 下拉框

2.4 Spin Box 微调框

2.5 Date Edit 和 Time Edit 微调框

2.6 Dial 旋钮

2.7 Slider 滑动条

本篇完。


1. 显示类控件

1.1 Label 文本和图片

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

核心属性如下:


显示不同格式的文本,操作演示:

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

尺存放大一些,objectName 分别为 label、label_2、label_3


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


(3)运行程序


显示图片,操作演示:

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

(1)在界面上创建一个 QLabel、objectName 为 label

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

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

#include "widget.h"
#include "ui_widget.h"

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

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

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

可以看到窗口有的地方有空白,没有被图片填满。

可以修改代码,设置 scaledContents 属性:

ui->label->setScaledContents(true);

这样窗口就被自动拉伸填充了。

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

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

.cpp:

#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左上角设置到窗口的左上角这里
    // 让整个QLabel铺满整个窗口
    QRect windowRect = this->geometry();
    ui->label->setGeometry(0, 0, windowRect.width(), windowRect.height());
    QPixmap pixmap(":/background1.jpg");
    ui->label->setPixmap(pixmap);

    ui->label->setScaledContents(true);
}

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

// 此处的形参event非常有用,这里就包含了触发这个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)。

        此处属于是多态机制的一种经典用法。通过上述过程就可以把自定义的代码插入到框架内部执行,相当于 “注册回调函数”。


文本对齐、自动换行、缩进、边距,操作演示:

(1)创建四个 label,objectName 分别是 label 到 label_4,并且在 QFrame 中设置 frameShape 为 Box(设置边框之后看起来会更清晰一些)

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

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

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


(3)运行程序


设置伙伴,操作演示:

(1)创建两个 label 和 两个 radioButton

objectName 分别为 label、label_2、radioButton、radioButton_2

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

绑定了伙伴关系之后,通过快捷键就可以选中对应的单选按钮 / 复选按钮。


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

这里也可以使用 Qt Designer 直接设置:


(3)运行程序

可以看到,按下快捷键 alt + a 或者 alt + b,即可选中对应的选项:


1.2 LCD Number 数字

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

核心属性:


倒计时,操作演示:

(1)在界面上创建⼀个 QLCDNumber,初始值设为 10

objectName 为 lcdNumber


(2)修改 widget.h 代码,创建一个 QTimer 成员和一个 handle 函数


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

  • QTimer 表示定时器,通过 start 方法启动定时器之后,就会每隔一定周期触发一次 QTimer::timeout 信号
  • 使用 connect 把 QTimer::timeout 信号和 Widget::updateTime 连接起来,意味着每次触发 QTimer::timeout 都会执行 Widget::updateTime


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

  • 通过 intValue 获取到 QLCDNumber 内部的数值。
  • 如果 value 的值归 0 了,就停止 QTimer,接下来 QTimer 也就不会触发 timeout 信号了。


(5)执行程序

可以看到每隔一秒钟,显示的数字就减少 1:


(6)针对上述代码,存在两个问题

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

        显然,上面这段代码是不行的,循环会使 Widget 的构造函数无法执行完毕,此时界面是不能正确构造和显示的。


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

        这个代码同样是不行的。Qt 中规定:任何对于 GUI 上内容的操作必须在主线程中完成。像 Widget 构造函数,以及 connect 连接的 slot 函数,都是在主线程中调用的。而我们自己创建的线程则不是,当我们自己的线程中尝试对界面元素进行修改时,Qt 程序往往会直接崩溃。

        这样的约定主要是因为 GUI 中的状态往往是牵一发动全身的,修改一个地方,就需要同步的对其他内容进行调整。比如调整了某个元素的尺存,就可能影响到内部的文字位置,或者其他元素的位置。这里一连串的修改都是需要按照一定的顺序来完成的。

        由于多线程执行的顺序无法保障,因此 Qt 从根本上禁止了其他线程修改 GUI 状态,避免后续的一系列问题。

        对于 Qt 的槽函数来说,默认情况下,槽函数都是由主线程调用到,在槽函数中修改界面是没有任何问题的。

        综上所述,使用定时器是实现上述功能的最合理方案。后续如果也有类似的需要 “周期性修改界面状态” 的需求也需要优先考虑使用定时器。


1.3 ProgressBar 进度条

使用 QProgressBar 表示一个进度条

核心属性:


设置进度条按时间增长,操作演示:

(1)在界面上创建进度条,objectName 为 progressBar

其中最小值设为 0,最大值设为 100,当前值设为 0:


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

        虽然在 widget.h 中用到了 QTimer,但是却没在 widget.h 文件中包含 <QTimer> 头文件,为什么这个代码编译没有出错呢?

        上述问题其实是通过 Qt 内部提供的一个特殊技巧来实现的。在 Qt 中有一个专门的头文件(#include <QWidget>),这个头文件中包含了 Qt 中所有类的 “前置声明”(class QWidget,class QPushButton,class QTimer)。这个头文件我们一般不会直接接触到,但是包含其它的 Qt 的头文件,都会间接的包含到这个头文件。

        如果 Widget 类的前面以及提供了 QTimer 类的声明的话,此时就可以在 Widget 中声明 QTimer 的指针 / 引用类型的成员。后续如果要真正使用 QTimer 的头文件(包括创建实例,使用里面的成员),仍然要包含 QTimer 的头文件(包含了 QTimer 的详细的类的定义)。

Qt 为什么要使用上述技巧呢?上述技巧能解决什么问题?有什么提升呢?

        主要解决的是编译速度的问题。C/C++ 代码,编译速度在其他语言横向对比中是非常慢的。C++ 编译速度慢和 #include 头文件有直接关系。由于 include 关系错综复杂,所以尽可能减少 include 头文件的个数就可以有效地减少编译时间。

        Qt 中就使用 class 前置声明的方式来尽量减少头文件的包含。通过前置声明的方式,Qt 中每个头文件包含的其它头文件数量都能得到一定的降低。


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

此处设置 100ms 触发一次 timeout 信号,也就是一秒钟触发 10 次


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


(5) 运行程序

可以看到进度条中的进度在快速增长:

        在实际开发中,进度条的取值往往是根据当前任务的实际进度来进行设置的。比如需要读取一个很大的文件,就可以获取文件的总的大小和当前读取完毕的大小,来设置进度条的比例。

        前面介绍了 Qt 禁止在其他线程修改界面,因此进度条的更新往往也需要搭配定时器来完成。

        通过定时器周期触发信号,主线程调用对应的 slot 函数,再在 slot 函数中对当前的任务进度进行计算,并更新进度条的界面效果。


创建一个红色的进度条,操作演示:

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

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


(1)在界面上创建一个进度条


(2)在 Qt Designer 右侧的属性编辑器中找到 QWidget 的 styleSheet 属性

编辑内容:其中的 chunk 是选中进度条中的每个 “块”,使用 QProgressBar::text 则可以选中文本。

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

此处如果不设置 alignment,进度条中的数字会跑到左上角。


(3)执行程序

可以看到如下效果,就得到了一个红色的进度条:

通过上述方式,也可以修改文字的颜色,字体大小等样式。


1.4 Calendar Widget 日历

QCalendarWidget 表示⼀个 “日历”,形如:

核心属性:

重要信号:


获取选中的日期,操作演示:

(1)在界面上创建一个 QCalendarWidget 和一个 label

objectName 为 calendarWidget,TestLabel


(2)给 QCalendarWidget 添加 slot 函数

#include "widget.h"
#include "ui_widget.h"

#include <QDebug>

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

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

void Widget::on_calendarWidget_selectionChanged()
{
    QDate date = ui->calendarWidget->selectedDate();
    qDebug() << date;

    ui->label->setText(date.toString());
}

(3)执行程序

可以看到当选择不同的日期时,label 中的内容就会随之改变:


2. 输入类控件

2.1 Line Edit 单行输入框

QLineEdit 用来表示单行输入框,可以输入一段文本,但是不能换行。

核心属性:

核心信号:


录入个人信息,案例演示:

(1)在界面上创建三个输入框和两个单选按钮,一个普通按钮

三个输入框的 objectName 为 lineEdit_name、lineEdit_password、lineEdit_phone

两个单选按钮的 objectName 为 radioButton_male、radioButton_female

按钮的 objectName 为 pushButton


(2)编写 widget.cpp,在构造函数中编写初始化代码


(3)继续修改 widget.cpp,给按钮添加 slot 函数


(4)执行程序

可以看到,随着用户输⼊内容之后,点击 “提交” 按钮就能打印到输入的信息:

inputMask 只能进行简单的输入格式校验。实际开发中,基于正则表达式的方式是更核心的方法。


使用正则表达式验证输入框的数据,案例演示:

        此处要求在输入框中输入一个合法的电话号码(1 开头,11 位,全都是数字)。如果验证不通过,则确定按钮无法点击。

(1)关于正则表达式

        正则表达式是一种在计算机中常用的,使用特殊字符描述一个字符串的特征的机制,在进行字符串匹配时非常有用。

正则表达式的语法还比较复杂,一般都是随用随查,不需要背下来。

可参考:

正则表达式文档:正则表达式语法 | Microsoft Learn

正则表达式在线工具:正则表达式语法测试工具 - 在线工具 (buyaocha.com)


(2)在界面上创建输入框和一个按钮

此处的规则:输入框要检查输入的内容是否为合法的手机号码。

  • 如果是,则按钮设为可用状态。
  • 如果不是,则按钮设为禁用状态。

(3)编写 widget.cpp,把按钮初始 enabled 设为 false,给输入框添加验证器

  • 使用 QRegExp 创建一个正则表达式对象,"^1\\d{10}$" 表示" 表示以 xxx 开头,后面跟着的 1 表示以 1 开头,\d 表示数字,为了在 C++ 字符串中石油,需要写作 \\d,{10} 表示前面的内容重复出现 10 次,\d 数字要重复出现 10 次,$ 表示结尾。
  • 使用 QRegExpValidator 创建一个验证器对象,Qt 中内置了四个主要的验证器对象。

        QRegularExpressionValidator 在匹配性能上做出了一定优化。但是从使用角度讲,和 QRegExpValidator 差别不大,我们使用 QRegExpValidator 即可。


(4)编写 widget.cpp,给 lineEdit 添加 textEdited 信号的 slot 函数

  • on_lineEdit_textEdited 的参数是当前输入框的内容
  • 通过 lineEdit->validator() 获取到内置的验证器
  • 通过 validate 方法验证文本是否符合要求
  1. 第一个参数填写的是要验证的字符串。由于参数要求是 QString& 而不是 const QString&,需要把这个变量复制一下。
  2. 第二个参数是⼀个 int&,是输出型参数。当验证的字符串不匹配时,返回这个字符串的长度(没有什么实质作用)。
  3. 返回值是一个枚举。 QValidator::Acceptable 表示 验证通过,QValidator::Invalid 表示验证不通过。


(5)执行程序

        观察效果,可以看到此时尝试输入字母是无法输入的,并且只有当输入的内容符合要求,确定按钮才能被使用。


验证两次输入的密码一致,案例演示:

(1)在界面上创建两个输入框和一个 label


(2)编写代码,设置两个输入框的 echoMode 为 Password


(3)给两个输入框设置 textEdited slot 函数

由于两个槽函数内容一致,我们可以选择封装一个函数,然后在槽函数内直接调用即可。


(4)执行程序

可以看到当两个输入框内的密码相同时,就会提示密码相同:


切换显示密码,案例演示:

(1)创建一个输入框和一个复选按钮


(2)修改 widget.cpp,设置输入框的 echoMode 为 Password


(3)修改 widget.cpp,给 checkBox 添加 slot 函数


(4)执行程序

可以看到切换复选框的状态,就可以控制输入框显示密码:


2.2 Text Edit 多行输入框

        QTextEdit 表示多行输入框,也是一个富文本 & markdown 编辑器,并且能在内容超出编辑框范围时自动提供滚动条。

核心属性:

核心信号:


获取多行输入框的内容,案例演示:

(1)创建一个多行输入框和一个 label


(2)给多行输入框添加 slot 函数,处理 textChanged 信号

  • 通过 toPlainText 方法获取到内部的文本
  • 类似的,QTextEdit 还提供了 toMarkdown 和 toHtml,根据需要我们调整不同的获取方式


(3)执行程序

可以看到当输入框中的内容发生变化时,label 中的内容同步发生改变:


验证输入框的各种信号,案例演示:

(1)创建多行输入框


(2)给输入框添加以下几个 slot 函数

        QTextEdit 中包含了一个 QTextCursor 对象,通过这个对象可以获取到当前光标位置和选中的内容。


(3)执行程序

编写内容时,textChanged 和 cursorPositionChanged 会触发


选中一段文本时,cursorPositionChanged、selectChanged、copyAvailable 会触发


按下 Ctrl + Z 时,textChanged、undoAvailable、redoAvailable、cursorPositionChanged 会触发


按下 Ctrl + Y,textChanged、undoAvailable、redoAvailable、cursorPositionChanged 会触发


2.3 Combo Box 下拉框

QComboBox 表示下拉框

核心属性:

核心方法:

核心信号:


使用下拉框模拟麦当劳点餐,案例演示:

(1)在界面上创建三个下拉框和一个按钮


(2)编写 widget.cpp,初始化三个下拉框的内容

也可以选择直接在 ui 界面添加选项内容:


(3)编写 widget.cpp,给按钮添加 slot 函数


(4)执行程序

在点击确定按钮时,就能获取到当前下拉框中选中的内容:


从文件中加载下拉框的选项,案例演示:

很多时候下拉框的选项并非是固定的,而是通过读取文件 / 读取网络获取到的。

(1)在界面上创建一个下拉框


(2)创建文件 d:/config.txt,编写选项,每个选项占一行


(3)修改 widget.cpp,从文件中读取选项

  • 使用 ifstream 打开文件
  • 使用 getline 读取每一行
  • 使用 QString::fromStdString 把 std::string 转成 QString


(4)执行程序

可以看到文件内容已经被加载到下拉框中:

        Qt 中也提供了 QFile 实现读写文件的功能,当然使用 C++ 标准库的 std::fstream 也是完全可以的。之所以存在两套,是因为 Qt 诞生较早(1991 年左右),此时 C++ 还没有完成 “标准化” 的工作,C++ 标准库这样的概念自然也没有诞生。

        因此 Qt 就自己打造了一套库,实现了字符串、容器、文件操作、多线程、网络操作、定时器、正则表达式等内容。(由于 C++ 标准委员会的不作为,至今仍然有些 Qt 提供的功能,是标准库不具备的)


2.4 Spin Box 微调框

        使用 QSpinBox 或者 QDoubleSpinBox 表示 “微调框”,它是带有按钮的输入框,可以用来输入整数 / 浮点数,通过点击按钮来修改数值大小。

由于 SpinBox 和 QDoubleSpinBox 用法基本相同,就只介绍 SpinBox 的使用了。

Spin 英文原意为 “旋转”,此处引申成 “微调”。


(1)QSpinBox 关键属性

核心信号:


调整麦当劳购物车中的份数,案例演示:

(1)在界面上创建下列内容

  • 三个下拉框:objectName 为 comboBox 到 comboBox_3
  • 三个微调框:objectName 为 spinBox 到 spinBox_3
  • 一个按钮:objectName 为 pushButton


(2)编写代码,修改 widget.cpp,给下拉框设置初始值


(3)编写代码,给按钮添加 slot 函数


(4)执行程序

        可以看到当用户选择不同的内容时,点击按钮就能获取到对应的结果,同时我们也无法输入一些超出范围的非法值。


2.5 Date Edit 和 Time Edit 微调框

使用 QDateEdit 作为日期的微调框:

使用 QTimeEdit 作为时间的微调框:

使用 QDateTimeEdit 作为时间日期的微调框:

这几个控件用法非常相似,这里以 QDateTimeEdit 为例进行介绍。

QDateTimeEdit 核心属性:

(1)关于本地时间(LocalTime)和协调世界时(UTC)

        UTC 时间是一个基于原子钟的标准时间,不受地球的自转周期影响,和格林威治时间(GMT)是非常接近的,科学家会通过精密的设备来测量并维护。

我们的计算机内部使用的时间就是基于 UTC 时间。

        本地时间则是基于不同的时区,对 UTC 时间做出了一些调整。比如咱们使用的北京时间,位于 “东八区”,就需要在 UTC 时间基础上 +8 个小时的时差。


核心信号:


实现日期计算器,案例演示:

(1)在界面上创建两个 QDateTimeEdit 和一个按钮,一个 label

QDateTimeEdit 的 objectName 为 dateTimeEdit_old 和 dateTimeEdit_new


(2)编写计算按钮的 slot 函数

  • 使用 daysTo 函数可以计算两个日期的天数
  • 使用 secsTo 函数可以计算两个时间的秒数
  • 通过(秒数 / 3600)换算成小时数,再余上 24 得到零几个小时
  • 使用 QString::number 把整数转成 QString 进行拼接


(3)执行程序


2.6 Dial 旋钮

使用 QDial 表示个旋钮

有些程序通过鼠标拖动旋钮旋转,即可完成一些相关的设置。

核心属性:

核心信号:


调整窗口透明度,案例演示:

(1)在界面上创建一个旋钮,并对其数值进行初始化


(2)编写 widget.cpp,设置旋钮的 valueChanged slot 函数


(3)运行程序

观察效果,可以看到随着拖动旋钮旋转,不透明度发生明显变化:


2.7 Slider 滑动条

使用 QSlider 表示一个滑动条:

QSlider 和 QDial 都是继承自 QAbstractSlider,因此用法上基本相同。

核心属性:

核心信号:


调整窗口大小,案例演示:

(1)在界面上创建两个滑动条,分别是水平和垂直滑动条

objectName 分别为 horizontalSlider 和 verticalSlider


(2)编写代码初始化滑动条


(3)编写滑动条的 valueChanged slot 函数


(4)执行程序

可以看到调整滑动条,窗口大小就会随之改变:


通过自定义快捷键调整滑动条位置,案例演示:

设置 - 减小 value,设置 = 增加 value

默认情况下滑动条可以通过方向键或者 pageUp / pageDown 调整大小。

(1)在界面上创建滑动条和 label


(2)创建 valueChanged 的 slot 函数


(3)修改 widget.cpp 构造函数,增加快捷键

  • 使用 QShortCut 类设置快捷键
  • 快捷键触发时,会发出 QShortcut::activated 信号,我们连接到⾃⼰写的 slot 函数


(4)编写自定义 slot 函数


(5)执行程序

观察效果,可以看到此时按下 - 和 = 就可以调整 value 的值了:


本篇完。

下一篇是Qt开发⑥Qt常用控件_下_多元素控件+容器类控件+布局管理器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

GR鲸鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值