目录
[window frame](#window frame)
控件介绍
控件,也称为小组件、小部件,比如:按钮,文本框,下拉框等都是控件,控件可以通过代码Widget(内置了很多控件)来实现,也可以使用以ui的方式实现

早期要想在界面上设计可没有控件这一词,都是要通过调用api接口,把界面当成画板"画"出来的,呈现出现就不太美观;控件则是随着时间的发展前人为了方便后人的开发,一步一步实现出来的,从此我们就直接使用控件来开发不需要自己"造轮子"
enabled
描述一个控件是否处于"可用"状态,如果是"禁用"状态:显示出来的控件呈现灰色,用户无法使用;且继承它的各种控件也会被"禁用"
|------------|-------------------------|
| isEnabled | 获取控件当前状态 |
| setEnabled | 设置控件状态:true为可用,false为禁用 |
案例:ui设计两个按钮,一个正常使用,一个对按钮状态进行切换
cpp
void Widget::on_pushButton_2_clicked()
{
if(ui->pushButton->isEnabled())
ui->pushButton->setEnabled(false);
else
ui->pushButton->setEnabled(true);
}
void Widget::on_pushButton_clicked()
{
qDebug()<<"点击按钮";
}

geometry
获取控件的属性(x,y,width,height),结果需要改变控件的位置或者形状(单位像素)
|-----------------------------------------------|------------------|
| geometry() | 获取控件的属性,类型为QRect |
| setGeometry(QRect rect) | 改变控件的属性 |
| setGeometry(int x,int y,int width,int height) | 只改变一些属性其它保持不变 |
案例:通过四个控制按钮来控制按钮进行上下左右移动
cpp
void Widget::on_pushButton_up_clicked()
{
QRect rect = ui->pushButton->geometry();
qDebug()<<rect;
rect.setY(rect.y()-5);
//修改动作进行设置
ui->pushButton->setGeometry(rect);
}
void Widget::on_pushButton_down_clicked()
{
QRect rect = ui->pushButton->geometry();
qDebug()<<rect;
rect.setY(rect.y()+5);
//修改动作进行设置
ui->pushButton->setGeometry(rect);
}
void Widget::on_pushButton_left_clicked()
{
QRect rect = ui->pushButton->geometry();
qDebug()<<rect;
rect.setX(rect.x()-5);
//修改动作进行设置
ui->pushButton->setGeometry(rect);
}
void Widget::on_pushButton_right_clicked()
{
QRect rect = ui->pushButton->geometry();
qDebug()<<rect;
rect.setX(rect.x()+5);
//修改动作进行设置
ui->pushButton->setGeometry(rect);
}
此时虽然可以达到目标,但按钮的形状也因此发生改变

我们要实现移动的同时不改变形状(按钮平移)可以使用另一个版本的函数来实现
cpp
void Widget::on_pushButton_up_clicked()
{
QRect rect = ui->pushButton->geometry();
qDebug()<<rect;
ui->pushButton->setGeometry(rect.x(),rect.y()-5,
rect.width(),rect.height());
}
void Widget::on_pushButton_down_clicked()
{
QRect rect = ui->pushButton->geometry();
qDebug()<<rect;
ui->pushButton->setGeometry(rect.x(),rect.y()+5,
rect.width(),rect.height());
}
void Widget::on_pushButton_left_clicked()
{
QRect rect = ui->pushButton->geometry();
qDebug()<<rect;
ui->pushButton->setGeometry(rect.x()-5,rect.y(),
rect.width(),rect.height());
}
void Widget::on_pushButton_right_clicked()
{
QRect rect = ui->pushButton->geometry();
qDebug()<<rect;
ui->pushButton->setGeometry(rect.x()+5,rect.y(),
rect.width(),rect.height());
}

案例:按钮选择器
cpp
void Widget::on_pushButton_clicked()
{
ui->label->setText("明天好运连连");
}
//按住按钮后松开之前进行随机平移
void Widget::on_pushButton_2_pressed()
{
int x = rand()%this->x();
int y = rand()%this->y();
int width=ui->pushButton_2->width();
int height=ui->pushButton_2->height();
ui->pushButton_2->setGeometry(x,y,width,height);
}

按钮点击效果

window frame
qt界面形成时会自带系统的窗口框架window fram进行填充,导致是要以窗口框架的(0,0)为参照物还是widget窗口(0,0)作为参照物;为了解决这一问题,qt提供了不同的API对应不同参照物产生的效果,比如 geometry()返回的是以widget窗口(0,0)作为参照物,frameGeometry()则是以winodw fram的(0,0)作为参照物

使用代码进行验证
cpp
//不要再Widget构造里面获取,因为widget构造时,window frame还没有加载出来
void Widget::on_pushButton_clicked()
{
qDebug()<<"no window frame:"<<this->geometry();
qDebug()<<"window frame:"<<this->frameGeometry();
}

windowTitle
设置窗口标题,使用窗口控件widget设置后生效,其它控件像pushbutton虽然也能设置,但是没有生效,因为它只是作为按钮控件来使用的,无法更改窗口标题
|---------------------------|--------|
| windowtitle() | 获取窗口标题 |
| setWindowTitle(QString&) | 设置窗口标题 |
案例:设置窗口widget标题
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("window title");
//不起作用
ui->pushButton->setWindowTitle("按钮标题");
}

windowIcon
设置界面图标,也是只有窗口控件widget才能设置生效
|----------------------|-----------|
| windowIcon() | 返回QIcon对象 |
| setWindowIcon(QIcon) | 设置界面图标 |
创建QIcon对象时要传入设置图标的图片路径;不使用new的方式创建QIcon对象,直接在栈上开辟:设置后即使该对象销毁图标还是可以成功设置,QIcon不支持挂接到对象树上
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QIcon icon("C:/Users/29096/Desktop/qt.png");
this->setWindowIcon(icon);
}

但是上面的问题是:如果该程序开发后是在用户电脑上跑,在用户电脑的该目录上肯定是没有这个图片资源的,难不成还要用户在对应的文件路径自己设置?
为此Qt提供了qrc机制:创建后缀为.qrc文件,把图片资源加载进来;此时构造icon对象的路径就是.qrc文件,程序运行是就要在.qrc文件中找到图片资源后,自动生成的C++代码,使用数组把图片的二进制数据保存起来与程序一同执行,此时生成的图片资源就称为exe程序的一部分
- 先在当前项目新增 Qt Resource file文件

