文章目录
- 按钮类控件
-
- QPushButton
- [Radio Button 单选](#Radio Button 单选)
- [Check Box 多选](#Check Box 多选)
- 显示类控件
- 输入类控件
-
- [Line Edit](#Line Edit)
-
- [textEdited() 与 textChanged()的区别](#textEdited() 与 textChanged()的区别)
- [Text Edit](#Text Edit)
- [Combox Box 下拉框](#Combox Box 下拉框)
- [Spin Box](#Spin Box)
- [DateEdit &TimeEdit](#DateEdit &TimeEdit)
- [Dial 旋钮](#Dial 旋钮)
- [Slider 滑动条](#Slider 滑动条)
- 多元素控件
-
- [List Widget](#List Widget)
- [Table Widget](#Table Widget)
- [Tree Widget](#Tree Widget)
- 容器类控件
-
- [GroupBox 分组框](#GroupBox 分组框)
- [QTableWidget 标签页](#QTableWidget 标签页)
- 布局管理器
按钮类控件
QPushButton
QWidget中涉及到的各种属性/函数,针对继承QWidget类的控件都有效:

相关的属性:
| text | 按钮中的⽂本 |
|---|---|
| icon | 按钮中的图标 |
| iconSize | 按钮中图标的尺⼨ |
| shortCut | 按钮对应的快捷键 |
| autoRepeat | 按钮是否会重复触发。当⿏标左键按住不放时, 如果设为true,则会持续产⽣⿏标点击事件; 如果设为false,则必须释放⿏标,再次按下⿏标时才能产⽣点击事件。 (相当于游戏⼿柄上的"连发"效果) |
| autoRepeatDelay | 重复触发的延时时间。按住按钮多久之后,开始重复触发。 |
| autoRepeatInterval | 重复触发的周期。 |
给按钮夹图标:
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建图标对象
QIcon icon(":/picture.png");
// 设置图标
ui->pushButton->setIcon(icon);
// 设置图标的尺寸
ui->pushButton->setIconSize(QSize(100,100));
}
Widget::~Widget()
{
delete ui;
}
效果:

给QpushButton添加快捷键:
示例:现在用wasd去控制图片的移动。
首先,从阿里巴巴矢量图库中找到箭头图片,并自己准备一个图片。
然后添加到qrc文件中,关联槽函数等,点击上下左右能移动图片,后使用setShortcyt()函数与键盘关联:

cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//把图像写入
ui->pushButton_Target->setIcon(QIcon(":/image/picture.png"));
ui->pushButton_Target->setIconSize(QSize(300,300));
//四个箭头写入
ui->pushButton_UP->setIcon(QIcon(":/image/up.png"));
ui->pushButton_UP->setIconSize(QSize(125,125));
ui->pushButton_down->setIcon(QIcon(":/image/down.png"));
ui->pushButton_down->setIconSize(QSize(125,125));
ui->pushButton_left->setIcon(QIcon(":/image/left.png"));
ui->pushButton_left->setIconSize(QSize(125,125));
ui->pushButton_right->setIcon(QIcon(":/image/right.png"));
ui->pushButton_right->setIconSize(QSize(125,125));
//关联键盘去移动
ui->pushButton_UP->setShortcut(QKeySequence("w"));
ui->pushButton_down->setShortcut(QKeySequence("s"));
ui->pushButton_left->setShortcut(QKeySequence("a"));
ui->pushButton_right->setShortcut(QKeySequence("d"));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_UP_clicked()
{
//获取图片的坐标信息
QRect rect = ui->pushButton_Target->geometry();
//更改图片位置
ui->pushButton_Target->setGeometry(rect.x(),rect.y()-5,rect.width(),rect.height());
}
void Widget::on_pushButton_down_clicked()
{
//获取图片的坐标信息
QRect rect = ui->pushButton_Target->geometry();
//更改图片位置
ui->pushButton_Target->setGeometry(rect.x(),rect.y()+5,rect.width(),rect.height());
}
void Widget::on_pushButton_left_clicked()
{
//获取图片的坐标信息
QRect rect = ui->pushButton_Target->geometry();
//更改图片位置
ui->pushButton_Target->setGeometry(rect.x()-5,rect.y(),rect.width(),rect.height());
}
void Widget::on_pushButton_right_clicked()
{
//获取图片的坐标信息
QRect rect = ui->pushButton_Target->geometry();
//更改图片位置
ui->pushButton_Target->setGeometry(rect.x()+5,rect.y(),rect.width(),rect.height());
}
如果我们直接通过按键的名字来设置,虽然简单,但容易写错。
可用通过按键的枚举来设置按键的快捷键:
cpp
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));
组合键:
cpp
ui->pushButton_UP->setShortcut(QKeySequence("ctrl + w"));
ui->pushButton_UP->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_W));
在实际运行中发现,快捷键可以点着不动,图片会一直移动,但用鼠标点击不松手,图片不能移动。
开启鼠标点击的的连发功能:
cpp
ui->pushButton_UP->setAutoRepeat(true);
Radio Button 单选
QRadioButton是单选按钮,多个选项中选择一个。
| checkable | 是否可以选中。 |
|---|---|
| checked | 是否已经被选中。checkable是checked的前提条件。 |
| autoExclusive | 是否排他。 选中⼀个按钮之后是否会取消其他按钮的选中。 对于QRadioButton 来说默认就是排他的。 |
示例:性别选择,默认为男,禁用其它选项
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//添加一个默认的选项
ui->radioButton_boy->setChecked(true);
ui->label->setText("性别为:男");
//禁用 其它 选项
ui->radioButton_other->setCheckable(false);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_radioButton_boy_clicked()
{
ui->label->setText("性别为:男");
}
void Widget::on_radioButton_girl_clicked()
{
ui->label->setText("性别为:女");
}
void Widget::on_radioButton_other_clicked()
{
ui->label->setText("性别为:其它");
}
我们发现禁用 其它 选项,只是按钮不能被选择,但仍然可以响应点击事件:

需要用到setEnabled:

例子:做一个选餐的界面
用上面的方法做出来的效果,每次只能选择一个,而我们现实中都是汉堡+小食+饮料:

用QButtonGroup去分组来解决:
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QButtonGroup>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 使用QButtonGroup进行分组
QButtonGroup *group1 = new QButtonGroup(this);
QButtonGroup *group2 = new QButtonGroup(this);
QButtonGroup *group3 = new QButtonGroup(this);
//把按钮分配到各个组中
group1->addButton(ui->radioButton);
group1->addButton(ui->radioButton_2);
group1->addButton(ui->radioButton_3);
group2->addButton(ui->radioButton_4);
group2->addButton(ui->radioButton_5);
group2->addButton(ui->radioButton_6);
group3->addButton(ui->radioButton_8);
group3->addButton(ui->radioButton_9);
}
Widget::~Widget()
{
delete ui;
}

Check Box 多选
QCheck Box 表示复选按钮,可以选择多个。
示例:
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
QString result = "今天你的安排是:";
if(ui->checkBox_study->isChecked()){
result += ui->checkBox_study->text();
}
if(ui->checkBox_eat->isChecked()){
result += ui->checkBox_eat->text();
}
if(ui->checkBox_work->isChecked()){
result += ui->checkBox_work->text();
}
ui->label->setText(result);
}

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

pixmap 设置图片
我想把这个"滑稽"图片占满整个界面:

发现界面是800 x 600的,滑稽图片太小,做不到全面覆盖:

用到setScaledContents:

但是又有一个新问题:我们在拖拽Widget的时候,他不会覆盖全部

原因:在Qt 中,表示用户的操作,有两类概念:1.信号。2.事件。
当用户拖拽窗口的大小时,就会触发 resize 事件 (resizeEvent)。
我们把窗口尺寸从A拖拽到B,还可以拖拽到C,说明可以是连续变化的,触发一系列的resizeEvent。
用鼠标拖拽Wiget窗口类,会重写父类(QWidget)的resizeEvent 虚函数。
代码:
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QResizeEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 让label铺满整个界面
QRect rect = this->geometry();
ui->label->setGeometry(0,0,rect.width(),rect.height());
// 获取图片
QPixmap pixmap(":/picture.png");
ui->label->setPixmap(pixmap);
// 打开自动拉伸
ui->label->setScaledContents(true);
}
Widget::~Widget()
{
delete ui;
}
void Widget::resizeEvent(QResizeEvent *event)
{
qDebug() << event->size();
ui->label->setGeometry(0,0,event->size().width(),event->size().height());
}
效果:当拉伸的时候,左边的日志显示了界面的尺寸

文本对齐方式
当我们把标签拖进界面时,希望能看清边框:

这里选了box作为效果:

水平居中 + 垂直居中:


自动换行
第一个标签没有自动换行,第二个标签自动换行:

设置缩进

设置边距
可以看到上下左右都缩进:

QLabel 伙伴
先看样例:

效果:

由上述可知:Qt 中,QLabel 中写的文本,可以指定"快捷键",但快捷键的规则功能上比QPushButon弱很多。
语法:在文本中使用 &字符 来表示快捷键。绑定了伙伴关系后,通过快捷键就能选中对应的单选框/复选框。
LCD Number 显示数字
QLCD Number 是一个专门用来显示数字的控件。
| intValue | QLCDNumber显⽰的数字值(int)。 |
|---|---|
| value | QLCDNumber显⽰的数字值(double)。 例如给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 | 设置⽐较⼩的⼩数点。 |
示例:倒计时,使用QLCDNumber 显示一个初始的数值,每隔一秒,数字-1

Qt 中封装了对应的定时器:QTimer 通过这个类创建出来的对象,会产生timeout的信号,通过start开始,并且能在start中参数设定多少ms触发一次timeout信号。后通过connect来连接信号与槽。
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QTimer>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置初始值
ui->lcdNumber->display(10);
// 把Qtime函数与timeout 信号与槽函数相关联
timer = new QTimer(this);
connect(timer,&QTimer::timeout,this,&Widget::handle);
// 启动定时器 单位:ms
timer->start(1000);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handle()
{
//先拿到LCDNumber的数字
int value = ui->lcdNumber->intValue();
if(value <= 0){
timer->stop();
return;
}
ui->lcdNumber->display(value - 1);
}
效果这里就不再演示了。
想一个问题,如果不用QTimer能不能实现,用sleep去解决:
cpp
#include "widget.h"
#include "ui_widget.h"
#include <thread>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
int value = ui->lcdNumber->intValue();
while(true){
// 休眠1s
std::this_thread::sleep_for(std::chrono::seconds(1));
if(value <=0){
break;
}
value -= 1;
ui->lcdNumber->display(value);
}
}
Widget::~Widget()
{
delete ui;
}
运行后发现,等了10才弹窗,且并没显示成功。
原因:

想了第二个方案,自己搞一个线程,在线程里循环 + 更新,就避免了直接在主线程的构造函数中等待:
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
std::thread t([this] (){
int value = ui->lcdNumber->intValue();
while(true){
std::this_thread::sleep_for(std::chrono::seconds(1));
if(value <= 0){
break;
}
value -= 1;
ui->lcdNumber->display(value);
}
});
}
发现依旧不行,还有报错:

原因:在Qt 中,里面有一个专门的线程去负责回鹘更新的主线程(main函数所在的线程)。
对于GUI来说,内部包含了很多的隐藏状态,Qt 为了保证修改界面的过程中,线程的安全会受到影响,Qt禁止了其它线程直接修改见面。
这种操作,就是在修改界面:

因此,所有的对界面修改操作,必须在主线程中完成。槽函数是主线程调用的,所以我们会用槽函数修改界面。
return a.exec();
重点说一下:
cpp
return a.exec();
a.exec() 就是这个无限循环的启动命令。它会启动一个 QEventLoop,并阻塞在 a.exec() 这一行代码上。这个循环会一直运行,直到你关闭所有窗口或主动退出,a.exec()返回一个退出码,开始执行 return a.exec(),整个main函数结束。
在循环期间,他会做:
1.监听和收集所有事件:
用户输入事件:鼠标点击、移动、键盘按键。
系统事件:定时器超时、窗口重绘请求、文件读写完成、网络数据到达。
2.分发事件:
将收集到的事件分发给相应的目标对象(QObject)。比如:把鼠标点击事件发给被点击的按钮。把键盘事件发给获得焦点的输入框。
3.处理事件并触发槽函数(Slots)。
ProgressBar 进度条
属性:
| minimum | 进度条最⼩值 |
|---|---|
| maximum | 进度条最⼤值 |
| value | 进度条当前值 |
| alignment | ⽂本在进度条中的对⻬⽅式。 Qt::AlignLeft::左对⻬ Qt::AlignRight::右对⻬ Qt::AlignCenter::居中对⻬ Qt::AlignJustify::两端对⻬ |
| textVisible | 进度条的数字是否可⻅。 |
| orientation | 进度条的⽅向是⽔平还是垂直。 |
| invertAppearance | 是否是朝反⽅向增⻓进度。 |
| textDirection | ⽂本的朝向。 |
| format | 展⽰的数字格式。 %p:表⽰进度的百分⽐(0-100) %v:表⽰进度的数值(0-100) %m:表⽰剩余时间(以毫秒为单位) %t:表⽰总时间(以毫秒为单位) |
例子:写一个进度条

widget.h:
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void handle();
private:
Ui::Widget *ui;
QTimer *timer;
};
#endif // WIDGET_H
widget.cpp:
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QTimer>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
timer = new QTimer();
connect(timer,&QTimer::timeout,this,&Widget::handle);
timer->start(100);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handle()
{
int value = ui->progressBar->value();
if(value >= 100){
timer->stop();
return;
}
ui->progressBar->setValue(value+1);
}
样子:

发现了一个问题:.h文件中并没有加头文件QTimer,成员变量居然没有报错

原因如下:(不用过多深究原理,只需要知道有这回事就行)

而这个包含机制:
cpp
我们写一个普通Qt头文件 → 比如 <QWidget>
↓
这个头文件可能包含模块级头文件 → 比如 <QtWidgets/qtwidgetsglobal.h>
↓
模块级头文件可能包含前置声明头文件 → 比如间接包含 <QtCore/QtCoreFwd>
↓
前置声明头文件包含了所有类声明 → 包括 class QTimer;
更具体一点:
cpp
// 代码:
#include <QWidget> // 看似只包含一个类
// 实际上编译器看到:
#include <QtCore/QtCoreFwd> // 所有 Core 类前置声明
#include <QtGui/QtGuiFwd> // 所有 GUI 类前置声明
#include <QtWidgets/QtWidgetsFwd> // 所有 Widgets 类前置声明
// 加上 QWidget 的具体实现

Qt 为什么要有这种技巧?
主要是解决编译速度问题,横向对比中,C/C++的编译速度非常慢,原因跟头文件有直接关系。include头文件关系错综复杂,减少个数可以有效减少编译时间,Qt就用class前置声明来减少头文件的包含。
但在实际开发中,该包含头文件还是包含。提升硬件能力换取高效的编译比用特殊技巧更好。
Calendar Widget 日历
QCalendarWidget 表⽰⼀个⽇历。
属性:
| selectDate | 当前选中的⽇期 |
|---|---|
| minimumDate | 最⼩⽇期 |
| maximumDate | 最⼤⽇期 |
| firstDayOfWeek | 每周的第⼀天(也就是⽇历的第⼀列)是周⼏. |
| gridVisible | 是否显⽰表格的边框 |
| selectionMode | 是否允许选择⽇期 |
| navigationBarVisible | ⽇历上⽅标题是否显⽰ |
| horizontalHeaderFormat | ⽇历上⽅标题显⽰的⽇期格式 |
| verticalHeaderFormat | ⽇历第⼀列显⽰的内容格式 |
| dateEditEnabled | 是否允许⽇期被编辑 |
信号:
| selectionChanged(constQDate&) | 当选中的⽇期发⽣改变时发出 |
|---|---|
| activated(const QDate&) | 当双击⼀个有效的⽇期或者按下回⻋键时发出,形参是⼀个QDate类型,保存了选中的⽇期 |
| currentPageChanged(int,int) | 当年份⽉份改变时发出,形参表⽰改变后的新年份和⽉份。 |
例子:


输入类控件
Line Edit
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是新的⽂本。 代码对⽂本的修改不能触发这个信号。 |
例子:让用户输入个人信息,输入后点提交按钮,日志打印出来
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//姓名
ui->lineEdit_name->setPlaceholderText("请输入姓名");
ui->lineEdit_name->setClearButtonEnabled(true);
//密码
ui->lineEdit_password->setPlaceholderText("请输入密码");
ui->lineEdit_password->setClearButtonEnabled(true);
//显示模式转成密码模式
ui->lineEdit_password->setEchoMode(QLineEdit::Password);
//手机号
ui->lineEdit__telephone->setPlaceholderText("请输入手机号");
ui->lineEdit__telephone->setClearButtonEnabled(true);
//手机号的固定格式,0代表数字
ui->lineEdit__telephone->setInputMask("000-0000-0000");
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
QString gender = ui->radioButton_male->isChecked() ? "男" : "女";
qDebug() << "姓名:" << ui->lineEdit_name->text()
<< "密码:" << ui->lineEdit_password->text()
<< "性别:" << gender
<< "电话号码:" << ui->lineEdit__telephone->text();
}
效果:


在上面代码中,限制手机号用到了inputMask,功能有限,只能进行简单的验证。需要"正则表达式"。
"正则表达式":本质上是一个带有特殊字符的字符串,特殊字符用来表示另一个字符串的特征。
就比如我要找个女朋友,条件:1.白;2.富;3.身材好。我就要拿着这三个条件去对比,然后找到心仪的女生。正则表达式就是拿着条件去匹配。
这里用一下就懂了:
例子:输入框检查输入的内容是否是合法的手机号,如果是,则按钮为可用状态;不是,则为禁用状态。

cpp
//给单行输入框设置验证器,基于正则表达式来完成
QRegularExpression regExp("^1\\d{10}$");
//注册验证器
ui->lineEdit->setValidator(new QRegularExpressionValidator(regExp));
注册完验证器,后使用验证器,使用的方法
由其它代码来完成。
比如这里的使用规则为:验证输入框的内容是否合法,只要输入框中的内容发生改变,验证器操作就应被执行。

全部代码:
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QRegularExpression>
#include <QRegularExpressionValidator>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//给单行输入框设置验证器,基于正则表达式来完成
QRegularExpression regExp("^1\\d{10}$");
//注册验证器
ui->lineEdit->setValidator(new QRegularExpressionValidator(regExp));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_lineEdit_textChanged(const QString &text)
{
QString content = text;
int pos = 0;
if(ui->lineEdit->validator()->validate(content,pos) == QValidator::Acceptable){
//验证通过
ui->pushButton->setEnabled(true);
}else{
//验证失败
ui->pushButton->setEnabled(false);
}
}
效果:

正则表达式的语法:语法
正则表达式的在线检测工具:工具
textEdited() 与 textChanged()的区别
| 信号 | 触发时机 |
|---|---|
| textEdited() | 用户手动编辑时触发 |
| textChanged() | 文本内容改变就触发(包括代码设置) |
例子:验证两次输入的密码一致
代码:
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::compare()
{
const QString& s1 = ui->lineEdit->text();
const QString& s2 = ui->lineEdit_2->text();
if(s1.isEmpty() && s2.isEmpty()){
ui->label->setText("密码都为空");
}else if(s1 == s2){
ui->label->setText("两次输入的密码一致");
}else
{
ui->label->setText("两次输入的密码不一致");
}
}
void Widget::on_lineEdit_textEdited(const QString &arg1)
{
this->compare();
}
void Widget::on_lineEdit_2_textEdited(const QString &arg1)
{
this->compare();
}
效果:

我们平常会遇到这样的问题:有用不到的参数,会警告

可用骗过编译器,不让警告发生:

Text Edit
QTextEdit表示多⾏输⼊框,也是⼀个富⽂本&markdown编辑器。并且能在内容超出编辑框范围时⾃动提供滚动条。
| markdown | 输⼊框内持有的内容。⽀持markdown格式。能够⾃动的对markdown⽂本进⾏渲染成html |
|---|---|
| html | 输⼊框内持有的内容。可以⽀持⼤部分html标签,包括img和table等。 |
| placeHolderText | 输⼊框为空时提⽰的内容。 |
| readOnly | 是否是只读的。 |
| undoRedoEnable | 是否开启undo/redo功能。 按下ctrl+z触发undo(撤销) 按下ctrl+y触发redo(还原) |
| autoFormating | 开启⾃动格式化。 |
| tabstopWidth | 按下缩进占多少空间 |
| overwriteMode | 是否开启覆盖写模式 |
| acceptRichText | 是否接收富⽂本内容 |
| verticalScrollBarPolicy | 垂直⽅向滚动条的出现策略。 Qt::ScrollBarAsNeeded:根据内容⾃动决定是否需要滚动条。这是默认值。 Qt::ScrollBarAlwaysOff:总是关闭滚动条。 Qt::ScrollBarAlwaysOn:总是显⽰滚动条。 |
| horizontalScrollBarPolicy | ⽔平⽅向滚动条的出现策略。 Qt::ScrollBarAsNeeded:根据内容⾃动决定是否需要滚动条,这是默认值。 Qt::ScrollBarAlwaysOff:总是关闭滚动条。 Qt::ScrollBarAlwaysOn:总是显⽰滚动条。 |
信号:
| textChanged() | ⽂本内容改变时触发 |
|---|---|
| selectionChanged() | 选中范围改变时触发 |
| cursorPositionChanged() | 光标移动时触发 |
| undoAvailable(bool) | 可以进⾏undo操作时触发 |
| redoAvailable(bool) | 可以进⾏redo操作时触发 |
| copyAvaiable(bool) | ⽂本被选中/取消选中时触发 |
示例:用法很简单
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_textEdit_textChanged()
{
qDebug() << "textChange: " << ui->textEdit->toPlainText();
}
void Widget::on_textEdit_selectionChanged()
{
qDebug() << "textChange: " << ui->textEdit->toPlainText();
}
void Widget::on_textEdit_cursorPositionChanged()
{
qDebug() << "cursorPositionChanged: " << ui->textEdit->toPlainText();
}
用法简单,效果很显然易见,这里就不演示了。
Combox Box 下拉框
Combox Box 表示下拉框。
| 属性 | 说明 |
|---|---|
| currentText | 当前选中的⽂本 |
| currentIndex | 当前选中的条⽬下标。从0开始计算。如果当前没有条⽬被选中,值为-1。 |
| editable | 是否允许修改设为true时,QComboBox的⾏为就⾮常接近QLineEdit,也可以设置validator。 |
| iconSize | 下拉框图标(⼩三⻆)的⼤⼩ |
| maxCount | 最多允许有多少个条⽬ |
| 方法 | 说明 |
|---|---|
| addItem(constQString&) | 添加⼀个条⽬ |
| currentIndex() | 获取当前条⽬的下标 从0开始计算.如果当前没有条⽬被选中,值为-1。 |
| currentText() | 获取当前条⽬的⽂本内容。 |
| ⽅法 | 说明 |
|---|---|
| activated(int) activated(constQString&text) | 当⽤⼾选择了⼀个选项时发出。 这个时候相当于⽤⼾点开下拉框,并且⿏标划过某个选项。 此时还没有确认做出选择。 |
| currentIndexChanged(int) currentIndexChanged(constQString&text) | 当前选项改变时发出。 此时⽤⼾已经明确的选择了⼀个选项。 ⽤⼾操作或者通过程序操作都会触发这个信号。 |
| editTextChanged(constQString&text) | 当编辑框中的⽂本改变时发出(editable为true时有效) |
例子:点餐
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
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("雪碧");
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
qDebug() << ui->comboBox->currentText()
<< ui->comboBox_2->currentText()
<< ui->comboBox_3->currentText();
}
效果:

也可以在界面中添加条目:

下拉框里面的内容,很多时候不是代码写死的,而是通过文件/网络加载数据得到的。
例子:把下面文件的内容做成下拉框

代码:
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <fstream>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//读取文件内容
std::ifstream file("C:/Program1/config.txt");
if(!file.is_open()){
qDebug() << "文件打开失败";
return;
}
//按行来读取内容
std::string line;
while(std::getline(file,line)){
//每取到一行,放到下拉框中
ui->comboBox->addItem(QString::fromStdString(line));
}
file.close();
}
Widget::~Widget()
{
delete ui;
}
效果:

Spin Box
使⽤QSpinBox 或者QDoubleSpinBox 表示"微调框",它是带有按钮的输⼊框,可以⽤来输⼊整数/浮点数,通过点击按钮来修改数值⼤⼩。
| 属性 | 说明 |
|---|---|
| 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,表⽰当前的数值. |
例子:结合上面的点餐服务,用 微调框 选择数量
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
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("雪碧");
//设置数量范围
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);
}
Widget::~Widget()
{
delete ui;
}
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();
}
效果:

DateEdit &TimeEdit

这⼏个控件⽤法⾮常相似,我们以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:显⽰本地时间。北京时间"东八区",LocalTime = UTC + 8h Qt::UTC:显⽰协调世界时(UTC)。是标准时间,通过原子钟 Qt::OffsetFromUTC:显⽰相对于UTC的偏移量(时差)。 |
| 信号 | 说明 |
|---|---|
| dateChanged(QDate) | ⽇期改变时触发. |
| timeChanged(QTime) | 时间改变时触发. |
| dateTimeChanged(QDateTime) | 时间⽇期任意⼀个改变时触发. |
例子:时间计算器,计算两个时间中间的间隔是多少天/多少小时
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
//得到时间
QDateTime dateOld = ui->dateTimeEdit->dateTime();
QDateTime dateNew = ui->dateTimeEdit_2->dateTime();
//做差
int days = dateOld.daysTo(dateNew);
int seconds = dateOld.secsTo(dateNew);
// 把秒换成小时
int hours = (seconds / 3600) %24;
//计算结果放到label中
ui->label->setText(QString::number(days) + QString("天") + QString::number(hours) + QString("小时") );
}
效果:

会有一个BUG:这里应该是0天7小时

查一下文档:他说23:55到0:05算一天

那就加上:
cpp
void Widget::on_pushButton_clicked()
{
//得到时间
QDateTime dateOld = ui->dateTimeEdit->dateTime();
QDateTime dateNew = ui->dateTimeEdit_2->dateTime();
//做差
int seconds = dateOld.secsTo(dateNew);
// 把秒换成小时
int hours = (seconds / 3600) %24;
int days = (seconds / 3600) /24;
//计算结果放到label中
ui->label->setText(QString::number(days) + QString("天") + QString::number(hours) + QString("小时") );
}

