【QT】常⽤控件详解(一)QWidget核⼼属性_控件

@TOC


📝导言:控件概述

Widget 是Qt中的核⼼概念.英⽂原义是"⼩部件",我们此处也把它翻译为"控件".

【QT】常⽤控件详解(一)QWidget核⼼属性_控件_02

  • 按钮,列表视图,树形视图,单⾏输⼊框,多⾏输⼊框,滚动条,下拉框等,都可以称为"控件".
  • Qt 作为⼀个成熟的GUI开发框架,内置了⼤量的常⽤控件.这⼀点在QtDesigner中就可以看到端倪.
  • 并且Qt也提供了"⾃定义控件"的能⼒,可以让程序猿在现有控件不能满⾜需求的时候,对现有控件做出扩展,或者⼿搓出新的控件.

综上,学习Qt,其中⼀个很重要的任务就是熟悉并掌握Qt内置的常⽤控件. 这些控件对于我们快速开发出符合需求的界⾯,是⾄关重要的.

一、QWidget核⼼属性

使⽤QWidget类表⽰"控件".像按钮,视图,输⼊框,滚动条等具体的控件类,都是继承⾃QWidget. QWidget中就包含了Qt整个控件体系中,通⽤的部分

QtDesigner中,随便拖⼀个控件过来,选中该控件,即可在右下⽅看到QWidget中的属性

【QT】常⽤控件详解(一)QWidget核⼼属性_控件_03

这些属性既可以通过QtDesigner会直接修改,也可以通过代码的⽅式修改. 这些属性的具体含义,在QtAssistant中均有详细介绍.

在QtAssistant中搜索QWidget,即可找到对应的⽂档说明.(或者在QtCreator代码中,选中QWidget, 按F1也可).

二、enabled

2.1 API使用

API

说明

isEnabled()

获取到控件的可用状态.

setEnabled

设置控件是否可使用. true 表示可用,false 表示禁用.

所谓"禁⽤"指的是该控件不能接收任何⽤⼾的输⼊事件,并且外观上往往是灰⾊的.如果⼀个widget被禁⽤,则该widget的⼦元素也被禁⽤.

代码⽰例:使⽤代码创建⼀个禁⽤状态的按钮

widget.cpp:

#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{QPushButton* btn = new QPushButton("这是一个被禁用的按钮", this);btn->setEnabled(false);ui->setupUi(this);
}Widget::~Widget()
{delete ui;
}

【QT】常⽤控件详解(一)QWidget核⼼属性_控件_04

运⾏程序,可以看到按钮处于灰⾊状态,⽆法被点击

2.2 代码⽰例

:通过按钮2切换按钮1的禁⽤状态.

  1. 使⽤QtDesigner拖两个按钮到Widget中.两个按钮的objectName 分别为pushButtonpushButton_2, 转到槽⽣成两个按钮的slot函数
  • 使用isEnabled获取当前按钮的可用状态.
  • 使用setEnabled修改按钮的可用状态.此处是直接针对原来的可用状态进行取反后设置.

运⾏程序,可以看到,初始情况下,上⾯的按钮是可⽤状态.点击下⽅按钮,即可使上⽅按钮被禁⽤;再次点击下⽅按钮,上⽅按钮就会解除禁⽤.(禁⽤状态的按钮为灰⾊,且不可点击).

void Widget::on_pushButton_clicked()
{qDebug()<<"按下按钮";
}void Widget::on_pushButton_2_clicked()
{bool flag = this->ui->pushButton->isEnabled();this->ui->pushButton->setEnabled(!flag);
}

【QT】常⽤控件详解(一)QWidget核⼼属性_控件_05