- 在.qrc文件中添加图片文件

- 代码路径指向.qrc文件,使用:表示要使用qrc文件
cpp
QIcon icon(":/qt.png");
this->setWindowIcon(icon);
生成的C++代码文件:qrc_resource.cpp中可以看到确实使用了数组保存图片二进制文件

windowOpacity
数组界面透明度:值范围【0.0,1.0】,值越小越透明
|-------------------------|-----------|
| windowOpacity() | 获取当前界面透明度 |
| setWindowOpacity(float) | 设置透明度 |
案例:通过两个按钮调节界面透明度
cpp
void Widget::on_pushButton_add_clicked()
{
float opacity = this->windowOpacity();
//不加程序也正确,但这是为了代码的严谨性
if(opacity==1.0) return;
opacity+=0.1;
qDebug()<<opacity;
this->setWindowOpacity(opacity);
}
void Widget::on_pushButton_sub_clicked()
{
float opacity = this->windowOpacity();
if(opacity==0.0) return;
opacity-=0.1;
qDebug()<<opacity;
this->setWindowOpacity(opacity);
}
修改 opacity 后的结果

为什么加减0.1不是准确的?
因为浮点数在内存的储存要遵循 IEEE 754的规定(使用科学计数法来表示浮点数)把浮点数分为三部分:符号位,有效数字,指数部分,其中有效数字是使用二进制表示的小数,最小为0.5,想0.1这种数取不到完整的部分就只能去一个接近的数进行浮点数的加减,所以会出现以上情况,像 0.1 + 0.2 == 0.3这样的判断结果为false也是存在的,需要使用作差方式进行判断
cursor
设置界面鼠标移动时产生的光标
|------------------------------------------------|---------------|
| cursor() | 返回空间的cursor对象 |
| setCursor(QCursor& ) | 使用cursor设置光标 |
| QGuiApplication::setOverrideCursor(QCursor& ) | 设置全局光标 |
使用ui进行属性设置cursor

使用代码进行设置,但Qt提供的光标样式有点历史的味道,我们可以传入图片(qrc机制)设置光标变好看
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//使用 pixmap对象接收图片资源
QPixmap pixmap(":/qt.png");
//图片光标的哪个位置进行点击时生效
pixmap.scaled(10,10);
//后面参数(热点)用来设置图片样式
QCursor cursor(pixmap,50,50);
ui->pushButton->setCursor(cursor);
}
font
设置控件字体
|-----------------|--------------|
| font() | 返回控件 QFont对象 |
| setFont(font&) | 设置控件字体 |
属性 | 说明 |
---|---|
setFamily(QString&) | 设置字体样式,如"仿宋","微软雅黑" |
setPointSize(int) | 设置字体大小 |
setWeight(int) | 字体粗细程度,范围【0,99】 |
setBold(bool) | 是否加粗 |
setItalic(bool) | 是否倾斜 |
setUnderline(bool) | 是否带有下划线 |
setStrikeOut(bool) | 是否带有删除线 |
案例:设置按钮字体样式
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QFont font;
font.setFamily("仿宋");
font.setPointSize(10);
font.setWeight(10);
font.setBold(true);
font.setItalic(true);
font.setUnderline(true);
font.setStrikeOut(true);
ui->pushButton->setText("这是一个按钮");
ui->pushButton->setFont(font);
}

也可以使用ui来进行设置

toolTip
光标指导某个控件时,有悬空提示词的效果
|--------------------|---------|
| setToolTip() | 设置提示词对象 |
| setToolTipDuring() | 提示词提示时间 |
案例:添加按钮提示词3秒
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->pushButton->setToolTip("这是一个按钮");
ui->pushButton->setToolTipDuration(3000);//单位是毫秒
}

focusPolicy
设置控件获取到焦点的策略,⽐如某个控件能否⽤⿏标选中或者能否通过 tab 键选中
|----------------------------------------|---------------------|
| focusPolicy() | 获取控件的 FocusPolicy对象 |
| setFocusPolicy(Qt::FocusPolicy policy) | 设置控件的焦点 |
Qt::FocusPolicy 是一个枚举类型
- Qt::NoFocus :控件不会接收键盘焦点
- Qt::TabFocus :控件可以通过Tab键接收焦点
- Qt::ClickFocus :控件在⿏标点击时接收焦点
- Qt::StrongFocus :控件可以通过Tab键和⿏标点击接收焦点
- Qt::WheelFocus : 类似于 Qt::StrongFocus ,同时控件也通过⿏标滚轮获取到焦点
案例:设置三个行输入框为不同焦点
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//只能通过 tab 获取焦点进行输入
ui->lineEdit->setFocusPolicy(Qt::FocusPolicy::TabFocus);
//只能通过鼠标点击获取焦点进行输入
ui->lineEdit_2->setFocusPolicy(Qt::FocusPolicy::ClickFocus);
//都可以
ui->lineEdit_3->setFocusPolicy(Qt::FocusPolicy::StrongFocus);
}
也可以通过ui的方式设置

styleSheet
Qt搞了一套与CSS(层叠样式表)类似的QSS来对控件设置样式("打扮")
案例:实现两个按钮:一个按下变成夜间模式(背景黑文字白),一个按下变成白天模式(背景白文字黑)
cpp
void Widget::on_pushButton_day_clicked()
{
//界面窗口
this->setStyleSheet("background: white;");
//输入框窗口
ui->lineEdit->setStyleSheet("background: white; color: black;");
//按钮
ui->pushButton_day->setStyleSheet("background: white; color: black;");
ui->pushButton_night->setStyleSheet("background: white; color: black;");
}
void Widget::on_pushButton_night_clicked()
{
//界面窗口
this->setStyleSheet("background: black;");
//输入框窗口
ui->lineEdit->setStyleSheet("background: blank; color: white;");
//按钮
ui->pushButton_day->setStyleSheet("background: blank; color: white;");
ui->pushButton_night->setStyleSheet("background: blank; color: white;");
}


但你可以发现上面的白天模式与程序启动时的窗口颜色不太对

怎么进行调整使得与上面效果一致?
涉及计算机表示颜色表示:使用 rgb(red green blue)的数值来表示,范围【0,255】,电脑上不同的颜色通过不同的比例调制出来的;使用数值表示(设置)颜色:如rgb(255,0,0)表示的是纯红色;那如何知道程序启动时窗口的颜色比例? 可能使用qq自带的截图工具来获取