Dial 旋钮
使⽤QDial 表示⼀个旋钮。
| 属性 | 说明 |
|---|---|
| value | 持有的数值。 |
| minimum | 最⼩值 |
| maximum | 最⼤值 |
| singleStep | 按下⽅向键的时候改变的步⻓。 |
| pageStep | 按下pageUp/pageDown的时候改变的步⻓。 |
| sliderPosition | 界⾯上旋钮显⽰的初始位置。 |
| tracking | 外观是否会跟踪数值变化. 默认值为true.⼀般不需要修改. |
| wrapping | 是否允许循环调整。 即数值如果超过最⼤值,是否允许回到最⼩值。(调整过程能否"套圈") |
| notchesVisible | 是否显⽰刻度线 |
| notchTarget | 刻度线之间的相对位置。 数字越⼤,刻度线越稀疏。 |
信号:
| valueChanged(int) | 数值改变时触发 |
|---|---|
| rangeChanged(int, int) | 范围变化时触发 |
Slider 滑动条
使⽤QSlider 表⽰⼀个滑动条。
| 属性 | 说明 |
|---|---|
| value | 持有的数值。 |
| minimum | 最⼩值 |
| maximum | 最⼤值 |
| singleStep | 按下⽅向键的时候改变的步⻓。 |
| pageStep | 按下pageUp/pageDown的时候改变的步⻓。 |
| sliderPosition | 滑动条显⽰的初始位置 |
| tracking | 外观是否会跟踪数值变化. 默认值为true.⼀般不需要修改 |
| orientation | 滑动条的⽅向是⽔平还是垂直 |
| invertedAppearance | 是否要翻转滑动条的⽅向 |
| tickPosition | 刻度的位置 |
| tickInterval | 刻度的密集程度. |
| 信号 | 说明 |
|---|---|
| valueChanged(int) | 数值改变时触发 |
| rangeChanged(int,int) | 范围变化时触发 |
多元素控件
两两为一组:

xxWidget VS xxView
xxView更倾向于底层,xxWidget基于xxView封装而来的。
xxView 是MVC结构的一种典型实现:
M model 数据 ;V view 视图(界面);C controller 控制器 数据与试图之间的业务流程。
xxView只负责视图。如果程序员需要自己实现mdoel和controller的部分比较麻烦,所以xxWidget基于xxView同时把model和controller做好了,提供了API。
xxView的上限高,能实现复杂的功能,但麻烦。(CPU、内存等零件自己组装电脑)
xxWidget的下限高,使用方便,功能有限。(买整机,内存等零件可能会拉后腿,不兼容)
List Widget
使⽤QListWidget 能够显⽰⼀个纵向的列表。形如:每个选项可以被选中

| 属性 | 说明 |
|---|---|
| currentRow | 当前被选中的是第⼏⾏ |
| count | ⼀共有多少⾏ |
| sortingEnabled | 是否允许排序 |
| isWrapping | 是否允许换⾏ |
| itemAlignment | 元素的对⻬⽅式 |
| selectRectVisible | 被选中的元素矩形是否可⻅ |
| spacing | 元素之间的间隔 |
| 方法 | 说明 |
|---|---|
| addItem(constQString&label) addItem(QListWidgetItem*item) | 列表中添加元素 |
| currentItem() | 返回QListWidgetItem*表⽰当前选中的元素 |
| setCurrentItem(QListWidgetItem*item) | 设置选中哪个元素 |
| setCurrentRow(introw) | 设置选中第⼏⾏的元素 |
| insertItem(constQString&label,introw) insertItem(QListWidgetItem*item,introw) | 在指定的位置插⼊元素 |
| item(introw) | 返回QListWidgetItem*表⽰第row⾏的元素 |
| takeItem(introw) | 删除指定⾏的元素,返回QListWidgetItem*表⽰是哪个元素被删除了 |
| 信号 | 说明 |
|---|---|
| currentItemChanged(QListWidgetItemcurrent,QListWidgetItemold) | 选中不同元素时会触发.参数是当前选中的元素和之前选中的元素 |
| currentRowChanged(int) | 选中不同元素时会触发.参数是当前选中元素的⾏数 |
| itemClicked(QListWidgetItem*item) | 点击某个元素时触发 |
| itemDoubleClicked(QListWidgetItem*item) | 双击某个元素时触发 |
| itemEntered(QListWidgetItem*item) | ⿏标进⼊元素时触发 |
例子:在右边写入添加到列表中;或者选中列表的项目删除