QobjectobjectName属性介绍:Qobject 是QWidget的父类.里面最主要的属性就是objectName .在一个Qt程序中, objectName相当于对象的身份标识,彼此之间不能重复.在使用Qt Designer时,尤其是界面上存在多个widget 的时候,可以通过objectName获取到指定的widget对象.Qt Designer生成的ui文件,本身是xml格式的. qmake 会把这个xml文件转换成C++的.h文件(这个文件生成在 build目录中),构成一个ui_widget类.每个widgetobjectName最终就会成为ui_widget类的属性名字.最终这个类的实例,就是(Ui : : widget *ui,因此就可以通过形如ui->pushButton或者ui->pushButton_2这样的代码获取到界面上的widget对象了.

在QtDesigner中创建按钮的时候,可以设置按钮的初始状态是"可⽤"还是"禁⽤".如果把enabled这⼀列的对钩去掉,则按钮的初始状态就是"禁⽤"状态.

【QT】常⽤控件详解(一)QWidget核⼼属性_控件_06

三、 geometry

3.1 API使用

位置和尺⼨.其实是四个属性的统称:

  • x横坐标
  • y纵坐标
  • width宽度
  • height高度但是实际开发中,我们并不会直接使⽤这⼏个属性,⽽是通过⼀系列封装的⽅法来获取/修改 对于Qt的坐标系,不要忘记是⼀个"左⼿坐标系".其中坐标系的原点是当前元素的⽗元素的左上⻆. API说明geometry()获取到控件的位置和尺⼨.返回结果是⼀个QRect,包含了x,y,width,height.其中x,y是左上⻆的坐标.

API

说明

geometry()

获取到控件的位置和尺寸。返回结果是一个 QRect,包含了 x, y, width, height。其中 x, y 是左上角的坐标。

setGeometry(QRect)

设置控件的位置和尺寸。可以直接设置一个 QRect,也可以分四个属性单独设置。

setGeometry(int x, int y, int width, int height)


2.2 简单示例

代码⽰例:控制按钮的位置

  1. 在界⾯中拖五个按钮.

五个按钮的objectName分别为

pushButton_target pushButton_down pushButton_up pushButton_left 
pushButton_right

【QT】常⽤控件详解(一)QWidget核⼼属性_控件_07

2) 在widget.cpp中编写四个按钮的slot函数

void Widget::on_PushButton_up_clicked()
{QRect rect = ui->PushButton_target->geometry();rect.setY(rect.y() - 5);ui->PushButton_target->setGeometry(rect);
}void Widget::on_PushButton_down_clicked()
{QRect rect = ui->PushButton_target->geometry();rect.setY(rect.y() + 5);ui->PushButton_target->setGeometry(rect);
}void Widget::on_PushButton_left_clicked()
{QRect rect = ui->PushButton_target->geometry();rect.setX(rect.x() - 5);ui->PushButton_target->setGeometry(rect);
}void Widget::on_PushButton_right_clicked()
{QRect rect = ui->PushButton_target->geometry();rect.setX(rect.x() + 5);ui->PushButton_target->setGeometry(rect);
}

【QT】常⽤控件详解(一)QWidget核⼼属性_标题栏_08

运⾏程序,可以看到,按下下⽅的四个按钮,就会控制target的左上⻆的位置.对应的按钮整个尺⼨也会发⽣改变.

上述代码中我们是直接设置的QRect中的x,y.实际上QRect内部是存储了左上和右下两个点的坐标,再通过这两个点的坐标差值计算⻓宽.单纯修改左上坐标就会引起整个矩形的⻓宽发⽣改变.

如果想让整个按钮都移动,可以改成下列代码:

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());
}

上述代码使用move方法也是可以的.

代码⽰例: 点我抢红包1)往界面上拖拽两个按钮和一个Label.LabelobjectNamepushButton_acceptpushButton_reject , label的objectName为label控件中文本如下图所示.

【QT】常⽤控件详解(一)QWidget核⼼属性_控件_09

2) 在widget.cpp中添加slot函数

void Widget::on_pushButton_not_grabbing_clicked()
{ui->label->setText("恭喜抢到红包!");
}void Widget::on_pushButton_rob_clicked()
{//获取窗口的宽度和高度int windowwidgth = this->geometry().width();int windowheight = this->geometry().height();//重新生成位置int x = rand() % windowwidgth;int y = rand() % windowheight;//移动按钮ui->pushButton_rob->move(x, y);
}

