赞
踩
Widget是Qt中的核心概念英文原义是"小部件",我们此处也把它翻译为"控件" .
控件是构成一个图形化界面的基本要素.
像上述示例中的,按钮,列表视图,树形视图,单行输入框,多行输入框,滚动条,下拉框等,都可以称为"控件".
Qt作为一个成熟的GUI开发框架,内置了大量的常用控件.这一点在Qt Designer中就可以看到端倪.
并且Qt也提供了"自定义控件"的能力,可以让程序员在现有控件不能满足需求的时候,对现有控件做出扩展,或者手搓出新的控件.
综上,学习Qt,其中一个很重要的任务就是熟悉并掌握Qt内置的常用控件. 这些控件对于我们快速开发出符合需求的界面,是至关重要的.
关于控件体系的发展
控件是GUI开发中的通用概念.不仅仅局限在Qt中.
第一阶段:
完全没有控件.此时需要通过一些绘图 API手动的绘制出按钮或者输入框等内容,代码编写繁琐.
例如文曲星的Lava平台开发.
第二阶段: .
只包含粗略的控件.只是提供了按钮,输入框,单选框,复选框等最常用的控件.
例如html的原生控件.
第三阶段:
更完整的控件体系,基本可以覆盖到GUI开发中的大部分场景.
例如早期的MFC, VB, C++ Builder, Qt, Delphi,后来的Android SDK, Java FX,前端的各种UI库等.
在Qt中,使用QWidget类表示"控件".像按钮,视图,输入框,滚动条等具体的控件类,都是继承自QWidget.
可以说,QWidget中就包含了Qt整个控件体系中,通用的部分.
在Qt Designer中,随便拖一个控件过来,选中该控件,即可在右下方看到QWidget中的属性
在Qt Designer中,随便拖- -个控件过来,选中该控件,即可在右下方看到QWidget中的属性
这些属性既可以通过QtDesigner会直接修改,也可以通过代码的方式修改.
这些属性的具体含义,在Qt Assistant中均有详细介绍.
属性 | 作用 |
---|---|
enabled | 设置控件是否可使用. true 表示可用,false 表示禁用. |
geometry | 位置和尺寸.包含x, y, width, height四个部分.其中坐标是以父元素为参考进行设置的. |
windowTitle | 设置widget标题 |
windowlcon | 设置widget图标 |
windowOpacity | 设置widget透明度 |
cursor | 鼠标悬停时显示的图标形状.是普通箭头,还是沙漏,还是十字等形状.在Qt Designer界面中可以清楚看到可选项. |
font | 字体相关属性.涉及到字体家族,字体大小,粗体,斜体,下划线等等样式. |
toolTip | 鼠标悬停在widget.上会在状态栏中显示的提示信息. |
toolTipDuring | toolTip显示的持续时间. |
statusTip | Widget状态发生改变时显示的提示信息(比如按钮被按下等). |
whatsThis | 允许使用CSS来设置widget中的样式. |
styleSheet | 允许使用CSS来设置widget中的样式.Qt中支持的样式非常丰富,对于前端开发人员上手是非常友好的. |
focusPolicy | focusPolicy该widget如何获取到焦点. ●Qt::NoFocus: 控件不参与焦点管理,即无法通过键盘或鼠标获取焦点●Qt::TabFocus:控件可以通过Tab键获得焦点●Qt::ClickFocus: 控件可以通过鼠标点击获得焦点●Qt::StrongFocus: 控件可以通过键盘和鼠标获得焦点●Qt::WheelFocus: 控件可以通过鼠标滚轮获得焦点(在某些平台或样式中可能不可用) |
contextMenuPolicy | .上下文菜单的显示策略.●Qt::DefaultContextMenu: 默认的上下文菜单策略,用户可以通过鼠标右键或键盘快捷键触发上下文菜单●Qt::NoContextMenu: 禁用上下文菜单,即使用户点击鼠标右键也不会显示菜单●Qt::PreventContextMenu: 防止控件显示上下文菜单,即使用户点击鼠标右键也不会显示菜单●Qt::ActionsContextMenu:将上下文菜单替换为控件的“动作”菜单,用户可以通过鼠标右键或键盘快捷键触发这个菜单●Qt::CustomContextMenu:使用自定义的上下文菜单,用户可以通过鼠标右键或键盘快捷键触发这个菜单 |
locale | 设置语言和国家地区. |
acceptDrops | 该部件是否接受拖放操作。如果设置为true,那么该部件就可以接收来自其他部件的拖放操作。当一一个部件被拖放到该部件_上时,该部件会接收到相应的拖放事件(如dropEvent) 。如果设置为false,那么该部件将不会接收任何拖放操作。 |
minimumSize | 控件的最小尺寸.包含最小宽度和最小高度. |
maximumSize | 控件的最大尺寸.包含最大宽度和最大高度. |
sizePolicy | 尺寸策略.设置控件在布局管理器中的缩放方式. |
windowModality | 指定窗口是否具有"模态"行为. |
sizelncrement | 拖动窗口大小时的增量单位. |
baseSize | 窗口的基础大小,用来搭配sizelncrement调整组件尺寸是计算组件应该调整到的合适的值. |
palette | 调色板.可以设置widget的颜色风格. |
mouseTracking | 是否要跟踪鼠标移动事件.如果设为true,表示需要跟踪,则鼠标划过的时候该widget就能持续收到鼠标移动事件.如果设为false,表示不需要跟踪,则鼠标划过的时候widget不会收到鼠标移动事件,只能收到鼠标按下或者释放的事件. |
tabletTracking | 是否跟踪触摸屏的移动事件.类似于mouseTracking. Qt 5.9中引入的新属性. |
layoutDirection | 布局方向. .●Qt::LeftToRight: 文本从左到右排列,也是默认值。●Qt::RightToLeft:文本从右到左排列。●Qt::GlobalAtomics:部件的布局方向由全局原子性决定(其实就是根据应用程序中的其他widget布局方向确定的). |
autoFillBackground | 是否 自动填充背景颜色. |
windowFilePath | 能够把widget和一个本地文件路径关联起来. |
accessibleName | 设置widget的可访问名称.这个名称可以被辅助技术(像屏幕阅读器)获取到.设置widget的可访问名称.这个名称可以被辅助技术(像屏幕阅读器)获取到.这个属性用于实现无障碍程序的场景中(也就是给盲人写的程序).其实盲人也是可以使用电脑和手机的.甚至盲人还能成为程序猿.参见https://www.bilibili.com/video/BV1954y1d7z9 |
accessibleDescription | 设 置widget的详细描述.作用同accessibleName |
inputMethodHints | 针对输入框有效,用来提示用户当前能输入的合法数据的格式.比如只能输入数字,只能输入日期等. |
接下来我们会介绍其中一些比较重要比较常用的属性,并附有代码示例.
注意:本章节的每个代码示例都是创建了单独的Qt项目.省略了创建项目的具体步骤.
示例:使用代码创建一个禁用状态的按钮
运行程序,可以看到按钮处于灰色状态,无法被点击
在 ui 编辑界面右下角可以设置按钮的初始状态
geometry 意思是几何。这里表示位置和尺寸,其实是四个属性的统称
实际开发中,我们并不会直接使用这几个属性,而是通过一系列封装的方法来获取/修改。
对于 Qt 的坐标系,不要忘记是一个 “左手坐标系”,其中坐标系的原点是当前父元素的左上角。
QRect 表示矩形
代码示例 1 :通过按钮控制另外一个按钮的大小。
点击方向键即可控制按钮的大小。按下下方的四个按钮,就会控制 target 的左上角的位置,对应的按钮整个尺寸也会发生改变。
上述代码中我们是直接设置的 QRect 中的 x ,y 。实际上 QRect 内部是存储了左上和右下两个点的坐标,再通过这两个点的坐标差值计算长宽。
单纯修改左上坐标就会引起整个矩形的长宽发生改变。
代码示例 2 : 控制按钮让另外一个按钮移动
如果想让按钮发生移动可以使用下列代码
此时就可以通过下面四个按钮让上面的按钮移动。
如果widget作为一个窗口(带有标题栏,最小化,最大化,关闭按钮),那么在计算尺寸和坐标的 时候就有两种算法.包含window frame和不包含window frame. 其中x(), y(), frameGeometry(), pos(),
move()都是按照包含window frame的方式来计算的. 其中geometry(), width(), height(),
rect(), size()则是按照不包含window frame的方式来计算的. 当然,如果一个不是作为窗口的widget,上述两类方式得到的结果是一致的.
相关 API
代码示例 :对比 geometry 和 frame geometry 的区别
注意,如果直接在构造函数中使用geimetry和frame geometry,那么得到的窗口大小是没有区别的,因为此时 Widget 对象正在构造,还没有被加入到 window frame 中
我们设置一个按钮,然后点击按钮才触发槽函数,把这两个 API放在槽函数里面。
可以看到窗口大小的区别。
如果我们改成获取按钮的大小,可以看到是一样的,因为按钮并不是一个窗口。
代码示例 : 设置窗口标题
注意:上述设置操作针对不同的 widget 会有不同的行为。如果是顶层的 widget (独立窗口)这个操作才有效。如果是子 widget ,这个操作无任何效果,且不会报错。
同windowTitle, .上述操作仅针对顶层widget有效.
代码示例 1 :设置窗口图标
icon 可以去阿里巴巴矢量图这个网站去下载
先在 D 盘中放一个图片,名字为 txt.png
修改 widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QIcon>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建图标对象
QIcon icon("d:/txt.png");
//设置图标
this->setWindowIcon(icon);
}
Widget::~Widget()
{
delete ui;
}
运行程序,可以看到窗口图标已经变为上述图片
程序在任务栏的图标也发生改变
实际开发中,我们一般不会在代码中通过绝对路径引入图片.因为我们无法保证程序发布后,用
户的电脑上也有同样的路径.
如果使用相对路径,则需要确保代码中的相对路径写法和图片实际所在的路径匹配(比如代码
中写作"./image/txt.png",就需要在当前工作目录中创建image目录,并把txt.png放进去).
绝对路径:以盘符(windows)或者以/ (Linux) 开头的路径.
相对路径:以。(表示当前路径)或者以.。(表示当前路径.上级路径)开头的路径.其中|。经常也
会省略.相对路径的前提是需要明确"当前工作目录".
对于Qt程序来说,当前工作目录可能是变化的.比如通过Qt Creator运行的程序,当前工作目录是项目的构建目录;直接双击exe运行,工作目录则是exe所在目录.
所谓构建目录,是和Qt项目并列的,专门用来放生成的临时文件和最终exe的目录.
注意,上述构建目录,是随时可删除的.比如点击菜单栏中的"构建" -> “清理项目” ,就会把这个目录中的内容清空掉.
因此如果我们把图片文件放到构建目录中,可能在不小心删除后就丢失了.我们还是希望能够把图片和源代码放到一起,并且使我们的程序无论拷贝到任何位置中都能正确使用图片.
Qt使用qrc机制帮我们自动完成了.上述工作,更方便的来管理项目依赖的静态资源.
qrc文件是一种XML格式的资源配置文件,它用XML记录硬盘上的文件和对应的随意指定的资
源名称.应用程序通过资源名称来访问这些资源.
在Qt开发中,可以通过将资源文件添加到项目中来方便地访问和管理这些资源.这些资源文件
可以位于qrc文件所在目录的同级或其子目录下.
在构建程序的过程中,Qt会把资源文件的二进制数据转成cpp代码,编译到exe中.从而使依
赖的资源变得"路径无关".
这种资源管理机制并非Qt独有,很多开发框架都有类似的机制.例如Android的Resources
和AssetManager也是类似的效果.
代码示例 2 :通过 qrc 管理图片作为图标
右键项目,创建一个Qt Resource File (qrc文件),文件名随意起(不要带中文),此处叫做
resource.qrc .
在 qrc 编辑器中,添加前缀
此处设置成 / 即可
所谓的前缀,可以理解成"目录" .这个前缀决定了后续我们如何在代码中访问资源.
在资源编辑器中,点击add Files 添加资源文件.此处我们需要添加的是 txt.png
注意:添加的文件必须是在qrc文件的同级目录,或者同级目录的子目录中.因此我们需要把之前D盘 中的 txt.png 复制到.上述目录中.
添加完毕后,可以在 资源编辑器 中查看添加好的文件
在代码中使用这个 icon
注意上述路径的访问规则:
需要确保代码中编写的路径和添加到qrc中资源的路径匹配.否则资源无法被访问(同时也不会有报错提示).
运行程序,图标正确被设置
接下来,我们可以进入到项目的构建目录,可以看到,目录中多了一个 qrc_ resource.cpp 文件.直
接打开这个文件,可以看到类似如下代码: .
上述代码其实就是通过unsigned char数组,把 txt.png 中的每个字节都记录下来.这些代码会被编译到exe中.后续无论exe被复制到哪个目录下,都确保能够访问到该图片资源.
上述qrc这一套资源管理方案,优点和缺点都很明显.
优点:确保了图片,字体,声音等资源能够真正做到"目录无关",无论如何都不会出现资源丢失
的情况.
缺点:不适合管理体积大的资源.如果资源比较大(比如是几个MB的文件),或者资源特别多,
生成的最终的exe体积就会比较大,程序运行消耗的内存也会增大,程序编译的时间也会显著
增加.
代码示例:调整窗口透明度
在界面上拖放两个按钮,分别用来增加不透明度和减少不透明度.
objectName分别为pushButton_ add 和pushButton_ sub
编写wdiget.cpp,编写两个按钮的slot函数
点击pushButton_ sub会减少不透明度,也就是窗口越来越透明.
点击pushButton_ add会增加不透明度,窗口会逐渐恢复.
void Widget::on_pushButton_add_clicked()
{
float opacity = this->windowOpacity();
if(opacity >= 1.0)
{
return;
}
qDebug() << opacity;
opacity += 0.1;
this->setWindowOpacity(opacity);
}
void Widget::on_pushButton_sub_clicked()
{
float opacity = this->windowOpacity();
if(opacity <= 0.0)
{
return;
}
qDebug() << opacity;
opacity -= 0.1;
this->setWindowOpacity(opacity);
}
执行程序,可以看到,点击了 sub 之后,窗口就变透明了。
同时控制台中也可以看到opacity数值的变化
注意: C++中float类型遵守IEEE 754标准,因此在进行运算的时候会有一定的精度误差.因此1 -
0.1的数值并非是0.9.
代码示例 1 :通过 Qt Designer 中设置按钮的光标
创建一个按钮,然后选中,可以直接在右下角的属性设置光标。
代码示例 2 : 通过代码设置按钮的光标
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QCursor cursor(Qt::WaitCursor);//创建一个“等待”光标的对象
ui->pushButton->setCursor(cursor);//设置光标
}
Widget::~Widget()
{
delete ui;
}
系统内置的光标如下:
Ctrl+左键点击Qt: :WaitCursor|跳转到源码即可看到.
代码示例 3 : 自定义鼠标光标
Qt 自带的鼠标光标也有限,但是允许我们自定义光标。以下面这个图片为例子。
创建 qrc 资源文件,添加前缀 / ,并加入该图片
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个位图对象,加载自定义光标图片
QPixmap pixmap(":/cursor.png");
//scaled 函数可以设置图片尺寸
pixmap = pixmap.scaled(64,64);
//创建 QCursor对象,并指定“热点”为(2,2)坐标位置
//所谓“热点” 就是鼠标点击时生效的位置
QCursor cursor(pixmap,2,2);
//设置光标
this->setCursor(cursor);
}
Widget::~Widget()
{
delete ui;
}
代码示例 1 : 在 Qt Designer 中设置字体属性
在界面上创建一个 label
在右侧的属性编辑区,设置该 label 的 font 的相关属性
在这里调整属性,可以实时看到文字的变化
代码示例 2 :在代码中设置字体属性
#include "widget.h"
#include "ui_widget.h"
#include <QLabel>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QLabel *label = new QLabel(this);
label->setText("这是一段文本");
QFont font;//创建字体对象
font.setFamily("仿宋");//设置字体家族
font.setPointSize(20);//设置字体大小
font.setBold(true);//设置字体加粗
font.setItalic(true);//设置字体倾斜
font.setUnderline(true);//设置字体下划线
font.setStrikeOut(true);//设置字体删除线
label->setFont(font);//设置字体对象到 label 上
}
Widget::~Widget()
{
delete ui;
}
代码示例:设置 toolTip
在界面上拖放两个按钮. obj ectName设置为pushButton_ yes 和pushButton_ no
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->pushButton_yes->setToolTip("这是一个yes按钮");
ui->pushButton_yes->setToolTipDuration(3000);//设置为 3 秒
ui->pushButton_no->setToolTip("这是一个no按钮");
ui->pushButton_no->setToolTipDuration(3000);
}
Widget::~Widget()
{
delete ui;
}
也可以通过 ui 界面右边的属性直接设置 toolTip
把鼠标光标移到按钮上,即可看到提示
设置控件获取到焦点的策略.比如某个控件能否用鼠标选中或者能否通过tab键选中.
所谓"焦点",指的就是能选中这个元素.接下来的操作(比如键盘操作),就都是针对该焦点元素进行的了.这个对于输入框,单选框,复选框等控件非常有用的.
代码示例 :通过 Qt Designer 设置focusPolicy
在界面上创建四个单行输入框(Line Edit)
通过右下角的属性界面可直接设置每个输入框获取到焦点的策略
通过CSS设置widget的样式.
CSS (Cascading Style Sheets层叠样式表)本身属于网页前端技术.主要就是用来描述界面的 样式.
所谓"样式",包括不限于大小,位置,颜色,间距,字体,背景,边框等. 我们平时看到的丰富多彩的网页,就都会用到大量的CSS.
Qt虽然是做GUI开发,但实际上和网页前端有很多异曲同工之处.因此Qt也引入了对于CSS 的支持.
CSS中可以设置的样式属性非常多.基于这些属性Qt只能支持其中一部分,称为QSS (Qt Style Sheet). 具体的支持情况可以参考Qt文档中"Qt Style Sheets Reference"章节.
此处只是进行一个简单的演示.
代码示例 1 : 设置文本样式
在界面上创建 label
编辑右侧的styleSheet属性,设置样式
编辑完成样式之后,可以看到在Qt Designer中能够实时预览出效果.
此处的语法格式同CSS,使用键值对的方式设置样式.其中键和值之间使用:分割.键值对之间使用 ;分割. 另外, Qt
Designer只能对样式的基本格式进行校验,不能检测出哪些样式不被Qt支持.比如text- align: center
这样的文本居中操作,就无法支持.
运行程序,可以看到实际效果和预览效果基本一致.
代码示例 2 : 实现切换夜间模式
在界面上创建-一个多行输入框(Text Edit)和两个按钮.
objectName分别为pushButton light和pushButton dark
#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_light_clicked()
{
//设置窗口的样式
this->setStyleSheet("background-color: rgb(240,240,240);");
//设置输入框的样式
ui->textEdit->setStyleSheet("background-color:white;color:black");
//设置按钮的样式
ui->pushButton_light->setStyleSheet("color:black");
ui->pushButton_dark->setStyleSheet("color:black");
}
void Widget::on_pushButton_dark_clicked()
{
//设置窗口的样式
this->setStyleSheet("background-color: black;");
//设置输入框的样式
ui->textEdit->setStyleSheet("background-color:black;color:white");
//设置按钮的样式
ui->pushButton_light->setStyleSheet("color:white");
ui->pushButton_dark->setStyleSheet("color:white");
}
关于计算机中的颜色表示
计算机中使用"像素"表示屏幕上的一一个基本单位(也就是一个 发亮的光点).
每个光点都使用三个字节表示颜色,分别是R (red), G (green), B (blue) - -个字节表示(取值范围是0-255,或者0x00-0xFF).
混合三种不同颜色的数值比例,就能搭配出千千万万的颜色出来.
rgb(255,0,0)或者#FF0000 或者#F00 表示纯红色.
rgb(0,255,0)或者#00FF00 或者#0FO 表示纯绿色.
rgb(0, 0,255)或者#0000FF或者#00F表示纯蓝色.
rgb(255,255, 255)或者#FFFFFF或者#FFF 表示纯白色.
rgb(0, 0,0)或者#000000 或者#000表示纯黑色.
当然,上述规则只是针对一般的程序而言是这么设定的.实际的显示器,可能有8bit色深或者
10bit色深等,实际情况会更加复杂.
获取计算机中 RGB 的小技巧。
在 QQ截图中,内置了一个获取颜色的RGB方式
QQ 截图的时候把鼠标光标放在想要获取的颜色上,即可看到 RGB
运行程序,查看效果
运行程序,点击"日间模式",就是浅色背景,深色文字;点击"夜间模式",就是黑色背景,白色文字.
使用 QPushButton 表示一个按钮.这也是当前我们最熟悉的一个控件了.
QPushButton继承自QAbstractButton这个类是一个抽象类.是其他按钮的父类.
在Qt Designer中也能够看到这里的继承关系
QAbstractButton中,和QPushButton相关性较大的属性
QAbstractButton作为QWidget的子类,当然也继承了QWidget 的属性.上面
介绍的QWidget里的各种属性用法,对于QAbstractButton |同样适用.因此表格仅
列出QAbstractButton独有的属性.
Qt的api设计风格是非常清晰的.此处列出的属性都是可以获取和设置的.例如,使
用text() |获取按钮文本;使用setText() |设置文本.
事实上,QPushButton|的核心功能都是QAbstr actButton提供的.自身提供的属性都比较简
单.其中default和audoDefault 影响的是按下enter时自动点击哪个按钮的行为; flat把按钮
设置:为扁平的样式.这里我们暂时都不做过多关注.
代码示例 1 : 带有图标的按钮
创建resource.qrc 文件,并导入图片,具体操作步骤参见QWidget中的,windowIcon部分.此处不再赘述.
在界面上创建一个按钮
修改 widget.cpp 给按钮设置图标
#include "widget.h"
#include "ui_widget.h"
#include <Qicon>
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建图标
QIcon icon(":/image/pushbutton.png");
//设置图标
ui->pushButton->setIcon(icon);
//设置图标大小
ui->pushButton->setIconSize(QSize(50,50));
}
Widget::~Widget()
{
delete ui;
}
执行程序,观察效果
代码示例2:带有快捷键的按钮
在界面中拖五个按钮.
五个按钮的objectName分别为pushButton_ target ,pushButton_ up ,
pushButton_ down, pushButton_ left, | pushButton_ right
五个按钮的初始位置随意,其中pushButton_ target 尺寸设置为100 * 100,其余按钮设为50 *
50.文本内容均清空
创建 resource.qrc ,并导入五个图片
修改widget.cpp,设置图标资源和快捷键
使用setShortcut给按钮设置快捷键.参数是一个QKeySequence对象.表示一个按键序列.支
持组合键(ctrl+ c这种).
QKeySequence的构造函数参数,可以直接使用"ctrl+c"这样的按键名字符串表示,也可以使用预定义好的常量(形如Qt: :CTRL + Qt: :Key_ C )表示.
#include "widget.h"
#include "ui_widget.h"
#include <QIcon>
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置图标
ui->pushButton_target->setIcon(QIcon(":/image/pushbutton.png"));
ui->pushButton_target->setIconSize(QSize(50,50));
ui->pushButton_up->setIcon(QIcon(":/image/up.png"));
ui->pushButton_down->setIcon(QIcon(":/image/down.png"));
ui->pushButton_left->setIcon(QIcon(":/image/left.png"));
ui->pushButton_right->setIcon(QIcon(":/image/right.png"));
//设置快捷键
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));
//按键快捷键默认可以重复触发,鼠标不行,以下代码设置鼠标重复触发
ui->pushButton_up->setAutoRepeat(true);
ui->pushButton_down->setAutoRepeat(true);
ui->pushButton_left->setAutoRepeat(true);
ui->pushButton_right->setAutoRepeat(true);
}
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());
}
运行程序,使用快捷键让按钮移动
QRadioButton是单选按钮.可以让我们在多个选项中选择一个.
作为QAbstractButton和QWidget的子类,上面介绍的属性和用法,对于QRadioButton
同样适用.
QAbstractButton中和QRadioButton 关系较大的属性
代码示例 1 :选择性别
在界面上创建一-个label,和3个单选按钮
设置的文本如下图.3个单选按钮的objectName分别为radioButton_ male ,
radioButton_ female, radioButton_ other
修改widget.cpp,编辑三个QRadioButton的slot函数.
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("您选择的性别为:其他");
}
运行程序,可以看到随着选择不同的单选按钮,label中的提示文字就会随之变化.
当前代码中,如果程序启动,则不会选择任何选项.
可以修改代码,让程序启动默认选中性别男
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->radioButton_male->setChecked(true);
ui->label->setText("您选择的性别为:男");
}
运行程序,性别“男”默认被选中
运行程序,可以看到,点击"其他"按钮的时候,虽然不会被选中,但是可以触发点击事件,使上面的label显示性别为其他.
使用setEnabled 是更彻底的禁用按钮的方式.此时该按钮无法被选中,也无法响应任何输入
ui->radioButton_other->setEnabled(false);
代码示例 2 : clicked,press,release,toggled 的区别
在界面上创建四个单选按钮
obj ectName 分别为| radioButton,radioButton_ 2,radioButton 3 ,radioButton_ 4
给1创建clicked槽函数,给2创建pressed槽函数,给3创建released槽函数,给4创建toggled槽函数.
void Widget::on_radioButton_clicked(bool checked)
{
qDebug() << "clicked : " << checked;
}
void Widget::on_radioButton_2_pressed()
{
qDebug() << "pressed";
}
void Widget::on_radioButton_3_released()
{
qDebug() << "released";
}
void Widget::on_radioButton_4_toggled(bool checked)
{
// checked 状态发生改变,就会触发这个信号
qDebug() << "toggled: " << checked;
}
运行程序,可以看到:
代码示例 3 :单选框分组
1)在界面.上创建6个单选框,用来模拟麦当劳点餐界面.
objectName分别为radioButton到radioButton_ 6
此时直接运行程序,可以看到,这六个QRadioButton之间都是排他的.
我们希望每一组内部来控制排他,但是组和组之间不能排他.
引入QButtonGroup进行分组.
修改widget.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);
//把 QRadioButton 两两一组,放到三个 QButtonGroup 中
group1->addButton(ui->radioButton);
group1->addButton(ui->radioButton_2);
group2->addButton(ui->radioButton_3);
group2->addButton(ui->radioButton_4);
group3->addButton(ui->radioButton_5);
group3->addButton(ui->radioButton_6);
}
Widget::~Widget()
{
delete ui;
}
执行程序,可以看到可以按照正确的分组方式来完成排他了。
QCheckBox表示复选按钮.可以允许选中多个.
和QCheckBox最相关的属性也是checkable 和checked , 都是继承自QAbstractButton.至于QCheckBox独有的属性tristate用来实现"三态复选框" .这个东西比较冷门,这里不讨论。
代码示例 : 获取复选按钮的取值
在界面上创建三个复选按钮,和一个普通按钮.
objectName分别为checkBox_ study,checkBox_ game, checkBox_ work, 以及
pushButton
给 pushbutton 添加 slot 函数
void Widget::on_pushButton_clicked()
{
QString result = "今天你的安排是:";
if(ui->checkBox_study->isChecked())
{
result += ui->checkBox_study->text() + " ";
}
if(ui->checkBox_game->isChecked())
{
result += ui->checkBox_game->text() + " ";
}
if(ui->checkBox_work->isChecked())
{
result += ui->checkBox_work->text() + " ";
}
ui->label->setText(result);
}
运行程序:
QLabel 可以用来显示文本和图片
核心属性如下
代码示例 1 :显示不同格式的文本
在界面上创建三个QLabel,尺寸放大一些. objectName分别为label, label_ 2, label_ 3
修改 widget.cpp ,设置三个 label 的属性
#include "widget.h"
#include "ui_widget.h"
#include <QLabel>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setTextFormat(Qt::PlainText);
ui->label->setText("这是一段纯文本");
ui->label_2->setTextFormat(Qt::RichText);
ui->label_2->setText("<b>这是一段富文本</b>");
ui->label_3->setTextFormat(Qt::MarkdownText);
ui->label_3->setText("### 这是一段 markdown 文本");
}
Widget::~Widget()
{
delete ui;
}
运行程序,观察效果
代码示例 2 :显示图片
虽然QPushButton|也可以通过设置图标的方式设置图片,但是并非是一个好的选择.更多的时候
还是希望通过QLabel来作为一个更单纯的显示图片的方式.
在界面上创建一个 QLabel, objectName为label
创建 resource.qrc 文件,并把图片导入到 qrc 中
#include "widget.h"
#include "ui_widget.h"
#include <QPixMap>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置 label 大小和窗口一样大
QRect windowRect = this->geometry();
ui->label->setGeometry(0,0,windowRect.width(),windowRect.height());
QPixmap pixmap(":/loong.png");
ui->label->setPixmap(pixmap);
}
Widget::~Widget()
{
delete ui;
}
执行程序,观察效果
因为图片本身尺寸的问题,并没有把窗口填满
修改代码,设置 setScaledContents 属性
// 设置内容伸缩
ui->label->setScaledContents(true);
再次运行,观察效果,可以看到窗口已经被图片填满了
此时如果拖动窗口大小,可以看到图片并不会因为窗口的改变而发生变化。
为了解决这个问题,可以在 Widget 中重写 resizeEvent 函数
#include "widget.h"
#include "ui_widget.h"
#include <QPixMap>
#include <QDebug>
#include <QResizeEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置 label 大小和窗口一样大
QRect windowRect = this->geometry();
ui->label->setGeometry(0,0,windowRect.width(),windowRect.height());
QPixmap pixmap(":/loong.png");
ui->label->setPixmap(pixmap);
// 设置内容伸缩
ui->label->setScaledContents(true);
}
Widget::~Widget()
{
delete ui;
}
void Widget::resizeEvent(QResizeEvent *event)//这个函数会在窗口大小改变时被自动调用
{
ui->label->setGeometry(0,0,event->size().width(),event->size().height());
qDebug() << event->size();
}
执行程序,此时改变窗口大小,图片也会随之变化
在控制台查看窗口大小的改变过程
此处的resizeEvent函数我们没有手动调用,但是能在窗口大小变化时被自动调用.
这个过程就是依赖C++中的多态来实现的. Qt框架内部管理着QWidget对象表示咱们的窗
口.在窗口大小发生改变时,Qt就会自动调用resizeEvent函数.
但是由于实际上这个表示窗口的并非是QWidget,而是QWidget的子类,也就是咱们自己写
的Widget.此时虽然是通过父类调用函数,但是实际上执行的是子类的函数(也就是我们重写后的resizeEvent).此处属于是多态机制的一种经典用法.通过上述过程,就可以把自定义的代码,插入到框架内部执行.相当于"注册回调函数".
代码示例 3 :文本对齐 自动换行 缩进 边距
创建四个label, obj ectName分别是label到label_ 4
并且在QFrame中设置frameShape 为Box (设置边框之后看起来会更清晰一些)
QFrame|是QLabel的父类.其中frameShape 属性用来设置边框性质.
QFrame: :Box:矩形边框
QFrame: :Panel:带有可点击区域的面板边框
QFrame: :Wi nPanel: Windows风格的边框
QFrame: :HLine:水平线边框
QFrame: :VLine:垂直线边框
QFrame: : StyledPanel:带有可点击区域的面板边框,但样式取决于窗口主题
编写 widget.cpp,给这四个 label 设置属性
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置文字居中对齐
ui->label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);//ctrl + 鼠标左键可查看其他的选项
ui->label->setText("设置垂直且居中的文本");
//设置自动换行
ui->label_2->setAlignment(Qt::AlignTop | Qt::AlignLeft);//设置文字左上对齐
ui->label_2->setWordWrap(true);
ui->label_2->setText("这是一段长文本这是一段长文本这是一段长文本这是一段长文本这是一段长文本这是一段长文本");
//设置首行缩进
ui->label_3->setAlignment(Qt::AlignTop | Qt::AlignLeft);//设置文字左上对齐
ui->label_3->setIndent(20);
ui->label_3->setText("这是一段长文本这是一段长文本这是一段长文本这是一段长文本这是一段长文本这是一段长文本");
//设置边距
ui->label_4->setAlignment(Qt::AlignTop | Qt::AlignLeft);
ui->label_4->setMargin(10);
ui->label_4->setText("这是一段长文本这是一段长文本这是一段长文本这是一段长文本这是一段长文本这是一段长文本");
}
运行程序,可以看到如下效果:
代码示例 4 :设置伙伴
创建两个label和两个radioButton.
objectName分别问label, label_ 2,radioButton,radioButton_ 2
此处把label中的文本设置为"快捷键&A"这样的形式.
其中&后面跟着的字符,就是快捷键.
可以通过alt + A的方式来触发该快捷键.
但是注意,这里的快捷键和QPushButton的不同. 需要搭配alt和单个字母的方式才能触
发.
编写 widget.cpp,设置 buddy 属性,也可以使用 Qt Designer 直接设置
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setBuddy(ui->radioButton);
ui->label_2->setBuddy(ui->radioButton_2);
}
运行程序,可以看到,按下快捷键alt+ a或者alt+ b,即可选中对应的选项.
QLCDNumer是一个专门用来显示数字的控件.类似于"老式计算器"的效果.
核心属性
代码示例:倒计时
在界面上创建一个 LCD Number,初始值设为 10,objectName 为 lcdNumber
修改 widget.h 代码,创建一个 QTimer 成员,和一个 updateTime 函数
QTimer *timer;
void updateTime();
修改 widget.cpp,在构造函数中初始化 QTimer
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建 timer 实例
timer = new QTimer(this);
//连接信号槽,QTimer 会每隔一定的时间触发一个 timeout 信号,现在把 timeout 信号和 updateTime 连接起来
//此时意味着每次触发 timeout 信号都会伴随 updateTimt 函数的执行
connect(timer,&QTimer::timeout,this,&Widget::updateTime);
timer->start(1000);//1 秒
}
实现 updatetime
void Widget::updateTime()
{
qDebug() << "updateTime";
int value = ui->lcdNumber->intValue();
if(value <= 0)
{
// 如果时间到,停止定时器
timer->stop();
return;
}
ui->lcdNumber->display(value - 1);
}
执行程序,可以看到每隔一秒钟,显示的数字就减少 1
针对上述代码,存在两个问题
1.上述代码如果直接在Widget构造函数中,通过一个循环+ sleep的方式是否可以呢?
代码如下:
#include "widget.h"
#include "ui_widget.h"
#include <thread>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
while(true)
{
int value = ui->lcdNumber->intValue();
std::this_thread::sleep_for(std::chrono::seconds(1));
if(value <= 0)
{
break;
}
ui->lcdNumber->display(value - 1);
}
}
Widget::~Widget()
{
delete ui;
}
显然,这个代码是不行的.循环会使Widget的构造函数无法执行完毕,此时界面是不能正确构造和显示的.
2.上述代码如果是在Widget构造函数中,另起一个线程,在新线程中完成循环+ sleep是否可以呢?
std::thread t([this](){
int value = this->ui->lcdNumber->intValue();
while(true)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
if(value <= 0)
{
break;
}
this->ui->lcdNumber->display(value - 1);
}
});
这个代码同样是不行的. Qt中规定,任何对于GUI.上内容的操作,必须在主线程中完成.像Widget构造函数,以及connect连接的slot函数,都是在主线程中调用的.而我们自己创建的线程则不是.
当我们自己的线程中尝试对界面元素进行修改时,Qt程序往往会直接崩溃.
这样的约定主要是因为GUI中的状态往往是牵一发动全身的,修改一个地方,就需要同步的对
其他内容进行调整.
比如调整了某个元素的尺寸,就可能影响到内部的文字位置,或者其他元素的位置.这里一连串
的修改,都是需要按照-定的顺序来完成的.
由于多线程执行的顺序无法保障,因此Qt从根本上禁止了其他线程修改GUI状态,避免后续的
一系列问题.
综上所述,使用定时器,是实现.上述功能的最合理方案.
后续如果我们也有类似的需要"周期性修改界面状态"的需求,也需要优先考虑使用定时器.
使用 QProgressBar 表示一个进度条
核心属性
代码示例 : 设置进度条按时间增长
在界面上创建进度条,objectName为progressBar
其中最小值设为0,最大值设为100.当前值设为0
修改widget.h,创建QTimer和updateProgressBar函数
QTimer *timer;
void updateProgressBar();
修改widget.cpp,初始化QTimer
此处设置100ms触发一次timeout信号.也就是一秒钟触发10次
#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(this);
connect(timer,&QTimer::timeout,this,&Widget::updateProgressBar);
timer->start(100);//100 毫秒
}
Widget::~Widget()
{
delete ui;
}
void Widget::updateProgressBar()
{
int value = ui->progressBar->value();
if(value >= 100)
{
timer->stop();
return;
}
ui->progressBar->setValue(value + 1);
}
运行程序,可以看到进度条中的进度在快速增长
在实际开发中,进度条的取值,往往是根据当前任务的实际进度来进行设置的.
比如需要读取一个很大的文件,就可以获取文件的总的大小,和当前读取完毕的大小,来设置进
度条的比例.
由于上面我们介绍了Qt禁止在其他线程修改界面,因此进度条的更新往往也是需要搭配定时
器来完成的.
通过定时器周期触发信号,主线程调用对应的slot函数.再在slot函数中对当前的任务进度进
行计算,并更新进度条的界面效果.
代码示例 : 创建一个红色的进度条
在界面上创建一个进度条
在Qt Designer右侧的属性编辑器中,找到QWidget的styleSheet 属性.
编辑如下内容:
其中的chunk 是选中进度条中的每个"块" .使用QProgressBar: :text则可以选中文本.
QProgressBar::chunk {background-color: red;}
同时把QProcessBar的alignment属性设置为垂直水平居中.
此处如果不设置ali gnment ,进度条中的数字会跑到左.上角.这个怀疑是Qt本身的bug,暂时只能
先使用alignment来手动调整下.
执行程序,可以看到如下效果.我们就得到了一个红色的进度条.
通过上述方式,也可以修改文字的颜色,字体大小等样式.
QCalendarWidget表示一个“日历",形如
核心属性
重要信号
代码示例:获取选中的日期
在界面上创建一个QCalendarWidget和一个label,objectName为calendarWidget, label
给QCalendarWidget添加slot函数
void Widget::on_calendarWidget_selectionChanged()
{
QDate date = ui->calendarWidget->selectedDate();
qDebug() << date;
ui->label->setText(date.toString());
}
执行程序,可以看到当选择不同的日期时,label中的内容就会随之改变.
QLineEdit 用来表示单行输入框,可以输入一段文本,但是不能换行。
核心属性
核心信号
代码示例 1 :录入个人信息
在界面上创建三个输入框和两个单选按钮,一个普通按钮,三个输入框的obj ectName为lineEdit_ name ,lineEdit_ password ,lineEdit_ phone
两个单选按钮的objectName为radioButton_male, radioButton_ female
按钮的objectName为pushButton
编写widget.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_phone->setPlaceholderText("请输入电话号码");
ui->lineEdit_phone->setClearButtonEnabled(true);
//验证手机号码必须是 11 位数字,并且按照 “344”的格式输入
ui->lineEdit_phone->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_phone->text();
}
执行程序,可以看到,随着用户输入内容之后,点击按钮,就能打印到输入的信息
inputMask只能进行简单的输入格式校验. 实际开发中,基于正则表达式的方式是更核心的方法.
代码示例 2 :使用正则表达式验证输入框的数据
此处要求在输入框中输入一个合法的电话号码(1 开头,11位,全都是数字),如果验证不通过,则确定按钮无法点击。
关于正则表达式
正则表达式是一种在计算机中常用的,使用特殊字符描述一一个字符串的特征的机制.在进行字
符串匹配时非常有用.
正则表达式的语法还比较复杂,–般都是随用随查,不需要背下来.
正则表达式参考文档
正则表达式在线工具
继续使用上个例子的界面
编写 widget.cpp ,把按钮初始 enabled 设为 false,给输入框添加验证器
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置按钮默认是禁用状态
ui->pushButton->setEnabled(false);
//初始化第一个输入框
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);
//验证手机号码必须是 11 位数字,并且按照 “344”的格式输入
//ui->lineEdit_phone->setInputMask("000-0000-0000");
//给 lineEdit_phone 注册一个 validator
ui->lineEdit_phone->setValidator(new QRegularExpressionValidator(QRegularExpression("^1\\d{10}$")));
}
编写 widget.cpp 给lineEdit 添加 textEdited 信号的 slot 函数
void Widget::on_lineEdit_phone_textEdited(const QString &arg1)
{
QString content = arg1;
int pos = 0;
if(ui->lineEdit_phone->validator()->validate(content,pos) == QValidator::Acceptable)
{
//验证通过,设置按钮为可用状态
ui->pushButton->setEnabled(true);
}
else
{
//验证不通过,按钮为禁用状态
ui->pushButton->setEnabled(false);
}
}
执行程序,观察效果,此时电话内容输入字母是没用的,并且只有当输入的内容符合要求时,确定按钮才能被使用。
代码示例 3 :验证两次输入的密码是否一致
在界面上创建一个 label 和两个输入框
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->lineEdit->setEchoMode(QLineEdit::Password);
ui->lineEdit_2->setEchoMode(QLineEdit::Password);
}
Widget::~Widget()
{
delete ui;
}
给两个输入框设置 textEdited slot 函数
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->lineEdit->setEchoMode(QLineEdit::Password);
ui->lineEdit_2->setEchoMode(QLineEdit::Password);
ui->label->setText("密码为空");
}
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)
{
(void) arg1;//因为没有用到这个参数,编译器会报警告,这样写可以骗过编译器。
compare();
}
void Widget::on_lineEdit_2_textEdited(const QString &arg2)
{
(void)arg2;
compare();
}
运行程序,观察效果。当两次密码输入相同时,就会提示密码相同
代码示例 4 :切换显示密码
创建一个输入框和一个复选按钮
修改 widget.cpp,设置输入框的 echoMode 为 Password
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->lineEdit->setEchoMode(QLineEdit::Password);
}
给 checkBox 添加 slot 函数
void Widget::on_checkBox_toggled(bool checked)
{
if(checked)
{
ui->lineEdit->setEchoMode(QLineEdit::Normal);
}
else
{
ui->lineEdit->setEchoMode(QLineEdit::Password);
}
}
执行程序,可以看到,切换复选框的状态,就可以控制输入框显示密码
QTextEdit 表示多行输入框.也是一个富文本& markdown编辑器.
并且能在内容超出编辑框范围时自动提供滚动条.
核心属性
核心信号
代码示例:获取多行输入框的内容
创建一个多行输入框和一个label
给多行输入框添加slot函数.处理textChanged 信号.
通过toPlainText 方法获取到内部的文本.
类似的,QTextEdit还提供了toMarkdown 和toHtml .根据需要我们调整不同的获取方式.
void Widget::on_textEdit_textChanged()
{
const QString& content = ui->textEdit->toPlainText();
ui->label->setText(content);
}
执行程序,可以看到当输入框中的内容发生变化时, label中的内容同步发生改变.
代码示例:验证输入框的各种信号
创建多行输入框
给输入框添加以下几个slot函数
QTextEdit中包含了一个QTextCursor对象,通过这个对象可以获取到当前光标位置和选中
的内容.
执行程序,观察结果.
可以看到:
编写内容时,textChanged 和cursorPositionChanged 会触发
选中一段文本时,cursorPositionChanged ,selecti onChanged,copyAvailable
会触发.
按下ctrl+ z时,textChanged,undoAvailable,redoAvailable,cursorPosi tionChanged会触发
按下ctrl +y, textChanged, undoAvailable,cursorPositionChanged会触发
QComboBox 表示下拉框
核心属性
核心方法
核心信号
代码示例 : 使用下拉框模拟点餐
在界面上创建三个下拉框和一个按钮
编写代码
#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_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();
qDebug() << "小食选择: " << ui->comboBox_2->currentText();
qDebug() << "饮料选择: " << ui->comboBox_3->currentText();
}
运行程序,查看效果
代码示例:从文件中加载下拉框的选项
很多时候下拉框的选项并非是固定的,而是通过读取文件/读取网络获取到的
在界面上创建一个下拉框
创建文件 d:\config,txt,编写选项,每个选项占一行
编写代码
#include "widget.h"
#include "ui_widget.h"
#include <fstream>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
std::ifstream file("D:\\config.txt");
std::string line;
while(std::getline(file,line))
{
ui->comboBox->addItem(QString::fromStdString(line));
}
file.close();
}
Widget::~Widget()
{
delete ui;
}
执行程序,查看效果
使用QSpinBox或者QDoubleSpinBox表示"微调框",它是带有按钮的输入框.可以用来输入整
数/浮点数.通过点击按钮来修改数值大小.
由于SpinBox 和QDoubleSpinBox用法基本相同,就只介绍SpinBox| 的使用了.
QSpinBox 关键属性
核心信号
代码示例 :调整麦当劳购物车的份数
在界面上创建下列内容
三个下拉框: objectName为comboBox 到comboBox_ 3
三个微调框: objectName为
spinBox到spinBox_ 3
一个按钮: objectName 为pushButton
修改代码
#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_2->addItem("薯条");
ui->comboBox_2->addItem("麦辣鸡翅");
ui->comboBox_3->addItem("可乐");
ui->comboBox_3->addItem("雪碧");
//初始化微调框
ui->spinBox->setValue(1);
ui->spinBox->setRange(1,5);
ui->spinBox_2->setValue(1);
ui->spinBox_2->setRange(1,5);
ui->spinBox_3->setValue(1);
ui->spinBox_3->setRange(1,5);
}
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();
}
运行程序,查看效果
使用 QDateEdit 作为日期的微调框
使用 QTimeEdit 作为时间的微调框
使用 QDateTimeDate 作为时间日期的微调框
这几个控件用法非常相似,我们以 QDateTimeEdit 为例进行介绍
QDateTimeEdit 核心属性
关于本地时间(LocalTime)和协调世界时(UTC)
UTC时间是一个基于原子钟的标准时间.不受地球的自转周期影响.和格林威治时间(GMT)是
非常接近的.科学家会通过精密的设备来测量并维护.
咱们的计算机内部使用的时间就是基于UTC时间.
本地时间则是基于不同的时区,对UTC时间做出了一些调整.比如咱们使用的北京时间,位于
“东八区”,就需要在UTC时间基础上+8个小时的时差.
核心信号
代码示例:实现日期计算器
在界面上创建两个QDateTimeEdit和一个按钮,一个label
QDateTimeEdit的objectName 为dateTimeEdit_ old和dateTimeEdit_ new
编写计算按钮的slot函数
使用daysTo 函数可以计算两个日期的天数.
secsTo函数可以计算两个时间的秒数.
通过(秒数/ 3600) 换算成小时数,再余上24得到零几个小时.
使用QString: :number把整数转成QString进行拼接.
void Widget::on_pushButton_clicked()
{
// 获取到两个时间框的时间日期
QDateTime timeOld = ui->dateTimeEdit_old->dateTime();
QDateTime timeNew = ui->dateTimeEdit_new->dateTime();
//计算日期差值
int days = timeOld.daysTo(timeNew);
int hours = (timeOld.secsTo(timeNew) / 3600) % 24;
//设置 label 内容
ui->label->setText(QString::number(days) + "天" + QString::number(hours) + QString("个小时"));
}
执行程序,观察结果
但是 Qt 给的计算天数的函数 daysTo 有一点问题
查阅文档可知
返回从该日期时间到另一日期时间的天数。天数是指从这个日期时间到另一个日期时间之间达到午夜的次数。这意味着从23:55到第二天0:05的10分钟差算作一天。
观察程序,发现确实是这样。
所以修改代码,用秒数来计算。
void Widget::on_pushButton_clicked()
{
// 获取到两个时间框的时间日期
QDateTime timeOld = ui->dateTimeEdit_old->dateTime();
QDateTime timeNew = ui->dateTimeEdit_new->dateTime();
//计算日期差值
//int days = timeOld.daysTo(timeNew);
int days = (timeOld.secsTo(timeNew) / 3600) / 24;
int hours = (timeOld.secsTo(timeNew) / 3600) % 24;
//设置 label 内容
ui->label->setText(QString::number(days) + "天" + QString::number(hours) + QString("个小时"));
}
使用QDial 表示一个旋钮.
有些程序,通过鼠标拖动旋钮旋转,即可完成一些相关的设置.
核心属性
核心信号
代码示例:调整窗口透明度
在界面上创建一个旋钮和一个label
编写代码
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置可以循环旋转
ui->dial->setWrapping(true);
//设置刻度线可见
ui->dial->setNotchesVisible(true);
//设置最大值
ui->dial->setMaximum(100);
//设置最小值
ui->dial->setMinimum(0);
//设置初始值为
ui->dial->setValue(100);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_dial_valueChanged(int value)
{
ui->label->setText(QString("当前不透明度为: ") + QString::number(value));
this->setWindowOpacity((double)value / 100);
}
运行程序,查看效果
使用 QSlider 表示一个滑动条
QSlider和QDial都是继承自QAbstractSlider, 因此用法上基本相同.
核心属性
核心信号
代码示例:调整窗口大小
在界面上创建两个滑动条,分别是水平和垂直滑动条.
objectName分别为horizontalSlider 和verticalSlider
编写代码
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->horizontalSlider->setMinimum(500);
ui->horizontalSlider->setMaximum(2000);
ui->horizontalSlider->setSingleStep(100);
ui->horizontalSlider->setValue(800);
ui->verticalSlider->setMinimum(500);
ui->verticalSlider->setMaximum(1500);
ui->verticalSlider->setSingleStep(100);
ui->verticalSlider->setValue(600);
//翻转朝向,默认滑块从下往上增长,改成从上往下增长
ui->verticalSlider->setInvertedAppearance(true);
}
Widget::~Widget()
{
delete ui;
}
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);
}
运行程序,查看效果
当滑动滑块时,窗口的大小就会发生相应的变化。
代码示例:通过自定义快捷键调整滑动条位置.
设置-减小value,设置=增加value.(+和=在同一个键),控制水平方向的滑动条
默认情况下滑动条可以通过方向键或者pageUp / pageDown调整大小.
继续用上个例子的界面
#include "widget.h"
#include "ui_widget.h"
#include <QShortcut>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->horizontalSlider->setMinimum(500);
ui->horizontalSlider->setMaximum(2000);
ui->horizontalSlider->setSingleStep(100);
ui->horizontalSlider->setValue(800);
ui->verticalSlider->setMinimum(500);
ui->verticalSlider->setMaximum(1500);
ui->verticalSlider->setSingleStep(100);
ui->verticalSlider->setValue(600);
//翻转朝向,默认滑块从下往上增长,改成从上往下增长
ui->verticalSlider->setInvertedAppearance(true);
//设置快捷键
QShortcut* shortCut1 = new QShortcut(this);
shortCut1->setKey(QKeySequence("-"));
connect(shortCut1,&QShortcut::activated,this,&Widget::sub);
QShortcut* shortCut2 = new QShortcut(this);
shortCut2->setKey(QKeySequence("="));
connect(shortCut2,&QShortcut::activated,this,&Widget::add);
}
Widget::~Widget()
{
delete ui;
}
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);
}
void Widget::sub()
{
int value = ui->horizontalSlider->value();
ui->horizontalSlider->setValue(value - 20);
}
void Widget::add()
{
int value = ui->horizontalSlider->value();
ui->horizontalSlider->setValue(value + 20);
}
执行程序,观察效果
通过 “-”和“=” 快捷键可以控制水平方向的窗口大小
Qt 中提供的多元素控件有:
xxWidget 和 xxView 的区别
以 QTableView 和 QTableView 为例
QTableView 是基于 MVC 设计的控件,QTableView 自身不持有数据。使用QTableView 的时候需要用户创建一个 Model 对象(比如 QStandardModel),并且把Model 和 QTableView 关联起来。后续修改 Model 中的数据就会影响 QTableView 的显示;修改 QTableView 中的显示也会影响到 Model 中的数据 (双向绑定)
QTableWidget 则是QTableView 的子类,对Model 进行封装。不需要用户手动创建 Model 对象,直接就可以往 QTableWidget 中添加数据了。
本节只讲解 xxWidget 的使用方式
使用 QListWidget 能够显示一个纵向列表,形如:
每个选项都可以被选中
核心属性
核心方法
核心信号
在上述介绍中,涉及到一个关键的类,QListWidgetItem。这个类表示 QListWidget 中的一个元素。核心方法如下:本质就是一个 “文本” + “图标” 组成的
代码示例 : 使用 ListWidget
在界面上创建一个 ListWidget (也可以使用ListView,然后右键点击 “变型为”,可以变型为 ListWidget),再创建一个 lineEdit 和两个按钮
注意: ListWidget是ListView的子类,功能比ListView更丰富.使用 ListWidget即可.
编写 widget.cpp ,在构造函数中添加初始元素
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->listWidget->addItem("C++");
ui->listWidget->addItem("Java");
ui->listWidget->addItem("Python");
}
编写按钮的 slot 函数
void Widget::on_pushButton_clicked()
{
// 获取当前输入框的内容
const QString& text = ui->lineEdit->text();
if(text.isEmpty())
{
return;
}
ui->listWidget->addItem(text);
}
void Widget::on_pushButton_2_clicked()
{
//获取当前被选中元素的行数
int row = ui->listWidget->currentRow();
//如果未选中 row 为 -1
//删除这一行
if(row != -1)
{
ui->listWidget->takeItem(row);
}
}
执行程序,可以新增元素,选中元素,删除元素
使用QTableWidget 表示一个表格控件. -个表格中包含若干行,每一行又包含若干列.
表格中的每个单元格,是一个QTableWidgetItem对象.
QTableWidget核心方法
QTableWidgetItem 核心信号
QTableWidgetItem 核心方法
代码示例:使用QTableWidget
1)在界面上创建QTableWidget 和四个按钮,一个输入框
注意: QTableWidget是QTableView的子类,功能比QTableView更丰富.咱们使用 QTableWidget即可.
在 widget.cpp 编写代码
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//默认情况下运行后单元格是可允许被编辑的,如不想被编辑可以加入这行代码
//ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
//创建三行
ui->tableWidget->insertRow(0);
ui->tableWidget->insertRow(1);
ui->tableWidget->insertRow(2);
//创建三列
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->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("21"));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_addRow_clicked()
{
//获取到行数
int rowCount = ui->tableWidget->rowCount();
//插入新行
ui->tableWidget->insertRow(rowCount);
}
void Widget::on_pushButton_add_Col_clicked()
{
//获取到列数
int colCount = ui->tableWidget->columnCount();
//插入新列
ui->tableWidget->insertColumn(colCount);
//设置列名
const QString& name = ui->lineEdit->text();
ui->tableWidget->setHorizontalHeaderItem(colCount,new QTableWidgetItem(name));
}
void Widget::on_pushButton_delRow_clicked()
{
//获取到选中的行号
int curRow = ui->tableWidget->currentRow();
//删除对应的行
ui->tableWidget->removeRow(curRow);
}
void Widget::on_pushButton_delCol_clicked()
{
//获取到选中列号
int curCol = ui->tableWidget->currentColumn();
//删除对应的行
ui->tableWidget->removeColumn(curCol);
}
运行程序,可以操作该表格
使用QTreeWidget表示一个树形控件.里面的每个元素,都是一个QTreeWidgetItem , 每个
QTreeWidgetItem可以包含多个文本和图标,每个文本/图标为一个列.
可以给QTreeWidget 设置顶层节点(顶层节点可以有多个),然后再给顶层节点添加子节点,从而构成树形结构.
QTreeWidget 核心方法
QTreeWidget 核心信号
QTreeWidgetItem 核心属性
QTreeWidgetItem 核心方法
代码示例:使用QTreeWidget
1)在界面.上创建一个TreeView ,右键=>变形为=> TreeWi dget ,再创建一个lineEdit和三个按.
钮.
注意: TreeWidget是TreeVi ew的子类,功能比TreeVi ew更丰富.使用TreeWidget 即可.
编写 widget.cpp 代码,也可以直接在 ui 界面添加
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->treeWidget->setHeaderLabel("动物");
QTreeWidgetItem* item1 = new QTreeWidgetItem();
item1->setText(0,"猫");
ui->treeWidget->addTopLevelItem(item1);
QTreeWidgetItem* item2 = new QTreeWidgetItem();
item2->setText(0,"狗");
ui->treeWidget->addTopLevelItem(item2);
QTreeWidgetItem* item3 = new QTreeWidgetItem();
item3->setText(0,"鸟");
ui->treeWidget->addTopLevelItem(item3);
}
编写按钮的槽函数
void Widget::on_pushButton_clicked()
{
//获取输入框的内容
const QString& text = ui->lineEdit->text();
if(text.isEmpty())
{
return;
}
//添加到顶层节点中
QTreeWidgetItem* item = new QTreeWidgetItem();
item->setText(0,text);
ui->treeWidget->addTopLevelItem(item);
}
void Widget::on_pushButton_2_clicked()
{
//获取输入框的内容
const QString& text = ui->lineEdit->text();
if(text.isEmpty())
{
return;
}
//获取到当前选中的节点
QTreeWidgetItem* currentItem = ui->treeWidget->currentItem();
if(currentItem == nullptr)
{
return;
}
//构造新的 Item
QTreeWidgetItem* newItem = new QTreeWidgetItem();
newItem->setText(0,text);
//添加 item 到选中节点
currentItem->addChild(newItem);
//展开父节点
currentItem->setExpanded(true);
}
void Widget::on_pushButton_3_clicked()
{
//获取到当前选中的节点
QTreeWidgetItem* currentItem = ui->treeWidget->currentItem();
if(currentItem == 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和QButtonGroup混淆. (之前在介绍QRadi onButton的时候提
到了QButtonGroup ).
核心属性
分组框只是一个用来"美化界面"这样的组件,并不涉及到用户交互和业务逻辑.属于"锦上添
花".
代码示例:给麦当劳案例加.上分组框
1)在界面上创建三个分组框,并且在分组框内部创建下拉框和微调框.
运行程序,查看效果
使用QTabWi dget实现一个带有标签页的控件,可以往里面添加一-些 widget.进一步 的就可以通过标签页来切换.
核心属性
核心信号
代码示例:使用标签页管理多组控件
1)在界面上创建一个QTabWidget ,和一个按钮.
按钮的objectName 为pushButton,并勾选标签页的可关闭按钮的显示
注意:
QTabWidget中的每个标签页都是一个QWidget
点击标签页,就可以直接切换.
右键QTabWidget,可以添加标签页或者删除标签页.
2)编写widget.cpp,进行初始化,给标签页中放个简单的label
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QLabel* label1 = new QLabel(ui->tab);
label1->setText("标签页1");
label1->resize(100,50);
QLabel* label2 = new QLabel(ui->tab_2);
label2->setText("标签页2");
label2->resize(100,50);
}
编写按钮的槽函数
void Widget::on_pushButton_clicked()
{
//获取当前有多少个标签页
int count = ui->tabWidget->count();
//创建新的 widget
QWidget* w = new QWidget();
ui->tabWidget->addTab(w,QString("Tab") + QString::number(count + 1) );
//给 widget 中添加label
QLabel* label = new QLabel(w);
label->setText(QString("标签页") + QString::number(count + 1));
label->resize(100,50);
//选中这个标签页
ui->tabWidget->setCurrentIndex(count);
}
编写点击关闭标签页时的槽函数
void Widget::on_tabWidget_tabCloseRequested(int index)
{
ui->tabWidget->removeTab(index);
}
运行程序,查看效果
点击新增标签页可以新增标签页
点击关闭,可以删除标签页
之前使用Qt在界面.上创建的控件,都是通过"绝对定位"的方式来设定的.
也就是每个控件所在的位置,都需要计算坐标,最终通过setGeometry| 或者move| 方式摆放过去.
这种设定方式其实并不方便.尤其是界面如果内容比较多,不好计算.而且一个窗口大小往往是可以调整的,按照绝对定位的方式,也无法自适应窗口大小。
因此Qt引入"布局管理器" (Layout)机制,来解决上述问题.
当然,布局管理器并非Qt独有.其他的GUI开发框架,像Android,前端等也有类似的机制.
使用QVBoxL ayout表示垂直的布局管理器.V是vertical的缩写.
核心属性
代码示例:使用QVBoxLayout 管理多个控件.
1)编写代码,创建布局管理器和三个按钮.并且把按钮添加到布局管理器中.
使用addWidget 把控件添加到布局管理器中.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建三个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
//创建布局管理器,并且把按钮添加进去。
//指定父元素为 this
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(btn1);
layout->addWidget(btn2);
layout->addWidget(btn3);
}
2)运行程序,可以看到此时界面上的按钮就存在于布局管理器中.随着窗口尺寸变化而发生改变.
此时三个按钮的尺寸和位 置,都是自动计算出来的.
通过上述代码的方式,只能给这个widget设定一个布局管理器.实际上也可以通过Qt Design在一-个窗口中创建多个布局管理器.
代码示例:创建两个QVBoxLayout
1)在界面.上创建两个QVBoxLayout , 每个QVBoxLayout 各放三个按钮.
2)运行程序,可以看到这些按钮已经自动排列好.只不过当前这些按钮的位置不能随着窗口大小自动变化.
通过Qt Designer创建的布局管理器,其实是先创建了一个widget,设置过geometry 属性
的.再把这个layout设置到这个widget中. 实际上,一个widget只能包含-个layout.
打开ui文件的原始xml,可以看到其中的端倪. 这种情况下layout并非是窗口widget的布局管理器,因此不会随着窗口大小改变.
使用QHBoxLayout 表示垂直的布局管理器.H是horizontal的缩写.
代码示例:使用QHBoxLayout 管理控件
1)编写代码,创建布局管理器和三个按钮.并且把按钮添加到布局管理器中.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
QHBoxLayout* layout = new QHBoxLayout(this);
layout->addWidget(btn1);
layout->addWidget(btn2);
layout->addWidget(btn3);
}
2)运行程序,可以看到此时界面上的按钮就存在于布局管理器中.随着窗口尺寸变化而发生改变.
此时三个按钮的尺寸和位置,都是自动计算出来的.
Layout里面可以再嵌套.上其他的layout,从而达到更复杂的布局效果.
代码示例:嵌套的layout
1)在代码中创建以下内容
使用addLayout给layout中添加子layout.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建顶层 layout
QVBoxLayout* vLayout = new QVBoxLayout(this);
//添加两个按钮进去
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
vLayout->addWidget(btn1);
vLayout->addWidget(btn2);
//创建要嵌套进去的 layout
QHBoxLayout* hLayout = new QHBoxLayout(this);
//添加两个按钮进去
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
hLayout->addWidget(btn3);
hLayout->addWidget(btn4);
//嵌套
vLayout->addLayout(hLayout);
}
执行程序,观察效果
结合QHBoxLayout和QVBoxLayout ,就可以做出各种复杂的界面了.
Qt中还提供了QGridLayout 用来实现网格布局的效果.可以达到M * N的这种网格的效果.
核心属性
整体和QVBoxLayout |以及QHBoxLayout相似.但是设置spacing的时候是按照垂直水平两个 方向来设置的.
代码示例:使用QGridLayout 管理元素
代码中创建QGridLayout和4个按钮.
使用addWidget |添加控件到布局管理器中.但是添加的同时会指定两个坐标.表示放在第几行,第
几列.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建四个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
//创建网格布局管理器,并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1,0,0);
layout->addWidget(btn2,0,1);
layout->addWidget(btn3,1,0);
layout->addWidget(btn4,1,1);
//设置 layout 到窗口中
this->setLayout(layout);
}
执行代码,观察效果.可以看到当前的这几个按钮是按照2行2列的方式排列的.
如果调整行列坐标为下列代码
//创建网格布局管理器,并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1,0,0);
layout->addWidget(btn2,0,1);
layout->addWidget(btn3,0,2);
layout->addWidget(btn4,0,3);
执行代码,可以看到这几个按钮都在同一行了.相当于QHBoxLayout
如果调整坐标为下列代码
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1,1,0);
layout->addWidget(btn2,2,0);
layout->addWidget(btn3,3,0);
layout->addWidget(btn4,4,0);
执行代码,可以看到这几个按钮都在同一-列了.相当于QVBoxLayout
任意调整行列,即可看到不同的效果.
//创建网格布局管理器,并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1,0,0);
layout->addWidget(btn2,1,1);
layout->addWidget(btn3,2,2);
layout->addWidget(btn4,3,3);
编写代码形如
//创建网格布局管理器,并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1,0,0);
layout->addWidget(btn2,1,1);
layout->addWidget(btn3,2,2);
layout->addWidget(btn4,10,10);
此处也要注意,设置行和列的时候,如果设置的是一个很大的值,但是这个值和上一一个值之间并 没有其他的元素,那么并不会在中间腾出额外的空间.
代码示例:设置QGri dL ayout中元素的大小比例.
创建6个按钮,按照2行3列的方式排列
使用setCo lumnStretch
设置每一列的拉伸系数.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建 6 个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
QPushButton* btn5 = new QPushButton("按钮5");
QPushButton* btn6 = new QPushButton("按钮6");
//创建网格布局管理器,并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1,0,0);
layout->addWidget(btn2,0,1);
layout->addWidget(btn3,0,2);
layout->addWidget(btn4,1,0);
layout->addWidget(btn5,1,1);
layout->addWidget(btn6,1,2);
//设置拉伸比例
//第0列拉伸比例设置为1
layout->setColumnStretch(0,1);
//第1列拉伸比例设为0,即为固定大小,不参与拉伸
layout->setColumnStretch(1,0);
//第2列拉伸比例设为3
layout->setColumnStretch(2,3);
//设置layout到窗口中
this->setLayout(layout);
}
执行程序,可以看到每一列的宽度是不同的.并且随着窗口调整动态变化.
另外,QGridLayout也提供了setRowStretch 设置行之间的拉伸系数. . 上述案例中,直接设置setRowStretch
效果不明显,因为每个按钮的高度是固定的.需要 把按钮的垂直方向的sizePolicy 属性设置为QSizePolicy: :
Expanding尽可能填 充满布局管理器,才能看到效果.
代码示例:设置垂直方向的拉伸系数
编写代码,创建6个按钮,按照3行2列方式排列.
使用setSizePolicy 设置按钮的尺寸策略.可选的值如下:
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建 6 个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
QPushButton* btn5 = new QPushButton("按钮5");
QPushButton* btn6 = new QPushButton("按钮6");
//设置按钮的 sizePolicy,此时按钮的水平方向和垂直方向都会尽量舒展开
btn1->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
btn2->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
btn3->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
btn4->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
btn5->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
btn6->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
//创建网格布局管理器,并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1,0,0);
layout->addWidget(btn2,0,1);
layout->addWidget(btn3,0,2);
layout->addWidget(btn4,1,0);
layout->addWidget(btn5,1,1);
layout->addWidget(btn6,1,2);
this->setLayout(layout);
}
执行代码,观察效果
此时的按钮垂直方向都舒展开了,并且调整窗口尺寸,按钮也会同步变化
总的来说,使用QGri dLayout |能够代替很多QHBoxLayout 和QVBoxLayout嵌套的场景.毕
竟嵌套的代码写起来是比较麻烦的.
另外不要忘了,QGridLayout里面也能嵌套QHBoxL ayout和QVBoxLayout
QHBoxLayout和QVBoxLayout里面也能嵌套QGridLayout|
灵活使用.上述布局管理器,就可以实现出任意的复杂界面.
除了上述的布局管理器之外, Qt还提供了QFormLayout ,属于是QGridLayout的特殊情况,专门用于实现两列表单的布局.
这种表单布局多用于让用户填写信息的场景.左侧列为提示,右侧列为输入框.
代码示例:使用QFormLayout 创建表单.
编写代码,创建QFormLayout ,以及三个label和三个lineEdit
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建 layout
QFormLayout *layout = new QFormLayout();
this->setLayout(layout);
//创建三个 label
QLabel *label1 = new QLabel("姓名");
QLabel *label2 = new QLabel("年龄");
QLabel *label3 = new QLabel("电话");
//创建三个 lineEdit
QLineEdit *lineEdit1 = new QLineEdit();
QLineEdit *lineEdit2 = new QLineEdit();
QLineEdit *lineEdit3 = new QLineEdit();
QPushButton *btn = new QPushButton("提交");
layout->addRow(label1,lineEdit1);
layout->addRow(label2,lineEdit2);
layout->addRow(label3,lineEdit3);
layout->addRow(nullptr,btn);
}
执行程序,可以看到如下效果
使用布局管理器的时候,可能需要在控件之间,添加一段空白.就可以使用QSpacerItem来表示.
核心属性
代码示例:创建一组左右排列的按钮.
在界面上创建一个QVBoxL ayout,并添加两个按钮.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QHBoxLayout* layout = new QHBoxLayout();
this->setLayout(layout);
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
layout->addWidget(btn1);
layout->addWidget(btn2);
}
直接运行程序,可以看到两个按钮是紧挨着的
在两个按钮之间添加一个 spacer
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QHBoxLayout* layout = new QHBoxLayout();
this->setLayout(layout);
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QSpacerItem* spacer = new QSpacerItem(200,20);
layout->addWidget(btn1);
layout->addSpacerItem(spacer);
layout->addWidget(btn2);
}
运行程序,查看效果
在 Qt Designer 中,也可以直接给界面上添加 spacer
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。