Qt开发——常见控件(1)
前言
编程 讲究的是 "站在巨人的肩膀上",而不是"从头发明轮子"
一个图形化界面的内容,不需要咱们全都从零去实现,Qt中已经提供了很多内置的控件了(按钮,文本框,单选按钮,复选按钮,下拉框......)这些咱们拿过来就可以直接使用了。

控件:界面上各种元素,各种部分的"统称"
很久以前,开发GUI时是没有啥控件的概念的,界面上显示出来的东西,全都是"画"的。显示器显示的内容,可以理解为"画布",操作系统,就可以提供一些API,让你在"画布"上面进行"绘画",这时候,开发一个图形化界面的程序,就相当于先画出一个窗口"矩形"。
后来,控件这样的概念就逐渐被引入了,早期的控件比较简单,数量也比较有限,就比如HTML中包含很多标签,不同标签有不同的效果。

随着时代的发展,新的GUI开发体系越来越丰富,提供的控件数量/质量越来越丰富/优秀。

Qt的控件虽然很多,但是整体来说,颜值还是比更现代的控件体系逊色一筹。
Qt Designer 中展示的控件都是默认的样子,但是Qt还提供了一些优化的手段,可以让控件变得更好看。近几年Qt还提供了Qt Design Studio 对标的是现代化的界面体系了。
QWidget核心属性
在Qt中,使用QWidget类表示"控件"。像按钮,视图,输入框,滚动条等具体的控件类,都是继承自 QWidget。可以说,QWidget中就包含了Qt整个控件体系中,通用的部分. 在Qt Designer 中,随便拖一个控件过来,选中该控件,即可在右下方看到QWidget中的属性。

这些属性既可以通过QtDesigner会直接(在表格里)修改,也可以通过代码的方式修改
enabled
| API | 说明 |
|---|---|
| isEnabled() | 获取到控件的可用状态 |
| setEnabled | 设置控件是否可使用,true 表示可用,false 表示禁用 |

所谓"禁用"指的是该控件不能接收任何用户的输入事件,并且外观上往往是灰色的
如果⼀个widget被禁用,则该widget的子元素也被禁用
通过按钮2切换按钮1的禁用状态
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);
// QPushButton* btn = new QPushButton(this);
// btn->setText("按钮");
// btn->setEnabled(false);}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{qDebug()<<"执行了槽函数";
}void Widget::on_pushButton_enable_clicked()
{//切换第一个按钮的禁用状态//先取到第一个按钮当前的可用状态bool enable = ui->pushButton->isEnabled();//根据当前可用状态进行切换if(enable){ui->pushButton->setEnabled(false);}else{ui->pushButton->setEnabled(true);}
}
切换到不可用状态

切换回可用状态

geometry
位置和尺寸其实是4个属性的统称

x:横坐标
y:纵坐标
width:宽度
height:长度
但是实际开发中,我们并不会直接使用这几个属性,而是通过一系列封装的方法来获取/修改。
| API | 说明 |
|---|---|
| geometry() | 获取到当前控件的位置和尺寸,返回结果是⼀个QRect 包含了x,y,width,height。其中x,y是左上角的坐标 |
setGeometry(QRect) setGeometry(int x,int y,int width,int height) | 设置控件的位置和尺寸,可以直接设置⼀个QRect 也可以分四个属性单独设置 |
move只能修改位置,SetGeometry既能够修改位置又可以修改尺寸
代码示例:
在界面中拖五个按钮

然后根据按钮的名称修改一下objectname
修改属性的时候一定要确认好你要当前选中的控件是否是你要修改的控件