运⾏程序,可以看到,当“我抢”点击时,按钮就跑了.

【QT】常⽤控件详解(一)QWidget核⼼属性_控件_10

更狠一点,代码使⽤的是pressed,⿏标按下未松开,按钮就跑了

void Widget::on_pushButton_rob_pressed()
{//获取窗口的宽度和高度int windowwidgth = this->geometry().width();int windowheight = this->geometry().height();//重新生成位置int x = rand() % windowwidgth;int y = rand() % windowheight;//移动按钮ui->pushButton_rob->move(x, y);
}

【QT】常⽤控件详解(一)QWidget核⼼属性_控件_11

四、windowframe

4.1 API使用

如果widget作为⼀个窗⼝(带有标题栏,最⼩化,最⼤化,关闭按钮),那么在计算尺⼨和坐标的时候就有两种算法.包含windowframe和不包含windowframe.其中x(),y(), frameGeometry(), pos(), move() 都是按照包含windowframe的⽅式来计算的.其中geometry(),width(), height(), rect(), size() 则是按照不包含windowframe的⽅式来计算的.当然,如果⼀个不是作为窗⼝的widget,上述两类⽅式得到的结果是⼀致的

核心概念

  • pos() / x() / y():窗口整体(含标题栏、边框)的左上角在屏幕坐标系的位置,x() 是水平坐标,y() 是垂直坐标,pos() 返回 (x, y) 点。
  • geometry():指窗口客户区(Client Area,即标题栏下方可绘制内容的区域 )的几何信息,geometry().x() / geometry().y() 是客户区左上角相对窗口整体的偏移(因标题栏存在,通常 y 有正值 ),geometry().width() / geometry().height() 是客户区的宽高。
  • width() / height():等价于 geometry().width() / geometry().height(),直接获取客户区宽高。
  • frameGeometry():窗口整体(含标题栏、边框)的几何信息,frameGeometry().width() / frameGeometry().height() 是窗口整体的宽高,包含边框、标题栏的尺寸。

【QT】常⽤控件详解(一)QWidget核⼼属性_控件_12

API

说明

x()

获取横坐标

计算时包含 window frame

y()

获取纵坐标

计算时包含 window frame

pos()

返回 QPoint 对象,里面包含 x(), y(), setX(), setY() 等方法

计算时包含 window frame

frameSize()

返回 QSize 对象,里面包含 width(), height(), setWidth(), setHeight() 等方法

计算时包含 window frame

frameGeometry()

返回 QRect 对象。QRect 相当于 QPointQSize 的结合体,可以获取 x, y, width, size

计算时包含 window frame 对象

width()

获取宽度

计算时不包含 window frame

height()

获取高度

计算时不包含 window frame

size()

返回 QSize 对象,里面包含 width(), height(), setWidth(), setHeight() 等方法

计算时不包含 window frame

rect()

返回 QRect 对象。QRect 相当于 QPointQSize 的结合体,可以获取并设置 x, y, width, size

计算时不包含 window frame 对象

geometry()

返回 QRect 对象。QRect 相当于 QPointQSize 的结合体,可以获取 x, y, width, size

计算时不包含 window frame 对象

setGeometry()

直接设置窗口的位置和尺寸。可以设置 x, y, width, height,或者 QRect 对象

计算时不包含 window frame 对象

4.2 简单示例

代码⽰例:感受geometry和frameGeometry的区别.1)在界⾯上放置⼀个按钮.

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QRect rect1 = this->geometry();QRect rect2 = this->frameGeometry();qDebug() << rect1;qDebug() << rect2;}
  1. 在按钮的slot函数中,编写代码

为啥结果一下?在 Widget 构造函数中,窗口还未完成最终的绘制和布局计算。此时 Qt 尚未为窗口添加边框和标题栏(这些属于窗口管理器的装饰),因此 frameGeometry()(窗口整体,含边框)和 geometry()(客户区)暂时指向同一个区域。

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{QRect rect1 = this->geometry();QRect rect2 = this->frameGeometry();qDebug() << rect1;qDebug() << rect2;
}

