第三章 常用控件

文章目录

  • [1. 控件](#1. 控件)
  • [2. QWidget 核心属性](#2. QWidget 核心属性)
    • [2.1 核心属性概览](#2.1 核心属性概览)
    • [2.2 enabled](#2.2 enabled)
      • [QObject 的 objectName](#QObject 的 objectName)
    • 2.3geometry
      • [(包含 window frame 的 API)](#(包含 window frame 的 API))
      • [(包含 window frame 的 API)](#(包含 window frame 的 API))
      • [(不包含 window frame 的 API)](#(不包含 window frame 的 API))
      • 注意!
    • 2.4windownTile
    • [2.5 windowIcon](#2.5 windowIcon)
    • qrc
    • [2.6 windowOpacity](#2.6 windowOpacity)
    • [2.7 cursor](#2.7 cursor)
    • [2.8 font](#2.8 font)
    • [2.9 toolTip](#2.9 toolTip)
    • [2.10 focusPolicy](#2.10 focusPolicy)
    • [2.11 styleSheet](#2.11 styleSheet)
  • [3. 按钮类控件](#3. 按钮类控件)
    • [3.1 Push Button](#3.1 Push Button)
    • [3.2 Radio Buttion](#3.2 Radio Buttion)
    • [3.3. Check Box](#3.3. Check Box)
    • [3.4. Tool Button](#3.4. Tool Button)
  • [4. 显示类控件](#4. 显示类控件)
    • [4.1. Label](#4.1. Label)
      • [QLabel 控件属性说明表](#QLabel 控件属性说明表)
    • [4.2 LCDNumber](#4.2 LCDNumber)
    • [4.3 ProgressBar](#4.3 ProgressBar)
    • [4.4 CalendarWidget](#4.4 CalendarWidget)
  • [5. 输入类控件](#5. 输入类控件)
    • [5.1 LineEdit](#5.1 LineEdit)
    • [5.2 TextEdit](#5.2 TextEdit)
    • [5.3 ComboBox](#5.3 ComboBox)
    • [5.4 SpinBox](#5.4 SpinBox)
    • [5.5 DateEdit&TimeEdit](#5.5 DateEdit&TimeEdit)
    • [5.6 Dial](#5.6 Dial)
    • [5.7 Slider](#5.7 Slider)
  • [6. 多元素控件](#6. 多元素控件)
    • [6.1 ListWidget](#6.1 ListWidget)
    • [6.2 TableWidget](#6.2 TableWidget)
    • [6.3 TreeWidget](#6.3 TreeWidget)
  • [7. 容器类控件](#7. 容器类控件)
    • [7.1 GroupBox](#7.1 GroupBox)
    • [7.2 TabWidget](#7.2 TabWidget)
  • [8. 布局管理器](#8. 布局管理器)
    • [8.1 垂直布局](#8.1 垂直布局)
    • [8.2 水平布局](#8.2 水平布局)
    • [8.3 网格布局](#8.3 网格布局)
    • [8.4 表单布局](#8.4 表单布局)
    • [8.5 Spacer](#8.5 Spacer)

1. 控件

Widget 是 Qt 中的核心概念. 英文原义是 "小部件", 我们此处也把它翻译为 "控件" . 控件是构成一个图形化界面的基本要素.

且 Qt 也提供了 "自定义控件" 的能力, 可以让程序猿在现有控件不能满足需求的时候, 对现有控件做 出扩展, 或者手搓出新的控件

2. QWidget 核心属性

在 Qt 中, 使用 QWidget 类表示 "控件". 像按钮, 视图, 输入框, 滚动条等具体的控件类, 都是继承自 QWidget. 可以说, QWidget 中就包含了 Qt 整个控件体系中, 通用的部分. 在 Qt Designer 中, 随便拖一个控件过来, 选中该控件, 即可在右下方看到 QWidget 中的属性

这些属性既可以通过 QtDesigner 会直接修改, 也可以通过代码的方式修改. 这些属性的具体含义, 在 Qt Assistant 中均有详细介绍. 在 Qt Assistant 中搜索 QWidget, 即可找到对应的文档说明. (或者在 Qt Creator 代码中, 选中 QWidget, 按 F1 也可).

2.1 核心属性概览

属性名 作用
geometry 位置和尺寸(包含 x, y, width, height),坐标以父元素为参考
windowTitle 设置窗口标题
windowIcon 设置窗口图标
windowOpacity 设置窗口透明度(0.0~1.0)
cursor 鼠标悬停时显示的图标形状(箭头、沙漏、十字等)
font 字体相关属性(家族、大小、粗体、斜体、下划线等)
toolTip 鼠标悬停时在状态栏显示的提示信息
toolTipDuration toolTip 显示的持续时间(毫秒)
statusTip 控件状态改变时(如按钮按下)显示的提示信息
whatsThis 鼠标悬停并按下 Alt+F1 时弹出的帮助信息
styleSheet 允许使用 CSS 设置控件样式,Qt 支持的样式丰富
focusPolicy 控件获取焦点的策略: • Qt::NoFocus --- 不参与焦点管理 • Qt::TabFocus --- 通过 Tab 键获得焦点 • Qt::ClickFocus --- 通过鼠标点击获得焦点 • Qt::StrongFocus --- 键盘和鼠标均可获得焦点 • Qt::WheelFocus --- 可通过滚轮获得焦点(部分平台/样式可能不支持)
contextMenuPolicy 上下文菜单显示策略: • Qt::DefaultContextMenu --- 默认(右键/键盘快捷键触发) • Qt::NoContextMenu --- 禁用上下文菜单 • Qt::PreventContextMenu --- 阻止显示菜单 • Qt::ActionsContextMenu --- 替换为控件的"动作"菜单
locale 设置语言和国家地区
acceptDrops 是否接受拖放操作。为 true 时可接收拖放事件(如 dropEvent
minimumSize 控件的最小宽度和最小高度
maximumSize 控件的最大宽度和最大高度
sizePolicy 在布局管理器中的缩放策略
windowModality 指定窗口是否具有"模态"行为
sizeIncrement 拖动窗口大小时的增量单位
baseSize 窗口的基础大小,与 sizeIncrement 配合计算合适的调整值
palette 调色板,设置控件的颜色风格
mouseTracking 是否跟踪鼠标移动事件。true 时持续接收鼠标移动事件;false 时仅接收按下/释放事件
tabletTracking 是否跟踪触摸屏移动事件(Qt 5.9 引入),类似 mouseTracking
layoutDirection 布局方向:Qt::LeftToRight(默认,从左到右)、Qt::RightToLeft(从右到左)、Qt::GlobalAtomics(由应用程序其他控件决定)
autoFillBackground 是否自动填充背景颜色
windowFilePath 将窗口与本地文件路径关联(实际作用有限)
accessibleName 辅助技术(如屏幕阅读器)可获取的可访问名称,用于无障碍程序
accessibleDescription 可访问的详细描述,作用同 accessibleName
inputMethodHints 针对输入框有效,提示用户合法输入格式(如只能输入数字、日期等)

2.2 enabled

属性/API 说明
属性名 enabled
作用 设置控件是否可使用。true 表示可用,false 表示禁用。禁用后控件无法接收任何用户输入事件,外观通常变为灰色。
相关 API isEnabled() --- 获取控件的可用状态 setEnabled(bool) --- 设置控件是否可用

"禁用" 指的是该控件不能接收任何用户的输入事件, 并且外观上往往是灰色的. 如果一个 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);
}

通过按钮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 对象了.

cpp 复制代码
public:
 QPushButton *pushButton;
 QPushButton *pushButton_2;
  1. 生成两个按钮的 slot 函数.

• 使用 isEnabled 获取当前按钮的可用状态.

• 使用 setEnabled 修改按钮的可用状态. 此处是直接针对原来的可用状态进行取反后设置.

cpp 复制代码
void Widget::on_pushButton_clicked()
{
 qDebug() << "按下按钮";
}
void Widget::on_pushButton_2_clicked()
{
 bool flag = this->ui->pushButton->isEnabled();
 this->ui->pushButton->setEnabled(!flag);
}

编辑

cpp 复制代码
#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* btn=new QPushButton(this);
    btn->setText("这个是被禁用的按钮");
    btn->setEnabled(false);

}

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

void Widget::on_pushButton_clicked()
{
    qDebug()<<"按下按钮";

}


void Widget::on_pushButton_2_clicked()
{
    bool enable =ui->pushButton->isEnabled();
    if(enable)
    {
        ui->pushButton->setEnabled(false);
    }else
    {
        ui->pushButton->setEnabled(true);
    }
}

2.3geometry

位置和尺寸. 其实是四个属性的统称: x 横坐标 y 纵坐标 width 宽度 height 高度

实际开发中, 我们并不会直接使用这几个属性, 而是通过一系列封装的方法来获取/修改. 对于 Qt 的坐标系, 不要忘记是一个 "左手坐标系". 其中坐标系的原点是当前元素的父元素的左上角

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

  1. 在界面中拖五个按钮. 五个按钮的 objectName 分别为 pushButton_target , pushButton_up , pushButton_down , pushButton_left , pushButton_right 五个按钮的初始位置和大小都随意
  1. 在 widget.cpp 中编写四个按钮的 slot 函数
cpp 复制代码
#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本身的geometry
    QRect rect=ui->pushButton_target->geometry();
    qDebug()<<rect;
    rect.setY(rect.y()-5);
    ui->pushButton_target->setGeometry(rect);
}


void Widget::on_pushButton_down_clicked()
{
    QRect rect=ui->pushButton_target->geometry();
    qDebug()<<rect;
    rect.setY(rect.y()+5);
    ui->pushButton_target->setGeometry(rect);

}


void Widget::on_pushButton_left_clicked()
{
    QRect rect=ui->pushButton_target->geometry();
    qDebug()<<rect;
    rect.setX(rect.x()-5);
    ui->pushButton_target->setGeometry(rect);
}


void Widget::on_pushButton_right_clicked()
{
    QRect rect=ui->pushButton_target->geometry();
    qDebug()<<rect;
    rect.setX(rect.x()+5);
    ui->pushButton_target->setGeometry(rect);
}

运行程序, 可以看到, 按下下方的四个按钮, 就会控制 target 的左上角的位置. 对应的按钮整个尺寸也会 发生改变. 上述代码中我们是直接设置的 QRect 中的 x, y . 实际上 QRect 内部是存储了左上和右下两个点的坐 标, 再通过这两个点的坐标差值计算长宽. 单纯修改左上坐标就会引起整个矩形的长宽发生改变.

如果想让整个按钮都移动, 可以改成下列代码:

cpp 复制代码
#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本身的geometry
    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_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 控件中文本如下图所示.

  1. 在 widget.cpp 中添加 slot 函数
cpp 复制代码
#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_accept_clicked()
{
    ui->label->setText("已经同意");
}


void Widget::on_pushButton_reject_clicked()
{


}


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 方法). 此处暂时 不展开

window frame 的影响

如果 widget 作为一个窗口 (带有标题栏, 最小化, 最大化, 关闭按钮), 那么在计算尺寸和坐标的 时候就有两种算法. 包含 window frame 和 不包含 window frame.

其中 x(), y(), frameGeometry(), setFrameGeometry(),pos(), move() 都是按照包含 window frame 的方式来计算 的.

其中 geometry(), setGeometry(),width(), height(), rect(), size() 则是按照不包含 window frame 的方式来计 算的.

当然, 如果一个不是作为窗口的 widget , 上述两类方式得到的结果是一致的

(包含 window frame 的 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

(包含 window frame 的 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

(不包含 window frame 的 API)

API 说明
size() 返回 QSize 对象,里面包含 width(), height(), setWidth(), setHeight() 等方法。 计算时不包含 window frame
rect() 返回 QRect 对象。QRect 相当于 QPoint 和 QSize 的结合体。可以获取并设置 x, y, width, size。 计算时不包含 window frame 对象。
geometry() 返回 QRect 对象。QRect 相当于 QPoint 和 QSize 的结合体。可以获取 x, y, width, size。 计算时不包含 window frame 对象。
setGeometry() 直接设置窗口的位置和尺寸。可以设置 x, y, width, height, 或者 QRect 对象。 计算时不包含 window frame 对象。

认真观察上面的表格, 可以看到, 其实这里的 API 有 frameGeometry 和 geometry 两个就足够 完成所有的需求了.

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

  1. 在界面上放置一个按钮

2)编写代码

cpp 复制代码
#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;
}

执行程序, 可以看到, 构造函数中, 打印出的 geometry 和 frameGeometry 是相同的. 但是在点击按钮时, 打印的 geometry 和 frameGeometry 则存在差异.

注意!

在构造方法中, Widget 刚刚创建出来, 还没有加入到对象树中. 此时也就不具备 Window frame. 在按钮的 slot 函数中, 由于用户点击的时候, 对象树已经构造好了, 此时 Widget 已经具备了 Window frame, 因此在位置和尺寸上均出现了差异. 如果把上述代码修改成打印 pushButton 的 geometry 和 frameGeometry , 结果就是完全相 同的. 因为 pushButton 并非是一个窗口.

2.4windownTile

注意! 上述设置操作针对不同的 widget 可能会有不同的行为. 如果是顶层 widget (独立窗口), 这个操作才会有效. 如果是子 widget, 这个操作无任何效果.

代码示例: 设置窗口标题

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

2.5 windowIcon

同 windowTitle, 上述操作仅针对顶层 widget 有效

注意:我们之前推荐使用堆来创建对象,主要是因为要确保当前的控件的生命周期是足够的,要通过QT对象来释放对象。

QIcon自身是一个较小的对象,窗机拿出来后需要设置到某个QWidget里,QIcon对象本身是否释放不影响图标的最终显示。同时,QIcon本身也不支持对象树,无法为它指定父对象

代码示例: 设置窗口图标

  1. 先在 D 盘中放一个图片
  2. 修改 widget.cpp后运行
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QIcon>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QIcon icon("c:/aaa.jpg");
    this->setWindowIcon(icon);

}

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

qrc

实际开发中, 我们一般不会在代码中通过绝对路径引入图片. 因为我们无法保证程序发布后, 用 户的电脑上也有同样的路径.

如果使用相对路径, 则需要确保代码中的相对路径写法和图片实际所在的路径匹配 (比如代码 中写作 "./image/rose.jpg", 就需要在当前工作目录中创建 image 目录, 并把 rose.jpg 放进 去).

绝对路径: 以盘符(windows)或者以 / (Linux) 开头的路径.

相对路径: 以 . (表示当前路径) 或者 以 ... (表示当前路径上级路径) 开头的路径.

其中 . 经常也 会省略. 相对路径的前提是需要明确 "当前工作目录".

对于 Qt 程序来说, 当前工作目录可能是变化的. 比如通过 Qt Creator 运行的程序, 当前工作目录是项目 的构建目录;

直接双击 exe 运行, 工作目录则是 exe 所在目录. 所谓构建目录, 是和 Qt 项目并列的, 专门用来放生成的临时文件和最终 exe 的目录

Qt 使用 qrc 机制来 更方便的来管理项目依赖的静态资源.

qrc 文件是一种XML格式的资源配置文件 , 它用XML记录硬盘上的文件和对应的随意指定的资 源名称. 应用程序通过资源名称来访问这些资源. 在Qt开发中, 可以通过将资源文件添加到项目中来方便地访问和管理这些资源. 这些资源文件 可以位于qrc文件所在目录的同级或其子目录下.

在构建程序的过程中, Qt 会把资源文件的二进制数据转成 cpp 代码, 编译到 exe 中. 从而使依 赖的资源变得 "路径无关"

这种资源管理机制并非 Qt 独有, 很多开发框架都有类似的机制. 例如 Android 的 Resources 和 AssetManager 也是类似的效果.

代码示例: 通过 qrc 管理图片作为图标

  1. 右键项目, 创建一个 Qt Resource File (qrc 文件), 文件名随意起(不要带中文), 此处叫做 resource.qrc .
  1. 在 qrc 编辑器中, 添加前缀

此处我们前缀设置成 / 即可.

所谓的前缀, 可以理解成 "目录" . 这个前缀决定了后续我们如何在代码中访问资源

  1. 在 资源编辑器 中, 点击 add Files 添加资源文件. 此处我们需要添加的是 aaa.jpg

注意: 添加的文件必须是在 qrc 文件的同级目录, 或者同级目录的子目录中. 因此我们需要把之前 D 盘 中的 aaa.jpg 复制到上述目录中

添加完毕后, 可以在 资源编辑器 中看到添加好的文件

  1. 在代码中使用 aaa.jpg 编辑 widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QIcon>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
   // QIcon icon("c:/aaa.jpg");
    QIcon icon(":/aaa.jpg");
    this->setWindowIcon(icon);

}

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

注意上述路径的访问规则.

• 使用 : 作为开头, 表示从 qrc 中读取资源. • / 是上面配置的前缀

• aaa.jpg 是资源的名称 需要确保代码中编写的路径和添加到 qrc 中资源的路径匹配. 否则资源无法被访问 (同时也不会有报错 提示).

qrc中导入的图片资源会被转成这个qrc_resource.cpp

把这个cpp拖到qt中可以看到关于他的内容,发现这个cpp代码其实就是通过 unsigned char 数组, 把 aaa.jpg 中的每个字节都记录下来. 这些代码会被编译 到 exe 中. 后续无论 exe 被复制到哪个目录下, 都确保能够访问到该图片资源.

当QT项目进行编译的时候,这个cpp就被一起编译到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 函数
cpp 复制代码
#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);
}

同时控制台中也可以看到 opacity 数值的变化

注意, C++ 中 float 类型遵守 IEEE 754 标准, 因此在进行运算的时候会有一定的精度误差. 因此 1 - 0.1 的数值并非是 0.9 .

2.7 cursor

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

  1. 在界面中创建一个按钮
  2. 直接在右侧属性编辑区修改 cursor 属性为 "等待"
  1. 运行程序, 鼠标悬停到按钮上, 即可看到光标的变化

代码示例: 通过代码设置按钮的光标

  1. 编写 widget.cpp

• 其中 Qt::WaitCursor 就是自带的沙漏形状的光标.

cpp 复制代码
#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);
   // ui->pushButton->setCursor(cursor);
}

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

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

  1. 运行程序, 观察效果.(截图无法截到鼠标光标,自己写代码验证就晓得了)

代码示例: 自定义鼠标光标 Qt 自带的光标形状有限. 我们也可以自己找个图片, 做成鼠标的光标.

我是找的电脑上自己的图片,在这里给各位推荐一个素材网站:阿里巴巴矢量图标库

  1. 创建 qrc 资源文件, 添加前缀 / , 并加入 aaa.jpg
  1. 编写 widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //访问到图片文件
    QPixmap pixmap(":/aaa.jpg");
    //缩放图片
    pixmap=pixmap.scaled(100,100);
    //构造光标对象
    QCursor cursor(pixmap,10,10);
    //把光标设置进去
    this->setCursor(cursor);


}

Widget::~Widget()
{
    delete ui;
}
  1. 运行程序, 观察效果(截图无法截到鼠标光标)

2.8 font

关于 QFont

示例: 在 Qt Designer 中设置字体属性

  1. 在界面上创建一个 labe
  1. 在右侧的属性编辑区, 设置该 label 的 font 相关属性

在这里调整上述属性, 可以实时的看到文字的变化

执行:

另一种就是代码里设置字体属性

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

2.9 toolTip

API 说明
setToolTip 设置 toolTip.鼠标悬停在该 widget 上时会有提示说明.
setToolTipDuring 设置 toolTip 提示的时间. 单位 ms.时间到后 toolTip 自动消失.

toolTip 只是给用户看的. 在代码中一般不需要获取到 toolTip.

cpp 复制代码
    ui->pushButton_yes->setToolTip("这是一个yes按钮");
    ui->pushButton_yes->setToolTipDuration(3000);

    ui->pushButton_no->setToolTip("这是一个no按钮");
    ui->pushButton_no->setToolTipDuration(3000);

2.10 focusPolicy

设置控件获取到焦点的策略. 比如某个控件能否用鼠标选中或者能否通过 tab 键选中.

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 属性。 Qt::StrongFocus (默认取值, 一般不用额外修改),不同的属性就会有不同的效果

2.11 styleSheet

通过 CSS 设置 widget 的样式.

CSS (Cascading Style Sheets 层叠样式表) 本身属于网页前端技术. 主要就是用来描述界面的 样式. 所谓 "样式", 包括不限于 大小, 位置, 颜色, 间距, 字体, 背景, 边框等. 我们平时看到的丰富多彩的网页, 就都会用到大量的 CSS. Qt 虽然是做 GUI 开发, 但实际上和 网页前端 有很多异曲同工之处. 因此 Qt 也引入了对于 CSS 的支持.

CSS 中可以设置的样式属性非常多. 基于这些属性 Qt 只能支持其中一部分, 称为 QSS (Qt Style Sheet) . 具体的支持情况可以参考 Qt 文档中 "Qt Style Sheets Reference" 章节

直接拖一个label标签,然后设置右边的属性

用代码来实现切换夜间模式

  1. 在界面上创建一个多行输入框 (Text Edit) 和两个按钮. objectName 分别为 pushButton_light 和 pushButton_dark
  1. 编写按钮的 slot 函数.

• #333 是深色, 但是没那么黑.

• #fff 是纯白色.

• #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 色深等, 实际情况会更加复杂.

cpp 复制代码
void Widget::on_pushButton_light_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_dark_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");
}

3. 按钮类控件

3.1 Push Button

使用 QPushButton 表示一个按钮. 这也是当前我们最熟悉的一个控件了. QPushButton 继承自 QAbstractButton . 这个类是一个抽象类. 是其他按钮的父类.

在 Qt Designer 中也能够看到这里的继承关系

QAbstractButton 中, 和 QPushButton 相关性较大的属性

属性 说明
text 按钮中的文本
icon 按钮中的图标
iconSize 按钮中图标的尺寸
shortCut 按钮对应的快捷键
autoRepeat 按钮是否会重复触发. 当鼠标左键按住不放时, 如果设为 true, 则会持续产生鼠标点击事件; 如果设为 false, 则必须释放鼠标, 再次按下鼠标时才能产生点击事件. (相当于游戏手柄上的 "连发" 效果)
autoRepeatDelay 重复触发的延时时间. 按住按钮多久之后, 开始重复触发.
autoRepeatInterval 重复触发的周期.

QAbstractButton 作为 QWidget 的子类, 当然也继承了 QWidget 的属性. 上面 介绍的 QWidget 里的各种属性用法, 对于 QAbstractButton 同样适用. 因此表格仅 列出 QAbstractButton 独有的属性.

Qt 的 api 设计风格是非常清晰的. 此处列出的属性都是可以 获取 和 设置 的. 例如, 使 用 text() 获取按钮文本; 使用 setText() 设置文本.

下面做一个带有图标的按钮

  1. 创建 resource.qrc 文件, 并导入图片

  1. 在界面上创建一个 按钮

修改 widget.cpp, 给按钮设置图标

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

设置一个带有快捷键的按钮

在界面中拖五个按钮,五个按钮的 objectName 分别为 pushButton_target , pushButton_up , pushButton_down , pushButton_left , pushButton_right 五个按钮的初始位置随意, 其中 pushButton_target 尺寸设置为 100 * 100, 其余按钮设为 50 * 50. 文本内容均清空

创建 resource.qrc , 并导入 5 个图片.

修改 widget.cpp, 设置图标资源和快捷键

使用 setShortcut 给按钮设置快捷键. 参数是一个 QKeySequence 对象. 表示一个按键序列. 支 持组合键 (ctrl + c 这种).

QKeySequence 的构造函数参数, 可以直接使用 "ctrl+c" 这样的按键名字符串表示, 也可以使用预定 义好的常量 (形如 Qt::CTRL + Qt::Key_C ) 表示.

cpp 复制代码
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));
}

修改 widget.cpp, 设置四个方向键的 slot 函数.

cpp 复制代码
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";
}

运行,然后wasd可以让狗头移动

3.2 Radio Buttion

QRadioButton 是单选按钮. 可以让我们在多个选项中选择一个. 作为 QAbstractButton 和 QWidget 的子类, 上面介绍的属性和用法, 对于 QRadioButton 同样适用. QAbstractButton 中和 QRadioButton 关系较大的属性

属性 说明
checkable 是否能选中
checked 是否已经被选中. checkable 是 checked 的前提条件.
autoExclusive 是否排他.选中一个按钮之后是否会取消其他按钮的选中.对于 QRadioButton 来说默认就是排他的.

代码示例: 选择性别

在界面上创建一个 label, 和 3 个 单选按钮 设置的文本如下图. 3 个单选按钮的 objectName 分别为 radioButton_male , radioButton_female , radioButton_other

修改 widget.cpp, 编辑三个 QRadioButton 的 slot 函数.

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 设置默认选中该按钮
    ui->radioButton_male->setChecked(true);
    ui->label->setText("你选择的性别为: 男");
    // 禁用 other 选项
    ui->radioButton_other->setCheckable(false);
    // 禁用 other 选项
    ui->radioButton_other->setEnabled(false);

}

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

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("你选择的性别为: 其他");

}

这里讲一下click, press, release, toggled 的区别

• clicked 表示一次 "点击"

• pressed 表示鼠标 "按下"

• released 表示鼠标 "释放"

• toggled 表示按钮状态切换.
• clicked 是一次鼠标按下+鼠标释放触发的.

• pressed 是鼠标按下触发的.

• released 是鼠标释放触发的.

• toggled 是 checked 属性改变时触发的.

总的来说, toggled 是最适合 QRadioButton 的.

单选框分组:

在界面上创建 6 个单选框, 用来模拟麦当劳点餐界面. objectName 分别为 radioButton 到 radioButton_6

引入 QButtonGroup 进行分组. 修改 widget.cpp

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

执行程序, 可以看到可以按照正确的分组方式来完成排他了S

3.3. Check Box

QCheckBox 表示复选按钮. 可以允许选中多个. 和 QCheckBox 最相关的属性也是 checkable 和 checked , 都是继承自 QAbstractButton . 至于 QCheckBox 独有的属性 tristate 用来实现 "三态复选框" .

代码示例: 获取复选按钮的取值

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

给 pushButton 添加 slot 函数

cpp 复制代码
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;
}

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 内部显示的图片(QPixmap类型)。
scaledContents 是否自动拉伸内容以填充 QLabel:• true------ 自动拉伸• false------ 不拉伸(默认)
alignment 文本对齐方式,可设置水平/垂直方向(如 `Qt::AlignLeft
wordWrap 是否自动换行:• true------ 自动换行• false------ 不换行(默认)
indent 文本缩进,水平和垂直方向均生效(但受 alignment 影响,最多两个方向有效)。
margin 内部文本与边框的边距,上下左右四个方向同时生效(不同于 indent)。
openExternalLinks 是否允许点击 QLabel 中的 URL 打开外部链接(仅当文本包含 URL 时有效)。
buddy 关联的"伙伴"控件,点击 QLabel 时可激活该控件(如关联 QCheckBox 可实现点击选中)。

示例: 显示不同格式的文本

  1. 在界面上创建三个 QLabel 尺寸放大一些. objectName 分别为 label, label_2, label_3
cpp 复制代码
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 文本");
}

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

  1. 在界面上创建一个 QLabel, objectName 为 label

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

cpp 复制代码
// 设置 label 大小和窗口一样大
ui->label->setGeometry(0, 0, 800, 600);
QPixmap pixmap(":/huaji.png");
ui->label->setPixmap(pixmap);

修改代码, 设置 scaledContents 属性

cpp 复制代码
// 设置内容伸缩 
ui->label->setScaledContents(true);

再次运行, 观察效果, 可以看到图片已经被拉伸, 可以把窗口填满了

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

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

cpp 复制代码
// 重写 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();
}

执行程序, 此时改变窗口大小, 图片也会随之变化.

注意: 此处的 resizeEvent 函数我们没有手动调用, 但是能在窗口大小变化时被自动调用. 这个过程就是依赖 C++ 中的多态来实现的.

Qt 框架内部管理着 QWidget 对象表示咱们的窗 口. 在窗口大小发生改变时, Qt 就会自动调用 resizeEvent 函数. 但是由于实际上这个表示窗口的并非是 QWidget, 而是 QWidget 的子类, 也就是咱们自己写 的 Widget. 此时虽然是通过父类调用函数, 但是实际上执行的是子类的函数(也就是我们重写 后的 resizeEvent ).

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

代码示例:文本对齐,自动换行,缩进,边距

1)创建四个label, objectName 分别是 label 到 label_4

并且在 QFrame 中设置 frameShape 为 Box (设置边框之后看起来会更清晰一些)

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

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

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

cpp 复制代码
 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);
     // 14 ui->label_2->setText("这是一个很长的文本这是一个很长的文本这是一个很长的文本这是一
// 个很长的文本这是一个很长的文本这是一个很长的文本");

     // 设置首行缩进
     ui->label_3->setAlignment(Qt::AlignTop | Qt::AlignLeft);
     ui->label_3->setIndent(20);
     // 19 ui->label_3->setText("这是一个很长的文本这是一个很长的文本这是一个很长的文本这是一
// 个很长的文本这是一个很长的文本这是一个很长的文本");

     // 设置边距
     ui->label_4->setAlignment(Qt::AlignTop | Qt::AlignLeft);
     ui->label_4->setMargin(20);
     // 24 ui->label_4->setText("这是一个很长的文本这是一个很长的文本这是一个很长的文本这是一
// 个很长的文本这是一个很长的文本这是一个很长的文本");
}

3)运行程序,可以看到如下效果

第一个label垂直水平居中

第二个label设置了wordWrap,能够自动换行

第三个label设置了Indent,左侧和上方和边框有间距.右侧则没有.

第四个label设置了margin,四个方向均有间距(图上仅体现出三个方向,下方看不出来).

代码示例:设置伙伴

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

objectName 分别问 label , label_2 , radioButton , radioButton_2

此处把label中的文本设置为"快捷键&A"这样的形式.

其中&后面跟着的字符,就是快捷键.

可以通过alt+A的方式来触发该快捷键.

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

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

当然这里也可以使用QtDesigner直接设置.

cpp 复制代码
 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);
}

3)运行程序,可以看到,按下快捷键alt+a或者alt+b,即可选中对应的选项.

4.2 LCDNumber

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

核心属性

属性 说明
intValue QLCDNumber 显示的数字值(int).
value QLCDNumber 显示的数字值(double). 和intValue是联动的. 例如给value设为1.5,intValue的值就是2. 另外,设置value和intValue的方法名字为 display ,而不是 setValue 或者 setIntValue .
digitCount 显示几位数字.
mode 数字显示形式. 1. QLCDNumber::Dec :十进制模式,显示常规的十进制数字。 2. QLCDNumber::Hex :十六进制模式,以十六进制格式显示数字。 3. QLCDNumber::Bin :二进制模式,以二进制格式显示数字。 4. QLCDNumber::Oct :八进制模式,以八进制格式显示数字。 只有十进制的时候才能显示小数点后的内容.
segmentStyle 设置显示风格. 1. QLCDNumber::Flat :平面的显示风格,数字呈现在一个平坦的表面上。 2. QLCDNumber::Outline :轮廓显示风格,数字具有清晰的轮廓和阴影效果。 3. QLCDNumber::Filled :填充显示风格,数字被填充颜色并与背景区分开。
smallDecimalPoint 设置比较小的小数点.

代码示例:倒计时

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

objectName 为 lcdNumber

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

cpp 复制代码
 QTimer* timer;

 void updateTime();

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

QTimer 表示定时器.通过 start 方法启动定时器之后,就会每隔一定周期,触发一次

QTimer::timeout 信号.

使用 connect 把 QTimer::timeout 信号和 Widget::updateTime 连接起来,意味着每次触发 QTimer::timeout 都会执行 Widget::updateTime

cpp 复制代码
 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);
     }

4)修改widget.cpp,实现 updateTime

通过 intValue 获取到 QLCDNumber 内部的数值.

如果value的值归0了,就停止 QTimer .接下来 QTimer 也就不会触发timeout信号了.

cpp 复制代码
 void Widget::updateTime() {
     qDebug() << "updateTime";

     int value = ui->lcdNumber->intValue();
     if (value <= 0) {
         // 如果时间到, 停止定时器.
         timer->stop();
         return;
     }
     ui->lcdNumber->display(value - 1);
}

5)执行程序,可以看到每隔一秒钟,显示的数字就减少1.

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

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

代码形如

cpp 复制代码
 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是否可以呢?

代码形如

cpp 复制代码
 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!

核心属性

属性名 说明
text QLabel 中显示的文本内容。
textFormat 文本格式,可选值:• Qt::PlainText------ 纯文本• Qt::RichText------ 富文本(支持 HTML 标签)• Qt::MarkdownText------ Markdown 格式• Qt::AutoText------ 自动判断格式
pixmap QLabel 内部显示的图片(QPixmap类型)。
scaledContents 是否自动拉伸内容以填充 QLabel:• true------ 自动拉伸• false------ 不拉伸(默认)
alignment 文本对齐方式,可设置水平/垂直方向(如 `Qt::AlignLeft
wordWrap 是否自动换行:• true------ 自动换行• false------ 不换行(默认)
indent 文本缩进,水平和垂直方向均生效(但受 alignment 影响,最多两个方向有效)。
margin 内部文本与边框的边距,上下左右四个方向同时生效(不同于 indent)。
openExternalLinks 是否允许点击 QLabel 中的 URL 打开外部链接(仅当文本包含 URL 时有效)。
buddy 关联的"伙伴"控件,点击 QLabel 时可激活该控件(如关联 QCheckBox 可实现点击选中)。

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

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

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

|

cpp 复制代码
 QTimer* timer;
 void updateProgressBar ();

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

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

cpp 复制代码
 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);
}

4)修改widget.cpp,实现 updateProgressBar

cpp 复制代码
 void Widget::updateProgressBar() {
     int value = ui->progressBar->value();
     if (value >= 100) {
         timer->stop();
         return;
     }
     ui->progressBar->setValue(value + 1);
}

5)运行程序,可以看到进度条中的进度在快速增长.

在实际开发中,进度条的取值,往往是根据当前任务的实际进度来进行设置的.

比如需要读取一个很大的文件,就可以获取文件的总的大小,和当前读取完毕的大小,来设置进度条的比例.

由于上面我们介绍了Qt禁止在其他线程修改界面,因此进度条的更新往往也是需要搭配定时器来完成的.通过定时器周期触发信号,主线程调用对应的slot函数.再在slot函数中对当前的任务进度进行计算,并更新进度条的界面效果.

代码示例:创建一个红色的进度条

不要忘了, QProgressBar 同样也是 QWidget 的子类,因此我们可以使用 styleSheet 通过样

式来修改进度条的颜色.

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

2)在QtDesigner右侧的属性编辑器中,找到QWidget的 styleSheet 属性.

编辑如下内容:

其中的 chunk 是选中进度条中的每个"块".使用 QProgressBar::text 则可以选中文本.

cpp 复制代码
 QProgressBar::chunk {background-color: #FF0000;}

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

此处如果不设置 alignment ,进度条中的数字会跑到左上角.这个怀疑是Qt本身的bug,暂时只能S先使用alignment来手动调整下.

3)执行程序,可以看到如下效果.我们就得到了一个红色的进度条.

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

4.4 CalendarWidget

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函数

cpp 复制代码
 void Widget::on_calendarWidget_selectionChanged()
{
     QDate date = ui->calendarWidget->selectedDate();
     qDebug() << date;

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

3)执行程序,可以看到当选择不同的日期时,label中的内容就会随之改变.

5. 输入类控件

5.1 LineEdit

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

核心属性

属性 说明
text 输入框中的文本
inputMask 输入内容格式约束
maxLength 最大长度
frame 是否添加边框
echoMode 显示方式:• QLineEdit::Normal:默认,显示输入的文本。• QLineEdit::Password:隐藏字符,显示星号(*)或等号(=)。• QLineEdit::NoEcho:不显示任何字符。
cursorPosition 光标所在位置
alignment 文字对齐方式(设置和垂直方向的对齐)
dragEnabled 是否允许拖拽
readOnly 是否是只读(不允许修改)
placeHolderText 当输入框内容为空时,显示的提示信息
clearButtonEnabled 是否会自动显示出"清除按钮"

核心信号

信号 说明
void cursorPositionChanged(int old, int new) 当鼠标移动时发出此信号,old为先前的位置,new为新位置。
void editingFinished() 当按返回或者回车键时,或者行编辑失去焦点时,发出此信号。
void returnPressed() 当返回或回车键按下时发出此信号. 如果设置了验证器,必须要验证通过,才能触发.
void selectionChanged() 当选中的文本改变时,发出此信号。
void textChanged(const QString &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,在构造函数中编写初始化代码.

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);
cpp 复制代码
 // 初始化第三个输入框
     ui->lineEdit_phone->setPlaceholderText("请输入电话号码");
     ui->lineEdit_password->setClearButtonEnabled(true);
     // 验证手机号码必须是 11 位数字. 并且按照 "344" 的格式来输入.
     ui->lineEdit_phone->setInputMask("000-0000-0000");
}

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

cpp 复制代码
 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();
}

4)执行程序,可以看到,随着用戶输入内容之后,点击按钮,就能打印到输入的信息

inputMask只能进行简单的输入格式校验.

实际开发中,基于正则表达式的方式是更核心的方法.

代码示例:使用正则表达式验证输入框的数据

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

关于正则表达式

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

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

参考:

正则表达式文档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 即可.

cpp 复制代码
 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}$")));

}

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

on_lineEdit_textEdited 的参数是当前输入框的内容.

通过 lineEdit->validator() 获取到内置的验证器.

通过 validate 方法验证文本是否符合要求.

第一个参数填写的是要验证的字符串.由于参数要求是 QString& 而不是 const

QString& ,需要把这个变量复制一下.

第二个参数是一个int&,是输出型参数.当验证的字符串不匹配时,返回这个字符串的长度.(没有啥实质作用).

返回值是一个枚举. QValidator::Acceptable 表示验证通过,

cpp 复制代码
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);
     }
}

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

代码示例:验证两次输入的密码一致

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

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

cpp 复制代码
 Widget::Widget(QWidget *parent)
     : QWidget(parent)
     , ui(new Ui::Widget)
{
     ui->setupUi(this);

     ui->lineEdit->setEchoMode(QLineEdit::Password);
     ui->lineEdit_2->setEchoMode(QLineEdit::Password);
}

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

cpp 复制代码
 void 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("两次输入的密码不同!");
     }
}

4)执行程序,观察效果.

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

代码示例:切换显示密码

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

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

cpp 复制代码
 Widget::Widget(QWidget *parent)
     : QWidget(parent)
     , ui(new Ui::Widget)
{
     ui->setupUi(this);

     ui->lineEdit->setEchoMode(QLineEdit::Password);
}

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

cpp 复制代码
 void Widget::on_checkBox_toggled(bool checked)
{
     if (checked) {
         ui->lineEdit->setEchoMode(QLineEdit::Normal);
     } else {
         ui->lineEdit->setEchoMode(QLineEdit::Password);
     }
}

4)执行程序,可以看到切换复选框的状态,就可以控制输入框显示密码

5.2 TextEdit

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 方法获取到内部的文本.S

类似的, QTextEdit 还提供了 toMarkdown 和 toHtml .根据需要我们调整不同的获取方式.

cpp 复制代码
 void Widget::on_textEdit_textChanged()
{
     const QString& content = ui->textEdit->toPlainText();
     qDebug() << content;
     ui->label->setText(content);
}

3)执行程序,可以看到当输入框中的内容发生变化时,label中的内容同步发生改变.

代码示例:验证输入框的各种信号

1)创建多行输入框

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

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

的内容.

cpp 复制代码
 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;
}

3)执行程序,观察结果.

可以看到:

  1. 编写内容时, textChanged 和 cursorPositionChanged 会触发
  1. 选中一段文本时, cursorPositionChanged , selectionChanged ,copyAvailable

会触发.

  1. 按下ctrl+z时, textChanged , undoAvailable , redoAvailable ,

cursorPositionChanged 会触发

  1. 按下ctrl+y, textChanged , undoAvailable , redoAvailable ,

cursorPositionChanged 会触发

5.3 ComboBox

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,初始化三个下拉框的内容

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("雪碧");
}

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

cpp 复制代码
 void Widget::on_pushButton_clicked()
{
     qDebug() << "汉堡选择: " << ui->comboBox->currentText();
     qDebug() << "小食选择: " << ui->comboBox_2->currentText();
     qDebug() << "饮料选择: " << ui->comboBox_3->currentText();
}

4)执行程序,可以看到,在点击确定按钮时,就能获取到当前下拉框中选中的内容.

代码示例:从文件中加载下拉框的选项

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

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

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

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

使用 ifstream 打开文件

使用 getline 读取每一行

使用 QString::fromStdString 把 std::string 转成 QString

cpp 复制代码
 #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();
}

4)执行程序,可以看到文件内容已经被加载到下拉框中.

Qt中也提供了QFile实现读写文件的功能.当然使用C++标准库的std::fstream也是完全可以的.

之所以存在两套,是因为Qt诞生较早(1991年左右),此时C++还没有完成"标准化"的工作,C++标准库这样的概念自然也没有诞生.

因此Qt就自己打造了一套库,实现了字符串,容器,文件操作,多线程,网络操作,定时器,正则表达式等内容.

(由于C++标准委员会的不作为,至今仍然有些Qt提供的功能,是标准库不具备的)

5.4 SpinBox

使用 QSpinBox 或者 QDoubleSpinBox 表示"微调框",它是带有按钮的输入框.可以用来输入整

数/浮点数.通过点击按钮来修改数值大小.

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

Spin英文原意为"旋转".此处引申成"微调".

事实上很多术语在翻译的时候,不一定非要按照原始的翻译来表示,更追求的是"信达雅".

举个例子,地铁上的"PrioritySeat"会翻译成"爱心专座",而不是"优先座位".

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,给下拉框设置初始值.

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

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

cpp 复制代码
 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();
}

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

5.5 DateEdit&TimeEdit

使用 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的偏移量(时差).

核心信号

信号 说明
dateChanged(QDate) 日期改变时触发.
timeChanged(QTime) 时间改变时触发.
dateTimeChanged(QDateTi me) 时间日期任意一个改变时触发.

代码示例:实现日期计算器

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

QDateTimeEdit 的 objectName 为 dateTimeEdit_old 和 dateTimeEdit_new

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

使用 daysTo 函数可以计算两个日期的天数.

使用 secsTo 函数可以计算两个时间的秒数.

通过 ( 秒数 / 3600) 换算成小时数,再余上24得到零几个小时.

使用 QString::number 把整数转成 QString 进行拼接.

cpp 复制代码
 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);
}

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,对旋钮初始化

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

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

cpp 复制代码
 void Widget::on_dial_valueChanged(int value)
{
     ui->label->setText(QString("当前不透明度为: ") + QString::number(value));
     this->setWindowOpacity((double)value / 100);
}

4)运行程序,观察效果.可以看到随着拖动旋钮旋转,不透明度发生明显变化.

5.7 Slider

使用 QSlider 表示一个滑动条.

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

核心属性

属性 说明
value 持有的数值.
minimum 最小值
maximum 最大值
singleStep 按下方向键的时候改变的步长.
pageStep 按下pageUp/pageDown的时候改变的步长.
sliderPosition 滑动条显示的初始位置
tracking 外观是否会跟踪数值变化. 默认值为true.一般不需要修改.
orientation 滑动条的方向是水平还是垂直
invertedAppearance 是否要翻转滑动条的方向
tickPosition 刻度的位置.
tickInterval 刻度的密集程度.

代码示例:调整窗口大小

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

objectName 分别为 horizontalSlider 和 verticalSlider

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

cpp 复制代码
 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);
}

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

