【qt】基础知识(常用控件、信号与槽、布局管理器)

📚 博主的专栏

🐧 Linux | 🖥️ C++ | 📊 数据结构| 💡C++ 算法 | 🅒 C 语言 | 🌐 计算机网络 |🗃️ mysql
**本文摘要:**本文介绍了Qt框架中常用控件与布局管理器的核心功能与应用方法。首先阐述了Qt中的信号与槽机制,重点讲解了lambda表达式在槽函数中的使用技巧和变量捕获机制。随后详细说明了按钮类控件(QPushButton、QRadioButton、QCheckBox)的属性配置与事件处理,以及显示类控件(QLabel、QLCDNumber、QProgressBar)的数据展示方式。在输入类控件部分,涵盖了QLineEdit、QTextEdit、QComboBox等交互元素的使用场景和信号处理。对于多元素控件,重点介绍了QListWidget、QTableWidget和QTreeWidget的数据组织与操作方式。最后深入讲解了布局管理器(QVBoxLayout、QHBoxLayout、QGridLayout、QFormLayout)的原理与应用,包括Spacer的使用技巧和尺寸策略设置方法,为Qt界面开发提供了全面的技术参考。

目录

一、QT概述

小总结1:

二、信号与槽

使用lambda表达式的变量捕获

小总结2:

三、常用控件

enabled-控件可用状态

geometry-几何