【QT】常⽤控件详解(一)QWidget核⼼属性_Qt_13

对于标准窗口,标题栏通常只在垂直方向增加高度(如下方示例中的 630-600=30 就是标题栏高度),而水平方向的边框宽度可能被 Qt 计入客户区宽度计算,或在特定样式下被优化为与客户区宽度一致。

geometry:    QRect(454,156 800x600)  // 客户区:宽800,高600
frameGeometry: QRect(454,126 800x630)  // 整体:宽800(和客户区相同),高630(比客户区多30,即标题栏高度)
宽度方向无差异,说明水平方向没有额外的边框宽度,或边框宽度被包含在客户区宽度计算中。

在构造⽅法中,Widget刚刚创建出来,还没有加⼊到对象树中.此时也就不具备Window frame.

在按钮的slot函数中,由于⽤⼾点击的时候,对象树已经构造好了,此时Widget已经具备了Windowframe,因此在位置和尺⼨上均出现了差异.

如果把上述代码修改成打印pushButton的geometry和frameGeometry,结果就是完全相同的.因为pushButton并⾮是⼀个窗⼝.

五、windowTitle

5.1常用API:

API

说明

windowTitle()

获取到控件的窗口标题.

setWindowTitle(const QString& title)

设置控件的窗口标题.

注意!上述设置操作针对不同的widget可能会有不同的⾏为.如果是顶层widget(独⽴窗⼝),这个操作才会有效.如果是⼦widget,这个操作⽆任何效果.

5.2 代码⽰例

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//设置窗口标题this->setWindowTitle("这是标题");
}

【QT】常⽤控件详解(一)QWidget核⼼属性_Qt_14

六、 windowIcon

6.1API使用

API

说明

windowIcon()

获取到控件的窗口图标. 返回 QIcon 对象.

setWindowIcon(const QIcon& icon)

设置控件的窗口图标.

windowTitle,上述操作仅针对顶层widget有效.

【QT】常⽤控件详解(一)QWidget核⼼属性_控件_15

注意: Windows下路径的分隔符可以使用

/

也可以使用

\

.但是如果在字符串中使用

\

需要写作转义字符的形式

\\

.因此我们还是更推荐使用

/

.

绝对路径:以盘符(windows)或者以/(Linux)开头的路径.相对路径:以.(表示当前路径)或者以..(表示当前路径上级路径)开头的路径.其中.经常也会省略.相对路径的前提是需要明确"当前工作目录".

对于Qt程序来说,当前⼯作⽬录可能是变化的.⽐如通过QtCreator运⾏的程序,当前⼯作⽬录是项⽬的构建⽬录;直接双击exe运⾏,⼯作⽬录则是exe所在⽬录.

【QT】常⽤控件详解(一)QWidget核⼼属性_标题栏_16

6.2 代码⽰例

代码⽰例:获取当前的⼯作⽬录

  1. 在界⾯上创建⼀个⽐较⼤的label,确保能把路径显⽰完整.objectName使⽤默认的label 即可.
  2. 修改widget.cpp使⽤QDir::currentPath() 即可获取到当前⼯作⽬录
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//设置窗口标题this->setWindowTitle("这是哈基米");//创建图标对象//路径不能带中文,不能使用反斜杠\,因为\会跟后面转译, 如果要使用的是\\才可以QIcon icon("C:/Users/Acer/Desktop/image/hachimitsu.jpg");//设置图标this->setWindowIcon(icon);//获取到当前的工作目录QString currentDir = QDir::currentPath();//设置工作目录到label中ui->label->setText(currentDir);
}
  1. 直接在QtCreator中执⾏程序,可以看到当前⼯作⽬录是项⽬的构建⽬录.
  2. 进⼊上述构建⽬录,把⾥⾯的exe拷⻉到其他⽬录中,⽐如D:中.再次执⾏程序,可以看到当前⼯作⽬录已经发⽣改变.

