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

3.常⽤控件

文章目录

  • 1. 控件概述
      • 笔记
  • 2. QWidget 核⼼属性
    • 2.1 核⼼属性概览
    • 2.2 enabled
    • 实践
    • 实践
    • 2.3 geometry
    • 实践
    • 实践
    • 实践
    • 2.4 windowTitle
      • 实践
    • 2.5 windowIcon
      • 实践
      • 实践
    • 2.6 windowOpacity
      • 实践
    • 2.7 cursor
      • 实践
      • 实践
    • 2.8 font
      • 实践
    • 2.9 toolTip
    • 2.10 focusPolicy
    • 2.11 styleSheet
      • 注意
  • 3. 按钮类控件
    • 3.1 Push Button
      • 快捷键设置过程加源码
    • 3.2 Radio Buttion
      • 代码
      • 源码
    • 源码
    • 3.3 Check Box
      • 实践
    • 3.4 Tool Button
  • 4. 显⽰类控件
    • 4.1 Label
    • 4.2 LCD Number
    • 4.3 ProgressBar
      • 扩展
    • 4.4 Calendar Widget
  • 5. 输⼊类控件
    • 5.1 Line Edit
    • 5.2 Text Edit
    • 5.3 Combo Box
    • 5.4 Spin Box
    • 5.5 Date Edit & Time Edit
    • 5.6 Dial
    • 5.7 Slider
  • 6. 多元素控件
    • 6.1 List Widget
    • 6.2 Table Widget
    • 6.3 Tree Widget
  • 7. 容器类控件
    • 7.1 Group Box
    • 7.2 Tab Widget
  • 8. 布局管理器
    • 8.1 垂直布局
    • 8.2 ⽔平布局
    • 8.3 ⽹格布局
    • 8.4 表单布局
    • 8.5 Spacer
  • 9. ⼩结
    • 总结

1. 控件概述

Widget 是 Qt 中的核⼼概念. 英⽂原义是 “⼩部件”, 我们此处也把它翻译为 “控件” .
控件是构成⼀个图形化界⾯的基本要素.
在这里插入图片描述
像上述⽰例中的, 按钮, 列表视图, 树形视图, 单⾏输⼊框, 多⾏输⼊框, 滚动条, 下拉框等, 都可以称为 “控件”.
Qt 作为⼀个成熟的 GUI 开发框架, 内置了⼤量的常⽤控件. 这⼀点在 Qt Designer 中就可以看到端倪.
并且 Qt 也提供了 “⾃定义控件” 的能⼒, 可以让程序猿在现有控件不能满⾜需求的时候, 对现有控件做出扩展, 或者⼿搓出新的控件.
在这里插入图片描述
🏕 综上, 学习 Qt, 其中⼀个很重要的任务就是熟悉并掌握 Qt 内置的常⽤控件.
这些控件对于我们快速开发出符合需求的界⾯, 是⾄关重要的.

关于控件体系的发展
控件是 GUI 开发中的通⽤概念. 不仅仅局限在 Qt 中.
第⼀阶段:
完全没有控件. 此时需要通过⼀些绘图 API ⼿动的绘制出按钮或者输⼊框等内容, 代码编写繁琐.
例如⽂曲星的 Lava 平台开发.
在这里插入图片描述
第⼆阶段:
只包含粗略的控件. 只是提供了按钮, 输⼊框, 单选框, 复选框等最常⽤的控件.
例如 html 的原⽣控件.
在这里插入图片描述
第三阶段:
更完整的控件体系, 基本可以覆盖到 GUI 开发中的⼤部分场景.
例如早期的 MFC, VB, C++ Builder, Qt, Delphi, 后来的 Android SDK, Java FX, 前端的各种 UI 库等.
在这里插入图片描述
上图是 前端中 的 Element-ui 中的控件概览, ⽆论是丰富程度还是颜值, 都⽐ Qt ⾃带的控件更胜⼀筹.

笔记

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. QWidget 核⼼属性

在 Qt 中, 使⽤ QWidget 类表⽰ “控件”. 像按钮, 视图, 输⼊框, 滚动条等具体的控件类, 都是继承⾃ QWidget.
可以说, QWidget 中就包含了 Qt 整个控件体系中, 通⽤的部分.
在 Qt Designer 中, 随便拖⼀个控件过来, 选中该控件, 即可在右下⽅看到 QWidget 中的属性
在这里插入图片描述
这些属性既可以通过 QtDesigner 会直接修改, 也可以通过代码的⽅式修改.
这些属性的具体含义, 在 Qt Assistant 中均有详细介绍.
在 Qt Assistant 中搜索 QWidget, 即可找到对应的⽂档说明. (或者在 Qt Creator 代码中, 选中 QWidget, 按 F1 也可).

2.1 核⼼属性概览

下列表格列出了 QWidget 中的属性及其作⽤.
属性 作⽤
enabled 设置控件是否可使⽤. true 表⽰可⽤, false 表⽰禁⽤.
geometry 位置和尺⼨. 包含 x, y, width, height 四个部分.
其中坐标是以⽗元素为参考进⾏设置的.
windowTitle 设置 widget 标题
windowIcon 设置 widget 图标
windowOpacity 设置 widget 透明度
cursor ⿏标悬停时显⽰的图标形状.
是普通箭头, 还是沙漏, 还是⼗字等形状.
在 Qt Designer 界⾯中可以清楚看到可选项.
font 字体相关属性.
涉及到字体家族, 字体⼤⼩, 粗体, 斜体, 下划线等等样式.
toolTip ⿏标悬停在 widget 上会在状态栏中显⽰的提⽰信息
toolTipDuring toolTip 显⽰的持续时间.
statusTip Widget 状态发⽣改变时显⽰的提⽰信息(⽐如按钮被按下等).
whatsThis ⿏标悬停并按下 alt+F1 时, 显⽰的帮助信息(显⽰在⼀个弹出的窗⼝中).
styleSheet 允许使⽤ CSS 来设置 widget 中的样式.
Qt 中⽀持的样式⾮常丰富, 对于前端开发⼈员上⼿是⾮常友好的.
focusPolicy 该 widget 如何获取到焦点.
• Qt::NoFocus:控件不参与焦点管理,即⽆法通过键盘或⿏标获取焦点
• Qt::TabFocus:控件可以通过Tab键获得焦点
• Qt::ClickFocus:控件可以通过⿏标点击获得焦点
• Qt::StrongFocus:控件可以通过键盘和⿏标获得焦点
• Qt::WheelFocus:控件可以通过⿏标滚轮获得焦点(在某些平台或样式中可能不可
⽤)
contextMenuPolicy 上下⽂菜单的显⽰策略.
• Qt::DefaultContextMenu:默认的上下⽂菜单策略,⽤⼾可以通过⿏标右键或键盘
快捷键触发上下⽂菜单
• Qt::NoContextMenu:禁⽤上下⽂菜单,即使⽤⼾点击⿏标右键也不会显⽰菜单
• Qt::PreventContextMenu:防⽌控件显⽰上下⽂菜单,即使⽤⼾点击⿏标右键也不
会显⽰菜单
• Qt::ActionsContextMenu:将上下⽂菜单替换为控件的“动作”菜单,⽤⼾可以通
过⿏标右键或键盘快捷键触发这个菜单
• Qt::CustomContextMenu:使⽤⾃定义的上下⽂菜单,⽤⼾可以通过⿏标右键或键
盘快捷键触发这个菜单
locale 设置语⾔和国家地区.
acceptDrops 该部件是否接受拖放操作。
如果设置为true,那么该部件就可以接收来⾃其他部件的拖放操作。当⼀个部件被拖放
到该部件上时,该部件会接收到相应的拖放事件(如dropEvent)。
如果设置为false,那么该部件将不会接收任何拖放操作。
minimumSize 控件的最⼩尺⼨. 包含最⼩宽度和最⼩⾼度.
maximumSize 控件的最⼤尺⼨. 包含最⼤宽度和最⼤⾼度.
sizePolicy 尺⼨策略. 设置控件在布局管理器中的缩放⽅式.
windowModality 指定窗⼝是否具有 “模态” ⾏为.
sizeIncrement 拖动窗⼝⼤⼩时的增量单位.
baseSize 窗⼝的基础⼤⼩, ⽤来搭配 sizeIncrement 调整组件尺⼨是计算组件应该调整到的合适
的值.
palette 调⾊板. 可以设置 widget 的颜⾊⻛格.
mouseTracking 是否要跟踪⿏标移动事件.
如果设为 true, 表⽰需要跟踪, 则⿏标划过的时候该 widget 就能持续收到⿏标移动事件.
如果设为 false, 表⽰不需要跟踪, 则⿏标划过的时候 widget 不会收到⿏标移动事件, 只
能收到⿏标按下或者释放的事件.
tabletTracking 是否跟踪触摸屏的移动事件.
类似于 mouseTracking . Qt 5.9 中引⼊的新属性.
layoutDirection 布局⽅向.
• Qt::LeftToRight:⽂本从左到右排列,也是默认值。
• Qt::RightToLeft:⽂本从右到左排列。
• Qt::GlobalAtomics:部件的布局⽅向由全局原⼦性决定(PS 这个翻译其实有点尴
尬. 其实就是根据应⽤程序中的其他 widget 布局⽅向确定的).
autoFillBackground 是否⾃动填充背景颜⾊.
windowFilePath 能够把 widget 和⼀个本地⽂件路径关联起来. PS: 其实作⽤不⼤.
accessibleName 设置 widget 的可访问名称. 这个名称可以被辅助技术 (像屏幕阅读器) 获取到.
这个属性⽤于实现⽆障碍程序的场景中 (也就是给盲⼈写的程序).
PS: 其实盲⼈也是可以使⽤电脑和⼿机的. 甚⾄盲⼈还能成为程序猿. 参⻅https://www.bilibili.com/video/BV1954y1d7z9/
ccessibleDescripti
on
设置 widget 的详细描述. 作⽤同 accessibleName
inputMethodHints 针对输⼊框有效, ⽤来提⽰⽤⼾当前能输⼊的合法数据的格式. ⽐如只能输⼊数字, 只能输⼊⽇期等.
接下来我们会介绍其中⼀些⽐较重要⽐较常⽤的属性, 并附有代码⽰例.
注意: 本章节的每个代码⽰例都是创建了单独的 Qt 项⽬. 课件中省略了创建项⽬的具体步骤.

2.2 enabled

API 说明
isEnabled() 获取到控件的可⽤状态.
setEnabled 设置控件是否可使⽤. true 表⽰可⽤, false 表⽰禁⽤.
• 所谓 “禁⽤” 指的是该控件不能接收任何⽤⼾的输⼊事件, 并且外观上往往是灰⾊的.
• 如果⼀个 widget 被禁⽤, 则该 widget 的⼦元素也被禁⽤.

代码⽰例: 使⽤代码创建⼀个禁⽤状态的按钮
widget.cpp

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* btn = new QPushButton(this);
btn->setText("这是个被禁⽤的按钮");
btn->setEnabled(false);
}

运⾏程序, 可以看到按钮处于灰⾊状态, ⽆法被点击.
在这里插入图片描述

实践

#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton *button = new QPushButton(this);button->setText("按钮");//按钮此时处于禁用状态了button->setEnabled(false);connect(button,&QPushButton::clicked,this,&Widget::handle);
}Widget::~Widget()
{delete ui;
}void Widget::handle()
{qDebug()<<"handle";
}

代码⽰例: 通过按钮2 切换按钮1 的禁⽤状态.

  1. 使⽤ Qt Designer 拖两个按钮到 Widget 中
    在这里插入图片描述
    个按钮的 objectName 分别为 pushButton 和 pushButton_2

*QObject 的 objectName 属性介绍:
QObject 是 QWidget 的⽗类. ⾥⾯最主要的属性就是 objectName .
在⼀个 Qt 程序中, objectName 相当于对象的⾝份标识, 彼此之间不能重复.
在使⽤ Qt Designer 时, 尤其是界⾯上存在多个 widget 的时候, 可以通过 objectName 获取到指定的 widget 对象.
Qt Designer ⽣成的 ui ⽂件, 本⾝是 xml 格式的. qmake 会把这个 xml ⽂件转换成 C++ 的 .h ⽂件(这个⽂件⽣成在 build ⽬录中), 构成⼀个 ui_widget 类. 每个 widget 的 objectName 最终就会成为 ui_widget 类的属性名字.
最终这个类的实例, 就是 Ui::Widget ui , 因此就可以通过形如 ui->pushButton 或者 ui->pushButton_2 这样的代码获取到界⾯上的 widget 对象了.

class Ui_Widget
{
public:
QPushButton *pushButton;
QPushButton *pushButton_2;
// ..................
}
  1. ⽣成两个按钮的 slot 函数.
    • 使⽤ isEnabled 获取当前按钮的可⽤状态.
    • 使⽤ setEnabled 修改按钮的可⽤状态. 此处是直接针对原来的可⽤状态进⾏取反后设置.
void Widget::on_pushButton_clicked()
{
qDebug() << "按下按钮";
}
void Widget::on_pushButton_2_clicked()
{
bool flag = this->ui->pushButton->isEnabled();
this->ui->pushButton->setEnabled(!flag);
}

运⾏程序, 可以看到, 初始情况下, 上⾯的按钮是可⽤状态.
点击下⽅按钮, 即可使上⽅按钮被禁⽤; 再次点击下⽅按钮, 上⽅按钮就会解除禁⽤. (禁⽤状态的按钮为
灰⾊, 且不可点击).
在这里插入图片描述
在这里插入图片描述
在 Qt Designer 中创建按钮的时候, 可以设置按钮的初始状态是 “可⽤” 还是 “禁⽤” .
如果把 enabled 这⼀列的对钩去掉, 则按钮的初始状态就是 “禁⽤” 状态.
在这里插入图片描述

实践

在这里插入图片描述
在这里插入图片描述

#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_pushButton_clicked()
{qDebug()<<"执行了槽函数";
}void Widget::on_pushButton_enable_clicked()
{//切换第一个按钮的可用状态//1、先获取到第一个按钮当前的可用状态bool enable = ui->pushButton->isEnabled();if(enable){ui->pushButton->setEnabled(false);}else{ui->pushButton->setEnabled(true);}
}

2.3 geometry

在这里插入图片描述

位置和尺⼨. 其实是四个属性的统称:
• x 横坐标
• y 纵坐标
• width 宽度
• height ⾼度
在这里插入图片描述
但是实际开发中, 我们并不会直接使⽤这⼏个属性, ⽽是通过⼀系列封装的⽅法来获取/修改.
对于 Qt 的坐标系, 不要忘记是⼀个 “左⼿坐标系”. 其中坐标系的原点是当前元素的⽗元素的左上⻆.
在这里插入图片描述

API 说明
geometry() 获取到控件的位置和尺⼨. 返回结果是⼀个 QRect, 包含了 x, y, width, height. 其中 x, y 是左上⻆的坐标.
在这里插入图片描述

setGeometry(QRect)
setGeometry(int x, int y,
int width, int height) 设置控件的位置和尺⼨. 可以直接设置⼀个 QRect, 也可以分四个属性单独设置.
在这里插入图片描述

代码⽰例: 控制按钮的位置

  1. 在界⾯中拖五个按钮.
    五个按钮的 objectName 分别为 pushButton_target , pushButton_up ,
    pushButton_down , pushButton_left , pushButton_right
    五个按钮的初始位置和⼤⼩都随意.
    在这里插入图片描述
  2. 在 widget.cpp 中编写四个按钮的 slot 函数
void Widget::on_pushButton_up_clicked()
{
QRect rect = ui->pushButton_target->geometry();
rect.setY(rect.y() - 5);
ui->pushButton_target->setGeometry(rect);
}
void Widget::on_pushButton_down_clicked()
{
QRect rect = ui->pushButton_target->geometry();
rect.setY(rect.y() + 5);
ui->pushButton_target->setGeometry(rect);
}
void Widget::on_pushButton_left_clicked()
{
QRect rect = ui->pushButton_target->geometry();
rect.setX(rect.x() - 5);
ui->pushButton_target->setGeometry(rect);
}
void Widget::on_pushButton_right_clicked()
{
QRect rect = ui->pushButton_target->geometry();
rect.setX(rect.x() + 5);
ui->pushButton_target->setGeometry(rect);
}

运⾏程序, 可以看到, 按下下⽅的四个按钮, 就会控制 target 的左上⻆的位置. 对应的按钮整个尺⼨也会
发⽣改变.
上述代码中我们是直接设置的 QRect 中的 x, y . 实际上 QRect 内部是存储了左上和右下两个点的坐标, 再通过这两个点的坐标差值计算⻓宽.
单纯修改左上坐标就会引起整个矩形的⻓宽发⽣改变.
在这里插入图片描述
如果想让整个按钮都移动, 可以改成下列代码:

void Widget::on_pushButton_up_clicked()
{
QRect rect = ui->pushButton_target->geometry();
ui->pushButton_target->setGeometry(rect.x(), rect.y() - 5, rect.width(),
rect.height());
}
void Widget::on_pushButton_down_clicked()
{
QRect rect = ui->pushButton_target->geometry();
ui->pushButton_target->setGeometry(rect.x(), rect.y() + 5, rect.width(),
rect.height());
}
void Widget::on_pushButton_left_clicked()
{
QRect rect = ui->pushButton_target->geometry();
ui->pushButton_target->setGeometry(rect.x() - 5, rect.y(), rect.width(),
rect.height());
}
void Widget::on_pushButton_right_clicked()
{
QRect rect = ui->pushButton_target->geometry();
ui->pushButton_target->setGeometry(rect.x() + 5, rect.y(), rect.width(),
rect.height());
}

上述代码使⽤ move ⽅法也是可以的.

实践

在这里插入图片描述
在这里插入图片描述

#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_pushButton_up_clicked()
{//获取到target本身的geometryQRect rect = ui->pushButton_target->geometry();qDebug()<<rect;
//    rect.setY(rect.y()-5);
//    ui->pushButton_target->setGeometry(rect);ui->pushButton_target->setGeometry(rect.x(),rect.y()-5,rect.width(),rect.height());
}void Widget::on_pushButton_down_clicked()
{QRect rect = ui->pushButton_target->geometry();qDebug()<<rect;//    rect.setY(rect.y()+5);//     ui->pushButton_target->setGeometry(rect);ui->pushButton_target->setGeometry(rect.x(),rect.y()+5,rect.width(),rect.height());
}void Widget::on_pushButton_left_clicked()
{QRect rect = ui->pushButton_target->geometry();qDebug()<<rect;
//    rect.setX(rect.x()-5);
//    ui->pushButton_target->setGeometry(rect);ui->pushButton_target->setGeometry(rect.x()-5,rect.y(),rect.width(),rect.height());
}void Widget::on_pushButton_right_clicked()
{QRect rect = ui->pushButton_target->geometry();qDebug()<<rect;
//    rect.setX(rect.x()+5);
//    ui->pushButton_target->setGeometry(rect);ui->pushButton_target->setGeometry(rect.x()+5,rect.y(),rect.width(),rect.height());
}

代码⽰例: ⼀个表⽩程序

  1. 往界⾯上拖拽两个按钮和⼀个 Label.
    Label 的 objectName 为 pushButton_accept 和 pushButton_reject , label 的
    objectName 为 label
    控件中⽂本如下图所⽰.
    在这里插入图片描述
  2. 在 widget.cpp 中添加 slot 函数.
void Widget::on_pushButton_accept_clicked()
{
ui->label->setText("⼥神快来嘴⼀个! mua~~");
}
void Widget::on_pushButton_reject_pressed()
{
// 获取窗⼝的宽度和⾼度
int width = this->geometry().width();
int height = this->geometry().height();
// 重新⽣成按钮的位置.
int x = rand() % width;
int y = rand() % height;
// 设置新的位置
ui->pushButton_reject->move(x, y);
}

运⾏程序, 可以看到, 当点击 “残忍拒绝” 时, 按钮就跑了.
在这里插入图片描述
上述代码使⽤的是 pressed, ⿏标按下事件. 如果使⽤ mouseMoveEvent, 会更狠⼀些, 只要⿏标移动过来, 按钮就跑了.
对应的代码更⿇烦⼀些 (需要⾃定义类继承⾃ QPushButton, 重写 mouseMoveEvent ⽅法). 此处暂时不展开