cpp 复制代码
 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;
}

4)执行程序,可以看到调整滑动条,窗口大小就会随之改变.

代码示例:通过自定义快捷键调整滑动条位置.

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

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

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

2)编写初始化代码

cpp 复制代码
 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);
}

3)创建 valueChanged 的slot函数

cpp 复制代码
 void Widget::on_horizontalSlider_valueChanged(int value)
{
     ui->label->setText(QString::number(value));
}

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

使用 QShortCut 类设置快捷键.

快捷键触发时,会发出 QShortcut::activated 信号,我们连接到自己写的slot函数.

cpp 复制代码
 // 设置快捷键
 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);

5)编写自定义slot函数

cpp 复制代码
 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);
}

6)执行程序,观察效果.可以看到此时按下-和=就可以调整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 ListWidget

使用 QListWidget 能够显示一个纵向的列表.

每个选项都可以被选中.

核心属性

属性 说明
currentRow 当前被选中的是第几行
count 一共有多少行
sortingEnabled 是否允许排序
isWrapping 是否允许换行
itemAlignment 元素的对齐方式
selectRectVisible 被选中的元素矩形是否可见
spacing 元素之间的间隔

核心方法

方法 说明
addItem(constQString&label) addItem(QListWidgetItem*item) 列表中添加元素.
currentItem() 返回QListWidgetItem*表示当前选中的元素
setCurrentItem(QListWidgetItem*item) 设置选中哪个元素
setCurrentRow(introw) 设置选中第几行的元素
insertItem(constQString&label,int row) insertItem(QListWidgetItem*item,int row) 在指定的位置插入元素
item(introw) 返回QListWidgetItem*表示第row行的元素
takeItem(introw) 删除指定行的元素,返回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 更丰富.咱们使用SListWidget 即可.

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

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