[Window Frame 的影响](#Window Frame 的影响)

windowTitle-控件窗口标题

windowIcon-窗口图标

qrc机制

windowOpacity-控件的不透明度

cursor-修改鼠标光标样式​编辑

font-和字体相关的一系列的内容

toolTip-鼠标悬停提示

focusPolicy-获取焦点

styleSheet-设置属性样式

按钮类控件

[Push Button-按钮](#Push Button-按钮)

[Radio Buttion-单选按钮](#Radio Buttion-单选按钮)

[Check Box-复选按钮](#Check Box-复选按钮)

显示类控件:

Label-显示文本和图片

设置伙伴-setBuddy

[LCD Number-显示数字](#LCD Number-显示数字)

QTimer-产生timeout信号​编辑

​编辑

sleep_for-延时

​编辑

​编辑

C++ 编译速度慢。和#include头文件,是有直接关系的

[Calendar Widget-日历控件](#Calendar Widget-日历控件)

输入类控件

[Line Edit-单行输入框](#Line Edit-单行输入框)

关于正则表达式

[Text Edit-多行输入框](#Text Edit-多行输入框)

[Combo Box-下拉框(直观效果)、组合框](#Combo Box-下拉框(直观效果)、组合框)

[Spin Box-微调框带有按钮的输入框](#Spin Box-微调框带有按钮的输入框)

[Date Edit-日期微调框 & Time Edit-时间微调框-QDateTimeEdit 时间日期微调](#Date Edit-日期微调框 & Time Edit-时间微调框-QDateTimeEdit 时间日期微调)

Dial-旋钮

Slider-滑动条

快捷键的设置!!

多元素控件

[List Widget-列表组件​编辑](#List Widget-列表组件编辑)

[Table Widget-表格控件](#Table Widget-表格控件)

[Tree Widget-树形控件](#Tree Widget-树形控件)

容器类控件

[Group Box-带有标题的分组框](#Group Box-带有标题的分组框)

[Tab Widget-带有标签页的控件](#Tab Widget-带有标签页的控件)

布局管理器-尺寸和位置自动计算、geometry失效

垂直布局

水平布局​编辑

布局管理器之间能进行嵌套

网格布局

表单布局

Spacer-控件之间,添加⼀段空白


一、QT概述

qt中的connect与linux计算机网络当中的connect不同,就如同,数据结构中的堆栈、与操作系统中的堆栈不同一般:

小总结1:

二、信号与槽

按钮的创造方式:代码与图形化

坐标系

使用lambda表达式的变量捕获

cpp 复制代码
connect(button, &QPushButton::clicked, this, [](){
        qDebug() << "lambda 被执行了";
//        button->move(); 找不到定义,这是因为lambda,本质是一个回调函数,这个函数,无法直接获取到上层作用域的变量
    });

lambda表达式:找不到定义,这是因为lambda,本质是一个回调函数,这个函数,无法直接获取到上层作用域的变量,为了解决上述问题,引入变量捕获,获取到外层作用域中的变量

cpp 复制代码
    connect(button, &QPushButton::clicked, this, [button](){
        qDebug() << "lambda 被执行了";
//        button->move(); 找不到定义,这是因为lambda,本质是一个回调函数,这个函数,无法直接获取到上层作用域的变量
        button->move(300, 300);
    });

捕获多个变量:

cpp 复制代码
    connect(button, &QPushButton::clicked, this, [button, this](){
        qDebug() << "lambda 被执行了";
//        button->move(); 找不到定义,这是因为lambda,本质是一个回调函数,这个函数,无法直接获取到上层作用域的变量
        button->move(300, 300);
        this->move(100, 100);
    });

如果当前lambda里边想使用更多的外层变量咋办?

捕获方式写成[=],该写法的含义就是把上层作用域中的所有变量名都给捕获

一般我们对应的槽函数比较简单,而且是一次性使用的,就经常写做这种lambda的形式。

不能在lambda中捕获已经释放掉的对象,因此,关于对象生命周期的管理,很重要!!!

注意:

小总结2:

注意低耦合,高内聚

三、常用控件

enabled-控件可用状态

|-------------|-----------------------------------|
| API | 说明 |
| isEnabled() | 获取到控件的可⽤状态. |
| setEnabled | 设置控件是否可使⽤. true 表⽰可⽤, false 表⽰禁⽤. |

"禁用"状态表示控件无法接收任何用户输入事件,同时其外观通常会呈现为灰色显示。需要注意的是: • 当某个widget被禁用时,其包含的所有子元素也会自动继承禁用状态。

geometry-几何

以左上角为点,进行target大小形状改变:

cpp 复制代码
void MyWidget::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 MyWidget::on_pushButton_left_clicked()
{
    QRect rect = ui->pushButton_target->geometry();
    qDebug() << rect;
    rect.setX(rect.x() - 5);//向左移动
    ui->pushButton_target->setGeometry(rect);
}

void MyWidget::on_pushButton_right_clicked()
{
    QRect rect = ui->pushButton_target->geometry();
    qDebug() << rect;
    rect.setX(rect.x() + 5);//向右移动
    ui->pushButton_target->setGeometry(rect);
}

void MyWidget::on_pushButton_down_clicked()
{
    //获取到target本身的geometry
    QRect rect = ui->pushButton_target->geometry();
    qDebug() << rect;
    rect.setY(rect.y() + 5);//向下移动
    ui->pushButton_target->setGeometry(rect);
}

现在平移target:按照类似的进行调整

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

有趣的程序:

cpp 复制代码
#include "mywidget.h"
#include "ui_mywidget.h"

MyWidget::MyWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MyWidget)
{
    ui->setupUi(this);
    //设置随机种子,使用时间戳作为随机种子
    srand(time(0));
}

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


void MyWidget::on_pushButton_agree_clicked()
{
    ui->label->setText("吃什么?");
}


void MyWidget::on_pushButton_reject_pressed()
{
    ui->label->setText("必须一起吃饭!!!");
//    把这个按钮挪走,点不到
    //可以通过生成随机数的方式,确定按钮新位置

    //先获取到当前程序窗口的尺寸
    int width = this->geometry().width();
    int height = this->geometry().height();

    //重新生成随机整数
    int x = rand() % width;
    int y = rand() % height;

    //移动按钮位置,rand函数在使用之前,需要设置随机种子
    ui->pushButton_reject->move(x, y);
}

Window Frame 的影响

当 widget 作为窗口(带有标题栏、最小化、最大化、关闭按钮)时,计算尺寸和坐标存在两种计算方式:包含 window frame 和不包含 window frame。

计算方式说明:

  • 包含 window frame 的方法:x(), y(), frameGeometry(), pos(), move()
  • 不包含 window frame 的方法:geometry(), width(), height(), rect(), size()

注意:对于非窗口形式的 widget,这两种计算方式得到的结果是一致的。

示例:

当前代码是放到构造函数当中,此时这个Widget对象正在构造,还未被加入到window frame中,此时还看不到window frame的影响:

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

使用信号槽:利用一个按钮

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

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

windowTitle-控件窗口标题

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

windowIcon-窗口图标

复制代码
注意:C++11中引入了raw string 解决上述问题,字符串中,不包含任意转义字符,写图片路径时,注意/

qrc机制

(缺点:无法导入太大的资源文件,比如几个GB的视频,qrc就无能为力)

使用方法:

1.在项目中创建qrc文件

2.把图片导入到qrc文件中

1)先创建一个前缀:Prefix(虚拟目录,不在电脑上真实存在,是qt抽象出的)

windowOpacity-控件的不透明度

代码:

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

注意浮点数的使用!!!

《代码大全》进行了详细的讨论(防御性编程)


cursor-修改鼠标光标样式

转圈等待

cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QCursor cursor(Qt::WaitCursor);
    ui->pushButton->setCursor(cursor);
}

qt也允许自定义设置光标,允许用自己的图片来设置图标

先准备一个图片,仍然通哟qrc来管理:

cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPixmap pixmap(":/image.jpeg");
    pixmap = pixmap.scaled(50, 50);//针对图片进行缩放
    //构造光标对象
    QCursor cursor(pixmap);//相当于,图片的左上角在点击
//    QCursor cursor(pixmap, 10, 10); //以左上角为0,0远点,找到10,10这个位置作为点击位置
    this->setCursor(cursor);
}

font-和字体相关的一系列的内容

在图形化界面中:实时预览(Qt Designer)

toolTip-鼠标悬停提示

cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //设置两按钮的toolTips
    ui->pushButton_yes->setToolTip("这是一个 yes 按钮");
    ui->pushButton_yes->setToolTipDuration(3000);//显示3s

    ui->pushButton_no->setToolTip("这是一个 no 按钮");
    ui->pushButton_no->setToolTipDuration(3000);//显示3s
}

focusPolicy-获取焦点

设置控件获取焦点的策略,例如决定控件是否支持鼠标选中或通过Tab键切换选中。

所谓"焦点",指的是元素被选中后成为操作目标。后续的键盘操作都将作用于该焦点元素,这对输入框、单选框、复选框等控件尤为重要。

这个概念类似《魔兽争霸3》或《星际争霸2》中的操作逻辑:先选中单位,再执行命令。

代码示例:理解不同的 focusPolicy

  1. 在界面上创建四个单行输入框(Line Edit)

通过属性框中的focusPolicy,可以对每个lineEdit进行一个修改

styleSheet-设置属性样式

使用CSS自定义widget样式

🎨 CSS(层叠样式表)是一种前端网页技术,专门用于定义界面元素的视觉呈现效果。通过CSS,我们可以灵活控制元素的大小、位置、颜色、间距、字体、背景、边框等各种样式属性。

现代网页之所以能够呈现丰富多彩的视觉效果,很大程度上依赖于CSS的运用。虽然Qt主要用于GUI开发,但其界面设计理念与网页前端存在诸多共通之处。为此,Qt特别添加了对CSS的支持功能,使开发者能够像设计网页一样来美化Qt界面。

CSS提供了丰富的样式属性选项。Qt支持其中部分属性,称为QSS(Qt Style Sheet)。具体支持哪些属性,可查阅Qt文档中的"Qt Style Sheets Reference"章节。
编辑右侧的styleSheet属性,设置样式

通过代码。设置样式,实现一个"夜间模式"功能

按钮类控件

**Push Button-**按钮

QPushButton 代表一个按钮控件,这是我们最常用的控件之一。它继承自抽象类 QAbstractButton,后者是所有按钮控件的基类。

在Qt Designer中同样可以查看此处的继承关系。

QA``bstractButton 中,与 QPushButton 密切相关的属性

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

🌰 1. 作为 QWidget 的子类,QAbstractButton 自然继承了 QWidget 的所有属性。前文介绍的 QWidget 属性用法同样适用于 QAbstractButton,因此表格中仅列出 QAbstractButton 特有的属性。

  1. Qt 的 API 设计风格非常清晰。所有列出的属性都支持获取和设置操作。例如:通过 text() 方法获取按钮文本,使用 setText() 方法设置文本。

给按钮设置图标

给按钮设置快捷键

设置方向键的槽函数

设置快捷键:也能移动

也需要在Widget的构造函数中完成,程序一启动,快捷键就有效

复制代码
还可以通过按键的枚举来设置快捷键

组合键

键盘的连发默认支持,开启鼠标点击的连发功能:autoRepeat(true)

Radio Buttion-单选按钮

QRadioButton 是单选按钮控件,允许用户在多个选项中选择一个。

作为 QAbstractButton 和 QWidget 的子类,前面介绍的属性和方法同样适用于 QRadioButton。

以下是 QAbstractButton 中与 QRadioButton 密切相关的属性:

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

示例:

使用setEnabled(false) / setDisabled(true)禁用选项

Radio Buttion 每个信号的使用

基于QRadioButton 实现一个简单的模拟点餐的功能

直接添加后,由于RadioButton默认是排他的,一旦界面上需要存在多组"单选按钮",希望组和组之间不要有影响

QButtonGroup类,可以针对单选按钮进行分组

Check Box-复选按钮

QCheckBox 表示复选框控件,支持多选功能。

它继承自 QAbstractButton 类,主要具有 checkable 和 checked 两个属性。此外,QCheckBox 还提供 tristate 属性用于实现"三态复选框"功能,不过这个功能较为冷门。

显示类控件:

Label-显示文本和图片

QLabel 可用于显示文本和图片,主要功能特性包括:

示例1:不同文本格式

示例2:显示图片

复制代码
此时由于QLabel是固定大小的,所以当我们展开整个窗口时,图片大小不会与窗口同步变化

展开后:

cpp 复制代码
ui->label->setGeometry(0, 0, rect.width(), rect.height());
复制代码
上面的代码是在构造函数里,进行这样的尺寸设置,这个设置相当于是"一次性的",一旦程序运行起来后,QLabel的尺寸就固定下来了,窗口发生变化,此时,QLabel是不会变化的

使用事件,来解决:

用户的操作会对应一些信号,Qt中,表示用户的操作,有两类概念,一个是信号,另一个是事件

当用户拖拽修改窗口大小的时候,会触发resize事件(resizeEvent),像resize这样的事件,是连续变化的,把窗口尺寸从A拖到B的这个过程中,会触发一系列的resizeEvent。

思路:

借助resizeEvent来完成上述的功能,可以让Widget窗口类,重写父类(QWidget 的 resizeEvent 虚函数),在鼠标拖动窗口尺寸的过程中,resizeEvent这个函数就会被反复调用执行,每次触发一个resizeEvent事件,都会调用一次对应的虚函数

由于此处进行了函数重写,调用父类虚函数就会实际调用到子类对应的函数(多态)

1.重写函数,观看event参数效果

实时改变label大小:

cpp 复制代码
 ui->label->setGeometry(0, 0, event->size().width(), event->size().height());

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

在QFrame中,可以设置,给Label添加边框

效果示例:

设置伙伴-setBuddy

Qt中,QLabel中写的文本,是可以指定"快捷键"的,此处快捷键的规则功能要比QPushButton弱很多,是在文本中使用 & 跟上一个字符来表示快捷键,比如 &A通过键盘上的ALT + A 触发这个快捷键,&B通过键盘上的ALT + B 触发这个快捷键,绑定了伙伴关系,就可以选中对应的单选框

LCD Number-显示数字

QLCDNumber 是一个专门用于显示数字的控件,能够呈现类似"老式计算器"的视觉效果。

|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| intValue | 显示整数值 (int) 的 QLCDNumbe |
| value | QLCDNumber 显示的数字值为 double 类型。 其 intValue 值与显示值存在联动关系: * 当设置 value 为 1.5 时,intValue 会自动取整为 2 注意:设置数值的方法名为 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,周期性的执行某个组件,"定时器" C++标准库中,没有提供定时器的实现,Boost里面提供了对应的功能,QT中也封装了对应的定时器,结合了信号和槽机制

QTimer-产生timeout信号

效果示例:从10 9 8 7 依次变化到0


Sleep 是 Windows 的 api,需要包含"Windows.h"头文件才能使用

在C++11标准库中,就引入了 sleep操作:sleep_for

sleep_for-延时

这导致,10s 过后,窗口显示,并且已经为0

解决办法:

在构造函数中,另外创建一个线程,在新的线程中,执行上述循环+更新操作

线程操作本身是 操作系统 提供的api,Windows api 可以参阅MSDN windows的文档,标准库,就可以通过一些"条件编译"的方式 来兼容不同的系统

示例:当前进程终止,出现了异常

在Qt中,界面维护和更新由专门的线程(即主线程,也就是main函数所在的线程)负责处理。由于GUI内部包含众多隐藏状态,为了确保修改界面时的线程安全性,Qt禁止其他线程直接修改界面元素。

而我们这里的代码:就是在其他线程修改界面元素

cpp 复制代码
 ui->lcdNumber->display(value);

因此Qt为了确保线程安全,直接要求所有的对界面的修改操作,必须在主线程中完成,对于Qt的槽函数来说,默认情况下,槽函数都是由主线程调用的,在槽函数中修改界面是没有任何问题的


✍ 这种约定主要是因为 GUI 中的状态往往牵一发而动全身,修改一处就需要同步调整其他内容。例如调整某个元素的尺寸,就可能影响内部文本位置或其他元素的布局。这一连串的修改必须按照特定顺序执行。

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

ProgressBar-进度条

使用 QProgressBar 来显示进度条。

注意:不要将 ProgressBar 拼写为 ProcessBar

在QtDesigner中添加进度条

进度条的基本使用:

注意:

C++ 编译速度慢。和#include头文件,是有直接关系的

由于include关系错综复杂,因此尽可能减少include头文件个数,就可以有效减少编译时间,Qt中使用class前置声明的方式,来减少头文件 的包含,通过前置声明的方式,Qt中的头文件,每个头文件包含其他头文件数量都能得到一定的降低
但是在实际开发中,还是该包含就包含。与其通过特殊技巧来缩短编译时间、不如引入更好的硬件资源。来更搞笑的编译,一些互联网大厂,都有专门的"编译集群"(分布式编译)
因此在C++ 20标准开始,就引入了"模块"module来替代#include

将绿色进度条改为粉红色-使用stylesheet

使用选择器的方式:

qt的bug: 这里需要自己设置回去,就和我图中一样了

进度条具体的进度如何设置,一般都是根据实际的任务类型来灵活设置的

例如:要读取一个很大的文件,就可以先获取到文件的总大小,每读取一部分数据(计算出读了多少数据),更新一次进度条 的数值。设置进度条的过程,往往需要搭配定时器

Calendar Widget-日历控件

QCalendarWidget 表示一个日历控件

|------------------------|-----------------------|
| 属性 | 说明 |
| selectDate | 当前选中的⽇期 |
| minimumDate | 最⼩⽇期 |
| maximumDate | 最⼤⽇期 |
| firstDayOfWeek | 每周的第⼀天(也就是⽇历的第⼀列)是周⼏. |
| gridVisible | 是否显⽰表格的边框 |
| selectionMode | 是否允许选择⽇期 |
| navigationBarVisible | ⽇历上⽅标题是否显⽰ |
| horizontalHeaderFormat | ⽇历上⽅标题显⽰的⽇期格式 |
| verticalHeaderFormat | ⽇历第⼀列显⽰的内容格式 |
| dateEditEnabled | 是否允许⽇期被编辑 |

重要信号

使用示例:

输入类控件

Line Edit-单行输入框

QLineEdit 用于实现单行文本输入框,支持输入文本内容但不允许换行。

核心信号:


示例:

如图:

如图:

注意:

如图:通过提交,我们获取填入的内容(在实际开发中,提交 一般是客户端提交给服务器)

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

要求:在输入框中输入一个合法的电话号码(1开头,11位,全为数字)。验证不通过时,确定按钮将保持禁用状态。

关于正则表达式

正则表达式是一种利用特殊字符描述字符串特征的匹配机制,在计算机领域应用广泛。它能够高效完成字符串匹配任务,但语法较为复杂,通常使用时查阅文档即可,无需刻意记忆。

在线正则表达式工具:https://regextester.buyaocha.com/

**示例:**初始状态是禁用状态,此处的规则是,输入框要检查输入的内容是否是合法的手机号码,如果是,则按钮设置为可用状态,如果不是则按钮设为禁用状态

注意:

检验两次密码是否一致

警示符号,可以忽略,但是在有些公司中警示符号会无法编译通过

解决办法:

复制代码
绕过编译器的检查,类型转换,对于我们代码的实际逻辑是没有任何影响的,可以骗过编译器

针对密码,可以切换"显示密码"状态

Text Edit-多行输入框

QTextEdit是一个多功能的多行输入框,既支持富文本编辑,也能作为 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:**始终隐藏滚动 |

示例:

QTextEdit 的几个信号

cpp 复制代码
void Widget::on_textEdit_textChanged()
{
    qDebug() << "textChanged:" << ui->textEdit->toPlainText();
}

//表示当前光标选择的空间
void Widget::on_textEdit_selectionChanged()
{
    QTextCursor cursor = ui->textEdit->textCursor();
    qDebug() << "selectionChanged:" << cursor.selectedText();
}

//光标位置发生改变触发信号,光标移动字符数
void Widget::on_textEdit_cursorPositionChanged()
{
    QTextCursor cursor = ui->textEdit->textCursor();
    qDebug() << "cursorPositionChanged:" << cursor.position();
}
//一编辑内容,就触发,ctrl + z就撤回、undoAvailable为false
void Widget::on_textEdit_undoAvailable(bool b)
{

    qDebug() << "undoAvailable:" << b;
}
//ctrl + y
void Widget::on_textEdit_redoAvailable(bool b)
{
    qDebug() << "redoAvailable:" << b;
}
//选中
void Widget::on_textEdit_copyAvailable(bool b)
{
    qDebug() << "copyAvailable:" << b;
}

Combo Box-下拉框(直观效果)、组合框

QComboBox 表示下拉菜单控件。

activated ->下拉框展开、鼠标停留在某个选项上方、选项高亮、被激活的状态

示例:模拟麦当劳点餐

在实际开发中,下拉框中的内容不是在代码中写死的,而是通过文件、网络加载数据得到的

此处我们演示通过文件进行加载

文件中:config.txt

代码以及演示:

Spin Box-微调框带有按钮的输入框

使用 QSpinBox 或 QDoubleSpinBox 表示"微调框",这是一种带有增减按钮的数值输入控件。可用于输入整数(QSpinBox)或浮点数(QDoubleSpinBox),通过点击按钮即可调整数值大小。
由于 QSpinBox 和 QDoubleSpinBox 的使用方法基本一致,本文将重点介绍 QSpinBox 的用法。

"Spin"在英文中本意为"旋转",在此处引申为"微调"。
实际上,许多术语的翻译不必拘泥于字面直译,更应追求"信达雅"的境界。
例如,地铁中的"Priority Seat"通常译为"爱心专座",而非直译为"优先座位"。

示例:通过下拉框,选择每个食物种类,再通过微调框,选择每个食物的数量

针对QSpinBox设置数值范围:

Date Edit-日期微调框 & Time Edit-时间微调框-QDateTimeEdit 时间日期微调

使用 QDateEdit 作为日期微调框。

使用 QTimeEdit 作为时间选择器

使用 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:小时(24小时制) m:分钟 s:秒 注意事项: 不同编程语言/库的日期格式符号可能存在差异 使用时请查阅具体文档,不建议死记硬背 |
| minimumDateTime | 最小日期时间 |
| maximumDateTime | 最大日期时间 |
| timeSpec | • Qt::LocalTime:显示本地时间 • Qt::UTC:显示协调世界时(UTC时间) • Qt::OffsetFromUTC:显示与UTC时间的时差 |

时间计算器:计算两个时间中间的间隔是多少天、多少小时

有问题:需解决

解决办法:使用int的向下取整

Dial-旋钮

使用 QDial 控件表示旋钮。

某些程序只需通过鼠标拖动旋钮旋转,就能完成相关设置。

|----------------|----------------------------------------------|
| 属性 | 说明 |
| value | 持有的数值. |
| minimum | 最⼩值 |
| maximum | 最⼤值 |
| singleStep | 按下⽅向键的时候改变的步⻓. |
| pageStep | 按下pageUp/pageDown的时候改变的步⻓. |
| sliderPosition | 界⾯上旋钮显⽰的初始位置 |
| tracking | 外观是否会跟踪数值变化. 默认值为true.⼀般不需要修改. |
| wrapping | 是否允许循环调整. 即数值如果超过最⼤值,是否允许回到最⼩值. (调整过程能否"套圈") |
| notchesVisible | 是否显⽰刻度线 |
| notchTarget | 刻度线之间的相对位置. 数字越⼤,刻度线越稀疏. |

编写代码,通过旋钮控制窗口不透明度(opacity)

Slider-滑动条

使用 QSlider 控件实现滑动条功能

QSlider 和 QDial 都继承自 QAbstractSlider,因此它们的用法基本一致。

在窗口上放两个滑动条、一个是水平、一个垂直

滑动这两个滑动条,就能调整窗口的大小、高度和宽度

快捷键的设置!!

复制代码
使用快捷键:QShortCut类、需要用到两个快捷键、-进行减少、+/= 进行增加

多元素控件

xxWidget 使用比较方便、功能比较有限

xxView使用起来更加麻烦一些、但是可以根据情况自由diy,实现更复杂的功能

List Widget-列表组件

使用 QListWidget 可以显示一个纵向排列的列表,例如:

核心方法

|----------------------------------------------------------------------------|
| |

增加删除以及感知选中的变化

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("c#");

    //往这里添加一些元素-创建对象的方式进行添加,在QListWidgetItem
//    中可以设置字体属性、设置图标、设置文字大小、设置是否被选中等状态
    ui->listWidget->addItem(new QListWidgetItem("c++"));
    ui->listWidget->addItem(new QListWidgetItem("java"));
    ui->listWidget->addItem(new QListWidgetItem("c#"));
}

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_2_clicked()
{
    //1、先获取到被选中的元素
    int row = ui->listWidget->currentRow();
    if(row < 0){
         return;
    }
    ui->listWidget->takeItem(row);
}

void Widget::on_listWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
{
    //通过这个槽函数来感知到变化
    if(current){
        qDebug() << "当前选中的元素" << current->text();
    }
    if(previous){
        qDebug() << "上次选中的元素" << previous->text();
    }
}

Table Widget-表格控件

使用 QTableWidget 控件来显示表格数据。每个表格由多行组成,每行又包含若干列。表格中的每个单元格都是一个 QTableWidgetItem 对象。

准备好

编码:其实编码较为简单,只是看起来多

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

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

//    初始化
//    1、创建3行
    ui->tableWidget->insertRow(0);
    ui->tableWidget->insertRow(1);
    ui->tableWidget->insertRow(2);

    //2、创建3列
    ui->tableWidget->insertColumn(0);
    ui->tableWidget->insertColumn(1);
    ui->tableWidget->insertColumn(2);

//    3、给3个列设置列名(设置水平方向的表头
    ui->tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem("学号"));
    ui->tableWidget->setHorizontalHeaderItem(1, new QTableWidgetItem("姓名"));
    ui->tableWidget->setHorizontalHeaderItem(2, new QTableWidgetItem("年龄"));

//    4、给表格中添加数据
    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("22"));
}

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

void Widget::on_pushButton_insertRow_clicked()
{
    //需要知道当前一共有多少行
    int rowCount = ui->tableWidget->rowCount();
//    再在最后一行后新增一行
    ui->tableWidget->insertRow(rowCount);
}

void Widget::on_pushButton_DeleteRow_clicked()
{
    //获取选中行
    int currentRow = ui->tableWidget->currentRow();
    //删除这一行
    ui->tableWidget->removeRow(currentRow);

}

void Widget::on_pushButton_InsertColumn_clicked()
{
    //获取一共多少列
    int columnCount = ui->tableWidget->columnCount();
//    在对应位置新增这一列
    ui->tableWidget->insertColumn(columnCount);
//    设置列名(从输入框获取)
    const QString& text =  ui->lineEdit->text();
    ui->tableWidget->setHorizontalHeaderItem(columnCount, new QTableWidgetItem(text));
}

void Widget::on_pushButton_DeleteColumn_clicked()
{
    int currentColumn = ui->tableWidget->currentColumn();
    ui->tableWidget->removeColumn(currentColumn);
}

增删行、列,列添加列名再增加

Tree Widget-树形控件

使用 QTreeWidget 可以创建一个树形控件,其中的每个元素都是一个 QTreeWidgetItem。每个 QTreeWidgetItem 可以包含多列数据,每列可以显示文本或图标。

通过为 QTreeWidget 设置多个顶层节点,并在这些顶层节点下添加子节点,就能构建出完整的树形结构。

cpp 复制代码
#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, "中华田园犬");
    item2->addChild(item6);

}

Widget::~Widget()
{
    delete ui;
}
//添加到顶层节点中
void Widget::on_pushButton_insertTopLevelItem_clicked()
{
    //先获取到输入框的内容
    const QString& text = ui->lineEdit->text();
    //构造一个QTreeWidgetItem
    QTreeWidgetItem* 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();
    //构造一个QTreeWidgetItem
    QTreeWidgetItem* 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* parentItem = currentItem->parent();
    if(parentItem == nullptr){
        //说明是顶层元素
//        需要获取到是第几个顶层元素及其下标
        int index = ui->treeWidget->indexOfTopLevelItem(currentItem);
        ui->treeWidget->takeTopLevelItem(index);
    }else{
        //普通元素
        parentItem->removeChild(currentItem);
    }  
} 

容器类控件

Group Box-带有标题的分组框

使用 QGroupBox 可以创建一个带标题的分组框,方便将相关控件组织在一起,使界面更加美观整洁。

需要注意的是,QGroupBox 与 QButtonGroup 功能不同(后者在介绍 QRadioButton 时已提及)。

只是为了让界面看起来更好看、当一个界面比较复杂的时候,包含了很多控件的时候,分组框就可以把具有关联的控件,组织到一起

麦当劳点餐

示例:注意现在的父子类关系

Tab Widget-带有标签页的控件

使用 QTabWidget 实现一个带标签页的控件,可以向其中添加多个 widget,通过切换标签页来显示不同内容。

需要实现:

代码:

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

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //现在每个标签页中,添加一个label
    QLabel* label1 = new QLabel(ui->tab_1);
    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;
}

//想要创建标签页,需要使用到addTab方法,参数1指定QWidget、参数2要指定标签页的text(标题
void Widget::on_pushButton_clicked()
{
//    此处标题就叫做Tab + 数字
    //获取到标签页的数量
    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);
}

还能获取到当前选中页的下标

cpp 复制代码
void Widget::on_tabWidget_currentChanged(int index)
{
    qDebug() << "当前选中的标签页是:" << index;
}

布局管理器-尺寸和位置自动计算、geometry失效

在Qt界面设计中,之前创建的控件都采用"绝对定位"方式布局。这意味着每个控件的位置都需要手动计算坐标,最终通过setGeometry或move方法来设置。

这种设定方式确实不够便捷。特别是在界面内容较多的情况下,难以精确计算位置。况且窗口大小通常可以自由调整,采用绝对定位的方式无法实现自适应布局。

🦄 为此,Qt引入了"布局管理器"(Layout)机制来解决这些问题。

值得一提的是,布局管理器并非Qt独有,其他GUI开发框架如Android、前端等也都采用了类似的解决方案。

垂直布局

使用 QVBoxLayout 实现垂直排列的布局管理器。其中 V 代表 vertical(垂直)的缩写。

通过 Qt Designer 创建的布局管理器,实际上是先创建了一个 widget 并设置了 geometry 属性,再将这个 layout 应用到该 widget 中。

需要注意的是,一个 widget 只能包含一个 layout。通过查看 ui 文件的原始 xml 结构,可以清楚地观察到这一点。

ui的这种设置方式下,layout 不会作为窗口 widget 的布局管理器,因此不会随窗口大小变化而自动调整。

把选中的给套上布局方式

水平布局

同垂直布局

布局管理器之间能进行嵌套

实现:

网格布局

Qt提供了QGridLayout来实现网格布局效果,能够创建M×N的网格结构。

核心特性
与QVBoxLayout和QHBoxLayout类似,但在设置spacing时需要分别指定水平和垂直方向的间距。

示例:

示例:

代码:

cpp 复制代码
#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 个按钮,使用网格布局按照 2 * 3 的方式来排列
    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");

    // 创建网格布局管理器,把这些控件添加进去
    QGridLayout* layout = new QGridLayout();
    layout->addWidget(button1, 0, 0);
    layout->addWidget(button2, 0, 1);
    layout->addWidget(button3, 0, 2);
    layout->addWidget(button4, 1, 0);
    layout->addWidget(button5, 1, 1);
    layout->addWidget(button6, 1, 2);

    this->setLayout(layout);

    // 设置水平拉伸系数
    layout->setColumnStretch(0, 1);
    layout->setColumnStretch(1, 1);
    layout->setColumnStretch(2, 2);
}

拉伸系数设为0,意思是不参与拉伸,不再受拉伸窗口而调整了,固定的

修改代码

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

此时就达到预期:

表单布局

这种表单布局常用于用户填写信息的场景,左侧为提示文字,右侧为输入框。

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

    //设置成3行两列
    QFormLayout* layout = new QFormLayout();
    this->setLayout(layout);

    //创建三个label作为第一列

    QLabel* label1 = new QLabel("姓名:");
    QLabel* label2 = new QLabel("年龄:");
    QLabel* label3 = new QLabel("电话:");

    //创建 3 个输入框作为第2列
    QLineEdit* edit1 = new QLineEdit();
    QLineEdit* edit2 = new QLineEdit();
    QLineEdit* edit3 = new QLineEdit();

    // 把上述控件添加到表单中
    layout->addRow(label1, edit1);
    layout->addRow(label2, edit2);
    layout->addRow(label3, edit3);

    //创建一个提交按钮
    QPushButton* button = new QPushButton("提交");
    layout->addRow(nullptr, button);
}

Spacer-控件之间,添加⼀段空白

使用布局管理器时,若需在控件间添加空白区域,可通过 QSpacerItem 来实现。

核心属性

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

    QPushButton* button1 = new QPushButton("按钮1");
    QPushButton* button2 = new QPushButton("按钮2");

    //创建Spacer,使两个按钮之间存在空白
    QSpacerItem* spacer = new QSpacerItem(200, 20);

    layout->addWidget(button1);
    layout->addSpacerItem(spacer);
    layout->addWidget(button2);
}

示例:

ui中也可以设置

结语:

随着这篇博客接近尾声,我衷心希望我所分享的内容能为你带来一些启发和帮助。学习和理解的过程往往充满挑战,但正是这些挑战让我们不断成长和进步。我在准备这篇文章时,也深刻体会到了学习与分享的乐趣。

在此,我要特别感谢每一位阅读到这里的你。是你的关注和支持,给予了我持续写作和分享的动力。我深知,无论我在某个领域有多少见解,都离不开大家的鼓励与指正。因此,如果你在阅读过程中有任何疑问、建议或是发现了文章中的不足之处,都欢迎你慷慨赐教。

你的每一条反馈都是我前进路上的宝贵财富。同时,我也非常期待能够得到你的点赞、收藏,关注这将是对我莫大的支持和鼓励。当然,我更期待的是能够持续为你带来有价值的内容。