要想直接能双击exe运⾏,需要先把Qt的路径添加到path环境变量中,否则会提⽰找不到动态库.注意,上述构建⽬录,是随时可删除的.⽐如点击菜单栏中的"构建"->"清理项⽬",就会把这个⽬录中的内容清空掉.因此如果我们把图⽚⽂件放到构建⽬录中,可能在不⼩⼼删除后就丢失了.我们还是希望能够把图⽚和 源代码放到⼀起,并且使我们的程序⽆论拷⻉到任何位置中都能正确使⽤图⽚.

七、Qt 使⽤qrc机制

qrc机制帮我们⾃动完成了上述⼯作,更⽅便的来管理项⽬依赖的静态资源

  • qrc⽂件是⼀种XML格式的资源配置⽂件,它⽤XML记录硬盘上的⽂件和对应的随意指定的资源名称. 应⽤程序通过资源名称来访问这些资源.
  • Qt开发中,可以通过将资源⽂件添加到项⽬中来⽅便地访问和管理这些资源.这些资源⽂件可以位于qrc⽂件所在⽬录的同级或其⼦⽬录下.
  • 在构建程序的过程中,Qt会把资源⽂件的⼆进制数据转成cpp代码,编译到exe中.从⽽使依赖的资源变得"路径⽆关".
  • 这种资源管理机制并⾮Qt独有,很多开发框架都有类似的机制.例如Android的Resources和AssetManager也是类似的效果.

代码⽰例:通过qrc管理图⽚作为图标

  1. 右键项⽬,创建⼀个QtResourceFile(qrc⽂件),⽂件名随意起(不要带中⽂),此处叫做resource.qrc

【QT】常⽤控件详解(一)QWidget核⼼属性_Qt_17

2)在qrc编辑器中,添加前缀.

【QT】常⽤控件详解(一)QWidget核⼼属性_Qt_18

此处我们前缀设置成/即可.所谓的前缀,可以理解成"⽬录".这个前缀决定了后续我们如何在代码中访问资源.3) 在资源编辑器中,点击add Files 添加资源⽂件.此处我们需要添加的是hachimitsu.jpg

【QT】常⽤控件详解(一)QWidget核⼼属性_标题栏_19

添加完毕后,可以在资源编辑器中看到添加好的⽂件

【QT】常⽤控件详解(一)QWidget核⼼属性_标题栏_20

4) 在代码中使⽤hachimitsu.jpg编辑widget.cpp

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//访问 到`hachimitsu.jpg`资源QIcon icon(":/hachimitsu.jpg");//设置图标this->setWindowIcon(icon);
}

【QT】常⽤控件详解(一)QWidget核⼼属性_控件_21

注意上述路径的访问规则.

  • 使⽤: 作为开头,表⽰从qrc中读取资源.
  • /是上⾯配置的前缀
  • hachimitsu.jpg是资源的名称需要确保代码中编写的路径和添加到qrc中资源的路径匹配.否则资源⽆法被访问(同时也不会有报错提⽰).

接下来,我们可以进⼊到项⽬的构建⽬录,可以看到,⽬录中多了⼀个接打开这个⽂件,可以看到类似如下代码:

【QT】常⽤控件详解(一)QWidget核⼼属性_Qt_22

上述代码其实就是通过unsignedchar数组,把hachimitsu.jpg中的每个字节都记录下来.这些代码会被编译到exe中.后续⽆论exe被复制到哪个⽬录下,都确保能够访问到该图⽚资源.

上述qrc这⼀套资源管理⽅案,优点和缺点都很明显.优点:确保了图⽚,字体,声⾳等资源能够真正做到"⽬录⽆关",⽆论如何都不会出现资源丢失的情况.缺点:不适合管理体积⼤的资源.如果资源⽐较⼤(⽐如是⼏个MB的⽂件),或者资源特别多,⽣成的最终的exe体积就会⽐较⼤,程序运⾏消耗的内存也会增⼤,程序编译的时间也会显著增加.


🚩总结

【QT】常⽤控件详解(一)QWidget核⼼属性_控件_23