实践

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//设置随机种子,使用时间戳作为随机种子srand(time(0));
}Widget::~Widget()
{delete ui;}void Widget::on_pushButton_agree_clicked()
{ui->label->setText("那就笑一下吧!");
}void Widget::on_pushButton_reject_clicked()
{
//    //如果美女点击了这个按钮,就把这个按钮给挪走
//    //可以通过生成随机数的方式,来确定按钮的新的位置//    // 获取到当前程序窗口的尺寸
//    int width = this->geometry().width();
//    int height = this->geometry().height();//    //重新生成按钮位置
//    int x = rand() % width;
//    int y = rand() % height;//    //移动按钮的位置
//    ui->pushButton_reject->move(x,y);
}void Widget::on_pushButton_reject_pressed()
{//如果美女点击了这个按钮,就把这个按钮给挪走//可以通过生成随机数的方式,来确定按钮的新的位置// 获取到当前程序窗口的尺寸int width = this->geometry().width();int height = this->geometry().height();//重新生成按钮位置int x = rand() % width;int y = rand() % height;//移动按钮的位置ui->pushButton_reject->move(x,y);
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

window frame 的影响
如果 widget 作为⼀个窗⼝ (带有标题栏, 最⼩化, 最⼤化, 关闭按钮), 那么在计算尺⼨和坐标的时候就有两种算法. 包含 window frame 和 不包含 window frame.
其中 x(), y(), frameGeometry(), pos(), move() 都是按照包含 window frame 的⽅式来计算的.
其中 geometry(), width(), height(), rect(), size() 则是按照不包含 window frame 的⽅式来计算的.
当然, 如果⼀个不是作为窗⼝的 widget , 上述两类⽅式得到的结果是⼀致的.

在这里插入图片描述
相关 API
API 说明
x() 获取横坐标
计算时包含 window frame
y() 获取纵坐标
计算时包含 window frame
pos() 返回 QPoint 对象, ⾥⾯包含 x(), y(), setX(), setY() 等⽅法.
计算时包含 window frame
frameSize() 返回 QSize 对象, ⾥⾯包含 width(), height(), setWidth(), setHeight() 等⽅法.
计算时包含 window frame
frameGeometry() 返回 QRect 对象. QRect 相当于 QPoint 和 QSize 的结合体. 可以获取 x, y,
width, size.
计算时包含 window frame 对象.
width() 获取宽度
计算时不包含 window frame
height() 获取⾼度
计算时不包含 window frame
ize() 返回 QSize 对象, ⾥⾯包含 width(), height(), setWidth(), setHeight() 等⽅法.
计算时不包含 window frame
ect() 返回 QRect 对象. QRect 相当于 QPoint 和 QSize 的结合体. 可以获取并设置 x, y, width, size.
计算时不包含 window frame 对象.
eometry() 返回 QRect 对象. QRect 相当于 QPoint 和 QSize 的结合体. 可以获取 x, y,
width, size.
计算时不包含 window frame 对象.
etGeometry() 直接设置窗⼝的位置和尺⼨. 可以设置 x, y, width, height, 或者 QRect 对象.
计算时不包含 window frame 对象.

认真观察上⾯的表格, 可以看到, 其实这⾥的 API 有 frameGeometry 和 geometry 两个就⾜够完成所有的需求了.
为什么要提供这么多功能重复的 API 呢?
这个就涉及到 Qt API 的设计理念了: 尽量符合⼈的直觉.
举个栗⼦, Qt 的 QVector, 尾插元素操作, 有以下⽅法:
• push_back
• append
• +=
• <<
上述⽅法的效果都是等价的. 即使不翻阅⽂档, 单纯的凭借直觉就能把代码写对.

代码⽰例: 感受 geometry 和 frameGeometry 的区别.

  1. 在界⾯上放置⼀个按钮.
    在这里插入图片描述
  2. 在按钮的 slot 函数中, 编写代码
void Widget::on_pushButton_clicked()
{
QRect rect1 = this->geometry();
QRect rect2 = this->frameGeometry();
qDebug() << rect1;
qDebug() << rect2;
}
  1. 在构造函数中, 也添加同样的代码
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QRect rect1 = this->geometry();
QRect rect2 = this->frameGeometry();
qDebug() << rect1;
qDebug() << rect2;
}

执⾏程序, 可以看到, 构造函数中, 打印出的 geometry 和 frameGeometry 是相同的.
但是在点击按钮时, 打印的 geometry 和 frameGeometry 则存在差异.
在这里插入图片描述
注意!
在构造⽅法中, Widget 刚刚创建出来, 还没有加⼊到对象树中. 此时也就不具备 Window frame.
在按钮的 slot 函数中, 由于⽤⼾点击的时候, 对象树已经构造好了, 此时 Widget 已经具备了
Window frame, 因此在位置和尺⼨上均出现了差异.
如果把上述代码修改成打印 pushButton 的 geometry 和 frameGeometry , 结果就是完全相
同的. 因为 pushButton 并⾮是⼀个窗⼝

实践

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include<QPushButton>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//    //此处直接针对Widget 对象来使用geometry和frameGeometry,观察区别
//    QRect rect1 = this->geometry();
//    QRect rect2 = this->frameGeometry();
//    qDebug()<<rect1;
//    qDebug()<<rect2;QPushButton* button = new QPushButton(this);button->setText("按钮");button->move(100,100);connect(button,&QPushButton::clicked,this,&Widget::handle);}Widget::~Widget()
{delete ui;
}void Widget::handle()
{//此处直接针对Widget 对象来使用geometry和frameGeometry,观察区别QRect rect1 = this->geometry();QRect rect2 = this->frameGeometry();qDebug()<<rect1;qDebug()<<rect2;
}

在这里插入图片描述

2.4 windowTitle

API 说明
windowTitle() 获取到控件的窗⼝标题.
setWindowTitle(const QString& title) 设置控件的窗⼝标题.

注意! 上述设置操作针对不同的 widget 可能会有不同的⾏为.
如果是顶层 widget (独⽴窗⼝), 这个操作才会有效.
如果是⼦ widget, 这个操作⽆任何效果.

代码⽰例: 设置窗⼝标题
修改 widget.cpp

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置窗⼝标题
this->setWindowTitle("这是标题");
}

执⾏效果
在这里插入图片描述

实践

在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);this->setWindowTitle("这是一个窗口标题");QPushButton *button = new QPushButton(this);button->setText("按钮");button->setWindowTitle("这是一个窗口标题");
}Widget::~Widget()
{delete ui;
}

在这里插入图片描述

2.5 windowIcon

在这里插入图片描述

API 说明
windowIcon() 获取到控件的窗⼝图标. 返回 QIcon 对象.
setWindowIcon(const QIcon& icon) 设置控件的窗⼝图标.
同 windowTitle, 上述操作仅针对顶层 widget 有效.
代码⽰例: 设置窗⼝图标

  1. 先在 D 盘中放⼀个图⽚, 名字为 rose.jpg
    在这里插入图片描述
  2. 修改 widget.cpp
#include <QIcon>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建图标对象
QIcon icon("d:/rose.jpg");
// 设置图标
this->setWindowIcon(icon);
}

注意: Windows 下路径的分隔符可以使⽤ / 也可以使⽤ \ . 但是如果在 字符串 中使⽤ \ , 需要写
作转义字符的形式 \ . 因此我们还是更推荐使⽤ / .
3) 运⾏程序, 可以看到窗⼝图标已经成为上述图⽚
在这里插入图片描述
于此同时, 程序在任务栏中的图表也发⽣改变.

实践

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"
#include<QIcon>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//设置图标QIcon icon("d:\\rose.png");this->setWindowIcon(icon);
}Widget::~Widget()
{delete ui;
}

在这里插入图片描述

实际开发中, 我们⼀般不会在代码中通过绝对路径引⼊图⽚. 因为我们⽆法保证程序发布后, ⽤⼾的电脑上也有同样的路径.
如果使⽤相对路径, 则需要确保代码中的相对路径写法和图⽚实际所在的路径匹配 (⽐如代码中写作 “./image/rose.jpg”, 就需要在当前⼯作⽬录中创建 image ⽬录, 并把 rose.jpg 放进
去).

绝对路径: 以盘符(windows)或者以 / (Linux) 开头的路径.
相对路径: 以 . (表⽰当前路径) 或者 以 … (表⽰当前路径上级路径) 开头的路径. 其中 . 经常也会省略. 相对路径的前提是需要明确 “当前⼯作⽬录”.
对于 Qt 程序来说, 当前⼯作⽬录可能是变化的. ⽐如通过 Qt Creator 运⾏的程序, 当前⼯作⽬录是项⽬
的构建⽬录; 直接双击 exe 运⾏, ⼯作⽬录则是 exe 所在⽬录.
所谓构建⽬录, 是和 Qt 项⽬并列的, 专⻔⽤来放⽣成的临时⽂件和最终 exe 的⽬录.
在这里插入图片描述
代码⽰例: 获取当前的⼯作⽬录

  1. 在界⾯上创建⼀个⽐较⼤的 label, 确保能把路径显⽰完整. objectName 使⽤默认的 label 即可.
    在这里插入图片描述
  2. 修改 widget.cpp
    使⽤ QDir::currentPath() 即可获取到当前⼯作⽬录
#include <QDir>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 获取到当前⼯作⽬录
QString currentDir = QDir::currentPath();
// 设置⼯作⽬录到 label 中.
ui->label->setText(currentDir);
}
  1. 直接在 Qt Creator 中执⾏程序, 可以看到当前⼯作⽬录是项⽬的构建⽬录.
    在这里插入图片描述
  2. 进⼊上述构建⽬录, 把⾥⾯的 exe 拷⻉到其他⽬录中, ⽐如 D: 中. 再次执⾏程序, 可以看到当前⼯作⽬录已经发⽣改变.
    在这里插入图片描述
    要想直接能双击 exe 运⾏, 需要先把 Qt 的路径添加到 path 环境变量中, 否则会提⽰找不到动态库.
    这⼀点我们最开始搭建开发环境的时候已经操作过, 此处不再赘述.
    注意, 上述 构建⽬录, 是随时可删除的. ⽐如点击菜单栏中的 “构建” -> “清理项⽬” , 就会把这个⽬录中的内容清空掉.
    因此如果我们把图⽚⽂件放到构建⽬录中, 可能在不⼩⼼删除后就丢失了. 我们还是希望能够把图⽚和源代码放到⼀起, 并且使我们的程序⽆论拷⻉到任何位置中都能正确使⽤图⽚.
    Qt 使⽤ qrc 机制帮我们⾃动完成了上述⼯作, 更⽅便的来管理项⽬依赖的静态资源.
    在这里插入图片描述

**qrc ⽂件是⼀种XML格式的资源配置⽂件, 它⽤XML记录硬盘上的⽂件和对应的随意指定的资源名称. 应⽤程序通过资源名称来访问这些资源.
在Qt开发中, 可以通过将资源⽂件添加到项⽬中来⽅便地访问和管理这些资源. 这些资源⽂件
可以位于qrc⽂件所在⽬录的同级或其⼦⽬录下.
在构建程序的过程中, Qt 会把资源⽂件的⼆进制数据转成 cpp 代码, 编译到 exe 中. 从⽽使依赖的资源变得 “路径⽆关”.
这种资源管理机制并⾮ Qt 独有, 很多开发框架都有类似的机制. 例如 Android 的 Resources 和 AssetManager 也是类似的效果. **
代码⽰例: 通过 qrc 管理图⽚作为图标

  1. 右键项⽬, 创建⼀个 Qt Resource File (qrc ⽂件), ⽂件名随意起(不要带中⽂), 此处叫做
    resource.qrc .
    在这里插入图片描述
  2. 在 qrc 编辑器中, 添加前缀.
    在这里插入图片描述
    此处我们前缀设置成 / 即可.
    所谓的前缀, 可以理解成 “⽬录” . 这个前缀决定了后续我们如何在代码中访问资源.
  3. 在 资源编辑器 中, 点击 add Files 添加资源⽂件. 此处我们需要添加的是 rose.jpg
    在这里插入图片描述
    注意: 添加的⽂件必须是在 qrc ⽂件的同级⽬录, 或者同级⽬录的⼦⽬录中. 因此我们需要把之前 D 盘
    中的 rose.jpg 复制到上述⽬录中.
    添加完毕后, 可以在 资源编辑器 中看到添加好的⽂件
    在这里插入图片描述
  4. 在代码中使⽤ rose.jpg
    编辑 widget.cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 访问到 rose.jpg 资源
QIcon icon(":/rose.jpg");
// 设置图标
this->setWindowIcon(icon);
}

注意上述路径的访问规则.
• 使⽤ : 作为开头, 表⽰从 qrc 中读取资源.
• / 是上⾯配置的前缀
• rose.jpg 是资源的名称
需要确保代码中编写的路径和添加到 qrc 中资源的路径匹配. 否则资源⽆法被访问 (同时也不会有报错
提⽰).
5) 运⾏程序, 可以看到图标已经能正确设置.
在这里插入图片描述
接下来, 我们可以进⼊到项⽬的构建⽬录, 可以看到, ⽬录中多了⼀个 qrc_resource.cpp ⽂件. 直
接打开这个⽂件, 可以看到类似如下代码:

static const unsigned char qt_resource_data[] = {
// D:/project/ke/qt/DemoCode/DemoWindowIconQrc/rose.jpg
0x0,0x0,0x33,0x2,
0xff,
0xd8,0xff,0xe0,0x0,0x10,0x4a,0x46,0x49,0x46,0x0,0x1,0x1,0x0,0x0,0x1,0x0,
0x1,0x0,0x0,0xff,0xfe,0x0,0x3b,0x43,0x52,0x45,0x41,0x54,0x4f,0x52,0x3a,0x20,
0x67,0x64,0x2d,0x6a,0x70,0x65,0x67,0x20,0x76,0x31,0x2e,0x30,0x20,0x28,0x75,0x73
,
0x69,0x6e,0x67,0x20,0x49,0x4a,0x47,0x20,0x4a,0x50,0x45,0x47,0x20,0x76,0x36,0x32
,
0x29,0x2c,0x20,0x71,0x75,0x61,0x6c,0x69,0x74,0x79,0x20,0x3d,0x20,0x39,0x35,0xa,
0xff,0xdb,0x0,0x43,0x0,0x2,0x1,0x1,0x1,0x1,0x1,0x2,0x1,0x1,0x1,0x2,
0x2,0x2,0x2,0x2,0x4,0x3,0x2,0x2,0x2,0x2,0x5,0x4,0x4,0x3,0x4,0x6,
0x5,0x6,0x6,0x6,0x5,0x6,0x6,0x6,0x7,0x9,0x8,0x6,0x7,0x9,0x7,0x6,
0x6,0x8,0xb,0x8,0x9,0xa,0xa,0xa,0xa,0xa,0x6,0x8,0xb,0xc,0xb,0xa,
0xc,0x9,0xa,0xa,0xa,0xff,0xdb,0x0,0x43,0x1,0x2,0x2,0x2,0x2,0x2,0x2,
// .............

上述代码其实就是通过 unsigned char 数组, 把 rose.jpg 中的每个字节都记录下来. 这些代码会被编译到 exe 中. 后续⽆论 exe 被复制到哪个⽬录下, 都确保能够访问到该图⽚资源.

上述 qrc 这⼀套资源管理⽅案, 优点和缺点都很明显.
优点: 确保了图⽚, 字体, 声⾳等资源能够真正做到 “⽬录⽆关”, ⽆论如何都不会出现资源丢失的情况.
缺点: 不适合管理体积⼤的资源. 如果资源⽐较⼤ (⽐如是⼏个 MB 的⽂件), 或者资源特别多, ⽣成的最终的 exe 体积就会⽐较⼤, 程序运⾏消耗的内存也会增⼤, 程序编译的时间也会显著增加.

实践

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.6 windowOpacity

API 说明
windowOpacity() 获取到控件的不透明数值. 返回 float, 取值为 0.0 -> 1.0 其中 0.0 表⽰全透明, 1.0 表⽰完全不透明.
setWindowOpacity(float n) 设置控件的不透明数值.
代码⽰例: 调整窗⼝透明度

  1. 在界⾯上拖放两个按钮, 分别⽤来增加不透明度和减少不透明度.
    objectName 分别为 pushButton_add 和 pushButton_sub
    在这里插入图片描述
  2. 编写 wdiget.cpp, 编写两个按钮的 slot 函数
    • 点击 pushButton_sub 会减少不透明度, 也就是窗⼝越来越透明.
    • 点击 pushButton_add 会增加不透明度, 窗⼝会逐渐恢复.
void Widget::on_pushButton_add_clicked()
{
float opacity = this->windowOpacity();
if (opacity >= 1.0) {
return;
}
qDebug() << opacity;
opacity += 0.1;
this->setWindowOpacity(opacity);
}
void Widget::on_pushButton_sub_clicked()
{
float opacity = this->windowOpacity();
if (opacity <= 0.0) {
return;
}
qDebug() << opacity;
opacity -= 0.1;
this->setWindowOpacity(opacity);
}
  1. 执⾏程序, 可以看到, 点击了⼏下 - 之后, 就可以透过窗⼝看到后⾯的猫猫头了. 点击 + ⼜会逐渐恢复.
    在这里插入图片描述
    同时控制台中也可以看到 opacity 数值的变化
    在这里插入图片描述
    注意, C++ 中 float 类型遵守 IEEE 754 标准, 因此在进⾏运算的时候会有⼀定的精度误差. 因此 1 -0.1 的数值并⾮是 0.9 .

实践

#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_pushButton_add_clicked()
{float opacity = this->windowOpacity();if(opacity>=1.0){return ;}qDebug()<<opacity;opacity+=0.1;this->setWindowOpacity(opacity);
}void Widget::on_pushButton_sub_clicked()
{float opacity = this->windowOpacity();if(opacity<=0.0){return ;}qDebug()<<opacity;opacity-=0.1;this->setWindowOpacity(opacity);
}

在这里插入图片描述
第一个问题可以重点看。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.7 cursor

API 说明
cursor() 获取到当前 widget 的 cursor 属性, 返回 QCursor 对象.
当⿏标悬停在该 widget 上时, 就会显⽰出对应的形状.
setCursor(const QCursor& cursor) 设置该 widget 光标的形状. 仅在⿏标停留在该 widget 上时⽣效.
QGuiApplication::setOverrideCursor(const QCursor& cursor)
设置全局光标的形状. 对整个程序中的所有 widget 都会⽣效. 覆盖
上⾯的 setCursor 设置的内容.
在这里插入图片描述

代码⽰例: 在 Qt Designer 中设置按钮的光标

  1. 在界⾯中创建⼀个按钮.
    在这里插入图片描述

  2. 直接在右侧属性编辑区修改 cursor 属性为 “等待”
    在这里插入图片描述

  3. 运⾏程序, ⿏标悬停到按钮上, 即可看到光标的变化.
    截图⽆法截到⿏标光标, 同学们⾃⾏运⾏验证
    代码⽰例: 通过代码设置按钮的光标

  4. 编写 widget.cpp
    • 其中 Qt::WaitCursor 就是⾃带的沙漏形状的光标.

#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建按钮
QPushButton* button = new QPushButton(this);
button->resize(100, 50);
button->move(100, 100);
button->setText("这是⼀个按钮");
// 设置按钮的 cursor
button->setCursor(QCursor(Qt::WaitCursor));
}

系统内置的光标形状如下:
Ctrl + 左键 点击 Qt::WaitCursor 跳转到源码即可看到.

enum CursorShape {
ArrowCursor,
UpArrowCursor,
CrossCursor,
WaitCursor,
IBeamCursor,
SizeVerCursor,
SizeHorCursor,
SizeBDiagCursor,
SizeFDiagCursor,
SizeAllCursor,
BlankCursor,
SplitVCursor,
SplitHCursor,
PointingHandCursor,
ForbiddenCursor,
WhatsThisCursor,
BusyCursor,
OpenHandCursor,
ClosedHandCursor,
DragCopyCursor,
DragMoveCursor,
DragLinkCursor,
LastCursor = DragLinkCursor,
BitmapCursor = 24,
CustomCursor = 25
}

实践

在这里插入图片描述
在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QCursor cursor(Qt::WaitCursor);ui->pushButton->setCursor(cursor);}Widget::~Widget()
{delete ui;
}
  1. 运⾏程序, 观察效果.
    截图⽆法截到⿏标光标,⾃⾏运⾏验证.
    代码⽰例: ⾃定义⿏标光标
    Qt ⾃带的光标形状有限. 我们也可以⾃⼰找个图⽚, 做成⿏标的光标. ⽐如我们有请滑稽⽼铁.
    在这里插入图片描述
  2. 创建 qrc 资源⽂件, 添加前缀 / , 并加⼊ huaji.png
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  3. 编写 widget.cpp
#include <QPixmap>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建⼀个位图对象, 加载⾃定义光标图⽚
QPixmap pixmap(":/huaji.png");
// 缩放图⽚为 64 * 64 的尺⼨.
pixmap = pixmap.scaled(64, 64);
// 创建 QCursor 对象, 并指定 "热点" 为 (2, 2) 坐标位置.
// 所谓 "热点" 就是⿏标点击时⽣效的位置.
QCursor cursor(pixmap, 2, 2);
// 设置光标
this->setCursor(cursor);
}
  1. 运⾏程序, 观察效果
    截图⽆法截到⿏标光标, ⾃⾏运⾏验证

实践

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//访问到图片文件QPixmap pixmap(":/a.png");pixmap = pixmap.scaled(100,100);//构造光标对像QCursor cursor(pixmap,10,10);//把光标设置进去this->setCursor(cursor);
}Widget::~Widget()
{delete ui;
}

2.8 font

API 说明
font() 获取当前 widget 的字体信息. 返回 QFont 对象.
setFont(const QFont& font) 设置当前 widget 的字体信息.
关于 QFont