3)编写listWidget的slot函数

此处需要判定 current 和 previous 非空.初始情况下是没有元素选中的,就导致这俩指针可

能是NULL.

cpp 复制代码
 void Widget::on_listWidget_currentItemChanged(QListWidgetItem *current,
QListWidgetItem *previous)
{
     if (current != NULL && previous != NULL) {
         qDebug() << "当前选中: " << current->text()
              << "之前选中: " << previous->text();
     }
}
// 这里需要给widget.h前面加上 #include <QListWidgetItem>

4)编写按钮的slot函数

cpp 复制代码
 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);
}

5)执行程序,观察效果.可以新增元素,选中元素,删除元素.

6.2 TableWidget

使用 QTableWidget 表示一个表格控件.一个表格中包含若干行,每一行又包含若干列.

表格中的每个单元格,是一个 QTableWidgetItem 对象.

QTableWidget 核心方法

方法 说明
item(introw,intcolumn) 根据行数列数获取指定的 QTableWidgetItem*
setItem(introw,intcolumn, QTableWidget*) 根据行数列数设置表格中的元素
currentItem() 返回被选中的元素QTableWidgetItem*
currentRow() 返回被选中元素是第几行
currentColumn() 返回被选中元素是第几列
row(QTableWidgetItem*) 获取指定item是第几行
column(QTableWidgetItem*) 获取指定item是第几列
rowCount() 获取行数
columnCount() 获取列数
insertRow(introw) 在第row行处插入新行
insertColumn(intcolumn) 在第column列插入新列
removeRow(introw) 删除第row行
removeColumn(intcolumn) 删除第column列
setHorizontalHeaderItem(int column,QTableWidget*) 设置指定列的表头
setVerticalHeaderItem(introw, QTableWidget*) 设置指定行的表头