期望通过点击这几个按钮,就能够修改target的按钮的geometry
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QRect>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_up_clicked()
{QRect rect = ui->pushButton_target->geometry();qDebug() << rect;rect.setY(rect.y()-5);//向下移动5个像素ui->pushButton_target->setGeometry(rect);
}void Widget::on_pushButton_down_clicked()
{QRect rect = ui->pushButton_target->geometry();qDebug()<<rect;rect.setY(rect.y()+5);ui->pushButton_target->setGeometry(rect);
}void Widget::on_pushButton_right_clicked()
{QRect rect = ui->pushButton_target->geometry();qDebug()<<rect;rect.setX(rect.x()+5);ui->pushButton_target->setGeometry(rect);
}void Widget::on_pushButton_left_clicked()
{QRect rect = ui->pushButton_target->geometry();qDebug()<<rect;rect.setX(rect.x()-5);ui->pushButton_target->setGeometry(rect);
}
当前代码实际执行的效果,是在调整左上角的位置,左上角位置改变的同时,高度和宽度也同时发生了改变。
那么如果想让这个按钮发生平移(高度、宽度不变位置发生改变),如何实现呢?
刚才的代码,修改的是QRect 对象的x和y,这样修改就会使QRect宽度高度发生改变
如果要实现平移的话,不再修改QRect,而是通过QRect基于setGeometry 第二个版本的函数重新设置即可。
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QRect>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_up_clicked()
{QRect rect = ui->pushButton_target->geometry();qDebug() << rect;//移动按钮ui->pushButton_target->setGeometry(rect.x(),rect.y()-5,rect.width(),rect.height());//rect.setY(rect.y()-5);//向下移动5个像素//ui->pushButton_target->setGeometry(rect);
}void Widget::on_pushButton_down_clicked()
{QRect rect = ui->pushButton_target->geometry();qDebug()<<rect;ui->pushButton_target->setGeometry(rect.x(),rect.y()+5,rect.width(),rect.height());//修改target按钮的位置及大小//rect.setY(rect.y()+5);//ui->pushButton_target->setGeometry(rect);
}void Widget::on_pushButton_right_clicked()
{QRect rect = ui->pushButton_target->geometry();qDebug()<<rect;ui->pushButton_target->setGeometry(rect.x()+5,rect.y(),rect.width(),rect.height());//修改target按钮的位置及大小//rect.setX(rect.x()+5);//ui->pushButton_target->setGeometry(rect);
}void Widget::on_pushButton_left_clicked()
{QRect rect = ui->pushButton_target->geometry();qDebug()<<rect;ui->pushButton_target->setGeometry(rect.x()-5,rect.y(),rect.width(),rect.height());//修改target按钮的位置及大小//rect.setX(rect.x()-5);//ui->pushButton_target->setGeometry(rect);
}
验证一下效果

window frame 的影响
如果widget作为⼀个窗口(带有标题栏,最小化,最大化,关闭按钮),那么在计算尺寸和坐标的 时候就有两种算法。包含window frame和不包含 window frame。
window frame 窗口框架——就是上边的一条小标题栏,还有下边一个框框细边(限制界面)
其中 x(),y(),frameGeometry(),pos(),move()都是按照包含window frame的方式来计算的。
其中geometry(),width(),height(),rect(),size()则是按照不包含window frame的方式来计算的
当然,如果一个不是作为窗口的widget ,上述两类方式得到的结果是一致的

编写代码观察区别
怎么这两个结果都一样啊?

当前的代码放在了构造函数里边了,此时这个 widget 对象还在构造呢,还没有被加入到window frame里边。当然看不到window frame 的影响。
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include <QPushButton>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//此处直接针对 widget 对象 来使用 geometry 和 frameGeometry 然后观察区别
// QRect rect1 = this->geometry();
// QRect rect2 = this->frameGeometry();// qDebug() << rect1;
// qDebug() << rect2;QPushButton* btn = new QPushButton(this);btn->setText("按钮");btn->move(300,300);connect(btn,&QPushButton::clicked,this,&Widget::handle);}void Widget::handle()
{QRect rect1 = this->geometry();QRect rect2 = this->frameGeometry();qDebug() << rect1;qDebug() << rect2;
}Widget::~Widget()
{delete ui;
}

windowTitle
windowTitle()
获取到控件的窗口标题
setWindowTitle(const QString& title)
设置控件的窗口标题
当前 windowTitle 属性,是从属于 QWidget的,而QWidget 是一个广泛的概念。
windowTitle属性,只能针对 顶层窗口 这样的QWidget 才有效。