属性 说明
family 字体家族. ⽐如 “楷体”, “宋体”, “微软雅⿊” 等.
pointSize 字体⼤⼩
weight 字体粗细. 以数值⽅式表⽰粗细程度取值范围为 [0, 99], 数值越⼤, 越
粗.
bold 是否加粗. 设置为 true, 相当于 weight 为 75. 设置为 false 相当于 weight 为 50.
italic 是否倾斜
underline 是否带有下划线
strikeOut 是否带有删除线
代码⽰例: 在 Qt Designer 中设置字体属性

  1. 在界⾯上创建⼀个 label
    在这里插入图片描述

  2. 在右侧的属性编辑区, 设置该 label 的 font 相关属性
    在这⾥调整上述属性, 可以实时的看到⽂字的变化.
    在这里插入图片描述

  3. 执⾏程序, 观察效果
    在这里插入图片描述
    通过属性编辑这样的方式,虽然能快速方便的修改文字相关属性,但是还不够灵活。如果程序运行过程中,需要文字相关的属性—就要通过代码来操作了。
    代码⽰例: 在代码中设置字体属性

  4. 在界⾯中创建 label, objectName 使⽤默认的 label 即可.
    在这里插入图片描述

  5. 修改 widget.cpp

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置 label 的⽂本内容
ui->label->setText("这是⼀段⽂本");
// 创建字体对象
QFont font;
// 设置字体家族
font.setFamily("仿宋");
// 设置字体⼤⼩
font.setPointSize(20);
// 设置字体加粗
font.setBold(true);
// 设置字体倾斜
font.setItalic(true);
// 设置字体下划线
font.setUnderline(true);
// 设置字体删除线
font.setStrikeOut(true);
// 设置字体对象到 label 上
ui->label->setFont(font);
}
  1. 运⾏程序, 观察效果
    在这里插入图片描述
    实际开发中, 字体属性如何选择, 是⼀个 “审美问题”, ⽽不是 “技术问题”. 往往需要有⼀定的艺术细菌.
    幸运的是, 公司中往往有专业的 “美⼯” / “设计” 这样的岗位, 去做这⽅⾯的⼯作. 咱们程序员只要按照⼈家给的设计稿, 把代码写出来即可. 不必过多考虑怎样搭配才好看的问题.

实践

#include "widget.h"
#include "ui_widget.h"
#include<QLabel>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QLabel*label = new QLabel(this);label->setText("这是一段文字");//创建字体对象QFont font;font.setFamily("仿宋");font.setPixelSize(30);font.setBold(true);font.setItalic(true);font.setUnderline(true);font.setStrikeOut(true);//把font对象设置到label中label->setFont(font);
}Widget::~Widget()
{delete ui;
}

在这里插入图片描述

2.9 toolTip

一个GUI程序,界面比较复杂,按钮啥的很多。
当你把鼠标悬停到这个控件上的时候,就能弹出一个提示。
API 说明
setToolTip 设置 toolTip. ⿏标悬停在该 widget 上时会有提⽰说明.
(设置提示的内容)

setToolTipDuring 设置 toolTip 提⽰的时间. 单位 ms.
时间到后 toolTip ⾃动消失.
(设置提示的时间)
在这里插入图片描述

toolTip 只是给⽤⼾看的. 在代码中⼀般不需要获取到 toolTip.
代码⽰例: 设置按钮的 toolTip

  1. 在界⾯上拖放两个按钮. objectName 设置为 pushButton_yes 和 pushButton_no
    在这里插入图片描述
  2. 编写 widget.cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->pushButton_yes->setToolTip("这个是 yes 按钮");
ui->pushButton_yes->setToolTipDuration(3000);
ui->pushButton_no->setToolTip("这个是 no 按钮");
ui->pushButton_no->setToolTipDuration(10000);
}
  1. 运⾏程序, 观察效果
    可以看到⿏标停到按钮上之后, 就能弹出提⽰. 时间到后⾃⾏消失.
    在这里插入图片描述
    系统截图⽆法截取到⿏标光标. ⾃⾏尝试

2.10 focusPolicy

设置控件获取到焦点的策略. ⽐如某个控件能否⽤⿏标选中或者能否通过 tab 键选中.
所谓 “焦点” , 指的就是能选中这个元素. 接下来的操作 (⽐如键盘操作), 就都是针对该焦点元素进⾏的了. 这个对于 输⼊框, 单选框, 复选框等控件⾮常有⽤的.
这个事情就和 war3 或者 sc2 中, 先选中单位, 再下达命令是⼀样的.
在这里插入图片描述
在这里插入图片描述
API 说明
focusPolicy() 获取该 widget 的 focusPolicy, 返回 Qt::FocusPolicy
setFocusPolicy(Qt::FocusPolicy policy) 设置 widget 的 focusPolicy.

Qt::FocusPolicy 是⼀个枚举类型. 取值如下
• Qt::NoFocus :控件不会接收键盘焦点
• Qt::TabFocus :控件可以通过Tab键接收焦点
• Qt::ClickFocus :控件在⿏标点击时接收焦点
• Qt::StrongFocus :控件可以通过Tab键和⿏标点击接收焦点 (默认值)
• Qt::WheelFocus : 类似于 Qt::StrongFocus , 同时控件也通过⿏标滚轮获取到焦点 (新增的选项, ⼀般很少使⽤).

代码⽰例: 理解不同的 focusPolicy

  1. 在界⾯上创建四个单⾏输⼊框 (Line Edit)
  2. 修改四个输⼊框的 focusPolicy 属性为 Qt::StrongFocus (默认取值, ⼀般不⽤额外修改)
    在这里插入图片描述
  3. 修改四个输⼊框的 focusPolicy 属性为 Qt::StrongFocus (默认取值, ⼀般不⽤额外修改)
    在这里插入图片描述

此时运⾏程序, 可以看到, 使⽤⿏标单击/tab, 就可以移动光标所在输⼊框. 从⽽接下来的输⼊就是针对
这个获取焦点的输⼊框展开的了.
3) 修改第⼆个输⼊框的 focusPolicy 为 Qt::NoFocus , 则第⼆个输⼊框不会被 tab / ⿏标左键
选中.
在这里插入图片描述
此时这个输⼊框也就⽆法输⼊内容了.
4) 修改第⼆个输⼊框 focusPolicy 为 Qt::TabFocus , 则只能通过 tab 选中, ⽆法通过⿏标选
中.
在这里插入图片描述
5) 修改第⼆个输⼊框 focusPolicy 为 Qt::ClickFocus , 则只能通过 tab 选中, ⽆法通过⿏标
选中.
在这里插入图片描述

2.11 styleSheet

通过 CSS 设置 widget 的样式.
CSS (Cascading Style Sheets 层叠样式表) 本⾝属于⽹⻚前端技术. 主要就是⽤来描述界⾯的样式.
所谓 “样式”, 包括不限于 ⼤⼩, 位置, 颜⾊, 间距, 字体, 背景, 边框等.
我们平时看到的丰富多彩的⽹⻚, 就都会⽤到⼤量的 CSS.
Qt 虽然是做 GUI 开发, 但实际上和 ⽹⻚前端 有很多异曲同⼯之处. 因此 Qt 也引⼊了对于 CSS的⽀持.

SS 中可以设置的样式属性⾮常多. 基于这些属性 Qt 只能⽀持其中⼀部分, 称为 QSS (Qt Style Sheet)
. 具体的⽀持情况可以参考 Qt ⽂档中 “Qt Style Sheets Reference” 章节.
此处只是进⾏⼀个简单的演⽰.
代码⽰例: 设置⽂本样式

  1. 在界⾯上创建 label
    在这里插入图片描述
  2. 编辑右侧的 styleSheet 属性, 设置样式

在这里插入图片描述
此处的语法格式同 CSS, 使⽤键值对的⽅式设置样式. 其中键和值之间使⽤ : 分割. 键值对之间使⽤
; 分割.
另外, Qt Designer 只能对样式的基本格式进⾏校验, 不能检测出哪些样式不被 Qt ⽀持. ⽐如 textalign: center 这样的⽂本居中操作, 就⽆法⽀持.
编辑完成样式之后, 可以看到在 Qt Designer 中能够实时预览出效果.
在这里插入图片描述
3) 运⾏程序, 可以看到实际效果和预览效果基本⼀致.
代码⽰例: 实现切换夜间模式.

  1. 在界⾯上创建⼀个多⾏输⼊框 (Text Edit) 和两个按钮.
    objectName 分别为 pushButton_light 和 pushButton_dark
    在这里插入图片描述
  2. 编写按钮的 slot 函数.
    • #333 是深⾊, 但是没那么⿊.
    • #fff 是纯⽩⾊.
    • #000 是纯⿊⾊.
    使⽤在线调⾊板或者画图板, 都可以看到数字对应的颜⾊. 参考 https://www.sojson.com/web/online.html
void Widget::on_pushButton_dark_clicked()
{
this->setStyleSheet("background-color: #333");
ui->textEdit->setStyleSheet("background-color: #333; color: #fff;");
ui->pushButton_light->setStyleSheet("color: #fff");
ui->pushButton_dark->setStyleSheet("color: #fff");
}
void Widget::on_pushButton_light_clicked()
{
this->setStyleSheet("background-color: #f3f3f3");
ui->textEdit->setStyleSheet("background-color: #fff; color: #000;");
ui->pushButton_light->setStyleSheet("color: #000");
ui->pushButton_dark->setStyleSheet("color: #000");
}

关于计算机中的颜⾊表⽰
计算机中使⽤ “像素” 表⽰屏幕上的⼀个基本单位(也就是⼀个发亮的光点).
每个光点都使⽤三个字节表⽰颜⾊, 分别是 R (red), G (green), B (blue) ⼀个字节表⽰ (取值范围是 0-255, 或者 0x00-0xFF).
混合三种不同颜⾊的数值⽐例, 就能搭配出千千万万的颜⾊出来.
• rgb(255, 0, 0) 或者 #FF0000 或者 #F00 表⽰纯红⾊.
• rgb(0, 255, 0) 或者 #00FF00 或者 #0F0 表⽰纯绿⾊.
• rgb(0, 0, 255) 或者 #0000FF 或者 #00F 表⽰纯蓝⾊.
• rgb(255, 255, 255) 或者 #FFFFFF 或者 #FFF 表⽰纯⽩⾊.
• rgb(0, 0, 0) 或者 #000000 或者 #000 表⽰纯⿊⾊.
当然, 上述规则只是针对⼀般的程序⽽⾔是这么设定的. 实际的显⽰器, 可能有 8bit ⾊深或者 10bit ⾊深等, 实际情况会更加复杂.

3) 运⾏程序, 点击 “⽇间模式”, 就是浅⾊背景, 深⾊⽂字; 点击 “夜间模式”, 就是深⾊背景, 浅⾊⽂字.
在这里插入图片描述
在这里插入图片描述

注意

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3. 按钮类控件

3.1 Push Button

使⽤ QPushButton 表⽰⼀个按钮. 这也是当前我们最熟悉的⼀个控件了.
QPushButton 继承⾃ QAbstractButton . 这个类是⼀个抽象类. 是其他按钮的⽗类.
在这里插入图片描述
在 Qt Designer 中也能够看到这⾥的继承关系
在这里插入图片描述
QAbstractButton 中, 和 QPushButton 相关性较⼤的属性
属性 说明
text 按钮中的⽂本
icon 按钮中的图标
iconSize 按钮中图标的尺⼨
shortCut 按钮对应的快捷键
autoRepeat 按钮是否会重复触发. 当⿏标左键按住不放时,
如果设为 true, 则会持续产⽣⿏标点击事件;
如果设为 false, 则必须释放⿏标, 再次按下⿏标时才能产⽣点击事件. (相当于游戏⼿柄上的 “连发” 效果)
autoRepeatDelay 重复触发的延时时间. 按住按钮多久之后, 开始重复触发.
autoRepeatInterval 重复触发的周期.

1. QAbstractButton 作为 QWidget 的⼦类, 当然也继承了 QWidget 的属性. 上⾯
介绍的 QWidget ⾥的各种属性⽤法, 对于 QAbstractButton 同样适⽤. 因此表格仅列出 QAbstractButton 独有的属性.
2. Qt 的 api 设计⻛格是⾮常清晰的. 此处列出的属性都是可以 获取 和 设置 的. 例如, 使⽤ text() 获取按钮⽂本; 使⽤ setText() 设置⽂本.

3. 事实上, QPushButton 的核⼼功能都是 QAbstractButton 提供的. ⾃⾝提供的属性都⽐较简单.
其中 default 和 audoDefault 影响的是按下 enter 时⾃动点击哪个按钮的⾏为; flat 把按钮设置为扁平的样式. 这⾥我们暂时都不做过多关注.
代码⽰例: 带有图标的按钮

  1. 创建 resource.qrc ⽂件, 并导⼊图⽚
    具体操作步骤参⻅ QWidget 中的 windowIcon 部分. 此处不再赘述.
    在这里插入图片描述
  2. 在界⾯上创建⼀个 按钮
    在这里插入图片描述
  3. 修改 widget.cpp, 给按钮设置图标.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建图标
QIcon icon(":/doge.png");
// 设置图标
ui->pushButton->setIcon(icon);
// 设置图标⼤⼩
ui->pushButton->setIconSize(QSize(50, 50));
}
  1. 执⾏程序, 观察效果
    在这里插入图片描述
    代码⽰例: 带有快捷键的按钮
  2. 在界⾯中拖五个按钮.
    五个按钮的 objectName 分别为 pushButton_target , pushButton_up , pushButton_down , pushButton_left , pushButton_right 五个按钮的初始位置随意, 其中 pushButton_target 尺⼨设置为 100 * 100, 其余按钮设为 50 *
  1. ⽂本内容均清空
    在这里插入图片描述
  1. 创建 resource.qrc , 并导⼊ 5 个图⽚
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
像上述这样的图⽚资源, 可以在 “阿⾥巴巴⽮量图标库” 中查找并免费下载. https://www.iconfont.cn/illustrations/index?spm=a313x.search_index.i3.7.38493a81o3vIuH
3) 修改 widget.cpp, 设置图标资源和快捷键
使⽤ setShortcut 给按钮设置快捷键. 参数是⼀个 QKeySequence 对象. 表⽰⼀个按键序列. ⽀持组合键 (ctrl + c 这种).
• QKeySequence 的构造函数参数, 可以直接使⽤ “ctrl+c” 这样的按键名字符串表⽰, 也可以使⽤预定
义好的常量 (形如 Qt::CTRL + Qt::Key_C ) 表⽰.

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置图标
ui->pushButton_target->setIcon(QIcon(":/dog.png"));
ui->pushButton_target->setIconSize(QSize(100, 100));
ui->pushButton_up->setIcon(QIcon(":/caret-up.png"));
ui->pushButton_down->setIcon(QIcon(":/caret-down.png"));
ui->pushButton_left->setIcon(QIcon(":/caret-left.png"));
ui->pushButton_right->setIcon(QIcon(":/caret-right.png"));
// 设置快捷键
ui->pushButton_up->setShortcut(QKeySequence("w"));
ui->pushButton_down->setShortcut(QKeySequence("s"));
ui->pushButton_left->setShortcut(QKeySequence("a"));
ui->pushButton_right->setShortcut(QKeySequence("d"));
// 设置快捷键也可以写作
// ui->pushButton_up->setShortcut(QKeySequence(Qt::Key_W));
// ui->pushButton_down->setShortcut(QKeySequence(Qt::Key_S));
// ui->pushButton_left->setShortcut(QKeySequence(Qt::Key_A));
// ui->pushButton_right->setShortcut(QKeySequence(Qt::Key_D));
}
  1. 修改 widget.cpp, 设置四个⽅向键的 slot 函数.
void Widget::on_pushButton_up_clicked()
{
const QRect& rect = ui->pushButton_target->geometry();
ui->pushButton_target->setGeometry(rect.x(), rect.y() - 5, rect.width(),
rect.height());
qDebug() << "up";
}
void Widget::on_pushButton_down_clicked()
{
const QRect& rect = ui->pushButton_target->geometry();
ui->pushButton_target->setGeometry(rect.x(), rect.y() + 5, rect.width(),
rect.height());
qDebug() << "down";
}
void Widget::on_pushButton_left_clicked()
{
const QRect& rect = ui->pushButton_target->geometry();
ui->pushButton_target->setGeometry(rect.x() - 5, rect.y(), rect.width(),
rect.height());
qDebug() << "left";
}
void Widget::on_pushButton_right_clicked()
{
const QRect& rect = ui->pushButton_target->geometry();
ui->pushButton_target->setGeometry(rect.x() + 5, rect.y(), rect.width(),
rect.height());
qDebug() << "right";
}
  1. 运⾏程序, 此时点击按钮, 或者使⽤ wasd 均可让狗头移动.
    在这里插入图片描述
    代码⽰例: 按钮的重复触发
    在上述案例中, 按住快捷键, 是可以进⾏重复触发的. 但是⿏标点击则不能.
    修改 widget.cpp, 在构造函数中开启重复触发.
// 开启重复触发
ui->pushButton_up->setAutoRepeat(true);
ui->pushButton_down->setAutoRepeat(true);
ui->pushButton_left->setAutoRepeat(true);
ui->pushButton_right->setAutoRepeat(true);

此时, 按住⿏标时, 即可让狗头连续移动.

快捷键设置过程加源码

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//设置这些按钮的图标ui->pushButton_target->setIcon(QIcon(":/image/pie.png"));ui->pushButton_target->setIconSize(QSize(130,130));ui->pushButton_up->setIcon(QIcon(":/image/up.png"));ui->pushButton_up->setIconSize(QSize(40,40));ui->pushButton_down->setIcon(QIcon(":/image/down.png"));ui->pushButton_down->setIconSize(QSize(90,90));ui->pushButton_left->setIcon(QIcon(":/image/left.png"));ui->pushButton_left->setIconSize(QSize(60,60));ui->pushButton_right->setIcon(QIcon(":/image/right.png"));ui->pushButton_right->setIconSize(QSize(60,60));//直接通过按键的名字来设置,虽然简单,但是容易写错//设置快捷键
//    ui->pushButton_up->setShortcut(QKeySequence("ctrl+w"));
//    ui->pushButton_down->setShortcut(QKeySequence("s"));
//    ui->pushButton_left->setShortcut(QKeySequence("a"));
//    ui->pushButton_right->setShortcut(QKeySequence("d"));//还可以通过按键的枚举来设置按键的快捷键ui->pushButton_up->setShortcut(QKeySequence(Qt::CTRL+Qt::Key_W));ui->pushButton_down->setShortcut(QKeySequence(Qt::CTRL+Qt::Key_S));ui->pushButton_left->setShortcut(QKeySequence(Qt::CTRL+Qt::Key_A));ui->pushButton_right->setShortcut(QKeySequence(Qt::CTRL+Qt::Key_D));//开启鼠标点击连发功能(键盘的连发默认就是支持的)ui->pushButton_up->setAutoRepeat(true);ui->pushButton_down->setAutoRepeat(true);ui->pushButton_left->setAutoRepeat(true);ui->pushButton_right->setAutoRepeat(true);}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_up_clicked()
{//获取到taget的位置QRect rect = ui->pushButton_target->geometry();//基于上次的位置,设置新的位置ui->pushButton_target->setGeometry(rect.x(),rect.y()-5,rect.width(),rect.height());}
void Widget::on_pushButton_down_clicked()
{//获取到taget的位置QRect rect = ui->pushButton_target->geometry();//基于上次的位置,设置新的位置ui->pushButton_target->setGeometry(rect.x(),rect.y()+5,rect.width(),rect.height());}
void Widget::on_pushButton_left_clicked()
{//获取到taget的位置QRect rect = ui->pushButton_target->geometry();//基于上次的位置,设置新的位置ui->pushButton_target->setGeometry(rect.x()-5,rect.y(),rect.width(),rect.height());}
void Widget::on_pushButton_right_clicked()
{//获取到taget的位置QRect rect = ui->pushButton_target->geometry();//基于上次的位置,设置新的位置ui->pushButton_target->setGeometry(rect.x()+5,rect.y(),rect.width(),rect.height());}

在这里插入图片描述

3.2 Radio Buttion

QRadioButton 是单选按钮. 可以让我们在多个选项中选择⼀个.
作为 QAbstractButton 和 QWidget 的⼦类, 上⾯介绍的属性和⽤法, 对于 QRadioButton同样适⽤.
QAbstractButton 中和 QRadioButton 关系较⼤的属性
属性 说明
checkable 是否能选中
checked 是否已经被选中. checkable 是 checked 的前提条件.
autoExclusive 是否排他.
选中⼀个按钮之后是否会取消其他按钮的选中.
对于 QRadioButton 来说默认就是排他的

代码⽰例: 选择性别

  1. 在界⾯上创建⼀个 label, 和 3 个 单选按钮
    设置的⽂本如下图. 3 个单选按钮的 objectName 分别为 radioButton_male ,
    radioButton_female , radioButton_other
    在这里插入图片描述
  2. 修改 widget.cpp, 编辑三个 QRadioButton 的 slot 函数.
void Widget::on_radioButton_male_clicked()
{
ui->label->setText("你选择的性别为: 男");
}
void Widget::on_radioButton_female_clicked()
{
ui->label->setText("你选择的性别为: ⼥");
}
void Widget::on_radioButton_other_clicked()
{
ui->label->setText("你选择的性别为: 其他");
}
  1. 运⾏程序, 可以看到随着选择不同的单选按钮, label 中的提⽰⽂字就会随之变化.
    在这里插入图片描述
  2. 当前代码中, 如果程序启动, 则不会选择任何选项.
    可以修改代码, 让程序启动默认选中性别男
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置默认选中该按钮
ui->radioButton_male->setChecked(true);
ui->label->setText("你选择的性别为: 男");
}

此时运⾏程序, 即可看到 性别男 已经被选中了.
5) 当前代码中, 也可以禁⽤ “其他” 被选中.
修改 widget.cpp 的构造函数

// 禁⽤ other 选项
ui->radioButton_other->setCheckable(false);

运⾏程序, 可以看到, 点击 “其他” 按钮的时候, 虽然不会被选中, 但是可以触发点击事件, 使上⾯的 label
显⽰性别为其他.
在这里插入图片描述
使⽤ setEnabled 是更彻底的禁⽤按钮的⽅式. 此时该按钮⽆法被选中, 也⽆法响应任何输⼊

// 禁⽤ other 选项
ui->radioButton_other->setEnabled(false);

在这里插入图片描述

代码

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//添加一个默认的选项ui->radioButton_man->setChecked(true);ui->label->setText("您选择的性别为男");//禁用 其他选项//checkacle只是能够让按钮不被选中,仍然可以响应点击事件// ui->radioButton_other->setCheckable(false);ui->radioButton_other->setEnabled(false);//ui->radioButton_woman->setDisabled(true);
}Widget::~Widget()
{delete ui;
}void Widget::on_radioButton_man_clicked()
{//把界面的label的内容进行更新ui->label->setText("您选择的性别为男");}void Widget::on_radioButton_woman_clicked()
{//把界面的label的内容进行更新ui->label->setText("您选择的性别为女");
}void Widget::on_radioButton_other_clicked()
{//把界面的label的内容进行更新ui->label->setText("您选择的性别为其他");
}

代码⽰例: click, press, release, toggled 的区别
• clicked 表⽰⼀次 “点击”
• pressed 表⽰⿏标 “按下”
• released 表⽰⿏标 “释放”
• toggled 表⽰按钮状态切换.

  1. 在界⾯上创建四个单选按钮
    objectName 分别为 radioButton , radioButton_2 , radioButton_3 , radioButton_4

在这里插入图片描述
2) 给 1 创建 clicked 槽函数, 给 2 创建 pressed 槽函数, 给 3 创建 released 槽函数,
给 4 创建 toggled 槽函数

void Widget::on_radioButton_clicked()
{
qDebug() << "clicked";
}
void Widget::on_radioButton_2_pressed()
{
qDebug() << "pressed";
}
void Widget::on_radioButton_3_released()
{
qDebug() << "released";
}
void Widget::on_radioButton_4_toggled(bool checked)
{
if (checked) {
qDebug() << "toggled checked true";
} else {
qDebug() << "toggled checked false";
}
}
  1. 运⾏程序, 可以看到
    • clicked 是⼀次⿏标按下+⿏标释放触发的.
    • pressed 是⿏标按下触发的.
    • released 是⿏标释放触发的.
    • toggled 是 checked 属性改变时触发的.
    总的来说, toggled 是最适合 QRadioButton 的.

源码

#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_radioButton_clicked(bool checked)
{//此处从checked就表示了当前radioButton的选中状态qDebug()<<"clicked:"<<checked;
}void Widget::on_radioButton_2_pressed()
{qDebug()<<"pressed";
}void Widget::on_radioButton_3_released()
{qDebug()<<"released";
}void Widget::on_radioButton_4_toggled(bool checked)
{//checked状态发生了改变,就会触发这个信号qDebug()<<"toggled:"<<checked;
}

在这里插入图片描述

代码⽰例: 单选框分组

  1. 在界⾯上创建 6 个单选框, ⽤来模拟⻨当劳点餐界⾯.
    objectName 分别为 radioButton 到 radioButton_6

在这里插入图片描述
此时直接运⾏程序, 可以看到, 这六个 QRadioButton 之间都是排他的.
我们希望每⼀组内部来控制排他, 但是组和组之间不能排他.

  1. 引⼊ QButtonGroup 进⾏分组.
    修改 widget.cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建三个 QButtonGroup
QButtonGroup* group1 = new QButtonGroup(this);
QButtonGroup* group2 = new QButtonGroup(this);
QButtonGroup* group3 = new QButtonGroup(this);
// 把 QRadioButton 两两⼀组, 放到三个 QButtonGroup 中.
group1->addButton(ui->radioButton);
group1->addButton(ui->radioButton_2);
group2->addButton(ui->radioButton_3);
group2->addButton(ui->radioButton_4);
group3->addButton(ui->radioButton_5);
group3->addButton(ui->radioButton_6);
}

