一、控件概述
Widget 是 Qt 中的核心概念. 英文原义是 "小部件", 我们此处也把它翻译为 "控件" .
控件是构成一个图形化界面的基本要素.

像上述示例中的, 按钮, 列表视图, 树形视图, 单行输入框, 多行输入框, 滚动条, 下拉框等, 都可以称为 "控件".
在Qt中已经给我们提供了很多的"控件",我们就需要学习和了解这些控件,学会如何使用这些控件。

如上,在Qt Designer左侧的这一长条,就是Qt给我们已经内置好的控件了。
Qt中的各种控件都是继承自QWidget类,
二、enabled属性
enabled:描述一个控件是否处于"可用状态",相对的概念就是"禁用"。所谓的"禁用"指的是该控件不能接收任何用户的输入事件,并且外观上往往是灰色的。如果一个widget被禁用了,则该widget的子元素也被禁用。
| API | 说明 |
|---|---|
| isEnabled() | 获取到控件的可用状态 |
| setEnabled | 设置控件是否可使用,true表示可用,false表示禁用 |
举个例子:

执行上述的代码,我们发现这个创建的按钮变成了灰色,这就是被禁用了。有的人就提出了问题:是不是没连接槽函数导致这个按钮执行不了呢?

我们发现连接了槽函数依旧是灰色的按钮。
三、geometry属性
位置和尺寸,其实是四个属性的统称:x是横坐标,y是纵坐标,width是宽度,heigth是高度。

在实际开发中,我们通常不会直接操作这些属性,而是通过封装的方法来进行获取和修改。
Qt 采用左手坐标系系统,坐标系原点位于当前元素父元素的左上角。

| API | 说明 |
|---|---|
| geometry() | 获取控件的位置和尺寸信息。返回结果为QRect对象,包含x坐标、y坐标、宽度和高度值。其中x和y表示控件左上角的坐标位置。 |
| setGeometry(QRect)setGeometry(int x, int y,int width, int height) | 调整控件的位置和尺寸。您可以通过设置QRect对象整体定义,也可以分别设置其四个属性(x、y、width、height)进行精细调整。 |
QPoint表示一个点,QRect表示一个矩形。它们都属于小对象,里面的属性非常少,占用空间也小。C++中使用上述对象,通常就会按照值的方式来传递参数。
我们在之前的文章中也提到一个move,它只是用来修改位置的。而setGeometry则是既可以修改位置,也可以修改尺寸。
样例:

如上设置出五个按钮,我们期望通过点击下面的四个按钮,就能够修改target按钮的geometry。
编写对应按钮的槽函数:

运行代码:

target按钮的位置就被改变了,但是我们发现改变的很奇怪,这个改变是在调整左上角位置,左上角位置改变的同时,高度和宽度也同样发生了改变。

刚刚我们这个代码,修改的是QRect对象的x和y,这样的修改就会使QRect宽度和高度发生改变。
如果我们想要让这个按钮能够平移(宽度和高度不变,整个按钮的位置都发生改变)该怎么实现呢?
答案是:不再修改QRect,而是通过QRect基于setGeometry第二个版本的函数重新设置位置即可

运行代码:

如上我们就成功的平移target了。
我们再来一个样例,这个样例是找同学请吃饭:

创建一个文本,和两个按钮:

如上运行代码:

点击拒绝按钮后我们会发现一个问题,就是我们点击(按下,起来后)按钮才会改变,那我们能不能在按下的时候按钮的位置就改变呢?

右键按钮,点击转到槽函数后,将信号设成pressed。

将代码改成如下:
四、window frame的影响
看下图

在Qt中,关于位置尺寸,提供了很多API。有的API的位置信息是以Widget本体左上角为原点的(不考虑window frame),有的API的位置信息是以window frame左上角为原点的。
当 widget 作为窗口(包含标题栏、最小化、最大化、关闭按钮)时,尺寸和坐标计算存在两种方式:包含窗口边框和不包含窗口边框。
以下方法采用包含窗口边框的计算方式:x()、y()、
frameGeometry()、pos()、move()
以下方法采用不包含窗口边框的计算方式:geometry()、width()、height()、rect()、size()
需要注意的是,对于非窗口形式的 widget,这两种计算方式得到的结果是一致的。

代码示例: 感受 geometry 和 frameGeometry 的区别

如上我们运行了代码,但是发现它们输出的结果是一样的啊?!这是怎么回事?
当前代码是放在了构造函数中,此时这个Widget对象正在构造,还没有被加入到window frame中,因此还看不到window frame的影响。
修改上述代码:

五、windowTitle
| API | 说明 |
|---|---|
| windowTitle() | 获取到控件的窗口标题 |
| setWindowTitle(const QString& title) | 设置控件的窗口标题 |
代码样例:
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置窗⼝标题
this->setWindowTitle("这是标题");
}
上面的代码就不给出运行结果了。
六、windowlcon的属性
| API | 说明 |
|---|---|
| windowIcon() | 获取到控件的窗口图标,返回 QIcon 对象 |
| setWindowIcon(const QIcon& icon) | 设置控件的窗口图标 |
这两个API类似于windowTitle只能针对顶层窗口使用。
Qt把各种涉及到的相关概念,都封装成了类,Qlcon就表示一个图标。
代码样例:
cpp
#include <QIcon>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建图标对象
QIcon icon("d:/rose.jpg");
// 设置图标
this->setWindowIcon(icon);
}
之前推荐使用堆创建对象,主要是因为要确保当前的控件的生命周期是足够的,要通过Qt对象树来释放对象。Qlcon自身是一个比较小的对象,创建出来之后,就是要设置到某个QWidget里面,Qlcon对象本身释放不释放都不影响图标最终的显示,Qlcon也不支持对象树,无法给他执行父对象。
代码运行结果:

我们上述的代码是通过绝对路径的方式引入图片的,这样是不科学的。你写的程序最终是要发布到用户的电脑上的,你无法确保,你开发机上的图片路径和用户电脑上的图片路径完全一致。因此,相比于使用绝对路径,使用相对路径是更好的。
qrc机制:
这个机制就是从根本上解决上述的问题:
1、确保你的图片所在路径在目标用户机器上存在
2、确保你的图片不会被用户搞没
给Qt项目引入一个额外的xml文件(后缀名使用.qrc表示),在这个xml中把要使用的图片资源给导入进来,并且在xml中进行记录,Qt在编译项目的时候,就会根据qrc中描述的图片信息,找到图片内容,并且提取出图片的二进制数据,把这些二进制数据转成C++代码,最终编译到exe中。
qrc缺点:无法导入太大的资源文件
qrc的使用方式:
首先,在项目创建一个qrc文件,文件名不能带有中文和其他特殊符号

第二步:把图片导入qrc文件中

创建一个"前缀",所谓的"前缀"可以理解成虚拟目录,这个目录没有在你的电脑上真实存在,是Qt自己抽象出来的。
qrc机制本质上就是图片的二进制数据转化成C++(最终就会在代码中看到很大的char数组,里面就是图片的二进制数据),为了方便Qt代码中访问到这个图片,Qt就自己抽象出了虚拟目录。

把刚刚使用的图片导入到资源文件中,上述的按钮在创建prefix之前是禁用的,创建好了之后就可以使用了,添加文件就是添加到prefix下面的。注意导入图片的时候,需要确保你导入的图片必须在resource.qrc文件的同级目录下,或者是同级目录中的子目录里。

出现这样的效果就是导入成功了。
七、windowOpacity的属性
| API | 说明 |
|---|---|
| windowOpacity() | 获取到控件的不透明数值. 返回 float, 取值为 0.0 > 1.0 其中 0.0 表示全透明, 1.0 表示完全不透明 |
| setWindowOpacity(float n) | 设置控件的不透明数值 |
代码示例:
首先创建两个按钮

cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_add_clicked()
{
float opacity = this->windowOpacity();
if(opacity > 1.0) return;
qDebug() << opacity;
opacity += 0.1;
this->setWindowOpacity(opacity);
}
void Widget::on_pushButton_sub_clicked()
{
float opacity = this->windowOpacity();
if(opacity > 1.0) return;
qDebug() << opacity;
opacity -= 0.1;
this->setWindowOpacity(opacity);
}
运行结果:

打印结果:

上述代码中,在进行设置之前,先判定了opacity的范围,然后再决定是否要设置,这个判断其实可以不写。超过1.0的数字设置不进去,因为setWindowOpacity内部也进行了判定。
八、cursor的属性
| API | 说明 |
|---|---|
| cursor() | 获取当前控件的 cursor 属性,该属性返回一个 QCursor 对象。当鼠标悬停在该控件上时,将显示对应的光标形状。 |
| setCursor(const QCursor& cursor) | 设置该 widget 光标的形状. 仅在鼠标停留在该 widget 上时生效 |
| QGuiApplication::setOverrideCursor(const QCursor& cursor) | 全局设置光标形状。该设置会影响程序中的所有控件,并会覆盖之前通过 setCursor 方法设置的样式。 |

样例:

当光标划到按钮内,光标就会变成沙漏的等待状态。
以下是Qt中内置的光标形状:

除了上述的内置形状的光标,Qt允许我们通过自定义的图片来设置光标。
首先我们准备一张图片,把这个图片导入到项目中(qrc管理),在代码中访问到这个图片,基于这个图片构造出光标对象并设置。

再如下,代码

划红部分:这是热点所在的位置,以图片左上角为0,0原点,找到10,10这个位置作为鼠标真正的点击的位置。
如果运行上述代码发现光标很大或者很小,则可以添加下列代码:

通过这个函数对图片进行缩放。注意缩放不是修改图片本身,而是返回一个新的图片对象副本。
九、font的属性
| API | 说明 |
|---|---|
| font() | 获取当前 widget 的字体信息, 返回 QFont 对象 |
| setFont(const QFont& font) | 设置当前 widget 的字体信息 |
关于QFont
| 属性 | 说明 |
|---|---|
| family | 字体家族. 比如 "楷体", "宋体", "微软雅黑" 等 |
| pointSize | 字体大小 |
| weight | 字体粗细. 以数值方式表示粗细程度取值范围为 [0, 99], 数值越大, 越粗 |
| bold | 是否加粗, 设置为 true,相当于 weight 为 75,设置为 false 相当于weight 为 50 |
| italic | 是否倾斜 |
| underline | 是否带有下划线 |
| strikeOut | 是否带有删除线 |
代码样例:
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置 label 的⽂本内容
ui->label->setText("这是⼀段⽂本");
// 创建字体对象
QFont font;
// 设置字体家族
font.setFamily("仿宋");
// 设置字体⼤⼩
font.setPointSize(20);
// 设置字体加粗
font.setBold(true);
// 设置字体倾斜
font.setItalic(true);
// 设置字体下划线
font.setUnderline(true);
// 设置字体删除线
font.setStrikeOut(true);
// 设置字体对象到 label 上
ui->label->setFont(font);
}
运行结果:

十、toolTip的属性
| API | 说明 |
|---|---|
| setToolTip | 设置 toolTip,鼠标悬停在该 widget 上时会有提示说明 |
| setToolTipDuring | 设置 toolTip 提示的时间, 单位 ms,时间到后 toolTip 自动消失 |
toolTip 只是给用户看的,在代码中一般不需要获取到 toolTip
代码样例:
创建两个按钮

加入下列代码:
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->pushButton_yes->setToolTip("这个是 yes 按钮");
ui->pushButton_yes->setToolTipDuration(3000);
ui->pushButton_no->setToolTip("这个是 no 按钮");
ui->pushButton_no->setToolTipDuration(10000);
}
运行结果:

十一、focusPolicy的属性
设置控件获取焦点的策略,例如控制控件是否支持鼠标选中或通过Tab键切换选中。
"焦点"指的是当前被选中的元素。获得焦点后,所有后续操作(如键盘输入)都将作用于该元素。这一特性对输入框、单选按钮、复选框等控件尤为重要。
这就像在《魔兽争霸3》或《星际争霸2》中,需要先选中单位才能对其下达指令一样。

| API | 说明 |
|---|---|
| focusPolicy() | 获取该 widget 的 focusPolicy, 返回 Qt::FocusPolicy |
| setFocusPolicy(Qt::FocusPolicy policy) | 设置 widget 的 focusPolicy |
Qt::FocusPolicy 是一个枚举类型,定义了控件的焦点获取方式,包含以下取值:
- Qt::NoFocus:控件不接受键盘焦点
- Qt::TabFocus:控件可通过 Tab 键获取焦点
- Qt::ClickFocus:控件可通过鼠标点击获取焦点
- Qt::StrongFocus:默认值,控件可通过 Tab 键和鼠标点击获取焦点
- Qt::WheelFocus:在 StrongFocus 基础上增加鼠标滚轮获取焦点功能(新增选项,使用频率较低)
十二、styleSheet的属性
通过 CSS 设置 widget 的样式
CSS(层叠样式表)是一种网页前端技术,主要用于定义界面样式。
所谓"样式"包含但不限于以下元素:尺寸和位置、颜色和间距、字体和背景、边框效果
我们日常浏览的丰富多彩的网页,都大量运用了CSS技术。
虽然Qt主要用于GUI开发,但其与网页前端技术存在诸多相似之处。因此Qt框架也提供了对CSS的支持。
CSS 中可以设置的样式属性非常多. 基于这些属性 Qt 只能支持其中一部分, 称为 QSS
代码样例:
创建一个label(在右侧我们可以看到styleSheet的设置):

点击它会有一个按钮,再点击会出现一个输入框。或者是右键点击label,然后点击改变样式表也会出现这个输入框

我们设置样式,这里注意,和CSS类似,QSS设置样式也是键值对的格式。键和值之间使用:分隔,键值对和键值对之间使用;分隔

点击确定后

下面我们通过代码来设置样式,实现一个"夜间模式"功能
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_light_clicked()
{
//设置窗口样式
this->setStyleSheet("background-color: white;");
//设置输入框样式
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;");
}
运行代码:


咋一看,感觉对了,但是有没有发现这个日间模式怎么那么奇怪啊?

正常不应该是这样的吗?难道是代码出错了吗?
其实不是代码的问题,我们需要了解计算机中,使用RGB的方式来表示颜色。
举个例子,表示一个颜色:
- rgb(255, 0, 255) 前者表示红色拉满,中间表示绿色拉满,后者表示蓝色拉满。
- #FF00FF 前者表示红色拉满,中间表示绿色拉满,后者表示蓝色拉满
那我们怎么知道刚刚日间模式那个白色是怎么组成的能?我们可以用QQ截图,里面内置了一个取色器。


如图就完成了上述的程序。