QTableWidgetItem 核心信号

信号 说明
cellClicked(introw,intcolumn) 点击单元格时触发
cellDoubleClicked(introw,int column) 双击单元格时触发
cellEntered(introw,intcolumn) 鼠标进入单元格时触发
currentCellChanged(introw,int column,intpreviousRow,int previousColumn) 选中不同单元格时触发

QTableWidgetItem 核心信号

信号 说明
cellClicked(introw,intcolumn) 点击单元格时触发
cellDoubleClicked(introw,int column) 双击单元格时触发
cellEntered(introw,intcolumn) 鼠标进入单元格时触发
currentCellChanged(introw,int column,intpreviousRow,int previousColumn) 选中不同单元格时触发

QTableWidgetItem 核心方法

方法 说明
row() 获取当前是第几行
column() 获取当前是第几列
方法 说明
row() 获取当前是第几行
column() 获取当前是第几列
setText(constQString&) 设置文本
setTextAlignment(int) 设置文本对齐
setIcon(constQIcon&) 设置图标
setSelected(bool) 设置被选中
setSizeHints(constQSize&) 设置尺寸
setFont(constQFont&) 设置字体

代码示例:使用 QTableWidget

1)在界面上创建 QTableWidget 和三个按钮,一个输入框

注意: QTableWidget 是 QTableView 的子类,功能比 QTableView 更丰富.咱们使用QTableWidget 即可.

2)编写widget.cpp构造函数,构造表格中的初始数据.

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

3)编写按钮的slot函数

cpp 复制代码
 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);
}

4)执行程序,即可完成表格的基本操作.

默认情况下,单元格中的内容直接就是可编辑的.

如果不想让用戶编辑,可以设置 ui->tableWidget-

6.3 TreeWidget

使用 QTreeWidget 表示一个树形控件.里面的每个元素,都是一个 QTreeWidgetItem ,每个

QTreeWidgetItem 可以包含多个文本和图标,每个文本/图标为一个列.

可以给 QTreeWidget 设置顶层节点(顶层节点可以有多个),然后再给顶层节点添加子节点,从而构成树形结构.

QTreeWidget 核心方法

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

QTreeWidget 核心信号

信号 说明
currentItemChanged(QTreeWidgetItem* current,QTreeWidgetItem*old) 切换选中元素时触发
itemClicked(QTreeWidgetItem*item,intcol) 点击元素时触发
itemDoubleClicked(QTreeWidgetItem*item, intcol) 双击元素时触发
itemEntered(QTreeWidgetItem*item,intcol) 鼠标进入时触发
itemExpanded(QTreeWidgetItem*item) 元素被展开时触发
itemCollapsend(QTreeWidgetItem*item) 元素被折叠时触发