再次执⾏程序, 可以看到可以按照正确的分组⽅式来完成排他了.
在这里插入图片描述

源码

在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"
#include<QButtonGroup>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//使用QButtonGroup对单选按钮进行分组QButtonGroup* group1  = new QButtonGroup(this);QButtonGroup* group2  = new QButtonGroup(this);QButtonGroup* group3  = new QButtonGroup(this);//把上述单选按钮,放到不同的组里group1->addButton(ui->radioButton);group1->addButton(ui->radioButton_2);group1->addButton(ui->radioButton_3);group2->addButton(ui->radioButton_4);group2->addButton(ui->radioButton_5);group2->addButton(ui->radioButton_6);group3->addButton(ui->radioButton_7);group3->addButton(ui->radioButton_8);}Widget::~Widget()
{delete ui;
}

在这里插入图片描述

3.3 Check Box

QCheckBox 表⽰复选按钮. 可以允许选中多个.
和 QCheckBox 最相关的属性也是 checkable 和 checked , 都是继承⾃ QAbstractButton .
⾄于 QCheckBox 独有的属性 tristate ⽤来实现 “三态复选框” . 这个东西⽐较冷⻔, 咱们不做讨论.
代码⽰例: 获取复选按钮的取值

  1. 在界⾯上创建 三个复选按钮, 和⼀个普通按钮.
    objectName 分别为 checkBox_eat , checkBox_sleep , checkBox_play , 以及
    pushButton

在这里插入图片描述
2) 给 pushButton 添加 slot 函数

void Widget::on_pushButton_clicked()
{
QString result;
if (ui->checkBox_eat->isChecked()) {
result += ui->checkBox_eat->text();
}
if (ui->checkBox_sleep->isChecked()) {
result += ui->checkBox_sleep->text();
}
if (ui->checkBox_play->isChecked()) {
result += ui->checkBox_play->text();
}
qDebug() << "选中的内容: " << result;
}
  1. 运⾏程序, 可以看到点击确认按钮时, 就会在控制台中输出选中的内容.
    在这里插入图片描述
    在这里插入图片描述

实践

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{QString result = "今天你的安排是:";if(ui->checkBox_study->isChecked()){result+=ui->checkBox_study->text()+" ";}if(ui->checkBox_game->isChecked()){result+=ui->checkBox_game->text()+" ";}if(ui->checkBox_work->isChecked()){result+=ui->checkBox_work->text()+" ";}ui->label->setText(result);
}

ui
在这里插入图片描述
运行结果
在这里插入图片描述

3.4 Tool Button

QToolButton 的⼤部分功能, 和 QPushButton 是⼀致的. 但是 QToolButton 主要应⽤在⼯具栏, 菜单等场景. 这个我们暂时先不介绍.

4. 显⽰类控件

4.1 Label

QLabel 可以⽤来显⽰⽂本和图⽚.
核⼼属性如下
属性 说明
text QLabel 中的⽂本
textFormat ⽂本的格式.
• Qt::PlainText 纯⽂本
• Qt::RichText 富⽂本(⽀持 html 标签)
• Qt::MarkdownText markdown 格式
• Qt::AutoText 根据⽂本内容⾃动决定⽂本格式.
pixmap QLabel 内部包含的图⽚.
scaledContents 设为 true 表⽰内容⾃动拉伸填充 QLabel
设为 false 则不会⾃动拉伸
alignment 对⻬⽅式.
可以设置⽔平和垂直⽅向如何对⻬.
wordWrap 设为 true 内部的⽂本会⾃动换⾏.
设为 false 则内部⽂本不会⾃动换⾏.
indent 设置⽂本缩进. ⽔平和垂直⽅向都⽣效.
margin 内部⽂本和边框之间的边距.
不同于于 indent, 但是是上下左右四个⽅向都同时有效.
⽽ indent 最多只是两个⽅向有效(具体哪两个⽅向有效取决于 alignment )
openExternalLinks 是否允许打开⼀个外部的链接.
(当 QLabel ⽂本内容包含 url 的时候涉及到)
buddy 给 QLabel 关联⼀个 “伙伴” , 这样点击 QLabel 时就能激活对应的伙伴.
例如伙伴如果是⼀个 QCheckBox, 那么该 QCheckBox 就会被选中.
代码⽰例: 显⽰不同格式的⽂本

  1. 在界⾯上创建三个 QLabel
    尺⼨放⼤⼀些. objectName 分别为 label, label_2, label_3
    在这里插入图片描述

  2. 修改 widget.cpp, 设置三个 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>");
ui->label_3->setTextFormat(Qt::MarkdownText);
ui->label_3->setText("## 这是⼀段 markdown ⽂本");
}

在这里插入图片描述

  1. 运⾏程序, 观察效果
    在这里插入图片描述
    代码⽰例: 显⽰图⽚
    虽然 QPushButton 也可以通过设置图标的⽅式设置图⽚, 但是并⾮是⼀个好的选择. 更多的时候
    还是希望通过 QLabel 来作为⼀个更单纯的显⽰图⽚的⽅式.
  2. 在界⾯上创建⼀个 QLabel, objectName 为 label
    在这里插入图片描述
  3. 创建 resource.qrc ⽂件, 并把图⽚导⼊到 qrc 中.
    在这里插入图片描述
  4. 修改 widget.cpp, 给 QLabel 设置图⽚
// 设置 label ⼤⼩和窗⼝⼀样⼤
ui->label->setGeometry(0, 0, 800, 600);
QPixmap pixmap(":/huaji.png");
ui->label->setPixmap(pixmap);

执⾏程序, 观察效果
在这里插入图片描述
这个图⽚本⾝的尺⼨是 480 * 480, 并没有把 QLabel 填充满.
4) 修改代码, 设置 scaledContents 属性
// 设置内容伸缩
ui->label->setScaledContents(true);
再次运⾏, 观察效果, 可以看到图⽚已经被拉伸, 可以把窗⼝填满了.
在这里插入图片描述

  1. 此时, 如果拖动窗⼝⼤⼩, 可以看到图⽚并不会随着窗⼝⼤⼩的改变⽽同步变化.
    在这里插入图片描述
#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(":/rose.png");ui->label->setPixmap(pixmap);//自动启动拉伸,此时图片就能够填充满整个窗口了,ui->label->setScaledContents(true);}Widget::~Widget()
{delete ui;
}

为了解决这个问题, 可以在 Widget 中重写 resizeEvent 函数.
补充:
事件
用户的操作,会对应一些信号
Qt中,表示用户的操作,有两类概念,一个是信号,另一个是事件。
当用户拖拽修改窗口大小的时候,就会触发resize事件(resizeEvent)
像resize这样的事件,是连续变化的,把窗口尺寸从A拖到B的这个过程中,会触发出一系列的resizeEvent.
此时就可以借助resizeEvent来完成上述功能。
可以让Widget窗口类,重写父类(QWidget)的resizeEvent虚函数。

**在实际编程中,指定回调函数其实有很多种写法。
(1)设置函数指针
(2)设置仿函数(函数对象)
(3)设置lamda
(4)通过重写父类虚函数。(框架中拿着父类的指针调用这个函数,此时在多态机制下,实际执行的就是子类的函数了)
(5)Qt的信号槽
**
resizeEvent虚函数

// 重写 resizeEvent. 这个函数会在窗⼝⼤⼩发⽣改变时被⾃动调⽤
void Widget::resizeEvent(QResizeEvent *event) {
// 可以直接通过 this->width() 和 this->height() 设置 label 新的尺⼨, 也可以通过event 参数拿到新的尺⼨.
// ui->label->setGeometry(0, 0, this->width(), this->height());
ui->label->setGeometry(0, 0, event->size().width(), event-
>size().height());
qDebug() << event->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左上角设置到窗口的左上角这里//让QLabel铺满整个窗口QRect windowRect = this->geometry();ui->label->setGeometry(0,0,windowRect.width(),windowRect.height());QPixmap pixmap(":/rose.png");ui->label->setPixmap(pixmap);//自动启动拉伸,此时图片就能够填充满整个窗口了,ui->label->setScaledContents(true);}Widget::~Widget()
{delete ui;
}void Widget::resizeEvent(QResizeEvent *event)
{//此处的形参event是非常有用的,这里包含了触发这个resize事件这一时刻,窗口的尺寸的数值。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::WinPanel :Windows⻛格的边框
• QFrame::HLine :⽔平线边框
• QFrame::VLine :垂直线边框
• QFrame::StyledPanel :带有可点击区域的⾯板边框,但样式取决于窗⼝主题
2) 编写 widget.cpp, 给这四个 label 设置属性.

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置⽂字居中对⻬
ui->label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
ui->label->setText("垂直⽔平居中的⽂本");
// 设置⾃动换⾏
ui->label_2->setAlignment(Qt::AlignTop | Qt::AlignLeft);
ui->label_2->setWordWrap(true);
ui->label_2->setText("这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本");
// 设置⾸⾏缩进
ui->label_3->setAlignment(Qt::AlignTop | Qt::AlignLeft);
ui->label_3->setIndent(20);
ui->label_3->setText("这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本");
// 设置边距
ui->label_4->setAlignment(Qt::AlignTop | Qt::AlignLeft);
ui->label_4->setMargin(20);
ui->label_4->setText("这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本");
}

在这里插入图片描述

  1. 运⾏程序, 可以看到如下效果
    • 第⼀个 label 垂直⽔平居中
    • 第⼆个 label 设置了 wordWrap, 能够⾃动换⾏
    • 第三个 label 设置了 Indent, 左侧和上⽅和边框有间距. 右侧则没有.
    • 第四个 label 设置了 margin, 四个⽅向均有间距(图上仅体现出三个⽅向, 下⽅看不出来).
    在这里插入图片描述
    代码⽰例: 设置伙伴

  2. 创建两个 label 和 两个 radioButton.
    objectName 分别问 label , label_2 , radioButton , radioButton_2
    在这里插入图片描述
    此处把 label 中的⽂本设置为 “快捷键 &A” 这样的形式.
    其中 & 后⾯跟着的字符, 就是快捷键.
    可以通过 alt + A 的⽅式来触发该快捷键.

    但是注意, 这⾥的快捷键和 QPushButton 的不同. 需要搭配 alt 和 单个字⺟的⽅式才能触发.

  3. 编写 widget.cpp, 设置 buddy 属性
    当然这⾥也可以使⽤ Qt Designer 直接设置.

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置 label 的伙伴 widget
ui->label->setBuddy(ui->radioButton);
ui->label_2->setBuddy(ui->radioButton_2);
}
  1. 运⾏程序, 可以看到, 按下快捷键 alt + a 或者 alt + b, 即可选中对应的选项.
    在这里插入图片描述

4.2 LCD Number

QLCDNumer 是⼀个专⻔⽤来显⽰数字的控件. 类似于 “⽼式计算器” 的效果.
在这里插入图片描述
核⼼属性
属性 说明
ntValue 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 设置显⽰⻛格.
  5. QLCDNumber::Flat :平⾯的显⽰⻛格,数字呈现在⼀个平坦的表⾯上。
  6. QLCDNumber::Outline :轮廓显⽰⻛格,数字具有清晰的轮廓和阴影效
    果。
  7. QLCDNumber::Filled :填充显⽰⻛格,数字被填充颜⾊并与背景区分开。
    smallDecimalPoint 设置⽐较⼩的 ⼩数点.
    代码⽰例: 倒计时
    在这里插入图片描述
  1. 在界⾯上创建⼀个 QLCDNumber , 初始值设为 10.
    objectName 为 lcdNumber
    在这里插入图片描述
  2. 修改 widget.h 代码, 创建⼀个 QTimer 成员, 和⼀个 updateTime 函数
QTimer* timer;
void updateTime();
  1. 修改 widget.cpp, 在构造函数中初始化 QTimer
    • QTimer 表⽰定时器. 通过 start ⽅法启动定时器之后, 就会每隔⼀定周期, 触发⼀次
    QTimer::timeout 信号.
    • 使⽤ connect 把 QTimer::timeout 信号和 Widget::updateTime 连接起来, 意味着每
    次触发 QTimer::timeout 都会执⾏ Widget::updateTime
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建 QTimer 实例
timer = new QTimer(this);
// 连接信号槽. QTimer 会每隔⼀定的时间触发⼀个 timeout 信号. 现在把 timeout 信号
和 updateTime 连接起来.
// 此时意味着每次触发 timeout 信号都会伴随 updateTime 函数的执⾏.
connect(timer, &QTimer::timeout, this, &Widget::updateTime);
// 启动 QTimer, 并且规定每隔 1000ms 触发⼀次 timeout 信号.
timer->start(1000);
}
  1. 修改 widget.cpp, 实现 updateTime
    • 通过 intValue 获取到 QLCDNumber 内部的数值.
    • 如果 value 的值归 0 了, 就停⽌ QTimer . 接下来 QTimer 也就不会触发 timeout 信号了.
void Widget::updateTime() {
qDebug() << "updateTime";
int value = ui->lcdNumber->intValue();
if (value <= 0) {
// 如果时间到, 停⽌定时器.
timer->stop();
return;
}
ui->lcdNumber->display(value - 1);
}
  1. 执⾏程序, 可以看到每隔⼀秒钟, 显⽰的数字就减少 1.
    在这里插入图片描述
#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实例timer =new QTimer(this);//把QTimer的timeout信号和咱们得槽函数进行连接connect(timer,&QTimer::timeout,this,&Widget::handle);//启动定时器,参数是触发timeout的周期,单位是mstimer->start(1000);
}Widget::~Widget()
{delete ui;
}void Widget::handle()
{//  qDebug()<<"handle";//先拿到LCDNumber中的数字int value = ui->lcdNumber->intValue();if(value<=0){//数字减到0了,停止定时器timer->stop();return;}ui->lcdNumber->display(value-1);}

针对上述代码, 存在两个问题:

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

在这里插入图片描述

显然, 这个代码是不⾏的. 循环会使 Widget 的构造函数⽆法执⾏完毕, 此时界⾯是不能正确构造和显⽰
的.
2) 上述代码如果是在 Widget 构造函数中, 另起⼀个线程, 在新线程中完成 循环 + sleep 是否可以呢?
代码形如

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
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 中规定, 任何对于 GUI 上内容的操作, 必须在 主线程 中完成. 像 Widget 构造函数, 以及 connect 连接的 slot 函数, 都是在主线程中调⽤的. ⽽我们⾃⼰创建的线程则不是.
当我们⾃⼰的线程中尝试对界⾯元素进⾏修改时, Qt 程序往往会直接崩溃.

这样的约定主要是因为 GUI 中的状态往往是牵⼀发动全⾝的, 修改⼀个地⽅, 就需要同步的对其他内容进⾏调整.
⽐如调整了某个元素的尺⼨, 就可能影响到内部的⽂字位置, 或者其他元素的位置. 这⾥⼀连串的修改, 都是需要按照⼀定的顺序来完成的.
由于多线程执⾏的顺序⽆法保障, 因此 Qt 从根本上禁⽌了其他线程修改 GUI 状态, 避免后续的⼀系列问题.

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

4.3 ProgressBar

使⽤ QProgressBar 表⽰⼀个进度条.
注意, 不要把 ProgessBar 拼写成 ProcessBar !
在这里插入图片描述

核⼼属性
属性 说明
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 :表⽰总时间(以毫秒为单位)

代码⽰例: 设置进度条按时间增⻓

  1. 在界⾯上创建进度条, objectName 为 progressBar
    在这里插入图片描述
    其中最⼩值设为 0, 最⼤值设为 100. 当前值设为 0.
  2. 修改 widget.h, 创建 QTimer 和 updateProgressBar 函数.
QTimer* timer;
void updateProgressBar ();
  1. 修改 widget.cpp, 初始化 QTimer
    • 此处设置 100ms 触发⼀次 timeout 信号. 也就是⼀秒钟触发 10 次.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &Widget::updateProgressBar);
timer->start(100);
}
  1. 修改 widget.cpp, 实现 updateProgressBar
void Widget::updateProgressBar() {
int value = ui->progressBar->value();
if (value >= 100) {
timer->stop();
return;
}
ui->progressBar->setValue(value + 1);
}
  1. 运⾏程序, 可以看到进度条中的进度在快速增⻓.
    在这里插入图片描述
    在实际开发中, 进度条的取值, 往往是根据当前任务的实际进度来进⾏设置的.
    ⽐如需要读取⼀个很⼤的⽂件, 就可以获取⽂件的总的⼤⼩, 和当前读取完毕的⼤⼩, 来设置进度条的⽐例.
    由于上⾯我们介绍了 Qt 禁⽌在其他线程修改界⾯, 因此进度条的更新往往也是需要搭配定时器来完成的.
    通过定时器周期触发信号, 主线程调⽤对应的 slot 函数. 再在 slot 函数中对当前的任务进度进⾏计算, 并更新进度条的界⾯效果.

扩展

关于头文件在.h,.cpp文件中都可以。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码⽰例: 创建⼀个红⾊的进度条
上述的进度条使⽤绿⾊表⽰的, 但是考虑到有些⼈可能不喜欢绿⾊, 因此我们改成⼀个红⾊的进度条.
在这里插入图片描述
要忘了, QProgressBar 同样也是 QWidget 的⼦类, 因此我们可以使⽤ styleSheet 通过样式来修改进度条的颜⾊.

  1. 在界⾯上创建⼀个进度条.
    在这里插入图片描述
  2. 在 Qt Designer 右侧的属性编辑器中, 找到 QWidget 的 styleSheet 属性.
    编辑如下内容:
    • 其中的 chunk 是选中进度条中的每个 “块” . 使⽤ QProgressBar::text 则可以选中⽂本.
1 QProgressBar::chunk {background-color: #FF0000;}

在这里插入图片描述
同时把 QProcessBar 的 alignment 属性设置为垂直⽔平居中.
在这里插入图片描述
此处如果不设置 alignment , 进度条中的数字会跑到左上⻆. 这个怀疑是 Qt 本⾝的 bug, 暂时只能先使⽤ alignment 来⼿动调整下.
3) 执⾏程序, 可以看到如下效果. 我们就得到了⼀个红⾊的进度条.
在这里插入图片描述
通过上述⽅式, 也可以修改⽂字的颜⾊, 字体⼤⼩等样式.
在这里插入图片描述
在这里插入图片描述

4.4 Calendar Widget

QCalendarWidget 表⽰⼀个 “⽇历” , 形如
在这里插入图片描述
核⼼属性
属性 说明
selectDate 当前选中的⽇期
minimumDate 最⼩⽇期
maximumDate 最⼤⽇期
firstDayOfWeek 每周的第⼀天(也就是⽇历的第⼀列) 是周⼏.
gridVisible 是否显⽰表格的边框
selectionMode 是否允许选择⽇期
navigationBarVisible ⽇历上⽅标题是否显⽰
horizontalHeaderFormat ⽇历上⽅标题显⽰的⽇期格式
verticalHeaderFormat ⽇历第⼀列显⽰的内容格式
dateEditEnabled 是否允许⽇期被编辑
重要信号
信号 说明
selectionChanged(const QDate&) 当选中的⽇期发⽣改变时发出
activated(const QDate&) 当双击⼀个有效的⽇期或者按下回⻋键时发出,形参是⼀个QDate类型,保存了选中的⽇期
currentPageChanged(int, int)
当年份⽉份改变时发出,形参表⽰改变后的新年份和⽉份
代码⽰例: 获取选中的⽇期

  1. 在界⾯上创建⼀个 QCalendarWidget 和 ⼀个 label
    objectName 为 calendarWidget , label
    在这里插入图片描述
  2. 给 QCalendarWidget 添加 slot 函数
void Widget::on_calendarWidget_selectionChanged()
{
QDate date = ui->calendarWidget->selectedDate();
qDebug() << date;
ui->label->setText(date.toString());
}
  1. 执⾏程序, 可以看到当选择不同的⽇期时, label 中的内容就会随之改变.
    在这里插入图片描述

5. 输⼊类控件

5.1 Line Edit

QLineEdit ⽤来表⽰单⾏输⼊框. 可以输⼊⼀段⽂本, 但是不能换⾏.
核⼼属性
text 输⼊框中的⽂本
inputMask 输⼊内容格式约束
maxLength 最⼤⻓度
frame 是否添加边框
echoMode 显⽰⽅式.
• QLineEdit::Normal :这是默认值,⽂本框会显⽰输⼊的⽂本。
• QLineEdit::Password :在这种模式下,输⼊的字符会被隐藏,
通常⽤星号(*)或等号(=)代替。
• QLineEdit::NoEcho :在这种模式下,⽂本框不会显⽰任何输⼊
的字符。
cursorPosition 光标所在位置
alignment ⽂字对⻬⽅式, 设置⽔平和垂直⽅向的对⻬.
dragEnabled 是否允许拖拽
readOnly 是否是只读的(不允许修改)
placeHolderText 当输⼊框内容为空的时候, 显⽰什么样的提⽰信息
clearButtonEnabled 是否会⾃动显⽰出 “清除按钮”
在这里插入图片描述
在这里插入图片描述

核⼼信号
属性 说明
void
cursorPositionChanged(intold, int new)
当⿏标移动时发出此信号,old为先前的位置,new为新位置。
void editingFinished() 当按返回或者回⻋键时,或者⾏编辑失去焦点时,发出此信号。
void returnPressed() 当返回或回⻋键按下时发出此信号.
如果设置了验证器, 必须要验证通过, 才能触发.
void selectionChanged() 当选中的⽂本改变时,发出此信号。
void textChanged(constQString &text)
当QLineEdit中的⽂本改变时,发出此信号,text是新的⽂本。
代码对⽂本的修改能够触发这个信号.
void textEdited(const QString&text))
当QLineEdit中的⽂本改变时,发出此信号,text是新的⽂本。
代码对⽂本的修改不能触发这个信号.
代码⽰例: 录⼊个⼈信息

  1. 在界⾯上创建三个输⼊框和两个单选按钮, ⼀个普通按钮.
    三个输⼊框的 objectName 为 lineEdit_name , lineEdit_password , lineEdit_phone
    两个单选按钮的 objectName 为 radioButton_male , radioButton_female
    按钮的 objectName 为 pushButton
    在这里插入图片描述
  2. 编写 widget.cpp, 在构造函数中编写初始化代码.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 初始化第⼀个输⼊框
ui->lineEdit_name->setPlaceholderText("请输⼊姓名");
ui->lineEdit_name->setClearButtonEnabled(true);
// 初始化第⼆个输⼊框
ui->lineEdit_password->setPlaceholderText("请输⼊密码");
ui->lineEdit_password->setClearButtonEnabled(true);
ui->lineEdit_password->setEchoMode(QLineEdit::Password);
// 初始化第三个输⼊框
ui->lineEdit_phone->setPlaceholderText("请输⼊电话号码");
ui->lineEdit_password->setClearButtonEnabled(true);
// 验证⼿机号码必须是 11 位数字. 并且按照 "344" 的格式来输⼊.
ui->lineEdit_phone->setInputMask("000-0000-0000");
}
  1. 继续修改 widget.cpp, 给按钮添加 slot 函数
void Widget::on_pushButton_clicked()
{
QString gender = ui->radioButton_male->isChecked() ? "男" : "⼥";
qDebug() << "姓名: " << ui->lineEdit_name->text()
<< "密码: " << ui->lineEdit_password->text()
<< "性别: " << gender
<< "⼿机: " << ui->lineEdit_phone->text();
}
  1. 执⾏程序, 可以看到, 随着⽤⼾输⼊内容之后, 点击按钮, 就能打印到输⼊的信息
    在这里插入图片描述
    在这里插入图片描述
    inputMask 只能进⾏简单的输⼊格式校验.
    实际开发中, 基于正则表达式的⽅式是更核⼼的⽅法.

    代码⽰例: 使⽤正则表达式验证输⼊框的数据
    此处要求在输⼊框中输⼊⼀个合法的电话号码(1 开头, 11 位, 全都是数字). 如果验证不通过, 则确定按钮⽆法点击.
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//初始化第一个输入框,用来输入姓名ui->lineEdit_name->setPlaceholderText("请输入姓名:");ui->lineEdit_name->setClearButtonEnabled(true);//初始化第二个输入框,用来输入密码ui->lineEdit_password->setPlaceholderText("请输入密码");ui->lineEdit_password->setClearButtonEnabled(true);//把显示模式设置成显示密码的模式ui->lineEdit_password->setEchoMode(QLineEdit::Password);//初始化第三个输入框ui->lineEdit_phone->setPlaceholderText("请输入手机号");ui->lineEdit_phone->setClearButtonEnabled(true);//手机号码是有固定格式的,此处的0代表“数字”ui->lineEdit_phone->setInputMask("000-0000-0000");}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_submit_clicked()
{QString gender = ui->radioButton_male->isChecked()?"男":"女";qDebug()<<"姓名:"<<ui->lineEdit_name->text()<<"密码:"<<ui->lineEdit_password->text()<<"性别:"<<gender<<"手机号码:"<<ui->lineEdit_phone->text();}

在这里插入图片描述
细节:
在这里插入图片描述
在这里插入图片描述

关于正则表达式
在这里插入图片描述
在这里插入图片描述

正则表达式是⼀种在计算机中常⽤的, 使⽤特殊字符描述⼀个字符串的特征的机制. 在进⾏字符串匹配时⾮常有⽤.
正则表达式的语法还⽐较复杂, ⼀般都是随⽤随查, 不需要背下来.
参考:
正则表达式⽂档https://learn.microsoft.com/zh-cn/previous-versions/visualstudio/visual-studio-2008/ae5bf541(v=vs.90)?redirectedfrom=MSDN
正则表达式在线⼯具: https://regextester.buyaocha.com/

  1. 在界⾯上创建输⼊框和⼀个按钮.
    在这里插入图片描述
  2. 编写 widget.cpp, 把按钮初始 enabled 设为 false. 给输⼊框添加验证器.
    • 使⽤ QRegExp 创建⼀个正则表达式对象. “^1\d{10}$” 表⽰ “以 1 开头, 后⾯跟上任意的10
    个⼗进制数字”.
    • 使⽤ QRegExpValidator 创建⼀个验证器对象. Qt 中内置了四个主要的验证器对象.
    在这里插入图片描述
    QRegularExpressionValidator 在匹配性能上做出了⼀定优化. 但是从使⽤⻆度讲, 和
    QRegExpValidator 差别不⼤. 我们使⽤ QRegExpValidator 即可.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置按钮默认是禁⽤状态
ui->pushButton->setEnabled(false);
// 给 lineEdit 注册⼀个 validator
ui->lineEdit->setValidator(new QRegExpValidator(QRegExp("^1\\d{10}$")));
}
  1. 编写 widget.cpp, 给 lineEdit 添加 textEdited 信号的 slot 函数.
    • on_lineEdit_textEdited 的参数是当前输⼊框的内容.
    • 通过 lineEdit->validator() 获取到内置的验证器.
    • 通过 validate ⽅法验证⽂本是否符合要求.
    ◦ 第⼀个参数填写的是要验证的字符串. 由于参数要求是 QString& ⽽不是 const
    QString& , 需要把这个变量复制⼀下.
    ◦ 第⼆个参数是⼀个 int&, 是输出型参数. 当验证的字符串不匹配时, 返回这个字符串的⻓度. (没有
    啥实质作⽤).

返回值是⼀个枚举. QValidator::Acceptable 表⽰验证通过,
QValidator::Invalid 表⽰验证不通过.

void Widget::on_lineEdit_textEdited(const QString &arg1)
{
qDebug() << arg1;
QString content = arg1;
int pos = 0;
if (ui->lineEdit->validator()->validate(content, pos) ==
QValidator::Acceptable) {
// 验证通过, 设置按钮的可⽤状态为启⽤.
ui->pushButton->setEnabled(true);
} else {
// 验证不通过, 设置按钮的可⽤状态为禁⽤.
ui->pushButton->setEnabled(false);
}
}
  1. 执⾏程序, 观察效果. 可以看到此时尝试输⼊字⺟是⽆法输⼊的. 并且只有当输⼊的内容符合要求, 确
    定按钮才能被使⽤.
    在这里插入图片描述
    代码⽰例: 验证两次输⼊的密码⼀致
  2. 在界⾯上创建两个输⼊框和⼀个 label
    在这里插入图片描述
  3. 编写代码, 设置两个输⼊框的 echoMode 为 Password
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->lineEdit->setEchoMode(QLineEdit::Password);
ui->lineEdit_2->setEchoMode(QLineEdit::Password);
}
  1. 给两个输⼊框设置 textEdited slot 函数
oid Widget::on_lineEdit_textEdited(const QString &arg1)
{
const QString& s1 = ui->lineEdit->text();
const QString& s2 = ui->lineEdit_2->text();
if (s1.isEmpty() && s2.isEmpty()) {
ui->label->setText("密码为空!");
} else if (s1 == s2) {
ui->label->setText("两次输⼊的密码相同!");
} else {
ui->label->setText("两次输⼊的密码不同!");
}
}
void Widget::on_lineEdit_2_textEdited(const QString &arg1)
{
const QString& s1 = ui->lineEdit->text();
const QString& s2 = ui->lineEdit_2->text();
if (s1.isEmpty() && s2.isEmpty()) {
ui->label->setText("密码为空!");
} else if (s1 == s2) {
ui->label->setText("两次输⼊的密码相同!");
} else {
ui->label->setText("两次输⼊的密码不同!");
}
}
  1. 执⾏程序, 观察效果.
    可以看到当两个输⼊框内的密码相同时, 就会提⽰密码相同
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//初始化,把这两个输入框的echoMode设置一下ui->lineEdit->setEchoMode(QLineEdit::Password);ui->lineEdit_2->setEchoMode(QLineEdit::Password);
}Widget::~Widget()
{delete ui;
}void Widget::compare()
{const QString& s1 = ui->lineEdit->text();const QString&s2 = ui->lineEdit_2->text();if(s1.isEmpty()&&s2.isEmpty()){ui->label->setText("密码为空");}else if(s1==s2){ui->label->setText("两次输入的密码一致");}else{ui->label->setText("两次输入的密码不一致");}
}void Widget::on_lineEdit_textEdited(const QString &arg1)
{(void) arg1;//绕过编译器检查this->compare();
}void Widget::on_lineEdit_2_textEdited(const QString &arg1)
{(void) arg1;this->compare();
}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
代码⽰例: 切换显⽰密码

  1. 创建⼀个输⼊框和⼀个复选按钮.
    在这里插入图片描述
  2. 修改 widget.cpp, 设置输⼊框的 echoMode 为 Password
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->lineEdit->setEchoMode(QLineEdit::Password);
}
  1. 修改 widget.cpp, 给 checkBox 添加 slot 函数
void Widget::on_checkBox_toggled(bool checked)
{
if (checked) {
ui->lineEdit->setEchoMode(QLineEdit::Normal);
} else {
ui->lineEdit->setEchoMode(QLineEdit::Password);
}
}
  1. 执⾏程序, 可以看到切换复选框的状态, 就可以控制输⼊框显⽰密码
    在这里插入图片描述
    在这里插入图片描述

5.2 Text Edit

QTextEdit 表⽰多⾏输⼊框. 也是⼀个富⽂本 & markdown 编辑器. 并且能在内容超出编辑框范围时⾃动提供滚动条.

核⼼属性
属性 说明
markdown 输⼊框内持有的内容. ⽀持 markdown 格式. 能够⾃动的对markdown ⽂本进⾏
渲染成 html
html 输⼊框内持有的内容. 可以⽀持⼤部分 html 标签. 包括 img 和 table 等.
placeHolderText 输⼊框为空时提⽰的内容.
readOnly 是否是只读的
undoRedoEnable 是否开启 undo / redo 功能.
按下 ctrl + z 触发 undo (撤销功能)
按下 ctrl + y 触发 redo (撤销的撤销)
autoFormating 开启⾃动格式化.
tabstopWidth 按下缩进占多少空间
overwriteMode 是否开启覆盖写模式
acceptRichText 是否接收富⽂本内容
verticalScrollBarPolicy 垂直⽅向滚动条的出现策略
• Qt::ScrollBarAsNeeded : 根据内容⾃动决定是否需要滚动条。这是默认值。
• Qt::ScrollBarAlwaysOff : 总是关闭滚动条。
• Qt::ScrollBarAlwaysOn : 总是显⽰滚动条。
horizontalScrollBarPolicy ⽔平⽅向滚动条的出现策略
• Qt::ScrollBarAsNeeded : 根据内容⾃动决定是否需要滚动条。这是默认值。
• Qt::ScrollBarAlwaysOff : 总是关闭滚动条。
• Qt::ScrollBarAlwaysOn : 总是显⽰滚动条。
核⼼信号
信号 说明
textChanged() ⽂本内容改变时触发
selectionChanged() 选中范围改变时触发
cursorPositionChanged() 光标移动时触发
undoAvailable(bool) 可以进⾏ undo 操作时触发
redoAvailable(bool) 可以进⾏ redo 操作时触发
copyAvaiable(bool) ⽂本被选中/取消选中时触发

代码⽰例: 获取多⾏输⼊框的内容

  1. 创建⼀个多⾏输⼊框和⼀个label
    在这里插入图片描述
  2. 给多⾏输⼊框添加 slot 函数. 处理 textChanged 信号.
    • 通过 toPlainText ⽅法获取到内部的⽂本.
    • 类似的, QTextEdit 还提供了 toMarkdown 和 toHtml . 根据需要我们调整不同的获取⽅式.
void Widget::on_textEdit_textChanged()
{
//获取到多行输入框的内容
const QString& content = ui->textEdit->toPlainText();
qDebug() << content;
ui->label->setText(content);
}
  1. 执⾏程序, 可以看到当输⼊框中的内容发⽣变化时, label 中的内容同步发⽣改变.
    在这里插入图片描述
    代码⽰例: 验证输⼊框的各种信号
  2. 创建多⾏输⼊框
    在这里插入图片描述
  3. 给输⼊框添加以下⼏个 slot 函数
    • QTextEdit 中包含了⼀个 QTextCursor 对象, 通过这个对象可以获取到当前光标位置和选中的内容.
void Widget::on_textEdit_textChanged()
{
qDebug() << "[textChanged] " << ui->textEdit->toPlainText();
}
void Widget::on_textEdit_selectionChanged()
{
const QTextCursor& cursor = ui->textEdit->textCursor();
qDebug() << "[selectionChanged] " << cursor.selectedText();
}
void Widget::on_textEdit_cursorPositionChanged()
{
const QTextCursor& cursor = ui->textEdit->textCursor();
qDebug() << "[cursorPositionChanged] " << cursor.position();
}
void Widget::on_textEdit_undoAvailable(bool b)
{
qDebug() << "[undoAvailable] " << b;
}
void Widget::on_textEdit_redoAvailable(bool b)
{
qDebug() << "[redoAvailable] " << b;
}
void Widget::on_textEdit_copyAvailable(bool b)
{
qDebug() << "[copyAvailable] " << b;
}
  1. 执⾏程序, 观察结果.
    可以看到:
  1. 编写内容时, textChanged 和 cursorPositionChanged 会触发
    在这里插入图片描述
  2. 选中⼀段⽂本时, cursorPositionChanged , selectionChanged , copyAvailable 会触发.
    在这里插入图片描述
  3. 按下 ctrl + z 时, textChanged , undoAvailable , redoAvailable ,
    cursorPositionChanged 会触发
    在这里插入图片描述
  4. 按下 ctrl + y, textChanged , undoAvailable , redoAvailable ,
    cursorPositionChanged 会触发
    在这里插入图片描述

5.3 Combo Box

QComboBox 表⽰下拉框.
核⼼属性
属性 说明
currentText 当前选中的⽂本
currentIndex 当前选中的条⽬下标.
从 0 开始计算. 如果当前没有条⽬被选中, 值为 -1
editable 是否允许修改
设为 true 时, QComboBox 的⾏为就⾮常接近 QLineEdit , 也可以
设置 validator
iconSize 下拉框图标 (⼩三⻆) 的⼤⼩
maxCount 最多允许有多少个条⽬
核⼼⽅法
⽅法 说明
addItem(const QString&) 添加⼀个条⽬
currentIndex() 获取当前条⽬的下标
从 0 开始计算. 如果当前没有条⽬被选中, 值为 -1
currentText() 获取当前条⽬的⽂本内容.
核⼼信号
⽅法 说明
activated(int)
activated(const QString & text)
当⽤⼾选择了⼀个选项时发出.
这个时候相当于⽤⼾点开下拉框, 并且⿏标划过某个选项.
此时还没有确认做出选择.
currentIndexChanged(int)
currentIndexChanged(const QString
& text)
当前选项改变时发出.
此时⽤⼾已经明确的选择了⼀个选项.
⽤⼾操作或者通过程序操作都会触发这个信号.
editTextChanged(const QString &
text)
当编辑框中的⽂本改变时发出
(editable 为 true 时有效)

代码⽰例: 使⽤下拉框模拟⻨当劳点餐

  1. 在界⾯上创建三个下拉框, 和⼀个按钮.
    在这里插入图片描述
  2. 编写 widget.cpp, 初始化三个下拉框的内容
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->comboBox->addItem("巨⽆霸");
ui->comboBox->addItem("⻨辣鸡腿堡");
ui->comboBox_2->addItem("薯条");
ui->comboBox_2->addItem("⻨辣鸡翅");
ui->comboBox_3->addItem("可乐");
ui->comboBox_3->addItem("雪碧");
}
  1. 编写 widget.cpp, 给按钮添加 slot 函数
void Widget::on_pushButton_clicked()
{
qDebug() << "汉堡选择: " << ui->comboBox->currentText();
qDebug() << "⼩⻝选择: " << ui->comboBox_2->currentText();
qDebug() << "饮料选择: " << ui->comboBox_3->currentText();
}
  1. 执⾏程序, 可以看到, 在点击确定按钮时, 就能获取到当前下拉框中选中的内容.
    在这里插入图片描述
    在这里插入图片描述
    代码⽰例: 从⽂件中加载下拉框的选项
    很多时候下拉框的选项并⾮是固定的, ⽽是通过读取⽂件/读取⽹络获取到的.
  2. 在界⾯上创建⼀个下拉框
    在这里插入图片描述
  3. 创建⽂件 d:/config.txt , 编写选项. 每个选项占⼀⾏.
    形如:
    胡桃
    雷电将军
    宵宫
    神⾥绫华
    ⼋重神⼦
    ⽢⾬
  4. 修改 widget.cpp, 从⽂件中读取选项.
    • 使⽤ ifstream 打开⽂件
    • 使⽤ getline 读取每⼀⾏
    • 使⽤ QString::fromStdString 把 std::string 转成 QString
#include <fstream>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
std::ifstream file("d:/config.txt");
std::string line;
while (std::getline(file, line)) {
ui->comboBox->addItem(QString::fromStdString(line));
}
file.close();
}

源码

#include "widget.h"
#include "ui_widget.h"
#include<fstream>//流
#include<QDebug>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//需要读取文件内容,把文件中的每一行读取出来,作为ComboBox的选项std::ifstream file("C:/Users/shao1/Desktop/config.txt");if(!file.is_open()){qDebug()<<"文件打开失败!";}//按行来读取文本内容//getline函数来完成std::string line;while(std::getline(file,line)){//取到的每一行内容,设置到下拉框中ui->comboBox->addItem(QString::fromStdString(line));}file.close();
}Widget::~Widget()
{delete ui;
}
  1. 执⾏程序, 可以看到⽂件内容已经被加载到下拉框中.
    在这里插入图片描述
    **Qt 中也提供了 QFile 实现读写⽂件的功能. 当然使⽤ C++ 标准库的 std::fstream 也是完全可以的.
    之所以存在两套, 是因为 Qt 诞⽣较早 (1991 年左右), 此时 C++ 还没有完成 “标准化” 的⼯作,
    C++ 标准库这样的概念⾃然也没有诞⽣.
    因此 Qt 就⾃⼰打造了⼀套库, 实现了字符串, 容器, ⽂件操作, 多线程, ⽹络操作, 定时器, 正则表达式等内容.
    (由于 C++ 标准委员会的不作为, ⾄今仍然有些 Qt 提供的功能, 是标准库不具备的) **

5.4 Spin Box

使⽤ QSpinBox 或者 QDoubleSpinBox 表⽰ “微调框”, 它是带有按钮的输⼊框. 可以⽤来输⼊整数/浮点数. 通过点击按钮来修改数值⼤⼩.
由于 SpinBox 和 QDoubleSpinBox ⽤法基本相同, 就只介绍 SpinBox 的使⽤了.
在这里插入图片描述
Spin 英⽂原意为 “旋转”. 此处引申成 “微调”.
事实上很多术语在翻译的时候, 不⼀定⾮要按照原始的翻译来表⽰, 更追求的是 “信达雅”.
举个例⼦, 地铁上的 “Priority Seat” 会翻译成 “爱⼼专座”, ⽽不是 “优先座位”.

QSpinBox 关键属性
属性 说明
value 存储的数值.
singleStep 每次调整的 “步⻓”. 按下⼀次按钮数据变化多少.
displayInteger 数字的进制. 例如 displayInteger 设为 10, 则是按照 10 进制表⽰. 设为 2 则为 2 进制表⽰.
minimum 最⼩值
maximum 最⼤值
suffix 后缀
prefix 前缀
wrapping 是否允许换⾏
frame 是否带边框
alignment ⽂字对⻬⽅式.
readOnly 是否允许修改
buttonSymbol 按钮上的图标.

• UpDownArrows 上下箭头形式
• PlusMinus 加减号形式
• NoButtons 没有按钮
accelerated (加速的) 按下按钮时是否为快速调整模式
correctionMode 输⼊有误时如何修正.
• QAbstractSpinBox::CorrectToPreviousValue : 如果⽤⼾输⼊了⼀个⽆效的值(例如,在只能显⽰正整数的SpinBox中输⼊了负数),那么SpinBox会恢复为上⼀个有效值。例如,如果SpinBox的初始值是1,⽤⼾输⼊了-1(⽆效),然后SpinBox会恢复为1。
• QAbstractSpinBox::CorrectToNearestValue : 如果⽤⼾输⼊了⼀个
⽆效的值,SpinBox会恢复为最接近的有效值。例如,如果SpinBox的初始值是1,⽤⼾输⼊了-1(⽆效),那么SpinBox会恢复为0。
keyboardTrack 是否开启键盘跟踪.
设为 true, 每次在输⼊框输⼊⼀个数字, 都会触发⼀次 valueChanged() 和 textChanged() 信号.
设为 false, 只有在最终按下 enter 或者输⼊框失去焦点, 才会触发
valueChanged() 和 textChanged() 信号.

核⼼信号
信号 说明
textChanged(QString) 微调框的⽂本发⽣改变时会触发.
参数 QString 带有 前缀 和 后缀.
valueChanged(int) 微调框的⽂本发⽣改变时会触发.
参数 int, 表⽰当前的数值.

代码⽰例: 调整⻨当劳购物⻋中的份数.

  1. 在界⾯上创建下列内容
    • 三个下拉框: objectName 为 comboBox 到 comboBox_3
    • 三个微调框: objectName 为 spinBox 到 spinBox_3
    • ⼀个按钮: objectName 为 pushButton
    在这里插入图片描述
  2. 编写代码, 修改 widget.cpp, 给下拉框设置初始值.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 初始化下拉框
ui->comboBox->addItem("巨⽆霸");
ui->comboBox->addItem("⻨辣鸡腿堡");
ui->comboBox_2->addItem("薯条");
ui->comboBox_2->addItem("⻨辣鸡翅");
ui->comboBox_3->addItem("可乐");
ui->comboBox_3->addItem("雪碧");
// 初始化微调框
ui->spinBox->setValue(1);
ui->spinBox->setRange(1, 5);
ui->spinBox_2->setValue(1);
ui->spinBox_2->setRange(1, 5);
ui->spinBox_3->setValue(1);
ui->spinBox_3->setRange(1, 5);
}
  1. 编写代码, 给按钮添加 slot 函数
void Widget::on_pushButton_clicked()
{
qDebug() << "当前下单的内容: "
<< ui->comboBox->currentText() << ": " << ui->spinBox->value()
<< ui->comboBox_2->currentText() << ": " << ui->spinBox_2->value()
<< ui->comboBox_3->currentText() << ": " << ui->spinBox_3-
>value();
}
  1. 执⾏程序, 可以看到当⽤⼾选择不同的内容时, 点击按钮就能获取到对应的结果. 同时我们也⽆法输⼊
    ⼀些超出范围的⾮法值.
    在这里插入图片描述
    在这里插入图片描述

5.5 Date Edit & Time Edit

使⽤ QDateEdit 作为⽇期的微调框.
在这里插入图片描述
使⽤ QTimeEdit 作为时间的微调框.
在这里插入图片描述
使⽤ QDateTimeEdit 作为时间⽇期的微调框.
在这里插入图片描述
这⼏个控件⽤法⾮常相似, 我们以 QDateTimeEdit 为例进⾏介绍.
QDateTimeEdit 核⼼属性
属性 说明
dateTime 时间⽇期的值. 形如 2000/1/1 0:00:00
date 单纯⽇期的值. 形如 2001/1/1
time 单纯时间的值. 形如 0:00:00
displayFormat 时间⽇期格式. 形如 yyyy/M/d H:mm
• y 表⽰年份
• M 表⽰⽉份
• d 表⽰⽇期
• H 表⽰⼩时
• m 表⽰分钟
• s 表⽰秒
注意: 这⾥的格式化符号的含义, 不要记忆. 不同语⾔/库的设定规则
是存在差异的. ⼀定是⽤的时候再去查.
minimumDateTime 最⼩时间⽇期
maximumDateTime 最⼤时间⽇期
timeSpec • Qt::LocalTime :显⽰本地时间。
• Qt::UTC :显⽰协调世界时(UTC)。
• Qt::OffsetFromUTC :显⽰相对于UTC的偏移量(时差).

关于 本地时间(LocalTime) 和 协调世界时(UTC)
UTC 时间是⼀个基于原⼦钟的标准时间. 不受地球的⾃转周期影响. 和格林威治时间 (GMT) 是⾮常接近的. 科学家会通过精密的设备来测量并维护.
咱们的计算机内部使⽤的时间就是基于 UTC 时间.
本地时间则是基于不同的时区, 对 UTC 时间做出了⼀些调整. ⽐如咱们使⽤的北京时间, 位于 “东⼋区”, 就需要在 UTC 时间基础上 +8 个⼩时的时差.

核⼼信号
信号 说明
dateChanged(QDate) ⽇期改变时触发.
timeChanged(QTime) 时间改变时触发.
dateTimeChanged(QDateTime)
时间⽇期任意⼀个改变时触发.

代码⽰例: 实现⽇期计算器

  1. 在界⾯上创建两个 QDateTimeEdit 和⼀个按钮, ⼀个 label
    QDateTimeEdit 的 objectName 为 dateTimeEdit_old 和 dateTimeEdit_new
    在这里插入图片描述
  2. 编写计算按钮的 slot 函数
    • 使⽤ daysTo 函数可以计算两个⽇期的天数.
    • 使⽤ secsTo 函数可以计算两个时间的秒数.
    • 通过 (秒数 / 3600) 换算成⼩时数, 再余上 24 得到零⼏个⼩时.
    • 使⽤ QString::number 把整数转成 QString 进⾏拼接.
void Widget::on_pushButton_clicked()
{
// 获取到两个时间框的时间⽇期
QDateTime timeOld = ui->dateTimeEdit_old->dateTime();
QDateTime timeNew = ui->dateTimeEdit_new->dateTime();
// 计算⽇期差值
int days = timeOld.daysTo(timeNew);
int hours = (timeOld.secsTo(timeNew) / 3600) % 24;
// 设置 label 的内容
QString text = QString("爱你已经持续了 ") + QString::number(days) +
QString(" 天 零 ")
+ QString::number(hours) + QString(" 个⼩时!");
ui->label->setText(text);

在这里插入图片描述

#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_pushButton_clicked()
{//1、先获取到这两个输入框之间的时间是啥QDateTime timeold = ui->dateTimeEdit->dateTime();QDateTime timenew = ui->dateTimeEdit_2->dateTime();qDebug()<<timeold<<timenew;//2、计算日期的差值//int days =timeold.daysTo(timenew);//容易多算一天int seconds = timeold.secsTo(timenew);//3、要把秒数换算成多余出的小时int hour = (seconds/3600)%24;int days = (seconds/3600)/24;//4.把计算结果放到label中ui->label->setText(QString("爱你已经持续了")+QString::number(days)+QString("天零")+QString::number(hour)+QString("小时"));}
  1. 执⾏程序, 观察结果
    在这里插入图片描述

5.6 Dial

使⽤ QDial 表⽰⼀个 旋钮.
有些程序, 通过⿏标拖动旋钮旋转, 即可完成⼀些相关的设置.
在这里插入图片描述
核⼼属性
属性 说明
value 持有的数值.
minimum 最⼩值
maximum 最⼤值
singleStep 按下⽅向键的时候改变的步⻓.
pageStep 按下 pageUp / pageDown 的时候改变的步⻓.
sliderPosition 界⾯上旋钮显⽰的 初始位置
tracking 外观是否会跟踪数值变化.
默认值为 true. ⼀般不需要修改.
wrapping 是否允许循环调整.
即数值如果超过最⼤值, 是否允许回到最⼩值.
(调整过程能否 “套圈”)
notchesVisible 是否显⽰ 刻度线
notchTarget 刻度线之间的相对位置.
数字越⼤, 刻度线越稀疏.

核⼼信号
属性 说明
valueChanged(int) 数值改变时触发
rangeChanged(int, int) 范围变化时触发

代码⽰例: 调整窗⼝透明度

  1. 在界⾯上创建⼀个旋钮和⼀个 label
    在这里插入图片描述
  2. 编写 widget.cpp, 对旋钮初始化
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置可以循环旋转
ui->dial->setWrapping(true);
// 设置刻度线可⻅
ui->dial->setNotchesVisible(true);
// 设置最⼤值为
ui->dial->setMaximum(100);
// 设置最⼩值为
ui->dial->setMinimum(0);
// 设置初始值为
ui->dial->setValue(100);
}
  1. 编写 widget.cpp, 设置旋钮的 valueChanged slot 函数
void Widget::on_dial_valueChanged(int value)
{
ui->label->setText(QString("当前不透明度为: ") + QString::number(value));
this->setWindowOpacity((double)value / 100);
}

在这里插入图片描述

  1. 运⾏程序, 观察效果. 可以看到随着拖动旋钮旋转, 不透明度发⽣明显变化.
    在这里插入图片描述
    在这里插入图片描述

5.7 Slider

使⽤ QSlider 表⽰⼀个滑动条.
在这里插入图片描述
QSlider 和 QDial 都是继承⾃ QAbstractSlider , 因此⽤法上基本相同.
核⼼属性
属性 说明
value 持有的数值.
minimum 最⼩值
maximum 最⼤值
singleStep 按下⽅向键的时候改变的步⻓.
pageStep 按下 pageUp / pageDown 的时候改变的步⻓.
sliderPosition 滑动条显⽰的 初始位置
tracking 外观是否会跟踪数值变化.
默认值为 true. ⼀般不需要修改.
orientation 滑动条的⽅向是⽔平还是垂直
invertedAppearance 是否要翻转滑动条的⽅向
tickPosition 刻度的位置.
tickInterval 刻度的密集程度.

核⼼信号
属性 说明
valueChanged(int) 数值改变时触发
rangeChanged(int, int) 范围变化时触发
代码⽰例: 调整窗⼝⼤⼩

  1. 在界⾯上创建两个滑动条, 分别是⽔平和垂直滑动条.
    objectName 分别为 horizontalSlider 和 verticalSlider
    在这里插入图片描述
    horizontal 这个单词还⽐较复杂, ⼤家要能会读会拼. 知名 3A ⼤作 “地平线5” 就是⽤的这个单词的名词形式.
    在这里插入图片描述
  2. 编写代码初始化滑动条
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->horizontalSlider->setMinimum(500);
ui->horizontalSlider->setMaximum(2000);
ui->horizontalSlider->setSingleStep(100);
ui->horizontalSlider->setValue(800);
ui->verticalSlider->setMinimum(500);
ui->verticalSlider->setMaximum(1500);
ui->verticalSlider->setSingleStep(100);
ui->verticalSlider->setValue(600);
// 翻转朝向, 默认滑块从下向上增⻓, 改成从上往下增⻓.
ui->verticalSlider->setInvertedAppearance(true);
}
  1. 编写滑动条的 valueChanged slot 函数
void Widget::on_horizontalSlider_valueChanged(int value)
{
QRect rect = this->geometry();
this->setGeometry(rect.x(), rect.y(), value, rect.height());
qDebug() << value;
}
void Widget::on_verticalSlider_valueChanged(int value)
{
QRect rect = this->geometry();
this->setGeometry(rect.x(), rect.y(), rect.width(), value);
qDebug() << value;
}
  1. 执⾏程序, 可以看到调整滑动条, 窗⼝⼤⼩就会随之改变.
    在这里插入图片描述
    代码⽰例: 通过⾃定义快捷键调整滑动条位置.
    设置 - 减⼩ value, 设置 = 增加 value.
    默认情况下滑动条可以通过 ⽅向键 或者 pageUp / pageDown 调整⼤⼩.
  2. 在界⾯上创建滑动条和 label
    在这里插入图片描述
  3. 编写初始化代码
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setText("");
ui->horizontalSlider->setMinimum(0);
ui->horizontalSlider->setMaximum(100);
ui->horizontalSlider->setSingleStep(10);
ui->horizontalSlider->setValue(0);
}
  1. 创建 valueChanged 的 slot 函数
void Widget::on_horizontalSlider_valueChanged(int value)
{
ui->label->setText(QString::number(value));
}
  1. 修改 widget.cpp 构造函数, 增加快捷键
    • 使⽤ QShortCut 类设置快捷键.
    • 快捷键触发时, 会发出 QShortcut::activated 信号, 我们连接到⾃⼰写的 slot 函数.
// 设置快捷键
QShortcut* shortCut1 = new QShortcut(this);
shortCut1->setKey(QKeySequence("-"));
connect(shortCut1, &QShortcut::activated, this, &Widget::subValue);
QShortcut* shortCut2 = new QShortcut(this);
shortCut2->setKey(QKeySequence("="));
connect(shortCut2, &QShortcut::activated, this, &Widget::addValue);
  1. 编写⾃定义 slot 函数
void Widget::subValue() {
int value = ui->horizontalSlider->value();
ui->horizontalSlider->setValue(value - 20);
}
void Widget::addValue() {
int value = ui->horizontalSlider->value();
ui->horizontalSlider->setValue(value + 20);
}
  1. 执⾏程序, 观察效果. 可以看到此时按下 - 和 = 就可以调整 value 的值了.
    在这里插入图片描述
    源码
#include "widget.h"
#include "ui_widget.h"
#include<QShortcut>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//使用快捷键,需要用到QShortCut类//需要两个快捷键,-进行减少,=进行增加(=和+是一个按钮)QShortcut *shortCut1 = new QShortcut(this);shortCut1->setKey(QKeySequence("-"));QShortcut*shortCut2 = new QShortcut(this);shortCut2->setKey(QKeySequence("+"));//使用信号槽,感知快捷键被按下connect(shortCut1,&QShortcut::activated,this,&Widget::subValue);connect(shortCut2,&QShortcut::activated,this,&Widget::addValue);
}Widget::~Widget()
{delete ui;
}void Widget::subValue()
{//获取到当前的值int value = ui->horizontalSlider->value();if(value<=ui->horizontalSlider->minimum()){return;}ui->horizontalSlider->setValue(value-5);
}void Widget::addValue()
{//获取到当前的值int value = ui->horizontalSlider->value();if(value>=ui->horizontalSlider->maximum()){return;}ui->horizontalSlider->setValue(value+5);
}void Widget::on_horizontalSlider_valueChanged(int value)
{ui->label->setText("当前的值为:"+QString::number(value));
}

6. 多元素控件

Qt 中提供的多元素控件有:
• QListWidget
• QListView(列表)
• QTableWidget
• QTableView(表格)
• QTreeWidget
• QTreeView(树形)
xxWidget 和 xxView 之间的区别
以 QTableWidget 和 QTableView 为例.
• QTableView 是基于 MVC 设计的控件. QTableView ⾃⾝不持有数据. 使⽤ QTableView 的时候需要⽤⼾创建⼀个 Model 对象 (⽐如 QStandardModel ), 并且把 Model 和 QTableView 关联起来. 后续修改 Model 中的数据就会影响 QTableView 的显⽰; 修改
QTableView 的显⽰也会影响到 Model 中的数据(双向绑定).
• QTableWidget 则是 QTableView 的⼦类, 对 Model 进⾏了封装. 不需要⽤⼾⼿动创建 Model 对象, 直接就可以往 QTableWidget 中添加数据了.
在这里插入图片描述
在这里插入图片描述

6.1 List Widget

使⽤ QListWidget 能够显⽰⼀个纵向的列表. 形如:
在这里插入图片描述
每个选项都可以被选中.
核⼼属性
属性 说明
currentRow 当前被选中的是第⼏⾏
count ⼀共有多少⾏
sortingEnabled 是否允许排序
isWrapping 是否允许换⾏
itemAlignment 元素的对⻬⽅式
selectRectVisible 被选中的元素矩形是否可⻅
spacing 元素之间的间隔

核⼼⽅法
⽅法 说明
addItem(const QString& label)
addItem(QListWidgetItem item)
列表中添加元素.
currentItem() 返回 QListWidgetItem
表⽰当前选中的元素
setCurrentItem(QListWidgetItem* item) 设置选中哪个元素
setCurrentRow(int row) 设置选中第⼏⾏的元素
insertItem(const QString& label, int row)
insertItem(QListWidgetItem item, int row)
在指定的位置插⼊元素
item(int row) 返回 QListWidgetItem
表⽰第 row ⾏的元素
takeItem(int row) 删除指定⾏的元素, 返回 QListWidgetItem* 表⽰是哪个元素被删除了

核⼼信号
⽅法 说明
currentItemChanged(QListWidgetItem*
current, QListWidgetItem* old)
选中不同元素时会触发. 参数是当前选中的元素和之前选中的元素.
currentRowChanged(int) 选中不同元素时会触发. 参数是当前选中元素的⾏数.
itemClicked(QListWidgetItem* item) 点击某个元素时触发
itemDoubleClicked(QListWidgetItem*
item)
双击某个元素时触发
itemEntered(QListWidgetItem* item) ⿏标进⼊元素时触发

在上述介绍中, 涉及到⼀个关键的类, QListWidgetItem .
这个类表⽰ QListWidget 中的⼀个元素.
核⼼⽅法如下, 本质上就是⼀个 “⽂本+图标” 构成的.

⽅法 说明
setFont 设置字体
setIcon 设置图标
setHidden 设置隐藏
setSizeHint 设置尺⼨
setSelected 设置是否选中
setText 设置⽂本
setTextAlignment 设置⽂本对⻬⽅式.
在这里插入图片描述

代码⽰例: 使⽤ ListWidget

  1. 在界⾯上创建⼀个 ListView , 右键 => 变形为 => ListWidget , 再创建⼀个 lineEdit 和 两个按
    钮.
    注意: ListWidget 是 ListView 的⼦类, 功能⽐ ListView 更丰富. 咱们使⽤
    ListWidget 即可.
    在这里插入图片描述

  2. 编写 widget.cpp, 在构造函数中添加初始元素

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->listWidget->addItem("C++");
ui->listWidget->addItem("Java");
ui->listWidget->addItem("Python");
}
  1. 编写 listWidget 的 slot 函数
    • 此处需要判定 current 和 previous ⾮空. 初始情况下是没有元素选中的, 就导致这俩指针可
    能是 NULL.
void Widget::on_listWidget_currentItemChanged(QListWidgetItem *current,
QListWidgetItem *previous)
{
if (current != NULL && previous != NULL) {
qDebug() << "当前选中: " << current->text()
<< "之前选中: " << previous->text();
}
}

这⾥需要给 widget.h 前⾯加上 #include
4) 编写按钮的 slot 函数

