QT-常用控件
目录
QWIDGET
QWIDGET-enable属性
window frame的影响
QWIDGET-windowtitle属性
QWIDGET-windowlcon属性(图标)
qrc的使用
QWIDGET-windowOpacity属性
QWIDGET-cursor属性
QWIDGET-font属性(字体)
QWIDGET-toolTip属性
QWIDGET-focusPolicy属性(焦点)
QWIDGET-styleSheet属性
按钮类
QWIDGET-QPush Button控件属性(按钮)
QWIDGET-QradioButton控件属性(单选按钮)
QWIDGET-QChaekBox控件属性(复选按钮)
显示类
QLCDNunber的属性(计算器)
QProgressBar的属性(进度条)
QCalendarWidget(日历)
输入类
QLineEdit(单行输入框)
QTextEdit(多行输入框)
核心功能与信号
QCombo Box(下拉框)
核心方法
QSpin Box(数目微调框)
属性
信号
QDateTimeEdit(日期/时间微调框)
一、跨时区场景下的 “日期边界” 计算偏差
二、对 “无效日期” 的容错性不足
三、历史日期(历法变更前)的计算偏差
四、与 “时间戳” 计算的一致性问题
规避方案
QDial(旋钮)
QSlider(滑动条)
多元素类
QListWidget(纵向列表)
QTableWidget(表格)
QTreeWidget
容器类
QGroupBox
QTabWidget
布局类
QVBoxLayout(垂直)
QHBoxLayout(水平)
QGridBoxLayout(网格)
QFromLayout(表单)
QSpaceItem(空白段)
QWIDGET
QWIDGET-enable属性
表示一个控件是否处于可用状态,相对概念为禁用,所谓"禁用"指的是该控件不能接收任何用户的输入事件,并且外观上往往是灰色的,如果一个 widget 被禁用,则该 widget 的子元素也被禁用。
禁用效果:
QWIDGET-geometry属性(几何)
粗浅的理解为(x,y,width,height)四个属性的统称;
Qt 中针对一些几何上的概念也进行了封装。
QPoint 表示一个点,QRect 表示一个矩形,他们都属于是小对象, 里面的属性非常少,占用空间也小.C++中使用上述对象, 通常就会按照值的方式来传递参数了。
样例:我们期望点击对应的按钮来修改target对应的geometry。
控件函数操作操作:
当前代码实际执行的效果,是在调整左上角位置,左上角位置改变的同时,高度和宽度也同时发生了改变。
平移效果代码:
window frame的影响
Qt 中存在多种用于处理窗口位置和尺寸的 API,且不同 API 基于的坐标系原点有所不同,同时在特定阶段调用这些 API 会有特殊表现:
- 从坐标系和 API 本身来看,
geometry()
与setGeometry()
以 Widget 本体左上角为原点,不考虑窗口框架(Window frame);frameGeometry()
和setFrameGeometry()
以窗口框架左上角为原点,会考虑窗口框架。 -
Widget::Widget(Qwidget*parent):OWidget(parent),ui(new Ui::widget) {ui->setupui(this);QRect rect1=this->geometry();ORect rect2=this->frameGeometry();qDebug()<< rect1;qDebug()<< rect2;}
- 从代码运行阶段的情况来看,若在
Widget
类的构造函数中(此时Widget
对象正在构造,还未被加入到窗口框架),直接针对Widget
对象使用geometry
和frameGeometry
并通过qDebug()
输出结果,能观察到二者返回的位置尺寸信息存在区别。 - 进行链接并运行后,结果如下图所示:
QWIDGET-windowtitle属性
需要注意,下图使用方法并不正确。
关键问题:QPushButton
调用 setWindowTitle
无意义
setWindowTitle
是用于设置 ** 窗口(QWidget
及其顶层子类,如 QMainWindow
、QDialog
等)** 的标题,显示在窗口的标题栏上。
而 QPushButton
是 “按钮部件”,不是独立的窗口(它是依附于父窗口存在的子部件),调用 button->setWindowTitle("通过按钮设置窗口标题")
不会有任何效果 —— 按钮没有自己的 “标题栏” 来显示这个标题。
正确的逻辑(如果要通过按钮修改窗口标题)
应该在按钮的点击信号槽中,修改父窗口(或目标窗口)的标题。示例如下:
// 假设当前类是 QWidget 子类(如 MainWindow)
this->setWindowTitle("这是窗口标题");QPushButton* button = new QPushButton("按钮", this);
// 连接按钮的点击信号到自定义槽函数
connect(button, &QPushButton::clicked, this, [this]() {// 点击按钮时,修改“当前窗口”的标题this->setWindowTitle("通过按钮设置窗口标题");
});
这样,点击按钮时,整个窗口的标题栏才会更新为 “通过按钮设置窗口标题”。
总结
setWindowTitle
只对 “窗口级部件” 有效,子部件(如按钮、标签等)调用它不会生效。- 若要通过子部件(如按钮)修改窗口标题,需在子部件的交互逻辑中,操作目标窗口的
setWindowTitle
。
QWIDGET-windowlcon属性(图标)
Qt 把各种涉及到的相关概念,都封装成了 类,Qlcon 就表示一个图标,Qlcon 更推荐创建在栈上。
// 写法一:使用双反斜杠QIcon icon("C:\\icons\\example.ico"); // 写法二:使用正斜杠// QIcon icon("C:/icons/example.ico");
假设图标文件 icon.png
和程序可执行文件在同一目录,直接写文件名即可:
QIcon icon("icon.png");
qrc 机制.
这个机制就是从根本上解决上述的两个问题:
1.确保你的图片所在的路径在目标用户机器上存在
2.确保你的图片不会被用户搞没了~~
给 Qt 项目引入一个额外的 xml 文件(后缀名使用 .qrc 表示)在这个 xml 中把要使用的图片资源给导入进来,并自在 xml 中进行记录
Qt 在编译项目的时候,就会根据 qrc 中描述的图片信息, 找到图片内容, 并且提取出图片的二进制数据,把这些二进制数据转成 C++ 代码. 最终编译到 exe 里,
缺点:无法导入太大的文件。
qrc的使用
1.创建相应文件
2。导入文件资源
(1)创建虚拟目录-prefix;
(2)添加资源-点击add files;
注意:导入图片的时候,需要确保你导入的图片必须在 resource.qrc 文件的同级目录,或者同级目录中的子目录里.
代码引用:当代码中需要访问 qrc 中管理的文件时,就需要在路径上带有:前缀。
QWIDGET-windowOpacity属性
上述代码可以充分体现该控件的功能。
在透明度变化过程中,变化并非是精确的。
C++浮点数运算不精确的核心原因是:二进制无法精确表示所有十进制小数,如同十进制无法精确表示1/3(0.333...),导致存储时就存在微小误差,运算后误差进一步累积。
这本质是“进制转换”和“有限存储”的双重限制:
- 二进制只能精确表示分母为2的幂的十进制小数(如0.5=1/2、0.25=1/4),像0.1(1/10)这类小数,在二进制中是无限循环小数,只能截取近似值存储。
- 浮点数(如float、double)在计算机中用固定字节存储,会对无限循环的二进制小数进行舍入,这就从根源上决定了它不是“精确值”,而是“近似值”。
QWIDGET-cursor属性
自定义的光标设置:图标网站推荐:阿里巴巴矢量图标库
光标尺寸的设计
QWIDGET-font属性(字体)
代码案例:
QWIDGET-toolTip属性
QWIDGET-focusPolicy属性(焦点)
QWIDGET-styleSheet属性
通过CSS设置widget的样式
1.qlabel设置文本
2.右下角属性栏,右键都可以跳转设置样式。
3.进行期望的属性编辑
设置日间模式,夜间模式:
按钮类
QWIDGET-QPush Button控件属性(按钮)
qpushbutton是一个按钮,继承自qabstractbutton,这是一个抽象类,是其他按钮控件的父类。qabstractbutton包含纯虚函数,无法创建出子类的示例,旧的创建子类,重写上述的纯虚函数,才能创建出子类的实例。
1.设置按钮图标
2.设置快捷键
准备工作
设置槽函数
快捷键设置-通过按键名-简单易出错
快捷键设置-通过枚举
键盘的连发是默认的。
QWIDGET-QradioButton控件属性(单选按钮)
示例:
还可以进行初始化,提供默认内容
该控件还可以将按钮进行分组,实现类似于点餐的一种效果
槽信号介绍
clicked(bool)中bool部分表示按钮是否被选中后触法。
pressed为点的动作后触发,鼠标没弹起来。
release是弹起后触发,鼠标要弹起来。
toggled是状态切换的时候触发。
QWIDGET-QChaekBox控件属性(复选按钮)
代码演示、
显示类
该属性常用于显示文本和图片。
格式:在设置文本的格式testformat后,不同文本之间语法是并不互通的,<b></b>用于html格式,#等用于markdowntest格式。
图片:
// 先把 QLabe1 设置成和窗口一样大,并且把这个 QLabel 左上角设置到窗口的左上角这里
// 让整个 QLabel 铺满整个窗口
ORect windowRect=this->geometry();
ui->label->setGeometry(0,0,windowRect.width(), windowRect.height());
QPixmap pixmap(":/huaji.png");
ui->label->setPixmap(pixmap);
//启动自动拉伸。此时图片就能够填充满整个窗口了.
ui->label->setscaledcontents(true);
仅仅只设置setGeometry是不能让图片填充满整个窗口的,因为图片本身可能有尺寸限制,需要用到ui->label->setscaledcontents(true);才行,但是拖拽边框后图片的样式 可能就不那么好看,会出现大量的空白。
解决方式:事件
用户的操作,会对应一些信号,Qt 中,表示用户的操作,有两类概念一个是信号,另一个是事件。
当用户拖拽修改窗口大小的时候,就会触发 resize 事件(resizeEvent)像 resize 这样的事件,是连续变化的.把窗口尺寸从 A拖到 B这个过程中,会触发出一系列的 resizeEvent此时就可以借resizeEvent 来完成上述的功能.,可以让 Widget 窗囗类, 重写父类(QWidget) 的 resizeEvent 虚函数
在鼠标拖动窗口尺寸的 过程中 这个函数就会被反复调用执行,每次触发一个 resizeEvent 事件都会调用一次对应的虚函数。
由于此处进行了函数重写,调用父类的虚函数就会实际调用到子类的对应的函数(多态)。
设置文本对齐:
设置伙伴:
Qt 中, QLabel 中写的文本,是可以指定"快捷键”此处快捷键的规则功能上要比 QPushButton 弱很多
是在文本中使用 & 跟上一个字符来表示快捷键。
比如 &A =>通过键盘上的 alt +a来触发这个快捷键.&B =>通过键盘上的 alt +b 来触发,绑定了伙伴关系之后,通过快捷键就可以选中对应的单选按钮/复选按钮。
QLCDNunber的属性(计算器)
定时器的设定:
方式一:
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//设置初始值ui->lcdNumber->display(10);// 创建一个 QTimer 实例timer = new QTimer(this);// 把 QTimer 的 timeout 信号和咱们自己的槽函数进行连接connect(timer,&QTimer::timeout, this,&Widget::handle);// 启动定时器,参数是触发 timeout 的周期,单位是 mstimer->start(1000);
}Widget::~Widget()
{delete ui;
}void Widget::handle()
{//先拿到 LCDNumber 中的数字int value =ui->lcdNumber->intValue();if(value<=0){// 数字减到 0 了,停止定时器.timer->stop();return;}ui->lcdNumber->display(value -1);
}
屏幕录制 2025-10-12 165432
方式二:
现象:是10s后弹出结果为0的显示框。
原因:
方式三;多线程
结果依然是失败的,Qt 里,(main 函数所在的线程)界面有一个专门的线程去负责维护更新的(主线程),对于 GU 来说,内部包含了很多的隐藏状态,Qt 为了保证修改界面的过程中,线程安全是不会受到影响的,Qt禁止了其他线程直接修改界面。
QProgressBar的属性(进度条)
进度条
下面我们来创建一份进度条,让他随时间增长而增长,每个0.1秒+1;
进度条:
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);timer=new QTimer(this);connect(timer,&QTimer::timeout,this,&Widget::handle);timer->start(100);
}
void Widget::handle()
{int value=ui->progressBar->value();if(value>=100){timer->stop();return;}ui->progressBar->setValue(++value);
}
Widget::~Widget()
{delete ui;
}
进度条样式的设计:
QProgressBar {border: 2px solid #ffb6c1; /* 粉色边框*/border-radius: 10px; /* 圆角,让进度条更柔和 */background-color: #fff0f5; /* 背景为浅粉色 */
}QProgressBar::chunk {border-radius: 8px; /* 进度块的圆角,和外框呼应 */background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #ff69b4, stop:1 #ff1493); /* 进度块用亮粉色渐变,很活泼 */
}
Qt 中利用类前置声明减少头文件包含,从而优化编译速度的知识:
核心要点
-
前置声明的作用:在 Qt 中,若只需使用类的指针 / 引用类型成员(如
QTimer* timer;
),可通过类前置声明(如class QTimer;
),无需在头文件中直接包含QTimer
的头文件(#include <QTimer>
)。因为指针 / 引用的声明不需要知道类的完整定义,只需知道类的存在即可。 -
编译速度优化:C/C++ 编译速度慢,与
#include
头文件的复杂依赖关系直接相关。减少头文件包含能有效缩短编译时间。Qt 利用 “前置声明 + 仅在需要时包含头文件” 的方式,降低头文件间的依赖,从而优化编译效率。 -
实际开发的权衡:虽然前置声明能优化编译,但实际项目中,若为了开发便捷(避免手动管理头文件依赖),或项目有足够硬件资源(如大厂的 “编译集群”),也可直接包含所需头文件,优先保证开发效率。
简单来说:Qt 用 “类前置声明” 减少头文件包含来加速编译,但实际开发中,也可根据情况灵活选择是否直接包含头文件。
QCalendarWidget(日历)
目的:期望日期改变时,Qlabel进行显示;
代码:
#include "widget.h"
#include "ui_widget.h"
#include"QDebug"
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);
}Widget::~Widget()
{delete ui;
}void Widget::on_calendarWidget_selectionChanged()
{QDate date=ui->calendarWidget->selectedDate();qDebug()<<date;ui->label->setText(date.toString());
}
输入类
QLineEdit(单行输入框)
代码目的:让用户输入个人信息,并进行获取。
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 初始化第一个输入框,用来输入姓名ui->lineEdit_name->setPlaceholderText("请输入姓名");ui->lineEdit_name->setClearButtonEnabled(true);// 初始化第二个输入框,用来输入密码ui->lineEdit_password->setPlaceholderText("请输入密码");ui->lineEdit_password->setClearButtonEnabled(true);// 把显示模式设置成显示密码的模式.ui->lineEdit_password->setEchoMode(QLineEdit::Password);// 初始化第三个输入框ui->lineEdit_phone->setPlaceholderText("请输入手机号码");ui->lineEdit_phone->setClearButtonEnabled(true);// 手机号码是有固定格式的. 此处的 0 代表 "数字"ui->lineEdit_phone->setInputMask("000-0000-0000");
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_submit_clicked()
{QString gender = ui->radioButton_male->isChecked() ? "男" : "女";qDebug() << "姓名: " << ui->lineEdit_name->text()<< "密码: " << ui->lineEdit_password->text()<< "性别: " << gender<< "电话: " << ui->lineEdit_phone->text();
}
ui->lineEdit_phone->setInputMask大的限制能力比较弱,如果不满足需求可以考虑使用正则表达式。
使用样例1:
//创建正则表达式
QRegExp regExp("^1\\d{10}$");
用户输入内容符合要求则可提交,否则eable=flase;
^n:表示以n开始
\\d{10}:表示必须重复10次出现数字,\d表示数字,\\进行转义,{10}表示进行重复10次操作;
$:结尾;
//创建验证器(验证正则表达式是否正确)
ui->lineEdit->setValidator(new QRegExpValidator(regExp));
我们期望的是内容发生改变时,时刻进行验证,看用户输入的内容是否符合要求,符合要求按钮可用,否则不可用。
因此我们需要用到行文本框的textChanged或者textEdited属性。
void Widget::on_lineEdit_textEdited(const QString &text)
{int pos=0;QString content=text;//调用验证器,并验证,返回值是枚举类型(通过/不通过)//验证通过if(ui->lineEdit->validator()->validate(content,pos)==QValidator::Acceptable){ui->pushButton->setEnabled((true));}else{ui->pushButton->setEnabled((false));}
}
在调用是需要注意传参,第一个参数是要验证的字符串,第二个参数是错误出现时错误位置(int)。
在函数传参是参数是const的,而调用传参是非const,因此不能直接调用待检测字符串,可以进行拷贝,我们也可以自定义进行重写,也可以调用QT封装的QRegExpValidator;
正则表达式的运用
仔细观察发现:
当数字串长度不满足11位时,按钮不可用,等于11时可用,且不可以超过11位。
使用样例2:验证两次输入密码一致:
使用样例3:复选框实现密码显示与隐式的切换
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->lineEdit->setEchoMode(QLineEdit::Password);
}Widget::~Widget()
{delete ui;
}void Widget::on_checkBox_toggled(bool checked)
{if(checked){ui->lineEdit->setEchoMode(QLineEdit::Normal);}else{ui->lineEdit->setEchoMode(QLineEdit::Password);}
}
QTextEdit(多行输入框)
QTextEdit 支持多行文本输入,还可进行富文本编辑,适用于备注、文章内容等大量文本输入场景
核心功能与信号
- 多行输入:天然支持多行文本,用户可自由换行输入。
- 富文本支持:能设置文本的字体、颜色、字号等样式,例如
ui->textEdit->setFontWeight(QFont::Bold)
可将文本设为粗体。 - 文本变化信号:
textChanged
信号在文本内容发生改变时触发,可用于实时监控文本变化,如自动保存草稿等功能。 - 内容获取与设置:通过
toPlainText()
获取纯文本内容,setPlainText(const QString &text)
设置纯文本内容;若需富文本操作,可使用toHtml()
和setHtml(const QString &html)
。
核心信号:
QCombo Box(下拉框)
核心方法
- 添加选项:
addItem(const QString &text)
可添加单个选项,addItems(const QStringList &texts)
可批量添加多个选项。 - 插入选项:
insertItem(int index, const QString &text)
能在指定索引位置插入选项。 - 设置当前选项:
setCurrentIndex(int index)
或setCurrentText(const QString &text)
可设置当前选中的选项。 - 获取当前选项:
currentIndex()
获取当前选项的索引,currentText()
获取当前选项的文本内容。
核心信号:
样例:
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <fstream>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 需要读取文件内容,把文件中的每一行读取出来,作为一个 ComboBox 的选项std::ifstream file("C:/Users/1/Desktop/config.txt");if (!file.is_open()) {qDebug() << "文件打开失败";return;}// 按行来读取文本内容.// getline 函数完成.std::string line;while (std::getline(file, line)) {// 取到的每一行内容,设置到下拉框中.ui->comboBox->addItem(QString::fromStdString(line));}file.close();
}Widget::~Widget()
{delete ui;
}
QSpin Box(数目微调框)
QSpinBox 用于输入整数,带有上下调节按钮,方便用户微调数值;若需输入浮点数,可使用 QDoubleSpinBox。
属性
- 数值范围:
setRange(int min, int max)
设置数值的最小和最大值,限制输入范围。 - 步长:
setSingleStep(int step)
设置点击上下按钮时数值的变化步长。 - 初始值:
setValue(int val)
设置初始显示的数值。
信号
- 数值变化信号:
valueChanged(int i)
在数值发生改变时触发,可用于联动其他控件或执行相应逻辑。
样例:选餐
// 初始化下拉菜单
ui->comboBox->addItem("麦辣鸡腿堡");
ui->comboBox->addItem("巨无霸");
ui->comboBox->addItem("培根蔬粹双层牛堡");ui->comboBox_2->addItem("中薯条");
ui->comboBox_2->addItem("麦乐鸡块");
ui->comboBox_2->addItem("麦辣鸡翅");ui->comboBox_3->addItem("雪碧");
ui->comboBox_3->addItem("可乐");// 针对 QSpinBox 的范围进行设置
ui->spinBox->setRange(1, 5);
ui->spinBox_2->setRange(1, 5);
ui->spinBox_3->setRange(1, 5);ui->spinBox->setValue(1);
ui->spinBox_2->setValue(1);
ui->spinBox_3->setValue(1);
QDateTimeEdit(日期/时间微调框)
样例:时间计算器(两份时间的间隔:多少天/多少小时)
void Widget::on_pushButton_clicked(bool checked)
{QDateTime days1=ui->dateTimeEdit->dateTime();QDateTime days2=ui->dateTimeEdit_2->dateTime();int days=days1.daysTo(days2);int secs=((days1.secsTo(days2))/3600)%24;qDebug()<<"相差"<<days<<"天"<<secs<<"时";
}
daysTo函数存在缺陷:
QT 中
QDate::daysTo()
方法用于计算两个日期之间的天数差,虽然在多数场景下表现正常,但存在以下几类缺陷或需要注意的局限性,在实际开发中需规避:一、跨时区场景下的 “日期边界” 计算偏差
QDate
是纯日期类,不包含时区信息,仅基于 “年 - 月 - 日” 的数字逻辑计算天数差。若涉及跨时区的日期转换(如将 UTC 时间转为本地时间后计算),可能出现与预期不符的结果。
- 示例:假设本地时区为东八区(UTC+8),若将
2024-05-01 00:00:00 UTC
转为本地时间是2024-05-01 08:00:00
,再与本地时间2024-05-01 07:00:00
对应的QDate
(均为2024-05-01
)计算daysTo()
,结果为 0,但实际时间差仅 23 小时(不足 1 天)。- 本质原因:
QDate
忽略时间戳的 “小时 - 分钟 - 秒” 部分,仅按日期字面量比较,跨时区场景下易因 “日期切换时刻” 的差异导致偏差。二、对 “无效日期” 的容错性不足
若调用
daysTo()
的两个QDate
对象中存在无效日期(如2024-02-30
、2024-13-01
),方法不会报错,而是返回未定义的错误结果(可能为随机整数或固定值),且无法通过返回值判断计算是否有效。
- 示例:
QDate invalidDate(2024, 2, 30); // 无效日期(2月无30日) QDate validDate(2024, 3, 1); int days = invalidDate.daysTo(validDate); // 结果未定义(可能为1或其他值,无意义)
- 对比:QT 中
QDate::isValid()
可提前判断日期有效性,但daysTo()
本身不做校验,需开发者手动前置处理,否则易引入隐性 Bug。三、历史日期(历法变更前)的计算偏差
QDate
内部基于公历(格里高利历) 计算,但格里高利历并非全球同步启用(如中国 1912 年才正式采用,俄罗斯 1918 年启用)。若计算 “历法变更前的历史日期”(如 1900 年中国的日期),daysTo()
仍按格里高利历逻辑计算,与实际历史日期的天数差存在偏差。
- 示例:1900 年中国实际使用农历 + 公历(清历),若用
QDate(1900, 1, 1).daysTo(QDate(1900, 1, 10))
得到结果 9(格里高利历的 9 天),但实际历史中该时间段的 “天数统计方式” 可能不同,导致计算结果与历史事实不符。- 适用范围:
daysTo()
仅适用于 “格里高利历覆盖的时期和地区”,对历史研究、古日期计算等场景不适用,且无任何接口支持其他历法。四、与 “时间戳” 计算的一致性问题
若需结合时间戳(
QDateTime::toSecsSinceEpoch()
)计算天数差,daysTo()
的结果可能与 “时间戳差值换算的天数” 不一致,原因是前者忽略时间部分,后者基于 “24 小时 / 天” 的绝对时间差。
- 示例:
cpp
运行
QDateTime dt1(2024, 5, 1, 23, 0, 0); // 5月1日23点 QDateTime dt2(2024, 5, 2, 1, 0, 0); // 5月2日1点 // 1. QDate::daysTo() 结果 int daysDate = dt1.date().daysTo(dt2.date()); // 1(日期差1天) // 2. 时间戳换算天数 qint64 secDiff = dt2.toSecsSinceEpoch() - dt1.toSecsSinceEpoch(); int daysTs = secDiff / (24 * 3600); // 0(仅2小时,不足1天)
- 冲突场景:需精确按 “24 小时周期” 计算天数(如日志统计、计费周期)时,
daysTo()
的 “日期字面量差” 会与实际时间差冲突,需改用QDateTime::secsTo()
换算。规避方案
- 跨时区 / 时间敏感场景:改用
QDateTime
并指定时区(如QTimeZone::utc()
),通过secsTo()
计算秒差后换算天数(secDiff / 86400
),确保基于绝对时间差计算。- 无效日期校验:调用
daysTo()
前,必须用QDate::isValid()
检查两个日期,无效时抛出异常或返回错误标识。- 历史日期场景:若需支持非格里高利历,需集成第三方历法库(如
ICU
),或自定义历法计算逻辑,避免直接使用daysTo()
。- 结果一致性验证:当同时使用
QDate
和QDateTime
时,需明确计算逻辑(按日期字面量 vs 按绝对时间),避免混用导致数据矛盾。
解决方案:
void Widget::on_pushButton_clicked(bool checked)
{QDateTime days1=ui->dateTimeEdit->dateTime();QDateTime days2=ui->dateTimeEdit_2->dateTime();int secs=((days1.secsTo(days2))/3600)%24;int days=secs/86400;qDebug()<<"相差"<<days<<"天"<<secs<<"时";
}
QDial(旋钮)
样例:通过旋钮控制;窗口的不透明度
void Widget::on_dial_valueChanged(int value)
{qDebug()<<value;this->setWindowOpacity(value/100);
}
QSlider(滑动条)
样例:改变窗口大小(水平+垂直)
#include "widget.h"
#include "ui_widget.h"// Widget类的构造函数,用于初始化界面组件
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) // 初始化UI对象
{ui->setupUi(this); // 设置UI界面// 配置水平滑块:最小值100、最大值2000、初始值800、步长50ui->horizontalSlider->setMinimum(100);ui->horizontalSlider->setMaximum(2000);ui->horizontalSlider->setValue(800);ui->horizontalSlider->setSingleStep(50);// 配置垂直滑块:最小值100、最大值1500、初始值600、步长50ui->verticalSlider->setMinimum(100);ui->verticalSlider->setMaximum(1500);ui->verticalSlider->setValue(600);ui->verticalSlider->setSingleStep(50);
}// Widget类的析构函数,用于释放UI对象内存
Widget::~Widget()
{delete ui;
}// 水平滑块值改变时的槽函数:根据滑块值调整窗口宽度
void Widget::on_horizontalSlider_valueChanged(int value)
{const QRect& rect = this->geometry(); // 获取当前窗口的几何信息(位置、宽高)// 设置窗口新的几何信息,宽度为滑块值,其他属性(x、y、高度)保持不变this->setGeometry(rect.x(), rect.y(), value, rect.height());
}// 垂直滑块值改变时的槽函数:根据滑块值调整窗口高度
void Widget::on_verticalSlider_valueChanged(int value)
{const QRect& rect = this->geometry(); // 获取当前窗口的几何信息// 设置窗口新的几何信息,高度为滑块值,其他属性(x、y、宽度)保持不变this->setGeometry(rect.x(), rect.y(), rect.width(), value);
}
多元素类
QListWidget--------->列表
QListView
QTableWidget-------->表格
QTableView
QTreeWidget---------->树状结构
QTreeView
widget与view的区别在于,view是更加底层的,而widget是基于view进行封装实现的。
-
MVC 是软件开发中经典的软件结构组织形式,包含:
- M model 数据
- V view 视图 (界面)
- C controller 控制器,处理数据和视图之间的业务流程
-
xxView 仅负责实现视图,不处理数据存储表示和数据视图交互
-
使用 xxView 需要程序员自己实现 model 和 controller 部分,比较麻烦
-
xxWidget 基于 xxView,同时实现了 model 和 controller,提供了方便的 API,使用简单
QListWidget(纵向列表)
核心方法:
样例:
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->listWidget->addItem("C++");ui->listWidget->addItem("Java");ui->listWidget->addItem("Python");
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{QString text=ui->lineEdit->text();if(text!=nullptr)ui->listWidget->addItem(text);
}void Widget::on_pushButton_2_clicked(bool checked)
{int row=ui->listWidget->currentRow();if(row<0)return;ui->listWidget->takeItem(row);
}void Widget::on_listWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
{if(current!=nullptr)qDebug()<<"cur"<<current->text();if(previous!=nullptr)qDebug()<<"pre"<<previous->text();
}
样例
QTableWidget(表格)
表格每一行是一个QTableWidget Item对象
表格的创建操作:
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("19"));ui->tableWidget->setItem(2, 0, new QTableWidgetItem("1003"));
ui->tableWidget->setItem(2, 1, new QTableWidgetItem("王五"));
ui->tableWidget->setItem(2, 2, new QTableWidgetItem("23"));
通过按钮进行行的增删
void Widget::on_pushButton_insertRow_clicked()
{// 需要知道当前一共有多少行int rowCount = ui->tableWidget->rowCount();// 在最后一行之后新增行// 注意此处的参数是“下标”,表示你新增之后的这一行是第几行ui->tableWidget->insertRow(rowCount);
}
void Widget::on_pushButton_deleteRow_clicked()
{// 获取到选中的行号int curRow = ui->tableWidget->currentRow();// 删除这一行ui->tableWidget->removeRow(curRow);
}
按钮进行列的增删
void Widget::on_pushButton_insertColumn_clicked()
{// 先获取到一共有几列int colCount = ui->tableWidget->columnCount();// 在对应的位置新增一列.ui->tableWidget->insertColumn(colCount);// 设置列名(从输入框中获取到)const QString& text = ui->lineEdit->text();ui->tableWidget->setHorizontalHeaderItem(colCount, new QTableWidgetItem(text));
}
void Widget::on_pushButton_deleteColumn_clicked()
{// 获取到选中的列号int curCol = ui->tableWidget->currentColumn();// 删除这一列ui->tableWidget->removeColumn(curCol);
}
QTreeWidget
使用 `QTreeWidget` 表示一个树形控件。里面的每个元素,都是一个 `QTreeWidgetItem`,每个 `QTreeWidgetItem` 可以包含多个文本和图标,每个文本/图标为一个列。 可以给 `QTreeWidget` 设置顶层节点(顶层节点可以有多个),然后再给顶层节点添加子节点,从而构成树形结构。
顶层节点的添加
ui->setupUi(this);
// 设置根节点的名字
ui->treeWidget->setHeaderLabel("动物");// 新增顶层节点
QTreeWidgetItem* item1 = new QTreeWidgetItem();
// 每个节点都可以设置多个列。此处为了简单就只设置一列了。
item1->setText(0, "猫");
// 添加到顶层节点中。
ui->treeWidget->addTopLevelItem(item1);// 新增顶层节点
QTreeWidgetItem* item2 = new QTreeWidgetItem();
// 每个节点都可以设置多个列。此处为了简单就只设置一列了。
item2->setText(0, "狗");
// 添加到顶层节点中。
ui->treeWidget->addTopLevelItem(item2);// 新增顶层节点
QTreeWidgetItem* item3 = new QTreeWidgetItem();
// 每个节点都可以设置多个列。此处为了简单就只设置一列了。
item3->setText(0, "鸟");
// 添加到顶层节点中。
ui->treeWidget->addTopLevelItem(item3);
子节点的添加
// 添加一些子节点
QTreeWidgetItem* item4 = new QTreeWidgetItem();
item4->setText(0, "中华田园猫");
item1->addChild(item4);QTreeWidgetItem* item5 = new QTreeWidgetItem();
item5->setText(0, "布偶猫");
item1->addChild(item5);QTreeWidgetItem* item6 = new QTreeWidgetItem();
item6->setText(0, "暹罗猫");
item1->addChild(item6);
通过按钮实现节点的添加
void Widget::on_pushButton_insertItem_clicked()
{// 获取到当前选中的节点QTreeWidgetItem* currentItem = ui->treeWidget->currentItem();if (currentItem == nullptr) {return;}// 获取到输入框的内容const QString& text = ui->lineEdit->text();// 构造一个 QTreeWidgetItemQTreeWidgetItem* item = new QTreeWidgetItem();item->setText(0, text);// 插入到选中节点的子节点中currentItem->addChild(item);
}
通过按钮实现节点的删除
void Widget::on_pushButton_deleteItem_clicked()
{// 获取到选中的元素QTreeWidgetItem* currentItem = ui->treeWidget->currentItem();if (currentItem == nullptr) {return;}// 删除选中的元素,需要先获取到父元素,通过父元素进行删除QTreeWidgetItem* parent = currentItem->parent();if (parent == nullptr) {// 顶层元素int index = ui->treeWidget->indexOfTopLevelItem(currentItem);ui->treeWidget->takeTopLevelItem(index);} else {// 普通元素parent->removeChild(currentItem);}
}
容器类
QGroupBox
使用 QGroupBox 实现一个带有标题的分组框,可以把其他的控件放到里面作为一组.这样看起来能更好看一点。
注意不要把 QGroupBox 和 QButtonGroup 混淆,(之前在介绍 QRadionButton 的时候提到 QButtonGroup ).
QTabWidget
使用 QTabwidget 实现一个带有标签页的控件,可以往里面添加一些 widget.进一步的就可以通过标签页来切换。
通过按钮实现标签的增删
void Widget::on_pushButton_clicked()
{// 使用 addTab 方法来创建新的标签页.// 参数1 要指定一个 QWidget.// 参数2 指定这个标签页的 text(标题), 此处标题就叫做 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);
}
布局类
QVBoxLayout(垂直)
Qt 中手动布局控件的方式存在复杂不精确、无法自适应窗口大小等问题,因此提供了垂直布局、水平布局、网格布局、表单布局等多种布局管理器来解决这些问题,以更科学地对界面控件进行布局。
使用样例
ui->setupUi(this);// 创建三个按钮,使用垂直布局管理器管理起来.
QPushButton* button1 = new QPushButton("按钮1");
QPushButton* button2 = new QPushButton("按钮2");
QPushButton* button3 = new QPushButton("按钮3");// 创建布局管理器
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);// 把布局管理器添加到窗口中.
this->setLayout(layout);
QHBoxLayout(水平)
QGridBoxLayout(网格)
QFromLayout(表单)
使用样例
ui->setupUi(this);// 设置成 3 行 2 列.
QFormLayout* layout = new QFormLayout();
this->setLayout(layout);// 创建 3 个 label 作为第一列
QLabel* label1 = new QLabel("姓名");
QLabel* label2 = new QLabel("年龄");
QLabel* label3 = new QLabel("电话");// 创建 3 个输入框作为第二列
QLineEdit* edit1 = new QLineEdit();
QLineEdit* edit2 = new QLineEdit();
QLineEdit* edit3 = new QLineEdit();// 把上述控件添加到表单布局中
layout->addRow(label1, edit1);
layout->addRow(label2, edit2);
layout->addRow(label3, edit3);