当前不应该给按钮设置 window Title ,但是实际设置了之后,没有任何效果,也没有任何报错。此时,"没有报错"这样的设定是不太科学的。
windowIcon
windowIcon()
获取到控件的窗口图标,返回QIcon对象。
setWindowIcon(const QIcon& icon)
设置控件的窗口图标
程序左上角一般都有一个小图标
![]()
注意:这两api也类似于window Title 只能针对顶层窗口使用。
之前推荐使用堆来创建对象,主要是因为要确保当前的控件的生命周期是足够的,要通过Qt对象树来释放对象。而QIcon自身是一个比较小的对象,创建出来之后,就是要设置到某个QWidget里边。QIcon对象本身释放不释放,不影响图标最终的显示。
QIcon 也不支持对象树,无法给它指定一个父对象。
#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:/rose.webp");//设置图标this->setWindowIcon(icon);
}Widget::~Widget()
{delete ui;
}
qrc机制
使用了/作为路径分割符,是否可以使用\呢?
如果使用\,那么\r此时会被认为是一个字符,C++11中引入了raw string来解决这个问题。字符串里不包含任何转义字符(所有的字符都不会转义)。写法:r("D:\rose.webp")
刚才我们使用绝对路径的方式来引入图片,但实际开发中,我们一般不会在代码中通过绝对路径引入图片。因为我们无法保证程序发布后,用户的电脑上也有同样的路径(就比如:如果用户只有C盘而没有D盘,就找不到我们上边设置的图片了)。
因此相比于使用绝对路径的方式,使用相对路径是更好的。
相对路径,是以给定目录为基准,以 . 或者 .. 的方式开头。
如果使用相对路径,则需要确保代码中的相对路径写法和图片实际所在的路径匹配(比如代码 中写作"./image/rose.jpg",就需要在当前工作目录中找到image目录,并把找到rose.jpg)
qrc机制,这个机制就是从根本上解决两个问题
1.确保你的图片所在的路径在目标用户机器上存在。
2.确保你的图片不会被用户搞没了
给QT项目引入一个额外的xml 文件(后缀名用 .qrc表示)
在这个xml中把要使用的图片导入进来,并且在 xml 中进行记录
Qt 在编译项目的时候,就会根据qrc中描述的图片信息,找到图片内容,并且提取出图片的二进制数据,把这些二进制数据转成C++代码,最终编译到exe里边。
qrc缺点:
无法导入太大的资源文件。
qrc的使用方式
1.在项目中创建一个qrc文件
文件名不要带中文和特殊符号

文件名

创建完成后就得到了这样一个界面(可视化编辑器)

2.把图片导入到qrc文件中
1)先创建一个"前缀"(Prefix)
所谓的"前缀",可以理解为 虚拟的目录。这个目录没有在你的电脑中真实存在,而是Qt自己抽象出来的。qrc机制本质就是把 图片 的二进制数据转成C++代码(最终就会在代码中看到很大的char数组,里边就是图片的二进制数据)
为了方便Qt代码中访问到这个图片,qt就自己抽象出了虚拟的目录
直接把prefix改为/

2)把刚才使用的图片给导入到资源文件中

创建prefix后这个按钮就可以用了,点击这个按钮后会弹出当前代码工作目录

导入图片的时候,需要确保你的图片必须在resource.qrc文件的同级目录,或者同级目录中的子目录里。

现在就导入成功了
#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:/rose.webp");QIcon icon(":/rose.webp");//设置图标this->setWindowIcon(icon);
}Widget::~Widget()
{delete ui;
}
当代码中需要访问qrc中管理的文件时,就需要在路径上带有:前缀
再次运行程序

这里边多了一个qrc_resource.cpp

打开一下看看

这里的字节中的内容就是图片里的每个字节的数据
当qt项目进行编译的时候,这个cpp文件就被一起编译到了exe中,当exe程序运行的时候,上述图片也就被加载到内存中了。