void Widget::on_pushButton_clicked()
{
// 获取到输⼊框的内容
const QString& text = ui->lineEdit->text();
if (text.isEmpty()) {
return;
}
ui->listWidget->addItem(text);
}
void Widget::on_pushButton_2_clicked()
{
// 获取当前被选中的元素
int row = ui->listWidget->currentRow();
// 删除这⼀⾏
ui->listWidget->takeItem(row);
}
  1. 执⾏程序, 观察效果. 可以新增元素, 选中元素, 删除元素.
    在这里插入图片描述
    源码
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//往这里添加一些元素ui->listWidget->addItem("c++");ui->listWidget->addItem("jave");ui->listWidget->addItem("python");//    ui->listWidget->addItem(new QListWidgetItem("c++"));
//    ui->listWidget->addItem(new QListWidgetItem("java"));
//    ui->listWidget->addItem(new QListWidgetItem("python"));
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_insert_clicked()
{//1、先获取到输入框中的内容const QString&text = ui->lineEdit->text();//2、添加到QListWidget中ui->listWidget->addItem(text);
}void Widget::on_pushButton_delete_clicked()
{//1、先获取到被选中的元素是哪个int row = ui->listWidget->currentRow();if(row<0){return ;}//2、按照行号来删除元素ui->listWidget->takeItem(row);
}void Widget::on_listWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
{//通过这个槽函数来感知到变化if(current!=nullptr){qDebug()<<"当前选中的元素:"<<current->text();}if(previous!=nullptr){qDebug()<<"上次选中的元素:"<<previous->text();}
}

6.2 Table Widget

使⽤ QTableWidget 表⽰⼀个表格控件. ⼀个表格中包含若⼲⾏, 每⼀⾏⼜包含若⼲列.
表格中的每个单元格, 是⼀个 QTableWidgetItem 对象.
QTableWidget 核⼼⽅法
⽅法 说明
item(int row, int column) 根据⾏数列数获取指定的 QTableWidgetItem*
setItem(int row, int column,
QTableWidget*)
根据⾏数列数设置表格中的元素
currentItem() 返回被选中的元素 QTableWidgetItem*
currentRow() 返回被选中元素是第⼏⾏
currentColumn() 返回被选中元素是第⼏列
row(QTableWidgetItem* ) 获取指定 item 是第⼏⾏
column(QTableWidgetItem* ) 获取指定 item 是第⼏列
rowCount() 获取⾏数
columnCount() 获取列数
insertRow(int row) 在第 row ⾏处插⼊新⾏
insertColumn(int column) 在第 column 列插⼊新列
removeRow(int row) 删除第 row ⾏
removeColumn(int column) 删除第 column 列
setHorizontalHeaderItem(int
column, QTableWidget*)
设置指定列的表头
setVerticalHeaderItem(int row,
QTableWidget*)
设置指定⾏的表头

QTableWidgetItem 核⼼信号
信号 说明
cellClicked(int row, int column) 点击单元格时触发
cellDoubleClicked(int row, int
column)
双击单元格时触发
cellEntered(int row, int column) ⿏标进⼊单元格时触发
currentCellChanged(int row, int
column, int previousRow, int
previousColumn)
选中不同单元格时触发
QTableWidgetItem 核⼼⽅法
⽅法 说明
row() 获取当前是第⼏⾏
column() 获取当前是第⼏列
setText(const QString&) 设置⽂本
setTextAlignment(int) 设置⽂本对⻬
setIcon(const QIcon&) 设置图标
setSelected(bool) 设置被选中
setSizeHints(const QSize&) 设置尺⼨
setFont(const QFont&) 设置字体
代码⽰例: 使⽤ QTableWidget

  1. 在界⾯上创建 QTableWidget 和 三个按钮, ⼀个输⼊框
    注意: QTableWidget 是 QTableView 的⼦类, 功能⽐ QTableView 更丰富. 咱们使⽤
    QTableWidget 即可.
    在这里插入图片描述
  2. 编写 widget.cpp 构造函数, 构造表格中的初始数据.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建 3 ⾏
ui->tableWidget->insertRow(0);
ui->tableWidget->insertRow(1);
ui->tableWidget->insertRow(2);
// 创建 3 列
ui->tableWidget->insertColumn(0);
ui->tableWidget->insertColumn(1);
ui->tableWidget->insertColumn(2);
// 给 3 列设定列名
ui->tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem("学号"));
ui->tableWidget->setHorizontalHeaderItem(1, new QTableWidgetItem("姓名"));
ui->tableWidget->setHorizontalHeaderItem(2, new QTableWidgetItem("年龄"));
// 设置初始数据
ui->tableWidget->setItem(0, 0, new QTableWidgetItem("1001"));
ui->tableWidget->setItem(0, 1, new QTableWidgetItem("张三"));
ui->tableWidget->setItem(0, 2, new QTableWidgetItem("20"));
ui->tableWidget->setItem(1, 0, new QTableWidgetItem("1002"));
ui->tableWidget->setItem(1, 1, new QTableWidgetItem("李四"));
ui->tableWidget->setItem(1, 2, new QTableWidgetItem("21"));
ui->tableWidget->setItem(2, 0, new QTableWidgetItem("1003"));
ui->tableWidget->setItem(2, 1, new QTableWidgetItem("王五"));
ui->tableWidget->setItem(2, 2, new QTableWidgetItem("19"));
}
  1. 编写按钮的 slot 函数
void Widget::on_pushButton_addRow_clicked()
{
// 1. 获取到⾏数
int rowCount = ui->tableWidget->rowCount();
// 2. 插⼊新⾏
ui->tableWidget->insertRow(rowCount);
}
void Widget::on_pushButton_delRow_clicked()
{
// 1. 获取选中的⾏号
int curRow = ui->tableWidget->currentRow();
// 2. 删除对应⾏
ui->tableWidget->removeRow(curRow);
}
void Widget::on_pushButton_addCol_clicked()
{
// 1. 获取到列数
int colCount = ui->tableWidget->columnCount();
// 2. 插⼊新列
ui->tableWidget->insertColumn(colCount);
// 3. 设置列名
const QString& name = ui->lineEdit->text();
ui->tableWidget->setHorizontalHeaderItem(colCount, new
QTableWidgetItem(name));
}
void Widget::on_pushButton_delCol_clicked()
{
// 1. 获取选中的列号
int curCol = ui->tableWidget->currentColumn();
// 2. 删除对应的列
ui->tableWidget->removeColumn(curCol);
}
  1. 执⾏程序, 即可完成表格的基本操作.
    在这里插入图片描述
    默认情况下, 单元格中的内容直接就是可编辑的.
    如果不想让⽤⼾编辑, 可以设置 ui->tableWidget-

setEditTriggers(QAbstractItemView::NoEditTriggers);

源码

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//创建3行ui->tableWidget->insertRow(0);ui->tableWidget->insertRow(1);ui->tableWidget->insertRow(2);//创建3个列ui->tableWidget->insertColumn(0);ui->tableWidget->insertColumn(1);ui->tableWidget->insertColumn(2);//给3个列设定列名(设置水平方向的表头)ui->tableWidget->setHorizontalHeaderItem(0,new QTableWidgetItem("学号"));ui->tableWidget->setHorizontalHeaderItem(1,new QTableWidgetItem("姓名"));ui->tableWidget->setHorizontalHeaderItem(2,new QTableWidgetItem("年龄"));//给表格中添加数据ui->tableWidget->setItem(0,0,new QTableWidgetItem("1"));ui->tableWidget->setItem(0,1,new QTableWidgetItem("张三"));ui->tableWidget->setItem(0,2,new QTableWidgetItem("18"));ui->tableWidget->setItem(1,0,new QTableWidgetItem("2"));ui->tableWidget->setItem(1,1,new QTableWidgetItem("李四"));ui->tableWidget->setItem(1,2,new QTableWidgetItem("19"));ui->tableWidget->setItem(2,0,new QTableWidgetItem("3"));ui->tableWidget->setItem(2,1,new QTableWidgetItem("王五"));ui->tableWidget->setItem(2,2,new QTableWidgetItem("17"));
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_insertrow_clicked()
{//需要知道当前一共有多少行int rowcount = ui->tableWidget->currentRow();//在最后一行之后新增一行//注意此处的参数是“下标”,表示你新增的这一行是第几行ui->tableWidget->insertRow(rowcount);
}void Widget::on_pushButton_deleterow_clicked()
{//获取到选中的行号int currow = ui->tableWidget->currentRow();//删除这一行ui->tableWidget->removeRow(currow);
}void Widget::on_pushButton_insetculomn_clicked()
{//先获取到一共几列int colcount = ui->tableWidget->columnCount();//在对应位置新增这一列ui->tableWidget->insertColumn(colcount);//设置列名(从输入框中获取到)const QString& text = ui->lineEdit->text();ui->tableWidget->setHorizontalHeaderItem(colcount,new QTableWidgetItem(text));}void Widget::on_pushButton_4_clicked()
{//获取到选中的列好int curcol = ui->tableWidget->currentColumn();//删除这一列ui->tableWidget->removeColumn(curcol);
}

6.3 Tree Widget

使⽤ QTreeWidget 表⽰⼀个树形控件. ⾥⾯的每个元素, 都是⼀个 QTreeWidgetItem , 每个 QTreeWidgetItem 可以包含多个⽂本和图标, 每个⽂本/图标为⼀个 列.
可以给 QTreeWidget 设置顶层节点(顶层节点可以有多个), 然后再给顶层节点添加⼦节点, 从⽽构成树形结构.
注意:
这棵树没有根节点,是从下一层开始算的。

QTreeWidget 核⼼⽅法
⽅法 说明
clear 清空所有⼦节点
addTopLevelItem(QTreeWidgetItem* item) 新增顶层节点
topLevelItem(int index) 获取指定下标的顶层节点.
topLevelItemCount() 获取顶层节点个数
indexOfTopLevelItem(QTreeWidgetItem* item)
查询指定节点是顶层节点中的下标
takeTopLevelItem(int index) 删除指定的顶层节点. 返回 QTreeWidgetItem* 表⽰被删除
的元素
currentItem() 获取到当前选中的节点, 返回 QTreeWidgetItem*
setCurrentItem(QTreeWidgetItem* item) 选中指定节点
setExpanded(bool) 展开/关闭节点
setHeaderLabel(const QString& text) 设置 TreeWidget 的 header 名称.

QTreeWidget 核⼼信号
信号 说明
currentItemChanged(QTreeWidgetItem*
current, QTreeWidgetItem* old)
切换选中元素时触发
itemClicked(QTreeWidgetItem* item, int col) 点击元素时触发
itemDoubleClicked(QTreeWidgetItem* item,
int col)
双击元素时触发
itemEntered(QTreeWidgetItem* item, int col) ⿏标进⼊时触发
itemExpanded(QTreeWidgetItem* item) 元素被展开时触发
itemCollapsend(QTreeWidgetItem* item) 元素被折叠时触发
QTreeWidgetItem 核⼼属性
属性 说明
text 持有的⽂本
textAlignment ⽂本对⻬⽅式
icon 持有的图表
font ⽂本字体
hidden 是否隐藏
disabled 是否禁⽤
expand 是否展开
sizeHint 尺⼨⼤⼩
selected 是否选中
QTreeWidgetItem 核⼼⽅法
⽅法 说明
addChild(QTreeWidgetItem* child) 新增⼦节点
childCount() ⼦节点的个数
child(int index) 获取指定下标的⼦节点. 返回 QTreeWidgetItem*
takeChild(int index) 删除对应下标的⼦节点
removeChild(QTreeWidgetItem*
child)
删除对应的⼦节点
parent() 获取该元素的⽗节点

代码⽰例: 使⽤ QTreeWidget

  1. 在界⾯上创建⼀个 TreeView , 右键 => 变形为 => TreeWidget , 再创建⼀个 lineEdit 和 两个按
    钮.
    注意: TreeWidget 是 TreeView 的⼦类, 功能⽐ TreeView 更丰富. 咱们使⽤ TreeWidget
    即可

在这里插入图片描述
2) 编写代码, 构造初始数据

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->treeWidget->setHeaderLabel("动物");
QTreeWidgetItem* item1 = new QTreeWidgetItem();
item1->setText(0, "猫");
ui->treeWidget->addTopLevelItem(item1);
QTreeWidgetItem* item2 = new QTreeWidgetItem();
item2->setText(0, "狗");
ui->treeWidget->addTopLevelItem(item2);
QTreeWidgetItem* item3 = new QTreeWidgetItem();
item3->setText(0, "⻦");
ui->treeWidget->addTopLevelItem(item3);
}
  1. 编写代码, 实现按钮的 slot 函数
void Widget::on_pushButton_clicked()
{
// 获取输⼊框内容
const QString& text = ui->lineEdit->text();
if (text.isEmpty()) {
return;
}
// 添加到顶层节点中
QTreeWidgetItem* item = new QTreeWidgetItem();
item->setText(0, text);
ui->treeWidget->addTopLevelItem(item);
}
void Widget::on_pushButton_2_clicked()
{
// 获取输⼊框内容
const QString& text = ui->lineEdit->text();
if (text.isEmpty()) {
return;
}
// 获取到当前选中的节点
QTreeWidgetItem* currentItem = ui->treeWidget->currentItem();
if (currentItem == NULL) {
return;
}
// 构造新的 item
QTreeWidgetItem* newItem = new QTreeWidgetItem();
newItem->setText(0, text);
// 添加 item 到选中节点
currentItem->addChild(newItem);
// 展开⽗节点
currentItem->setExpanded(true);
}
void Widget::on_pushButton_3_clicked()
{
// 获取到当前选中的节点
QTreeWidgetItem* currentItem = ui->treeWidget->currentItem();
if (currentItem == NULL) {
return;
}
// 获取当前节点的⽗节点
QTreeWidgetItem* parent = currentItem->parent();
if (parent == NULL) {
// 顶层节点
int index = ui->treeWidget->indexOfTopLevelItem(currentItem);
ui->treeWidget->takeTopLevelItem(index);
} else {
// ⾮顶层节点
parent->removeChild(currentItem);
}
}
  1. 执⾏程序, 可以针对树形框进⾏编辑.
    在这里插入图片描述

源码

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//设置根节点的名字ui->treeWidget->setHeaderLabel("动物");//新增顶层节点QTreeWidgetItem *item1 = new QTreeWidgetItem();//每个节点都可以设置多一个列,此处为了简单就只设置一列了。item1->setText(0,"猫");//添加到顶层节点中ui->treeWidget->addTopLevelItem(item1);//新增顶层节点QTreeWidgetItem *item2 = new QTreeWidgetItem();//每个节点都可以设置多一个列,此处为了简单就只设置一列了。item2->setText(0,"狗");//添加到顶层节点中ui->treeWidget->addTopLevelItem(item2);//新增顶层节点QTreeWidgetItem *item3= new QTreeWidgetItem();//每个节点都可以设置多一个列,此处为了简单就只设置一列了。item3->setText(0,"鸟");//添加到顶层节点中ui->treeWidget->addTopLevelItem(item3);//添加一些子节点QTreeWidgetItem* item4 = new QTreeWidgetItem();item4->setText(0,"中华田园猫");item1->addChild(item4);//添加一些子节点QTreeWidgetItem* item5 = new QTreeWidgetItem();item5->setText(0,"布偶猫");item1->addChild(item5);//添加一些子节点QTreeWidgetItem* item6 = new QTreeWidgetItem();item6->setText(0,"仙猫");item1->addChild(item6);}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_insertTopLeveLitem_clicked()
{//获取到输入框中的内容const QString& text = ui->lineEdit->text();//构造一个QTreeWidgetItemQTreeWidgetItem*item = new QTreeWidgetItem();item->setText(0,text);//添加到顶层节点中ui->treeWidget->addTopLevelItem(item);
}void Widget::on_pushButton_insertitem_clicked()
{//获取当前选中的节点QTreeWidgetItem* currentItem = ui->treeWidget->currentItem();if(currentItem==nullptr){return ;}//获取到输入框中的内容const QString&text = ui->lineEdit->text();//构造一个QTreeWidgetItemQTreeWidgetItem*item = new QTreeWidgetItem();item->setText(0,text);//插入到选中节点的子节点中currentItem->addChild(item);}void Widget::on_pushButton_deleteitem_clicked()
{//获取到选中元素QTreeWidgetItem * currentItem = ui->treeWidget->currentItem();if(currentItem==nullptr){return ;}//删除选中的元素,需要先获取到父元素,通过父元素进行删除QTreeWidgetItem*parent = currentItem->parent();if(parent==nullptr){//顶层int index = ui->treeWidget->indexOfTopLevelItem(currentItem);ui->treeWidget->takeTopLevelItem(index);}else{//普通元素parent->removeChild(currentItem);}
}

上述这几个控件相关的操作,数据都是在内存中保存的,无论在界面上做任何操作,重新运行程序,之前的数据都没了。
如果要想让数据能够重启也不丢失,就需要编写更多的代码把内存存储的数据获取到,写入到文件中,并且在下次运行的时候从文件加载数据~~。

7. 容器类控件

7.1 Group Box

使⽤ QGroupBox 实现⼀个带有标题的分组框. 可以把其他的控件放到⾥⾯作为⼀组. 这样看起来能更好看⼀点.
注意, 不要把 QGroupBox 和 QButtonGroup 混淆. (之前在介绍 QRadionButton 的时候提到了 QButtonGroup ).
在这里插入图片描述
核⼼属性
属性 说明
title 分组框的标题
alignment 分组框内部内容的对⻬⽅式
flat 是否是 “扁平” 模式
checkable 是否可选择.
设为 true, 则在 title 前⽅会多出⼀个可勾选的部分.
checked 描述分组框的选择状态 (前提是 checkable 为 true)
分组框只是⼀个⽤来 “美化界⾯” 这样的组件, 并不涉及到⽤⼾交互和业务逻辑. 属于 “锦上添
花” .

代码⽰例: 给⻨当劳案例加上分组框

  1. 在界⾯上创建三个分组框, 并且在分组框内部创建下拉框和微调框
    在这里插入图片描述
    注意:
    在复制粘贴控件的时候, ⼀定要先选中对应的⽗控件, 再粘贴.
  2. 编写 widget.cpp, 添加初始化下拉框的代码
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->comboBox->addItem("巨⽆霸");
ui->comboBox->addItem("⻨辣鸡腿堡");
ui->comboBox_2->addItem("薯条");
ui->comboBox_2->addItem("⻨辣鸡翅");
ui->comboBox_3->addItem("可乐");
ui->comboBox_3->addItem("雪碧");
}
  1. 运⾏程序, 观察效果
    在这里插入图片描述

7.2 Tab Widget

使⽤ QTabWidget 实现⼀个带有标签⻚的控件, 可以往⾥⾯添加⼀些 widget. 进⼀步的就可以通过标
签⻚来切换.
核⼼属性
属性 说明
tabPosition 标签⻚所在的位置.
• North 上⽅
• South 下⽅
• West 左侧
• East 右侧
currentIndex 当前选中了第⼏个标签⻚ (从 0 开始计算)
currentTabText 当前选中的标签⻚的⽂本
currentTabName 当前选中的标签⻚的名字
currentTabIcon 当前选中的标签⻚的图标
currentTabToolTip 当前选中的标签⻚的提⽰信息
tabsCloseable 标签⻚是否可以关闭
movable 标签⻚是否可以移动

核⼼信号
属性 说明
currentChanged(int) 在标签⻚发⽣切换时触发, 参数为被点击的选项卡编号.
tabBarClicked(int) 在点击选项卡的标签条的时候触发. 参数为被点击的选项卡编号.
tabBarDoubleClicked(int) 在双击选项卡的标签条的时候触发. 参数为被点击的选项卡编号.
tabCloseRequest(int) 在标签⻚关闭时触发. 参数为被关闭的选项卡编号.
代码⽰例: 使⽤标签⻚管理多组控件

  1. 在界⾯上创建⼀个 QTabWidget , 和两个按钮.
    按钮的 objectName 为 pushButton_add 和 pushButton_remove

在这里插入图片描述
注意,
• QTabWidget 中的每个标签⻚都是⼀个 QWidget
• 点击标签⻚, 就可以直接切换.
• 右键 QTabWidget , 可以添加标签⻚或者删除标签⻚.

  1. 编写 widget.cpp, 进⾏初始化, 给标签⻚中放个简单的 label
    • 注意新创建的 label 的⽗元素, 是 ui->tab 和 ui->tab_2 . Qt 中使⽤⽗⼦关系决定该控件 “在哪⾥”.
QLabel* label = new QLabel(ui->tab);
label->setText("标签⻚1");
label->resize(100, 50);
QLabel* label2 = new QLabel(ui->tab_2);
label2->setText("标签⻚2");
label2->resize(100, 50);
  1. 编写按钮的 slot 函数
    • 使⽤ count() 获取到标签⻚的个数.
    • 使⽤ addTab 新增标签⻚.
    • 使⽤ removeTab 删除标签⻚.
    • 使⽤ currentIndex 获取到当前标签⻚的下标.
    • 使⽤ setCurrentIndex 切换当前标签⻚.
void Widget::on_pushButton_add_clicked()
{
// 获取当前有⼏个标签⻚了
int count = ui->tabWidget->count();
// 创建新的 widget
QWidget* w = new QWidget();
ui->tabWidget->addTab(w, QString("Tab ") + QString::number(count + 1));
// 给 widget 中添加 label
QLabel* label = new QLabel(w);
label->setText(QString("标签⻚") + QString::number(count + 1));
label->resize(100, 50);
// 选中这个新的标签⻚
ui->tabWidget->setCurrentIndex(count);
}
void Widget::on_pushButton_remove_clicked()
{
// 获取当前标签⻚的下标
int index = ui->tabWidget->currentIndex();
// 删除这个标签⻚
ui->tabWidget->removeTab(index);
}
  1. 编写 QTabWidget 的 currentChanged 函数