代码:
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_insert_clicked()
{
// 读取写入的内容
const QString text = ui->textEdit->toPlainText();
// 写入列表中
ui->listWidget->addItem(text);
// 删除输入框内的内容
ui->textEdit->clear();
}
void Widget::on_pushButton_delete_clicked()
{
// 选中要删除内容的行数
int row = ui->listWidget->currentRow();
// 删除
ui->listWidget->takeItem(row);
}
也可以用QListWidgetItem添加,在QListWidgetItem中可用设置字体属性,图标,文字大小,被选中状态等:
cpp
ui->listWidget->addItem(new QListWidgetItem("小草神"));
界面也能添加列表:

Table Widget
使⽤QTableWidget 表⽰⼀个表格控件。⼀个表格中包含若⼲⾏,每⼀⾏⼜包含若⼲列。表格中的每个单元格,是⼀个QTableWidgetItem 对象。(Table Widget是2维,List Widget是1维的 )
QTableWidget 核⼼⽅法:
| ⽅法 | 说明 |
|---|---|
| item(int row, int column) | 根据⾏数列数获取指定的QTableWidgetItem* |
| setItem(int row, int column,QTableWidget*) | 根据行数列数设置表格中的元素 |
| currentItem() | 返回被选中的元素QTableWidgetItem* |
| currentRow() | 返回被选中元素是第⼏⾏ |
| currentColumn() | 返回被选中元素是第⼏列 |
| row(QTableWidgetItem*) | 获取指定item是第⼏⾏ |
| column(QTableWidgetItem*) | 获取指定item是第⼏列 |
| rowCount() | 获取⾏数 |
| columnCount() | 获取列数 |
| insertRow(introw) | 在第row⾏处插⼊新⾏ |
| insertColumn(intcolumn) | 在第column列插⼊新列 |
| removeRow(introw) | 删除第row⾏ |
| removeColumn(intcolumn) | 删除第column列 |
| setHorizontalHeaderItem(int column,QTableWidgetItem*) | 设置指定列的表头 |
| setVerticalHeaderItem(introw,QTableWidgetItem*) | 设置指定⾏的表头 |
QTableWidgetItem核⼼信号:
| 信号 | 说明 |
|---|---|
| cellClicked(introw,intcolumn) | 点击单元格时触发 |
| cellDoubleClicked(introw,int column) | 双击单元格时触发 |
| cellEntered(introw,intcolumn) | ⿏标进⼊单元格时触发 |
| currentCellChanged(int row,int column,intpreviousRow,int previousColumn) | 选中不同单元格时触发 |
QTableWidgetItem核⼼⽅法:
| ⽅法 | 说明 |
|---|---|
| row() | 获取当前是第⼏⾏ |
| column() | 获取当前是第⼏列 |
| setText(const QString&) | 设置⽂本 |
| setTextAlignment(int) | 设置⽂本对⻬ |
| setIcon(const QIcon&) | 设置图标 |
| setSelected(bool) | 设置被选中 |
| setSizeHints(const QSize&) | 设置尺⼨ |
| setFont(const QFont&) | 设置字体 |
图形化:

代码:
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建三行
ui->tableWidget->insertRow(0);
ui->tableWidget->insertRow(1);
ui->tableWidget->insertRow(2);
// 创建三列
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("张三"));
ui->tableWidget->setItem(0,1,new QTableWidgetItem("男"));
ui->tableWidget->setItem(0,2,new QTableWidgetItem("100"));
ui->tableWidget->setItem(1,0,new QTableWidgetItem("李四"));
ui->tableWidget->setItem(1,1,new QTableWidgetItem("男"));
ui->tableWidget->setItem(1,2,new QTableWidgetItem("90"));
ui->tableWidget->setItem(2,0,new QTableWidgetItem("王五"));
ui->tableWidget->setItem(2,1,new QTableWidgetItem("男"));
ui->tableWidget->setItem(2,2,new QTableWidgetItem("95"));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_add_row_clicked()
{
// 获取目前行号
int row = ui->tableWidget->rowCount();
// 增加行
ui->tableWidget->insertRow(row);
}
void Widget::on_pushButton_delete_row_clicked()
{
// 获得选中的行号
int row = ui->tableWidget->currentRow();
// 删除
ui->tableWidget->removeRow(row);
}
void Widget::on_pushButton_add_Column_clicked()
{
// 获取目前列号
int column = ui->tableWidget->columnCount();
// 增加列
ui->tableWidget->insertColumn(column);
// 写入列的名字
ui->tableWidget->setHorizontalHeaderItem(column,new QTableWidgetItem(ui->lineEdit->text()));
}
void Widget::on_pushButton_Delete_Column_clicked()
{
// 获得选中的列号
int column = ui->tableWidget->currentColumn();
// 删除
ui->tableWidget->removeColumn(column);
}
效果:

Tree Widget
使⽤QTreeWidget 表示⼀个树形控件。⾥⾯的每个元素,都是⼀个QTreeWidgetItem ,每个QTreeWidgetItem 可以包含多个⽂本和图标。

QTreeWidget 核心方法:
| 方法 | 说明 |
|---|---|
| clear | 清空所有⼦节点 |
| addTopLevelItem(QTreeWidgetItem* item) | 新增顶层节点 |
| topLevelItem(int index) | 获取指定下标的顶层节点 |
| topLevelItemCount() | 获取顶层节点个数 |
| indexOfTopLevelItem(QTreeWidgetItem* item) | 查询指定节点是顶层节点中的下标 |
| takeTopLevelItem(int index) | 删除指定的顶层节点.返回QTreeWidgetItem*表示被删除的元素 |
| currentItem() | 获取到当前选中的节点,返回QTreeWidgetItem* |
| setCurrentItem(QTreeWidgetItem* item) | 选中指定节点 |
| setExpanded(bool) | 展开/关闭节点 |
| setHeaderLabel(const QString& text) | 设置TreeWidget的header名称. |
QTreeWidget 核⼼信号:
| 信号 | 说明 |
|---|---|
| currentItemChanged(QTreeWidgetItem* current,QTreeWidgetItem* old) | 切换选中元素时触发 |
| itemClicked(QTreeWidgetItem*item,int col) | 点击元素时触发 |
| itemDoubleClicked(QTreeWidgetItem* item,int col) | 双击元素时触发 |
| itemEntered(QTreeWidgetItem* item,int col) | ⿏标进⼊时触发 |
| itemExpanded(QTreeWidgetItem* item) | 元素被展开时触发 |
| itemCollapsend(QTreeWidgetItem* item) | 元素被折叠时触发 |
QTreeWidgetItem核⼼属性:
| 属性 | 说明 |
|---|---|
| text | 持有的⽂本 |
| setText | 设置文本 |
| textAlignment | ⽂本对⻬⽅式 |
| icon | 持有的图表 |
| font | ⽂本字体 |
| hidden | 是否隐藏 |
| disabled | 是否禁⽤ |
| expand | 是否展开 |
| sizeHint | 尺⼨⼤⼩ |
| selected | 是否选中 |
这里注意,setText(int column, const QString &text)column是列号。(是该结点的父节点往右数,第几列,从0开始数的)
QTreeWidgetItem核⼼⽅法:
| ⽅法 | 说明 |
|---|---|
| addChild(QTreeWidgetItem* child) | 新增⼦节点 |
| childCount() | ⼦节点的个数 |
| child(int index) | 获取指定下标的⼦节点.返回QTreeWidgetItem* |
| takeChild(int index) | 删除对应下标的⼦节点 |
| removeChild(QTreeWidgetItem* child) | 删除对应的⼦节点 |
| parent() | 获取该元素的⽗节点 |
界面:

代码:
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
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* item3 = new QTreeWidgetItem();
item3->setText(0,"龙猫");
item1->addChild(item3);
// 增加顶层节点
QTreeWidgetItem* item2 = new QTreeWidgetItem();
item2->setText(0,"鹿");
ui->treeWidget->addTopLevelItem(item2);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_add_top_clicked()
{
// 获取输入文本
const QString& message = ui->lineEdit->text();
QTreeWidgetItem* item = new QTreeWidgetItem();
item->setText(0,message);
ui->treeWidget->addTopLevelItem(item);
}
void Widget::on_pushButton_add_select_clicked()
{
QTreeWidgetItem* Current_item = ui->treeWidget->currentItem();
if(Current_item == NULL){
return;
}
// 获取输入文本
const QString& message = ui->lineEdit->text();
QTreeWidgetItem* item = new QTreeWidgetItem();
item->setText(0,message);
Current_item->addChild(item);
}
void Widget::on_pushButton_delete_clicked()
{
QTreeWidgetItem* current_item = ui->treeWidget->currentItem();
if(current_item == NULL){
return;
}
// 删除选中的元素,需要找到父亲节点
QTreeWidgetItem* item_parent = current_item->parent();
if(item_parent == NULL){
// 头节点
int index = ui->treeWidget->indexOfTopLevelItem(current_item);
ui->treeWidget->takeTopLevelItem(index);
} else {
// 普通节点
item_parent->removeChild(current_item);
}
}
容器类控件
GroupBox 分组框
使⽤QGroupBox 实现⼀个带有标题的分组框。可以把其他的控件放到⾥⾯作为⼀组。这样看起来能更好看⼀点。

核⼼属性:
| 属性 | 说明 |
|---|---|
| title | 分组框的标题 |
| alignment | 分组框内部内容的对⻬⽅式 |
| flat | 是否是"扁平"模式 |
| checkable | 是否可选择. 设为true,则在title前⽅会多出⼀个可勾选的部分. |
| checked | 描述分组框的选择状态(前提是checkable为true) |
界面操作:麦当劳点餐

可以看到,comboBox 和 spinBox 是 groupBox 的子元素:

QTableWidget 标签页
使⽤QTabWidget 实现⼀个带有标签⻚的控件,可以往⾥⾯添加⼀些widget.进⼀步的就可以通过标签⻚来切换。
| 属性 | 说明 |
|---|---|
| tabPosition | 标签⻚所在的位置。 North 上⽅ South 下⽅ West 左侧 East 右侧 |
| currentIndex | 当前选中了第⼏个标签⻚(从0开始计算) |
| currentTabText | 当前选中的标签⻚的⽂本 |
| currentTabName | 当前选中的标签⻚的名字 |
| currentTabIcon | 当前选中的标签⻚的图标 |
| currentTabToolTip | 当前选中的标签⻚的提⽰信息 |
| tabsCloseable | 标签⻚是否可以关闭 |
| movable | 标签⻚是否可以移动 |
| addTab | 创建新的标签页 |
| count | 标签页的个数 |
| removeTab | 删除新的标签页 |
核⼼信号:
| 属性 | 说明 |
|---|---|
| currentChanged(int) | 在标签⻚发⽣切换时触发,参数为被点击的选项卡编号. |
| tabBarClicked(int) | 在点击选项卡的标签条的时候触发.参数为被点击的选项卡编号. |
| tabBarDoubleClicked(int) | 在双击选项卡的标签条的时候触发.参数为被点击的选项卡编号. |
| tabCloseRequest(int) | 在标签⻚关闭时触发.参数为被关闭的选项卡编号. |
代码:创建一个程序,带有一个QTableWidget作为标签页。提供两个按钮,创建于关闭标签页。