QTreeWidgetItem 核心属性

属性 说明
text 持有的文本
textAlignment 文本对齐方式
icon 持有的图标
font 文本字体
hidden 是否隐藏
disabled 是否禁用
expand 是否展开
sizeHint 尺寸大小
selected 是否选中

QTreeWidgetItem 核心方法

方法 说明
addChild(QTreeWidgetItem*child) 新增子节点
childCount() 子节点的个数
child(intindex) 获取指定下标的子节点.返回QTreeWidgetItem*
takeChild(intindex) 删除对应下标的子节点
removeChild(QTreeWidgetItem* child) 删除对应的子节点
parent() 获取该元素的父节点

代码示例:使用 QTreeWidget

1)在界面上创建一个 TreeView ,右键=>变形为=> TreeWidget ,再创建一个lineEdit和两个按

钮.

注意: TreeWidget 是 TreeView 的子类,功能比 TreeView 更丰富.咱们使用 TreeWidget

即可.

2)编写代码,构造初始数据

cpp 复制代码
 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);
}

3)编写代码,实现按钮的slot函数

cpp 复制代码
 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);
     }
}

4)执行程序,可以针对树形框进行编辑.

7. 容器类控件

7.1 GroupBox

使用 QGroupBox 实现一个带有标题的分组框.可以把其他的控件放到里面作为一组.这样看起来能更好看一点.

注意,不要把 QGroupBox 和 QButtonGroup 混淆.(之前在介绍 QRadionButton 的时候提

到了 QButtonGroup ).

核心属性

属性 说明
title 分组框的标题
alignment 分组框内部内容的对齐方式
flat 是否是"扁平"模式
checkable 是否可选择. 设为true,则在title前方会多出一个可勾选的部分.
checked 描述分组框的选择状态(前提是checkable为true)

代码示例:给麦当劳案例加上分组框

1)在界面上创建三个分组框,并且在分组框内部创建下拉框和微调框.

注意:

在复制粘贴控件的时候,一定要先选中对应的父控件,再粘贴.

2)编写widget.cpp,添加初始化下拉框的代码

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("雪碧");
}

3)运行程序,观察效果

7.2 TabWidget

使用 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 ,可以添加标签页或者删除标签页.

2)编写widget.cpp,进行初始化,给标签页中放个简单的label

注意新创建的label的父元素,是 ui->tab 和 ui->tab_2 .Qt中使用父子关系决定该控件"在

哪里".

cpp 复制代码
     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);

3)编写按钮的slot函数

使用 count() 获取到标签页的个数.

使用 addTab 新增标签页.

使用 removeTab 删除标签页.

使用 currentIndex 获取到当前标签页的下标.

使用 setCurrentIndex 切换当前标签页.

cpp 复制代码
 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);
}

4)编写 QTabWidget 的 currentChanged 函数

cpp 复制代码
 void Widget::on_tabWidget_currentChanged(int index)
{

     qDebug() << "当前选中标签页为: " << index;
}

5)运行程序,观察效果

点击新建标签页,可以创建出新的标签.

点击删除当前标签页,可以删除标签.

切换标签页时,可以看到qDebug打印出的标签页编号.

8. 布局管理器

之前使用Qt在界面上创建的控件,都是通过"绝对定位"的方式来设定的.

也就是每个控件所在的位置,都需要计算坐标,最终通过 setGeometry 或者 move 方式摆放过去.

这种设定方式其实并不方便.尤其是界面如果内容比较多,不好计算.而且一个窗口大小往往是可以调整的,按照绝对定位的方式,也无法自适应窗口大小.因此Qt引入"布局管理器"(Layout)机制,来解决上述问题.

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

8.1 垂直布局

使用 QVBoxLayout 表示垂直的布局管理器.V是 vertical 的缩写.

核心属性

`

属性 说明
layoutLeftMargin 左侧边距
layoutRightMargin 右侧边距
layoutTopMargin 上方边距
layoutBottomMargin 下方边距
layoutSpacing 相邻元素之间的间距

代码示例:使用 QVBoxLayout 管理多个控件.

1)编写代码,创建布局管理器和三个按钮.并且把按钮添加到布局管理器中.

使用 addWidget 把控件添加到布局管理器中.

使用 setLayout 设置该布局管理器到widget中.

cpp 复制代码
 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);
}

2)运行程序,可以看到此时界面上的按钮就存在于布局管理器中.随着窗口尺寸变化而发生改变.

此时三个按钮的尺寸和位置,都是自动计算出来的.

通过上述代码的方式,只能给这个widget设定一个布局管理器.实际上也可以通过QtDesign在一个窗口中创建多个布局管理器.

代码示例:创建两个 QVBoxLayout

1)在界面上创建两个 QVBoxLayout ,每个 QVBoxLayout 各放三个按钮.

2)运行程序,可以看到这些按钮已经自动排列好.只不过当前这些按钮的位置不能随着窗口大小自动变化.

通过QtDesigner创建的布局管理器,其实是先创建了一个widget,设置过 属性geometry

的.再把这个layout设置到这个widget中.实际上,一个widget只能包含一个layout.打开ui文件的原始xml,可以看到其中的端倪.这种情况下layout并非是窗口widget的布局管理器,因此不会随着窗口大小改变.

cpp 复制代码
 <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)编写代码,创建布局管理器和三个按钮.并且把按钮添加到布局管理器中.

cpp 复制代码
 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

1)在代码中创建以下内容

使用 addLayout 给layout中添加子layout.

cpp 复制代码
 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);
}

2)执行程序,观察结果

结合 QHBoxLayout 和 QVBoxLayout ,就可以做出各种复杂的界面了.

8.3 网格布局

Qt中还提供了 QGridLayout 用来实现网格布局的效果.可以达到M*N的这种网格的效果.

核心属性

整体和 QVBoxLayout 以及 QHBoxLayout 相似.但是设置spacing的时候是按照垂直水平两个

方向来设置的.

属性 说明
layoutLeftMargin 左侧边距
layoutRightMargin 右侧边距
layoutTopMargin 上方边距
layoutBottomMargin 下方边距
layoutHorizontalSpacing 相邻元素之间水平方向的间距
layoutVerticalSpacing 相邻元素之间垂直方向的间距
layoutRowStretch 行方向的拉伸系数
layoutColumnStretch 列方向的拉伸系数

代码示例:使用 QGridLayout 管理元素

1)代码中创建 QGridLayout 和4个按钮.

使用 addWidget 添加控件到布局管理器中.但是添加的同时会指定两个坐标.表示放在第几行,第

几列.

cpp 复制代码
 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);
}

2)执行代码,观察效果.可以看到当前的这几个按钮是按照2行2列的方式排列的.

3)如果调整行列坐标为下列代码

cpp 复制代码
 // 创建网格布局管理器, 并且添加元素

 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)如果调整行列坐标为下列代码

cpp 复制代码
 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)任意调整行列,即可看到不同的效果.

cpp 复制代码
// 创建网格布局管理器, 并且添加元素

 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)编写代码形如

cpp 复制代码
 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 设置每一列的拉伸系数.

cpp 复制代码
 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);
}

2)执行程序,可以看到每一列的宽度是不同的.并且随着窗口调整动态变化.

另外, QGridLayout 也提供了setRowStretch 设置行之间的拉伸系数.

上述案例中,直接设置 setRowStretch 效果不明显,因为每个按钮的高度是固定的.需要把按钮的垂直方向的 sizePolicy 属性设置为 QSizePolicy::Expanding 尽可能填充满布局管理器,才能看到效果.

代码示例:设置垂直方向的拉伸系数

1)编写代码,创建6个按钮,按照3行2列方式排列.

使用 setSizePolicy 设置按钮的尺寸策略.可选的值如下:

QSizePolicy::Ignored :忽略控件的尺寸,不对布局产生影响。

QSizePolicy::Minimum :控件的最小尺寸为固定值,布局时不会超过该值。

QSizePolicy::Maximum :控件的最大尺寸为固定值,布局时不会小于该值。

QSizePolicy::Preferred :控件的理想尺寸为固定值,布局时会尽量接近该值。

QSizePolicy::Expanding :控件的尺寸可以根据空间调整,尽可能占据更多空间。

QSizePolicy::Shrinking :控件的尺寸可以根据空间调整,尽可能缩小以适应空间。

cpp 复制代码
 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);
}

2)执行代码,观察效果.

此时的按钮垂直方向都舒展开了.并且调整窗口尺寸,也会按照设定的比例同步变化.

总的来说,使用 QGridLayout 能够代替很多 QHBoxLayout 和 QVBoxLayout 嵌套的场景.毕

竟嵌套的代码写起来是比较麻烦的.

另外不要忘了, QGridLayout 里面也能嵌套 QHBoxLayout 和 QVBoxLayout ,QHBoxLayout 和 QVBoxLayout 里面也能嵌套 QGridLayout .

灵活使用上述布局管理器,就可以实现出任意的复杂界面.

8.4 表单布局

除了上述的布局管理器之外,Qt还提供了 QFormLayout ,属于是 QGridLayout 的特殊情况,专

门用于实现两列表单的布局.

这种表单布局多用于让用戶填写信息的场景.左侧列为提示,右侧列为输入框.

代码示例:使用 QFormLayout 创建表单.

1)编写代码,创建 QFormLayout ,以及三个label和三个lineEdit

使用 addRow 方法来添加一行.每行包含两个控件.第一个控件固定是QLabel/文本,第二个控件

则可以是任意控件.

如果把第一个参数填写为NULL,则什么都不显示.

cpp 复制代码
 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);
}

2)执行程序,可以看到以下结果.

8.5 Spacer

使用布局管理器的时候,可能需要在控件之间,添加一段空白.就可以使用 QSpacerItem 来表示.

核心属性

属性 说明
width 宽度
height 高度
hData 水平方向的sizePolicy QSizePolicy::Ignored :忽略控件的尺寸,不对布局产生影响。 QSizePolicy::Minimum :控件的最小尺寸为固定值,布局时不会超过该值。 QSizePolicy::Maximum :控件的最大尺寸为固定值,布局时不会小于该值。 QSizePolicy::Preferred :控件的理想尺寸为固定值,布局时会尽量接近该 值。 QSizePolicy::Expanding :控件的尺寸可以根据空间调整,尽可能占据更多空 间。 QSizePolicy::Shrinking :控件的尺寸可以根据空间调整,尽可能缩小以适应 空间。
vData 垂直方向的sizePolicy 选项同上.

代码示例:创建一组左右排列的按钮.

1)在界面上创建一个 QVBoxLayout ,并添加两个按钮.

cpp 复制代码
 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);
}

2)直接运行程序,可以看到两个按钮是紧挨着的.

3)在两个按钮中间添加一个spacer

cpp 复制代码
 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);

     layout->addWidget(btn1);
     // 在两个 widget 中间添加空白
     layout->addSpacerItem(spacer);
     layout->addWidget(btn2);
}

4)运行程序,观察代码效果.可以看到两个按钮之间已经存在了间隔了.

调整QSpacerItem不同的尺寸,即可看到不同的间距.

在QtDesigner中,也可以直接给界面上添加spacer.