重新进行白天模式代码调整
cpp
//界面窗口
this->setStyleSheet("background: rgb(240,240,240);"); //也可以使用十六进制:#F0F0F0
按钮类控件
QPushBotton
常见的点击按钮
|------------|----------------------|
| text | 按钮中的⽂本 |
| icon | 按钮中的图标 |
| iconSize | 按钮中图标的尺寸 |
| shortCut | 按钮对应的快捷键 |
| autoRepeat | 按钮是否可以重复触发(单击和连击的效果) |
按钮图标
案例:设置按钮图标:与设置窗口图标类似:创建 QIcon 对象,调用 SetIcon 函数
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置图标
QIcon con(":/qt.png");
ui->pushButton->setIcon(con);
//设置大小
QSize size(50,50);
ui->pushButton->setIconSize(size);
}
按钮移动
案例:通过四个按钮的图标,控制目标按钮上下左右方向移动
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->pushButton_target->setIcon(QIcon(":/image/qt.png"));
ui->pushButton_target->setIconSize(QSize(50,50));
ui->pushButton_up->setIcon(QIcon(":/image/up.png"));
ui->pushButton_up->setIconSize(QSize(38,38));
ui->pushButton_up->setStyleSheet("background: rgb(231,231,231);");
ui->pushButton_down->setIcon(QIcon(":/image/down.png"));
ui->pushButton_down->setIconSize(QSize(38,38));
ui->pushButton_down->setStyleSheet("background: rgb(231,231,231);");
ui->pushButton_left->setIcon(QIcon(":/image/left.png"));
ui->pushButton_left->setIconSize(QSize(38,38));
ui->pushButton_left->setStyleSheet("background: rgb(231,231,231);");
ui->pushButton_right->setIcon(QIcon(":/image/right.png"));
ui->pushButton_right->setIconSize(QSize(38,38));
ui->pushButton_right->setStyleSheet("background: rgb(231,231,231);");
}
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("w"));
ui->pushButton_up->setShortcut(QKeySequence(Qt::Key_W));
//支持设置组合键
ui->pushButton_down->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
ui->pushButton_left->setShortcut(QKeySequence(Qt::Key_A));
ui->pushButton_right->setShortcut(QKeySequence(Qt::Key_D));
//设置鼠标连点效果
ui->pushButton_up->setAutoRepeat(true);
ui->pushButton_down->setAutoRepeat(true);
ui->pushButton_left->setAutoRepeat(true);
ui->pushButton_right->setAutoRepeat(true);
QRadioBotton
单选按钮
|---------------|------------------------------------------------|
| checkable | 是否能选中 |
| checked | 是否已经被选中 |
| autoExclusive | 是否排他(RadioButton的默认选项) 排他:选中⼀个按钮之后是否会取消其他按钮的选中 |
案例:性别选择器
cpp
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("你的性别:其它");
}

增加需求
- 默认选择第一个选项
- 禁用其它选项
cpp
ui->radioButton_male->setChecked(true);
ui->label->setText("你的性别:男");
//虽然不能选,但是可能通过点击信号修改label内容,不靠谱
//ui->radioButton_other->setCheckable(false);
ui->radioButton_other->setEnabled(false);
单选按钮的信号选择有多种

特别注意clicked(bool) 与 toggled(bool)的区别:clicked的bool为true代表点击了按钮(一直点击一直生效,bool为false代表取消选择了按钮),toggled的bool代表是否选择了按钮(第一次生效,选择后再次点击就不生效;bool为false也是代码取消选择了按钮)
案例:点餐选择器
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QButtonGroup *g1 = new QButtonGroup(this);
QButtonGroup *g2 = new QButtonGroup(this);
QButtonGroup *g3 = new QButtonGroup(this);
//对选项进行分组
g1->addButton(ui->radioButton);
g1->addButton(ui->radioButton_2);
g1->addButton(ui->radioButton_3);
g2->addButton(ui->radioButton_4);
g2->addButton(ui->radioButton_5);
g2->addButton(ui->radioButton_6);
g3->addButton(ui->radioButton_7);
g3->addButton(ui->radioButton_8);
g3->addButton(ui->radioButton_9);
}

QCheckBox
复选按钮,与单选按钮类似,只不过一次可以选择多个
案例:每日选择器(选择两件事以上)
cpp
void Widget::on_pushButton_clicked()
{
QString result("今天要做的事情:");
if(ui->checkBox_study->isChecked())
result+=ui->checkBox_study->text()+" ";
if(ui->checkBox_work->isChecked())
result+=ui->checkBox_work->text()+" ";
if(ui->checkBox_watch->isChecked())
result+=ui->checkBox_watch->text()+" ";
ui->label->setText(result);
}

显示类控件
QLabel
显示文本和图片
文本格式
|------------|--------|
| textFormat | 设置文本格式 |
- Qt::PlainText 纯⽂本
- Qt::RichText 富⽂本(⽀持 html 标签)
- Qt::MarkdownText markdown 格式
- Qt::AutoText 根据⽂本内容⾃动决定⽂本格式
案例:创建三种不同的文本显示
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setTextFormat(Qt::PlainText);
ui->label->setText("<a>#这是一个纯文本</a>");
ui->label_2->setTextFormat(Qt::RichText);
ui->label_2->setText("<b>这是一个富文本</b>");
ui->label_3->setTextFormat(Qt::MarkdownText);
ui->label_3->setText("# 这是一个 markdown 文本");
}

图片格式
|----------------|----------------------------|
| pixmap | 使用 QPixMap 对象给 QLabel 设置图片 |
| scaledContents | 图片是否可以拉伸 |
案例:给 label 设置图片,并且将窗口给占满
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置label大小与窗口一样大
QRect rect=this->rect();
ui->label->setGeometry(0,0,rect.width(),rect.height());
//设置图片
QPixmap map(":/image/qt.png");
ui->label->setPixmap(map);
//图片尺寸如果过小,允许拉伸可以铺满窗口
ui->label->setScaledContents(true);
}

但如果此时拉伸窗口的话,图片并没有跟着改变

如果要想实现拉伸窗口的同时图片也跟着改,就需要再 Widget 中重写 QWidget 的 resize (事件)的函数:此后拉伸时就会自动调用该函数,执行 Widget 重写的 resize 函数
cpp
void Widget::resizeEvent(QResizeEvent *event)
{
// 获取当前Widget的坐标
QSize s = event->size();
ui->label->setGeometry(0,0,s.width(),s.height());
}

文本效果
|-----------|-----------------------------|
| alignment | 对⻬⽅式 可以设置⽔平和垂直⽅向对齐,也就是居中对齐 |
| wordWrap | 是否自动换行 |
| indent | 文本自动缩进的大小 |
| margin | 内部⽂本和边框之间的边距(文本上下左右四个方向都生效) |
案例:设置不同的文本效果
先设置 label 的边框,待会好观察现象

cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置文本居中
ui->label_2->setText("这是一段文本");
ui->label_2->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
//设置自动换行
ui->label_3->setText("这是很长的文本这是很长的文本这是很长的文本这是很长的文本");
ui->label_3->setWordWrap(true);
//设置自动缩进
ui->label_4->setText("这是一段文本");
ui->label_4->setIndent(30);
//设置边距
ui->label_5->setText("这是一段文本");
ui->label_5->setMargin(100);
}

绑定伙伴
|-------|---------------------------------------|
| buddy | 给 QLabel 关联⼀个 "伙伴" ,点击QLabel就能激活对应的伙伴 |
案例:绑定单选按钮,让它根据 QLabel 设置的快捷键进行跳转(设置快捷键:&键盘字母)
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置的快捷键要加上alt才能触发
ui->label->setBuddy(ui->radioButton);
ui->label_2->setBuddy(ui->radioButton_2);
}

QLCDNumber
显示数字
倒计时
|----------|----------------|
| intValue | 显示的数字值(int) |
| value | 显示的数字值(double) |
| display | 设置数字值 |
案例:设置一个从10到0的倒计时,在界面上显示(使用LCD number实现)
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->lcdNumber->display(10);
QTimer* timer = new QTimer(this);
//提前设置timeout信号的捕捉
connect(timer,&QTimer::timeout,this,&Widget::handler);
//每一秒收到一次timeout信号
timer->start(1000);
}
void Widget::handler()
{
int val=ui->lcdNumber->value();
if(val>0)
{
val--;
}
ui->lcdNumber->display(val);
}
//或者:值为0时,关闭timer发送timeout信号
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->lcdNumber->display(10);
timer = new QTimer(this);
//提前设置timeout信号的捕捉
connect(timer,&QTimer::timeout,this,&Widget::handler);
//每一秒收到一次timeout信号
timer->start(1000);
}
void Widget::handler()
{
int val=ui->lcdNumber->value();
if(val<=0)
{
timer->stop();
return;
}
ui->lcdNumber->display(val-1);
}

直接在 Widget 构造中使用循环while来实现倒计时行吗?
Qt 要先创建 Widget 对象后执行show 函数才能显示界面给用户,其中就需要先完成构造,所以上述方法无法成功实现;使用线程可以吗? 同样也是不行,Qt界面内置了很多隐藏状态,使用线程修改有线程安全问题,Qt禁止你创建线程修改,必须使用主线程来实现
QProgressBar
显示进度条
进度条增长
案例:使用一个进度条随着时间的增加则增长
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->progressBar->setValue(0);
timer = new QTimer(this);
//提前设置timeout信号的捕捉
connect(timer,&QTimer::timeout,this,&Widget::handler);
timer->start(100);
}
void Widget::handler()
{
int val=ui->progressBar->value();
if(val>=100)
{
timer->stop();
return;
}
ui->progressBar->setValue(val+1);
}

前面在 Widget.h 定义 QTimer* timer使用时,即使没包含QTimer头文件,编译也是可以通过的,为什么不会出现未定义的错误?
只要是因为在头文件 QWidget 中进行很多的类声明了,比如 class QTimer

这样做可以让定义时暂时通过,在 Widget.cpp 使用时才包含头文件 QTimer,调用内部实现各种函数,为什么Qt要怎么做?
主要是为了解决多个头文件包含导致编译速度慢的问题,编译时如果代码包含头文件在预处理阶段要把该头文件上的代码进行展开,如果头文件比较大非常影响编译速度;但实际开发时不要因为头文件的影响就尽量不包含头文件,与其通过这种方式提供编译速度,不如提高编译硬件资源来大幅度提高编译速度更实在点
如果觉得绿色不好看,我们也可以修改进度条的颜色

QCalendarWidget
显示日历
显示选中的日期
|------------|---------|
| selectDate | 当前选中的日期 |
|---------------------------------|-----------------|
| selectionChanged(const QDate&) | 选中的日期发生改变时发出该信号 |
案例:选择日期在标签上显示
cpp
void Widget::on_calendarWidget_selectionChanged()
{
QDate date = ui->calendarWidget->selectedDate();
ui->label->setText(date.toString());
}

输入类控件
QLineEdit
单行输入框
收集数据
|--------------------|----------------------------------------------------------------------|
| text | 输入框中的文本 |
| inputMask | 输入框内容格式约束 |
| placeHolderText | 输入框为空时可以设置提示信息 |
| clearButtonEnabled | 是否出现清除按钮 |
| echoMode | 显示方式 QLineEdit::Password 输入的字符使用其它字符代替 QLineEdit::NoEcho 输入的字符不会进行显示 |
案例:注册时收集用户账号的姓名,密码,电话,性别
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->lineEdit->setPlaceholderText("输入姓名");
ui->lineEdit->setClearButtonEnabled(true);
ui->lineEdit_2->setPlaceholderText("输入密码");
ui->lineEdit_2->setClearButtonEnabled(true);
//开启密码保护
ui->lineEdit_2->setEchoMode(QLineEdit::Password);
ui->lineEdit_3->setPlaceholderText("输入电话");
ui->lineEdit_3->setClearButtonEnabled(true);
//设置电话输入格式
ui->lineEdit_3->setInputMask("XXX-XXXX-XXXX");
}
void Widget::on_pushButton_clicked()
{
QString tmp = ui->radioButton_male->isChecked() ? "男" : "女";
qDebug()<<"性别:"<<ui->lineEdit->text()
<<"密码:"<<ui->lineEdit_2->text()
<<"电话:"<<ui->lineEdit_3->text()
<<"性别:"<<tmp;
}

验证数据
以上验证电话格式不是很严格,可以使用正则表达式检查得严格些
|-----------|----------------------------------|
| validator | 设置验证器,比如正则表达式验证器 RegEspValidator |
|----------------------|-----------------------------------------------------------|
| textChanged(QString) | 当QLineEdit中的文本改变时,发出此信号,QString是新的文本 代码对⽂本的修改能够触发这个信号 |
| textEdited(QString) | 当QLineEdit中的⽂本改变时,发出此信号,QString是新的⽂本 代码对⽂本的修改不能触发这个信号 |
案例:使用正则表达式检查电话格式是否符合11位
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->pushButton->setEnabled(false);
// 创建正则表达式对象并对号码进行规定
QRegExp reg("^1\\d{10}$");
// 设置正则表达式的验证器
ui->lineEdit->setValidator(new QRegExpValidator(reg));
}
void Widget::on_lineEdit_textChanged(const QString &text)
{
QString content = text;
int pos = 0;
// 通过volidate方法判断当前输入框text的字符串是否符合要求
if(ui->lineEdit->validator()->validate(content,pos)==QValidator::Acceptable)
ui->pushButton->setEnabled(true);
else
ui->pushButton->setEnabled(false);
}
密码是否一致
案例:输入两次密码,通过文本显示判断当前输入是否一致
cpp
void Widget::compare()
{
QString s1 = ui->lineEdit->text();
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)
{
//arg1不用有警告,这里是要去警告
(void)arg1;
this->compare();
}
void Widget::on_lineEdit_2_textEdited(const QString &arg2)
{
(void)arg2;
this->compare();
}
密码格式
案例:通过密码格式按钮,转换当前密码格式
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->lineEdit->setEchoMode(QLineEdit::Password);
}
void Widget::on_radioButton_toggled(bool checked)
{
if(checked)
ui->lineEdit->setEchoMode(QLineEdit::Normal);
else
ui->lineEdit->setEchoMode(QLineEdit::Password);
}
QTextEdit
多行输入框,也是⼀个富⽂本 && markdown编辑器
同步
|-------------------------|------------------------------------------------------------|
| toPlainTex | 获取 TextEdit 的文本 |
| VerticalScrollBarPolicy | 设置右边滚动框 Qt::ScrollBarAlwaysOn 开启 Qt::ScrollBarAlwaysOff 关闭 |
|-------------|-----------------------|
| textChanged | TextEdit 输入文本就会触发这个信号 |
案例:在多行输入框输入的文本,同步到 label 中
cpp
void Widget::on_textEdit_textChanged()
{
QString content = ui->textEdit->toPlainText();
//开启右边的下拉框
ui->textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
ui->label->setText(content);
}

认识其它信号
|----------------------------|-----------------------------|
| currentCharFormatChanged() | 文本格式发生改变时触发 |
| selectionChanged() | 选中范围改变时触发 |
| cursorPositionChanged() | 光标移动时触发 |
| undoAvailable(bool) | 可以进行 undo操作(ctrl + z 回退)时触发 |
| redoAvailable(bool) | 可以进行 redo操作(ctrl + y 前进)时触发 |
| copyAvaiable(bool) | ⽂本被选中/取消选中时触发 |
cpp
void Widget::on_textEdit_cursorPositionChanged()
{
QTextCursor curosr = ui->textEdit->textCursor();
// 获取当前的位置
qDebug()<<"cursorPosition:"<<curosr.position();
}
void Widget::on_textEdit_selectionChanged()
{
QTextCursor curosr = ui->textEdit->textCursor();
// 获取选择的文本
qDebug()<<"selectionChanged:"<<curosr.selectedText();
}
void Widget::on_textEdit_redoAvailable(bool b)
{
qDebug()<<"redoAvailable:"<<b;
}
void Widget::on_textEdit_undoAvailable(bool b)
{
qDebug()<<"undoAvailable:"<<b;
}
QComBox
下拉框
点餐系统
|-------------|----------|
| addItem | 往下拉框添加信息 |
| currentText | 当前选中的⽂本 |
案例:点餐系统
cpp
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->comboBox_3->addItem("美年达");
}
void Widget::on_pushButton_clicked()
{
ui->label_target->setText("您选择的是:"+ui->comboBox->currentText()+
" " + ui->comboBox_2->currentText() +
" " + ui->comboBox_3->currentText());
}

还可以使用 ui 设置文本

除了自己设置文本,还可以从文件中读取
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
std::ifstream in("C:/Users/29096/Desktop/config.text");
if(!in.is_open()) exit(1);
std::string content;
while(std::getline(in,content))
{
// string -> QString
ui->comboBox->addItem(QString::fromStdString(content));
}
in.close();
}

QSpinBox
微调框
|-------|----------|
| value | 存储的数值 |
| range | 限定微调框的范围 |
购物车
案例:选择商品后,可以调整需要的个数
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->comboBox->addItem("纸巾");
ui->comboBox->addItem("毛巾");
ui->comboBox->addItem("药品");
ui->spinBox->setRange(1,10);
}
void Widget::on_pushButton_clicked()
{
QString content = ui->label->text() + ui->comboBox->currentText()
+ " 共" + QString::number(ui->spinBox->value()) + "个";
ui->label->setText(content);
}

QDateTimeEdit
时间日期微调器
|----------|----------------------------|
| dateTime | 时间⽇期的值,形如 2000/1/1 0:00:00 |
日期计算器
案例:选择两个日期后,按下按钮得到二者相差的时间
cpp
void Widget::on_pushButton_clicked()
{
// 获取日期
QDateTime timeOld = ui->dateTimeEdit->dateTime();
QDateTime timeNew = ui->dateTimeEdit_2->dateTime();
// 进行计算
int days = timeOld.daysTo(timeNew);
// 不超过24小时
int hours = (timeOld.secsTo(timeNew) / 3600) % 24;
// 格式化结果
QString content = QString("日期相差: %1 天 零 %2 小时").arg(days).arg(hours);
ui->label->setText(content);
}

上面计算结果明明是0天却被多算了1天,原因在与数据天数的函数不准确,需要手动来计算
cpp
int seconds = timeOld.secsTo(timeNew);
int days = (seconds / 3600) / 24;
int hours = (seconds / 3600) % 24;

QDial
旋钮
透明度调节器
|----------------|----------|
| range | 旋钮值范围 |
| notchesVisible | 是否显示刻度线 |
| sliderPosition | 调整初始值 |
| wrapping | 是否允许循环调整 |
|-------------------|------------|
| valueChanged(int) | 数值改变时触发该信号 |
案例:使用旋钮调节窗口透明度
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->dial->setRange(0,100);
ui->dial->setNotchesVisible(true);
ui->dial->setSliderPosition(100);
ui->dial->setWrapping(true);
}
void Widget::on_dial_valueChanged(int value)
{
// 范围 [0,1]
this->setWindowOpacity((double)value / 100);
}
QSlider
滑动条
窗口调节器
|----------------|------------|
| value | 持有的数值 |
| sliderPosition | 滑动条显示的初始位置 |
| range | 数值范围 |
| singleStep | 移动的步长 |
|-------------------|---------|
| valueChanged(int) | 数值改变时触发 |
案例:使用水平垂直两个滑动条调节窗口大小
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->horizontalSlider->setRange(100,10000);
ui->horizontalSlider->setSingleStep(100);
ui->horizontalSlider->setValue(800);
ui->verticalSlider->setRange(100,10000);
ui->verticalSlider->setSingleStep(100);
ui->verticalSlider->setValue(600);
}
void Widget::on_horizontalSlider_valueChanged(int value)
{
QRect rect = this->geometry();
this->setGeometry(rect.x(),rect.y(),value,rect.height());
}
void Widget::on_verticalSlider_valueChanged(int value)
{
QRect rect = this->geometry();
this->setGeometry(rect.x(),rect.y(),rect.width(),value);
}
自定义快捷键
案例:使用自定义快捷键(+ 增加 - 减小)调整滑动条大小
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->horizontalSlider->setRange(0,10000);
ui->horizontalSlider->setSingleStep(100);
ui->horizontalSlider->setSliderPosition(0);
// 创建快捷键对象后初始化
QShortcut* plus = new QShortcut(this);
QShortcut* sub = new QShortcut(this);
plus->setKey(Qt::Key_Plus);
sub->setKey(Qt::Key_Minus);
// 快捷键被按下的信号进行绑定
connect(plus,&QShortcut::activated,this,&Widget::slider_add);
connect(sub,&QShortcut::activated,this,&Widget::slider_sub);
}
void Widget::slider_add()
{
int val = ui->horizontalSlider->value();
if(val >= ui->horizontalSlider->maximum())
return;
val +=100;
ui->horizontalSlider->setValue(val);
ui->label->setText("当前值: " + QString::number(val));
}
void Widget::slider_sub()
{
int val = ui->horizontalSlider->value();
if(val <= ui->horizontalSlider->minimum())
return;
val -=100;
ui->horizontalSlider->setValue(val);
ui->label->setText("当前值: " + QString::number(val));
}

多元素控件
多元素控件一般是成对存在:xxWidget 和 xxView,它们之间的区别
- xxView 是基于 MVC 设置的控件,只负责视图 View,不储存数据 Model,也不负责数据与视图的业务逻辑 Controller,需要使用者自己设计,虽然不方便,但是上限高
- xxWidget 则是对 xxView 的封装,实现了 xxView 不做的事情,通过 api 进行调用,虽然方便,但下限低,有些场景无法实现就需要使用 xxView 使用者实现
QListWidget
显示纵行的列表
新增与删除
|------------|------------|
| currentRow | 当前被选中的是第几行 |
|-----------------------------------------------------|-----------------------------------|
| addItem(const QString&) addItem(QListWidgetItem*) | 列表中添加元素 QListWidgetItem 可以对元素进行设置 |
| setCurrentRow(int) | 设置当前选择的行号 |
| insertItem(const QString&,int ) | 在指定的位置插⼊元素 |
| takeItem(int) | 删除指定行的元素 |
案例:标签上的用户输入的文本进行(使用按钮)新增到列表中,选择列表行号进行删除
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->lineEdit->setPlaceholderText("请输入编程语言");
ui->listWidget->addItem("C++");
ui->listWidget->addItem(new QListWidgetItem("Java"));
}
void Widget::on_pushButton_insert_clicked()
{
QString content = ui->lineEdit->text();
if(content.isEmpty()) return;
ui->lineEdit->clear();
ui->listWidget->addItem(content);
}
void Widget::on_pushButton_delete_clicked()
{
int row = ui->listWidget->currentRow();
ui->listWidget->takeItem(row);
}

QTableWidget
表格
|--------------------------------------------|----------------|
| setItem(int row,int column,QTableWidget*) | 根据行数列数设置表格中的元素 |
| currentItem | 返回被选中的元素 |
| currentRow | 返回被选中元素是第几行 |
| currentColumn | 返回被选中元素是第几列 |
| rowCount | 获取行数 |
| columnCount | 获取列数 |
|----------------------------------------------------|-----------------|
| insertRow(int row) | 在第 row 行处插⼊新行 |
| insertColumn(int column) | 在第 column 列插⼊新列 |
| removeRow(int row) | 删除第 row 行 |
| removeColumn(int column) | 删除第 column 列 |
| setHorizontalHeaderItem(int column,QTableWidget*) | 设置指定列的表头 |
| setVerticalHeaderItem(int row,QTableWidget*) | 设置指定行的表头 |
案例:设置四个按钮分别进行新增行,新增列(通过输入框内容设置表头),删除行,删除列
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->tableWidget->insertColumn(0);
ui->tableWidget->insertColumn(1);
ui->tableWidget->insertColumn(2);
ui->tableWidget->setHorizontalHeaderItem(0,new QTableWidgetItem("班级"));
ui->tableWidget->setHorizontalHeaderItem(1,new QTableWidgetItem("姓名"));
ui->tableWidget->setHorizontalHeaderItem(2,new QTableWidgetItem("年龄"));
ui->tableWidget->insertRow(0);
ui->tableWidget->setItem(0,0,new QTableWidgetItem("G107"));
ui->tableWidget->setItem(0,1,new QTableWidgetItem("张三"));
ui->tableWidget->setItem(0,2,new QTableWidgetItem("17"));
}
void Widget::on_pushButton_clicked()
{
int rowCount = ui->tableWidget->rowCount();
ui->tableWidget->insertRow(rowCount);
}
void Widget::on_pushButton_2_clicked()
{
int row = ui->tableWidget->currentRow();
ui->tableWidget->removeRow(row);
}
void Widget::on_pushButton_3_clicked()
{
int columnCount = ui->tableWidget->columnCount();
ui->tableWidget->insertColumn(columnCount);
//设置列表头
QString content = ui->lineEdit->text();
ui->tableWidget->setHorizontalHeaderItem(columnCount,new QTableWidgetItem(content));
}
void Widget::on_pushButton_4_clicked()
{
int column = ui->tableWidget->currentColumn();
ui->tableWidget->removeColumn(column);
}

QTreeWidget
树形控件,类似思维导图;可以给 QTreeWidget 设置顶层节点(顶层节点可以有多个),然后再给顶层节点添加子节点,从⽽构成树形结构;注意:在 qt 中设置顶层节点 QTreeWidget 的方法与其它节点 QTreeWidgetItem 提供的方法是不同的
QTreeWidget 方法
|---------------------------------------------|------------------------------------------|
| 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 方法
|--------------------------------------|---------------------------------|
| addChild(QTreeWidgetItem* child) | 新增⼦节点 |
| childCount() | ⼦节点的个数 |
| child(int index) | 获取指定下标的子节点,返回 QTreeWidgetItem* |
| takeChild(int index) | 删除对应下标的子节点 |
| removeChild(QTreeWidgetItem* child) | 删除对应的子节点 |
| parent() | 获取该元素的父节点 |
案例:根据按钮完成对树状图的修改
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置跟节点标题
ui->treeWidget->setHeaderLabel("编程语言");
QTreeWidgetItem* item1 = new QTreeWidgetItem;
item1->setText(0,"C语言");
ui->treeWidget->addTopLevelItem(item1);
QTreeWidgetItem* item2 = new QTreeWidgetItem;
item2->setText(0,"C++");
ui->treeWidget->addTopLevelItem(item2);
QTreeWidgetItem* item3 = new QTreeWidgetItem;
item3->setText(0,"Java");
ui->treeWidget->addTopLevelItem(item3);
}
void Widget::on_pushButton_add_top_clicked()
{
QString text = ui->lineEdit->text();
QTreeWidgetItem* item = new QTreeWidgetItem;
item->setText(0,text);
ui->treeWidget->addTopLevelItem(item);
}
void Widget::on_pushButton_insert_clicked()
{
QTreeWidgetItem* item = ui->treeWidget->currentItem();
QString text = ui->lineEdit->text();
QTreeWidgetItem* item1 = new QTreeWidgetItem;
item1->setText(0,text);
item->addChild(item1);
}
void Widget::on_pushButton_delete_clicked()
{
QTreeWidgetItem* item = ui->treeWidget->currentItem();
QTreeWidgetItem* parent = item->parent();
if(parent==nullptr)
{
//删除的节点是顶层节点
int index = ui->treeWidget->indexOfTopLevelItem(item);
ui->treeWidget->takeTopLevelItem(index);
}
else
{
int index = parent->indexOfChild(item);
parent->takeChild(index);
}
}

以上多元素控件如果程序结束后再重启,之前所执行的各种操作所产生的数据就都不见,因为是内存级数据,如果要想在程序结束之前把数据保留,就可以通过以文件的形式写到文件中,后续启动之前就从文件读即可恢复出来
容器类控件
容器类控件可以使用嵌套以上控件进行组合使用
QGroupBox
核心属性
|-----------|--------------------------------------|
| title | 分组框的标题 |
| alignment | 分组框内部内容的对⻬⽅式 |
| flat | 是否是 "扁平" 模式 |
| checkable | 是否可选择 设为 true,则在 title 前⽅会多出⼀个可勾选的部分 |
| checked | 描述分组框的选择状态 (前提是 checkable 为 true) |
案例:点餐系统(使用 ui 完成)

QTabWidget
带有多个标签页的控件,可以进行切换显示出不同的标签页
核心属性
|-------------------|----------------------------------------------------|
| tabPosition | 标签⻚所在的位置 • North 上⽅ • South 下⽅ • West 左侧 • East 右侧 |
| currentIndex | 当前选中了第⼏个标签⻚(从 0开始计算) |
| currentTabText | 当前选中的标签⻚的⽂本 |
| currentTabName | 当前选中的标签⻚的标题 |
| currentTabIcon | 当前选中的标签⻚的图标 |
| currentTabToolTip | 当前选中的标签⻚的提⽰信息 |
| tabsCloseable | 标签⻚是否可以关闭 |
| movable | 标签⻚是否可以移动 |
核心信号
|--------------------------|------------------------------|
| currentChanged(int) | 在标签⻚发⽣切换时触发,参数为被点击的选项卡编号 |
| tabBarClicked(int) | 在点击选项卡的标签条的时候触发,参数为被点击的选项卡编号 |
| tabBarDoubleClicked(int) | 在双击选项卡的标签条的时候触发,参数为被点击的选项卡编号 |
| tabCloseRequest(int) | 在标签⻚关闭时触发,参数为被关闭的选项卡编号 |
案例:设置两个按钮来分别进行新增标签页和删除标签页
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//为 TablWidget 设置 label 控件,注意要指定tab为父节点
QLabel* lab1 = new QLabel(ui->tab);
lab1->setText("标签页 1");
QLabel* lab2 = new QLabel(ui->tab_2);
lab2->setText("标签页 1");
}
void Widget::on_pushButton_clicked()
{
// 使用 QWidget 创建标签页
QWidget* tab = new QWidget;
int cnt = ui->tabWidget->count();
ui->tabWidget->addTab(tab,"tab_" + QString::number(cnt + 1));
QLabel* lab = new QLabel(tab);
lab->setText("标签页 " + QString::number(cnt + 1));
// 添加后定位到添加的标签页
ui->tabWidget->setCurrentWidget(tab);
}
void Widget::on_pushButton_2_clicked()
{
int index = ui->tabWidget->currentIndex();
ui->tabWidget->removeTab(index);
}

布局管理器
之前设计的控件中,位置 ,大小都是我们自己"手动"进行设置的,每次进行修改需要一点点调大小才能设计出较美观的设计,这是不怎么"科学"的,为了解决 Qt 引入了布局管理器机制
QVBoxLayout
垂直布局管理器
核心属性
|--------------------|-----------|
| layoutLeftMargin | 左侧边距 |
| layoutRightMargin | 右侧边距 |
| layoutTopMargin | 上⽅边距 |
| layoutBottomMargin | 下⽅边距 |
| layoutSpacing | 相邻元素之间的间距 |
案例:将三个按钮设置进 QVBoxLayout 中
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 此时就是 this 作为父节点
QPushButton* but1 = new QPushButton("按钮1");
QPushButton* but2 = new QPushButton("按钮2");
QPushButton* but3 = new QPushButton("按钮3");
QVBoxLayout* lay = new QVBoxLayout(this);
lay->addWidget(but1);
lay->addWidget(but2);
lay->addWidget(but3);
}
可以随着窗口大小进行调整

使用 ui 进行操作可以一次设计出多个 QVBoxLayout 出来,但是不能随着窗口变化

为什么不能随着窗口变化而变化?
因为通过 ui 创建出来的 QVBoxLayout 控件,已经提前创建出一个 QWidget 作为父节点管理者 QVBoxLayout,这也是能创建出多个 QVBoxLayout 的原因

QHBoxLayout
水平布局管理器
案例:垂直布局中嵌套水平布局
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* but1 = new QPushButton("按钮1");
QPushButton* but2 = new QPushButton("按钮2");
// 不加 this 进行挂接就要进行下面的 setLayout
QHBoxLayout* h_lay = new QHBoxLayout();
h_lay->addWidget(but1);
h_lay->addWidget(but2);
//this->setLayout(h_lay);
QPushButton* but3 = new QPushButton("按钮3");
QPushButton* but4 = new QPushButton("按钮4");
QVBoxLayout* v_lay = new QVBoxLayout(this);
v_lay->addWidget(but3);
v_lay->addWidget(but4);
// 使用 QVBoxLayout 添加(嵌套) QHBoxLayout 就只需要挂接 QVBoxLayout 即可
v_lay->addLayout(h_lay);
}

QGridLayout
网格布局,⽤来实现⽹格布局的效果,可以达到 M* N的这种⽹格的效果
核心属性
|-------------------------|---------------|
| layoutLeftMargin | 左侧边距 |
| layoutRightMargin | 右侧边距 |
| layoutTopMargin | 上⽅边距 |
| layoutBottomMargin | 下⽅边距 |
| layoutHorizontalSpacing | 相邻元素之间⽔平⽅向的间距 |
| layoutVerticalSpacing | 相邻元素之间垂直⽅向的间距 |
| layoutRowStretch | ⾏⽅向的拉伸系数 |
| layoutColumnStretch | 列⽅向的拉伸系数 |
案例1:使用网格布局设置六个按钮,实现 2 * 3 的效果
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* but1 = new QPushButton("按钮1");
QPushButton* but2 = new QPushButton("按钮2");
QPushButton* but3 = new QPushButton("按钮3");
QPushButton* but4 = new QPushButton("按钮4");
QPushButton* but5 = new QPushButton("按钮5");
QPushButton* but6 = new QPushButton("按钮6");
QGridLayout* lay = new QGridLayout(this);
lay->addWidget(but1,0,0);
lay->addWidget(but2,0,1);
lay->addWidget(but3,0,2);
lay->addWidget(but4,1,0);
lay->addWidget(but5,1,1);
lay->addWidget(but6,1,2);
}

案例2:设置水平拉伸系数,按照 1:1:2 比例进行排布
cpp
lay->setColumnStretch(0,1);
lay->setColumnStretch(1,1);
lay->setColumnStretch(2,2);
// 即使调的很大也不会发生(与上面设置为2时一样),因为数字大小仅仅是用来比较大小
//lay->setColumnStretch(2,100);

案例3:设置垂直拉伸系数,按照 1:1:2 比例进行排版
cpp
lay->setRowStretch(0,1);
lay->setRowStretch(1,2);

看起来是没有符合我们的预期:本来一个第2行的比例较前两个大的,但却没有,而设置的水平拉伸却可以,这是因为水平拉伸默认可以拉伸开,垂直却不行,需要进行设置才可以拉伸开
方法:使⽤ setSizePolicy 设置按钮的尺⼨策略,可选的值如下
- QSizePolicy::Ignored:忽略控件的尺⼨,不对布局产⽣影响
- QSizePolicy::Minimum :控件的最⼩尺⼨为固定值,布局时不会超过该值
- QSizePolicy::Maximum :控件的最⼤尺⼨为固定值,布局时不会⼩于该值
- QSizePolicy::Preferred :控件的理想尺⼨为固定值,布局时会尽量接近该值
- QSizePolicy::Expanding :控件的尺⼨可以根据空间调整,尽可能占据更多空间
- QSizePolicy::Shrinking :控件的尺⼨可以根据空间调整,尽可能缩⼩以适应空间
使用 QSizePolicy::Expanding 对 QWidget 进行设置
cpp
// 行和列都进行设置
but1->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
but2->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
but3->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
but4->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
but5->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
but6->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);

QFormLayout
表单布局,类似 html 实现的 form 表单
案例:创建表单,让用户可以输入:姓名,年龄,电话
- 使⽤ addRow ⽅法来添加⼀⾏:每⾏包含两个控件,第⼀个控件固定是 QLabel/⽂本,第⼆个控件则可以是任意控件
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QLabel* label1= new QLabel("姓名");
QLabel* label2 = new QLabel("年龄");
QLabel* label3 = new QLabel("电话");
QLineEdit* line1 = new QLineEdit();
QLineEdit* line2 = new QLineEdit();
QLineEdit* line3 = new QLineEdit();
QFormLayout* layout = new QFormLayout();
this->setLayout(layout);
layout->addRow(label1,line1);
layout->addRow(label2,line2);
layout->addRow(label3,line3);
QPushButton* button = new QPushButton("提交");
layout->addRow(nullptr,button);
}

QSpacer
Spacer 控件效果为空白,使用布局时要想让界面的控件不要紧挨着一起就可以使用它来调整
核心属性(在构造函数设置)
案例:设置的两个按钮之间使用 QSpacer 隔开
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QHBoxLayout* layout = new QHBoxLayout(this);
QPushButton* button1 = new QPushButton("按钮1");
QPushButton* button2 = new QPushButton("按钮2");
QSpacerItem* spacer = new QSpacerItem(100,200);
layout->addWidget(button1);
layout->addSpacerItem(spacer);
layout->addWidget(button2);
}
通过上⾯的学习,我们就了解了 Qt 中常⽤控件的使⽤⽅法,对于图形界⾯开发,知道有哪些控件,每个控件有什么作⽤,如何使⽤,是最核⼼的部分;基于上⾯内容其实我们就已经可以写出⼀些具有实际意义的图形化程序了
以上便是全部内容,有问题欢迎在评论区指正,感谢观看!