void Widget::on_tabWidget_currentChanged(int index)
{
qDebug() << "当前选中标签⻚为: " << index;
}

源码

#include "widget.h"
#include "ui_widget.h"
#include<QLabel>
#include<QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//先在每个标签页中,添加一个labelQLabel* label1 = new QLabel(ui->tab);label1->setText("标签页1");label1->resize(100,50);QLabel *label2 = new QLabel(ui->tab_2);label2->setText("标签页2");label2->resize(100,50);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{//使用addTab方法来创建新的标签页//参数1,要指定一个 Qwiget//参数2:指定这个标签页的text(标题)int count = ui->tabWidget->count();//获取到标签页的数量QWidget *w = new QWidget();ui->tabWidget->addTab(w,QString("Tab")+QString::number(count+1));//添加一个QLabel显示内容QLabel*label = new QLabel(w);label->setText(QString("标签页")+QString::number(count+1));label->resize(100,50);//设置新标签页被选中ui->tabWidget->setCurrentIndex(count);
}void Widget::on_pushButton_2_clicked()
{//获取到当前选中的标签页的下标int index = ui->tabWidget->currentIndex();//删除标签页ui->tabWidget->removeTab(index);
}void Widget::on_tabWidget_currentChanged(int index)
{qDebug()<<"当前选中的标签页是:"<<index;
}
  1. 运⾏程序, 观察效果
    • 点击新建标签⻚, 可以创建出新的标签.
    • 点击删除当前标签⻚, 可以删除标签.
    • 切换标签⻚时, 可以看到 qDebug 打印出的标签⻚编号.
    在这里插入图片描述

8. 布局管理器

之前使⽤ Qt 在界⾯上创建的控件, 都是通过 “绝对定位” 的⽅式来设定的.
也就是每个控件所在的位置, 都需要计算坐标, 最终通过 setGeometry 或者 move ⽅式摆放过去.

这种设定⽅式其实并不⽅便. 尤其是界⾯如果内容⽐较多, 不好计算. ⽽且⼀个窗⼝⼤⼩往往是可以调整的, 按照绝对定位的⽅式, 也⽆法⾃适应窗⼝⼤⼩.
因此 Qt 引⼊ “布局管理器” (Layout) 机制, 来解决上述问题.

当然, 布局管理器并⾮ Qt 独有. 其他的 GUI 开发框架, 像 Android, 前端等也有类似的机制.

8.1 垂直布局

使⽤ QVBoxLayout 表⽰垂直的布局管理器. V 是 vertical 的缩写.
核⼼属性
属性 说明
layoutLeftMargin 左侧边距
layoutRightMargin 右侧边距
layoutTopMargin 上⽅边距
layoutBottomMargin 下⽅边距
layoutSpacing 相邻元素之间的间距
Layout 只是⽤于界⾯布局, 并没有提供信号.
代码⽰例: 使⽤ QVBoxLayout 管理多个控件.

  1. 编写代码, 创建布局管理器和三个按钮. 并且把按钮添加到布局管理器中.
    • 使⽤ addWidget 把控件添加到布局管理器中.
    • 使⽤ setLayout 设置该布局管理器到 widget 中.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建三个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
// 创建布局管理器, 并且把按钮添加进去
// 如果创建的时候指定⽗元素为 this, 则后⾯不需要 setLayout ⽅法了.
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(btn1);
layout->addWidget(btn2);
layout->addWidget(btn3);
// 把布局管理器设置到 widget 中
this->setLayout(layout);
}
  1. 运⾏程序, 可以看到此时界⾯上的按钮就存在于布局管理器中. 随着窗⼝尺⼨变化⽽发⽣改变.
    此时三个按钮的尺⼨和位置, 都是⾃动计算出来的.
    在这里插入图片描述
    通过上述代码的⽅式, 只能给这个 widget 设定⼀个布局管理器. 实际上也可以通过 Qt Design 在⼀个窗⼝中创建多个布局管理器.
    代码⽰例: 创建两个 QVBoxLayout
  2. 在界⾯上创建两个 QVBoxLayout , 每个 QVBoxLayout 各放三个按钮.
    在这里插入图片描述
  3. 运⾏程序, 可以看到这些按钮已经⾃动排列好. 只不过当前这些按钮的位置不能随着窗⼝⼤⼩⾃动变化.
    在这里插入图片描述
    通过 Qt Designer 创建的布局管理器, 其实是先创建了⼀个 widget, 设置过 geometry 属性的. 再把这个 layout 设置到这个 widget 中.
    实际上, ⼀个 widget 只能包含⼀个 layout.
    打开 ui ⽂件的原始 xml, 可以看到其中的端倪.
    这种情况下 layout 并⾮是窗⼝ widget 的布局管理器, 因此不会随着窗⼝⼤⼩改变.
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>140</x>
<y>140</y>
<width>141</width>
<height>331</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
</widget>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8.2 ⽔平布局

使⽤ QHBoxLayout 表⽰垂直的布局管理器. H 是 horizontal 的缩写.
核⼼属性 (和 QVBoxLayout 属性是⼀致的)
属性 说明
layoutLeftMargin 左侧边距
layoutRightMargin 右侧边距
layoutTopMargin 上⽅边距
layoutBottomMargin 下⽅边距
layoutSpacing 相邻元素之间的间距
代码⽰例: 使⽤ QHBoxLayout 管理控件

  1. 编写代码, 创建布局管理器和三个按钮. 并且把按钮添加到布局管理器中.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建三个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
// 创建布局管理器
QHBoxLayout* layout = new QHBoxLayout();
layout->addWidget(btn1);
layout->addWidget(btn2);
layout->addWidget(btn3);
// 设置 layout 到 widget 上
this->setLayout(layout);
}
  1. 运⾏程序, 可以看到此时界⾯上的按钮就存在于布局管理器中. 随着窗⼝尺⼨变化⽽发⽣改变.
    此时三个按钮的尺⼨和位置, 都是⾃动计算出来的.
    在这里插入图片描述
    Layout ⾥⾯可以再嵌套上其他的 layout, 从⽽达到更复杂的布局效果.
    代码⽰例: 嵌套的 layout
  2. 在代码中创建以下内容
    • 使⽤ addLayout 给 layout 中添加⼦ layout.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建顶层 layout
QVBoxLayout* layoutParent = new QVBoxLayout();
this->setLayout(layoutParent);
// 添加两个按钮进去
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
layoutParent->addWidget(btn1);
layoutParent->addWidget(btn2);
// 创建⼦ layout
QHBoxLayout* layoutChild = new QHBoxLayout();
// 添加两个按钮进去
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
layoutChild->addWidget(btn3);
layoutChild->addWidget(btn4);
// 把这个⼦ layout 添加到 ⽗ layout 中
layoutParent->addLayout(layoutChild);
}
  1. 执⾏程序, 观察结果
    在这里插入图片描述
    结合 QHBoxLayout 和 QVBoxLayout , 就可以做出各种复杂的界⾯了.

8.3 ⽹格布局

Qt 中还提供了 QGridLayout ⽤来实现⽹格布局的效果. 可以达到 M * N 的这种⽹格的效果.
核⼼属性
整体和 QVBoxLayout 以及 QHBoxLayout 相似. 但是设置 spacing 的时候是按照垂直⽔平两个
⽅向来设置的.
属性 说明
layoutLeftMargin 左侧边距
layoutRightMargin 右侧边距
layoutTopMargin 上⽅边距
layoutBottomMargin 下⽅边距
layoutHorizontalSpacing 相邻元素之间⽔平⽅向的间距
layoutVerticalSpacing 相邻元素之间垂直⽅向的间距
layoutRowStretch ⾏⽅向的拉伸系数
layoutColumnStretch 列⽅向的拉伸系数
代码⽰例: 使⽤ QGridLayout 管理元素

  1. 代码中创建 QGridLayout 和 4 个按钮.
    • 使⽤ addWidget 添加控件到布局管理器中. 但是添加的同时会指定两个坐标. 表⽰放在第⼏⾏, 第
    ⼏列.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建 4 个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
// 创建⽹格布局管理器, 并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 0, 1);
layout->addWidget(btn3, 1, 0);
layout->addWidget(btn4, 1, 1);
// 设置 layout 到窗⼝中.
this->setLayout(layout);
}
  1. 执⾏代码, 观察效果. 可以看到当前的这⼏个按钮是按照 2 ⾏ 2 列的⽅式排列的.
    在这里插入图片描述
  2. 如果调整⾏列坐标为下列代码
// 创建⽹格布局管理器, 并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 0, 1);
layout->addWidget(btn3, 0, 2);
layout->addWidget(btn4, 0, 3);

执⾏代码, 可以看到这⼏个按钮都在同⼀⾏了. 相当于 QHBoxLayout
在这里插入图片描述
4) 如果调整⾏列坐标为下列代码

QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 1, 0);
layout->addWidget(btn2, 2, 0);
layout->addWidget(btn3, 3, 0);
layout->addWidget(btn4, 4, 0);

执⾏代码, 可以看到这⼏个按钮都在同⼀列了. 相当于 QVBoxLayout
在这里插入图片描述
5) 任意调整⾏列, 即可看到不同的效果.

// 创建⽹格布局管理器, 并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 1, 1);
layout->addWidget(btn3, 2, 2);
layout->addWidget(btn4, 3, 3);

在这里插入图片描述
6) 编写代码形如

QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 1, 0);
layout->addWidget(btn3, 2, 0);
layout->addWidget(btn4, 10, 0);

此处也要注意, 设置⾏和列的时候, 如果设置的是⼀个很⼤的值, 但是这个值和上⼀个值之间并没有其他的元素, 那么并不会在中间腾出额外的空间.
虽然把 btn4 设置在第 10 ⾏, 但是由于 3-9 ⾏没有元素. 因此 btn4 仍然会紧挨在 btn3 下⽅. 看起来和上⾯的 0 1 2 3 的情况是相同的.
在这里插入图片描述
代码⽰例: 设置 QGridLayout 中元素的⼤⼩⽐例.

  1. 创建 6 个按钮, 按照 2 ⾏ 3 列的⽅式排列
    • 使⽤ setColumnStretch 设置每⼀列的拉伸系数.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建 6 个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
QPushButton* btn5 = new QPushButton("按钮5");
QPushButton* btn6 = new QPushButton("按钮6");
// 创建⽹格布局管理器, 并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 0, 1);
layout->addWidget(btn3, 0, 2);
layout->addWidget(btn4, 1, 0);
layout->addWidget(btn5, 1, 1);
layout->addWidget(btn6, 1, 2);
// 设置拉伸⽐例
// 第 0 列拉伸⽐例设为 1;
layout->setColumnStretch(0, 1);
// 第 1 列拉伸⽐例设为 0, 即为固定⼤⼩, 不参与拉伸
layout->setColumnStretch(1, 0);
// 第 2 列拉伸⽐例设为 3, 即为第 2 列的宽度是第 0 列的 3 倍
layout->setColumnStretch(2, 3);
// 设置 layout 到窗⼝中.
this->setLayout(layout);
  1. 执⾏程序, 可以看到每⼀列的宽度是不同的. 并且随着窗⼝调整动态变化.
    在这里插入图片描述
    **另外, QGridLayout 也提供了 setRowStretch 设置⾏之间的拉伸系数.
    上述案例中, 直接设置 setRowStretch 效果不明显, 因为每个按钮的⾼度是固定的. 需要把按钮的垂直⽅向的 sizePolicy 属性设置为 QSizePolicy::Expanding 尽可能填充满布局管理器, 才能看到效果. **

代码⽰例: 设置垂直⽅向的拉伸系数

  1. 编写代码, 创建 6 个按钮, 按照 3 ⾏ 2 列⽅式排列.
    使⽤ setSizePolicy 设置按钮的尺⼨策略. 可选的值如下:
    • QSizePolicy::Ignored : 忽略控件的尺⼨,不对布局产⽣影响。
    QSizePolicy::Minimum : 控件的最⼩尺⼨为固定值,布局时不会超过该值。
    • QSizePolicy::Maximum : 控件的最⼤尺⼨为固定值,布局时不会⼩于该值。
    • QSizePolicy::Preferred : 控件的理想尺⼨为固定值,布局时会尽量接近该值。
    • QSizePolicy::Expanding : 控件的尺⼨可以根据空间调整,尽可能占据更多空间。
    • QSizePolicy::Shrinking : 控件的尺⼨可以根据空间调整,尽可能缩⼩以适应空间。
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建 6 个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
QPushButton* btn5 = new QPushButton("按钮5");
QPushButton* btn6 = new QPushButton("按钮6");
// 设置按钮的 sizePolicy, 此时按钮的⽔平⽅向和垂直⽅向都会尽量舒展开
btn1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
btn2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
btn3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
btn4->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
btn5->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
btn6->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// 创建⽹格布局管理器, 并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 0, 1);
layout->addWidget(btn3, 1, 0);
layout->addWidget(btn4, 1, 1);
layout->addWidget(btn5, 2, 0);
layout->addWidget(btn6, 2, 1);
// 设置拉伸⽐例
// 第 0 ⾏拉伸⽐例设为 1;
layout->setRowStretch(0, 1);
// 第 1 ⾏拉伸⽐例设为 0, 即为固定⼤⼩, 不参与拉伸
layout->setRowStretch(1, 0);
// 第 2 ⾏拉伸⽐例设为 3, 即为第 2 ⾏的宽度是第 0 ⾏的 3 倍
layout->setRowStretch(2, 3);//设置 layout 到窗⼝中.
this->setLayout(layout);
}
  1. 执⾏代码, 观察效果.
    此时的按钮垂直⽅向都舒展开了. 并且调整窗⼝尺⼨, 也会按照设定的⽐例同步变化
    在这里插入图片描述
    总的来说, 使⽤ QGridLayout 能够代替很多 QHBoxLayout 和 QVBoxLayout 嵌套的场景. 毕竟嵌套的代码写起来是⽐较⿇烦的.
    另外不要忘了, QGridLayout ⾥⾯也能嵌套 QHBoxLayout 和 QVBoxLayout , QHBoxLayout 和 QVBoxLayout ⾥⾯也能嵌套 QGridLayout . 灵活使⽤上述布局管理器, 就可以实现出任意的复杂界⾯.
    在这里插入图片描述
    在这里插入图片描述
    源码
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QGridLayout>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//创建6个按钮,按照3行2列的方式进行排列QPushButton*button1 = new QPushButton("按钮1");QPushButton*button2 = new QPushButton("按钮2");QPushButton*button3 = new QPushButton("按钮3");QPushButton*button4 = new QPushButton("按钮4");QPushButton*button5 = new QPushButton("按钮5");QPushButton*button6 = new QPushButton("按钮6");//把水平拉伸打开,垂直拉伸是默认打开的button1->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);button2->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);button3->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);button4->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);button5->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);button6->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);//创建layout并把按钮添加进去QGridLayout*layout = new QGridLayout();layout->addWidget(button1,0,0);layout->addWidget(button2,0,1);layout->addWidget(button3,1,0);layout->addWidget(button4,1,1);layout->addWidget(button5,2,0);layout->addWidget(button6,2,1);//把layout设置到窗口中this->setLayout(layout);//设置拉伸系数layout->setRowStretch(0,1);layout->setRowStretch(1,1);layout->setRowStretch(2,2);}Widget::~Widget()
{delete ui;
}

8.4 表单布局

除了上述的布局管理器之外, Qt 还提供了 QFormLayout , 属于是 QGridLayout 的特殊情况, 专⻔⽤于实现两列表单的布局.
这种表单布局多⽤于让⽤⼾填写信息的场景. 左侧列为提⽰, 右侧列为输⼊框.
代码⽰例: 使⽤ QFormLayout 创建表单.

  1. 编写代码, 创建 QFormLayout , 以及三个 label 和三个 lineEdit
    • 使⽤ addRow ⽅法来添加⼀⾏. 每⾏包含两个控件. 第⼀个控件固定是 QLabel / ⽂本, 第⼆个控件则可以是任意控件.
    • 如果把第⼀个参数填写为 NULL, 则什么都不显⽰.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建 layout
QFormLayout* layout = new QFormLayout();
this->setLayout(layout);
// 创建三个 label
QLabel* label1 = new QLabel("姓名");
QLabel* label2 = new QLabel("年龄");
QLabel* label3 = new QLabel("电话");
// 创建三个 lineEdit
QLineEdit* lineEdit1 = new QLineEdit();
QLineEdit* lineEdit2 = new QLineEdit();
QLineEdit* lineEdit3 = new QLineEdit();
// 创建⼀个提交按钮
QPushButton* btn = new QPushButton("提交");
// 把上述元素添加到 layout 中
layout->addRow(label1, lineEdit1);
layout->addRow(label2, lineEdit2);
layout->addRow(label3, lineEdit3);
layout->addRow(NULL, btn);
}
  1. 执⾏程序, 可以看到以下结果.
    在这里插入图片描述

8.5 Spacer

使⽤布局管理器的时候, 可能需要在控件之间, 添加⼀段空⽩. 就可以使⽤ QSpacerItem 来表⽰.
核⼼属性
属性 说明
width 宽度
height ⾼度
hData ⽔平⽅向的 sizePolicy
• QSizePolicy::Ignored : 忽略控件的尺⼨,不对布局产⽣影响。
• QSizePolicy::Minimum : 控件的最⼩尺⼨为固定值,布局时不会超过该值。
• QSizePolicy::Maximum : 控件的最⼤尺⼨为固定值,布局时不会⼩于该值。
• QSizePolicy::Preferred : 控件的理想尺⼨为固定值,布局时会尽量接近该
值。
• QSizePolicy::Expanding : 控件的尺⼨可以根据空间调整,尽可能占据更多空
间。
• QSizePolicy::Shrinking : 控件的尺⼨可以根据空间调整,尽可能缩⼩以适应
空间。
vData 垂直⽅向的 sizePolicy
选项同上.
上述属性在构造函数设置即可.

代码⽰例: 创建⼀组左右排列的按钮.

  1. 在界⾯上创建⼀个 QVBoxLayout , 并添加两个按钮.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QHBoxLayout* layout = new QHBoxLayout();
this->setLayout(layout);
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
layout->addWidget(btn1);
layout->addWidget(btn2);
}
  1. 直接运⾏程序, 可以看到两个按钮是紧挨着的
    在这里插入图片描述
  2. 在两个按钮中间添加⼀个 spacer
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QHBoxLayout>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QHBoxLayout* layout = new QHBoxLayout();this->setLayout(layout);QPushButton* btn1 = new QPushButton("按钮1");QPushButton* btn2 = new QPushButton("按钮2");// 创建 spacer使两个按钮之间存在空白QSpacerItem* spacer = new QSpacerItem(200, 20);//当前是要把空白添加到两个按钮之间,此处add的顺序是把addSpacerItem放到中间了layout->addWidget(btn1);layout->addSpacerItem(spacer);layout->addWidget(btn2);
}Widget::~Widget()
{delete ui;
}
  1. 运⾏程序, 观察代码效果. 可以看到两个按钮之间已经存在了间隔了.
    调整 QSpacerItem 不同的尺⼨, 即可看到不同的间距.
    在这里插入图片描述
    在 Qt Designer 中, 也可以直接给界⾯上添加 spacer.
    在这里插入图片描述

9. ⼩结

通过上⾯的学习, 我们就了解了 Qt 中常⽤控件的使⽤⽅法. 对于图形界⾯开发, 知道有哪些控件, 每个控件有什么作⽤, 如何使⽤, 是最核⼼的部分. 基于上⾯内容其实我们就已经可以写出⼀些具有实际意义的图形化程序了.

总结

以上就是qt的常用控件的全部内容了。

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

相关文章:

  • 跨平台ROS2视觉数据流:服务器运行IsaacSim+Foxglove本地可视化全攻略
  • 【动手学深度学习】4.9. 环境和分布偏移
  • MyBatis之数据操作增删改查基础全解
  • tinyxml2 开源库与 VS2010 结合使用
  • MySQL8.0基于GTID的组复制分布式集群的环境部署
  • 如何通过配置gitee实现Claude Code的版本管理
  • SpringBoot校园疫情防控系统源码
  • Flink1.20.1集成Paimon遇到的问题
  • stm32Cubmax的配置
  • 微信小程序91~100
  • Pycharm 报错 Environment location directory is not empty 如何解决
  • 基于Spring Boot+Vue的巴彦淖尔旅游网站(AI问答、腾讯地图API、WebSocket及时通讯、支付宝沙盒支付)
  • Ragas的Prompt Object
  • NHibernate案例
  • SAP ERP与Oracle EBS对比,两个ERP系统有什么区别?
  • aichat-core简化 LLM 与 MCP 集成的前端核心库(TypeScript)
  • C#项目 在Vue/React前端项目中 使用使用wkeWebBrowser引用并且内部使用iframe网页外链 页面部分白屏
  • Spring IoC 如何实现条件化装配 Bean?
  • HUAWEI HiCar6.0的新变化
  • 一条Redis命令是如何执行的?
  • C++随机打乱函数:简化源码与原理深度剖析
  • 从零开始学前端html篇2
  • 微信小程序控制空调之微信小程序篇
  • 双esp8266-01s间TCP通讯
  • 图像硬解码和软解码
  • RAM带宽计算及分析
  • 区块链系统开发技术应用构建可信数字生态链
  • 以太坊智能合约核心技术解析与应用实践
  • Android 组件内核
  • NO.2数据结构线性表|线性表|顺序表|链表|单链表|双向链表|循环链表|静态链表|插入|删除