cpp
#include "widget.h"
#include "ui_widget.h"
#include <QLabel>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QLabel* label1 = new QLabel(ui->tab);
label1->setText("标签 1");
label1->resize(50,100);
QLabel* label2 = new QLabel(ui->tab_2);
label2->setText("标签 2");
label2->resize(50,100);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
// 添加标签
QWidget* w = new QWidget();
int count = ui->tabWidget->count(); //获得标签页的个数
const QString& name = QString("标签") + QString::number(count + 1);
ui->tabWidget->addTab(w,name);
QLabel* label = new QLabel(w);
label->setText(QString("标签页" + QString::number(count + 1)));
label->resize(50,100);
}
void Widget::on_pushButton_2_clicked()
{
// 获取当前标签页
int index = ui->tabWidget->currentIndex();
// 删除
ui->tabWidget->removeTab(index);
}
布局管理器
之前使⽤Qt在界⾯上创建的控件,都是通过"绝对定位"的⽅式来设定的。也就是每个控件所在的位置,都需要计算坐标,最终通过setGeometry 或者move ⽅式摆放过去。
这就造成了:
- 手动布局的方式非常复杂,不精确。
- 无法对窗口的大小进行自适应。
垂直布局
使⽤QVBoxLayout 表示垂直的布局管理器。V是vertical 的缩写。
核⼼属性:
| 属性 | 说明 |
|---|---|
| layoutLeftMargin | 左侧边距 |
| layoutRightMargin | 右侧边距 |
| layoutTopMargin | 上⽅边距 |
| layoutBottomMargin | 下⽅边距 |
| layoutSpacing | 相邻元素之间的间距 |
代码:创建三个按钮,使用垂直布局管理起来
首先,函数他有"认父"功能:
比如:并没有按钮的显示,这是因为没有设置父节点,通俗来讲QPushButton* button1 = new QPushButton(this);


addWidget
首先:
cpp
layout->addWidget(button1);
// 布局的内部列表中添加了 button1 的记录
// 布局现在"知道"要管理这个按钮
// 但按钮与布局都还不知道自己的父对象是谁
然后:
cpp
this->setLayout(layout); //this(Widget)的布局为layout
// 此时自动发生:
layout->setParent(this); // 布局成为窗口的孩子
// 并且递归设置所有子控件:
// - button1->setParent(this)
// - button2->setParent(this)
// - button3->setParent(this)
递归后:
布局开始真正工作:
- 计算所有控件的位置
- 分配控件的大小
- 响应窗口大小变化
- 管理控件的显示/隐藏
我们有时候会用到:
cpp
window->setLayout(layout);
// window,从现在开始,layout 就是你的布局主管了
最后我们要知道QWidget是显现能力,而QLayout提供布局能力。QLayout不能显现,只能由QWidget提供布局,所以要把按钮放在Widget上。
每个Widget 只能设置一个布局管理器。
如果在代码中创建一个layout,只是创建了一个layout。
如果在QtDesigner 中创建的layout,先创建一个 Widget,然后在这个新的Widget中添加一个layout:


刚才是先拖了 layout 过去,然后再往 layout 中拖其它控件。
也可以先拖其它控件,后给这些控件套上 layout:

水平布局
使⽤QHBoxLayout 表⽰垂直的布局管理器。H是horizontal 的缩写。
核⼼属性(和QVBoxLayout 属性是⼀致的)
代码:

布局管理器之间,也能嵌套:

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

我们可以看到按钮5虽然是(100,100)但和按钮4没有拉的很远,说明此处设置的行数和列数,只是用来决定控件之间的相对位置。
以上创建的布局管理器,控件尺寸都是均等的。
当我们想要创建出尺寸不同的控件时,可以通过拉伸系数来设置(控件之间尺寸的比例)。
函数:
cpp
layout->setColumnStretch(第几列,伸拉系数);
这里设置的比列是1:1:2:

如果伸拉系数为0,则会出现:无论如何拖动,第一列大小不变

如果想调整上下拉伸:发现并没有变化
是因为SizePolicy 起到的影响,按钮垂直方向默认没有拉伸开(水平方向默认是拉伸的),因此垂直方向不会受到拉伸系数的影响。要想让垂直方向的拉伸系数生效,就需要让按钮先能拉伸展开。
SizePolicy是QWidget的属性:
- QSizePolicy::Ignored :忽略控件的尺⼨,不对布局产⽣影响。
- QSizePolicy::Minimum :控件的最⼩尺⼨为固定值,布局时不会超过该值。
- QSizePolicy::Maximum :控件的最⼤尺⼨为固定值,布局时不会⼩于该值。
- QSizePolicy::Preferred :控件的理想尺⼨为固定值,布局时会尽量接近该值。
- QSizePolicy::Expanding :控件的尺⼨可以根据空间调整,尽可能占据更多空间。
- QSizePolicy::Shrinking :控件的尺⼨可以根据空间调整,尽可能缩⼩以适应空间。
设置可拉伸:

表单布局
除了上述的布局管理器之外,Qt还提供了门⽤于实现两列表单的布局。这QFormLayout ,属于是QGridLayout 的特殊情况。(只许N行2列)
这种表单布局多⽤于让⽤⼾填写信息的场景.左侧列为提示,右侧列为输⼊框。
示例:

Spacer
使⽤布局管理器的时候,可能需要在控件之间,添加⼀段空⽩。就可以使⽤QSpacerItem来表示。
比如:默认就用空白

现在想把空白变大:
