一、控件概述
编程,讲究的是站在巨人的肩膀上,而不是从头发明轮子。一个图形化界面上的内容,不需要咱们全都从零区实现,Qt中已经提供了很多内置的控件了(按钮,文本框,单选按钮,复选按钮,下拉框)。我们拿过来就可以直接使用。
控件Widget,Qt Designer左侧的这一长条就是Qt内置的控件。
上古时期,开发GUI,没啥控件的概念,界面上显示出来的东西,都是画出来的。显示器显示的内容,可以理解成画布,操作系统就可以提供一些API,让你在画布上面进行画点画线,画矩形,画三角形,填充各种颜色。这种风格的开发其实已经非常古老了。
后来控件这样的概念就逐渐引入了。早期的控件比较简单,数量也比较有限。HTML包含很多标签。不同的标签有不同的效果。随着时代的发展,新的GUI开发体系越来越丰富,提供的控件数量/质量越来越提升了。Qt的控件虽然很多,但是整体来说,颜值还是比更现代的控件体系,要逊色一筹。Qt提供了一些优化手段,可以让控件变的更好看。Qt近几年还提供了Qt Design Studio =》对标现代化的界面体系了。制作出来的界面的美观程度就是业界最领先的一档。开发图形化界面程序,颜值确实还是一个挺重要的事情。
二、QWidget的enable属性
Qt中的各种控件都是继承自QWigdet,Qt控件体系中,通用的部分。
在这里可以直接编辑该控件的属性。这些属性不需要都了解,可以了解一些常用的属性。
enabled 描述了一个控件是否处于可用的状态。相对的概念是禁用。禁用值得是不能接收任何用户的输入事件,并且外观上往往是灰色的。
操作的API。
代码:
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
button2 = new QPushButton(this);
button1 = new QPushButton(this);
button2->move(100,0);
button1->setText("你好");
button2->setEnabled(true);
button2->setText("设置可用");
button1->setEnabled("true");
connect(button2,&QPushButton::clicked,this,&Widget::handler);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handler()
{
if(button1->isEnabled())
{
button1->setEnabled(false);
button1->setText("不你好");
}
else if(!button1->isEnabled())
{
button1->setEnabled(true);
button1->setText("你好");
}
}
在同一个界面中,要求不同的控件objectName也是必须不相同的(不能重复),后续就可以通过ui->objectName方式来获取到对应的控件对象了。元编程的概念,Qt会根据ui文件,生成一个ui_widget.h文件,生成的过程中就会感知到。界面上都有哪些控件,每个控件的objectName。很明显,以数字的方式命名,不是一个好的编程习惯。
三、QWidget的geometry的属性
计算中有时候会涉及到一些拗口的单词,大家一定要会拼写,会发音。
几何,可以把这个东西视为四个属性的统称。 x、y、width、height当前控件的位置和尺寸。
API
QRect就是矩形类,Qt中针对一些几何上的概念也进行了封装。QPoint表示一个点,QRect表示一个矩形。属于是小对象,里面的属性非常少,占用空间也少。C++中使用上述对象,通常就会按照值得方式来传递参数了。move只是修改位置,setGeometry既可以修改位置,也可以修改尺寸。
代码:期望通过点击四个方向键按钮,就能够修改target按钮得geometry。
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_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
void Widget::on_pushButton_up_clicked()
{
//得到旧的
QRect rect = ui->pushButton_target->geometry();
rect.setY(rect.y()-5);
ui->pushButton_target->setGeometry(rect);
//设置新的
//ui->pushButton_target->setGeometry(rect.x(),rect.y()-5,rect.width(),rect.height());
}
当前代码实际执行的效果。是在调整左上角的位置,左上角位置改变的同时,高度和宽度也同时发生了改变。刚才修改的代码,修改的是QRect对象的x和y,这样修改就会使QRect宽度高度发生改变。如何才能实现平移的效果,保持尺寸的不变,整个按钮位置变化?不在修改QRect,而是通过QRect基于setGeometry第二个版本的函数重新设置位置即可。
代码:给女神表白的例子,pressed鼠标按下就触发信号。也可以做到,鼠标不点击,只要挪动按钮上,就会让按钮移动。需要使用到Qt中的事件机制。后面我们去编写上述的代码。
cpp
#include "widget.h"
#include "ui_widget.h"
#include <random>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
srand(time(0));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_accept_clicked()
{
ui->label->setText("女神,快来嘴一个,mua~~");
}
void Widget::on_pushButton_reject_pressed()
{
int height = this->geometry().height();
int width = this->geometry().width();
int x = rand()%width;
int y = rand()%height;
ui->pushButton_reject->move(x,y);
}
window_frame的影响。
window frame窗口框架。操作系统自带的。按钮的父元素窗口widget。在Qt中,关于位置尺寸,提供了很多的API,有的考虑window frame,不以window frame左上角为源点。有的不考虑window frame,以window frame左上角为源点
代码:
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QRect rect = this->geometry();
QRect rect1 = this->frameGeometry();
qDebug()<< rect;
qDebug()<< rect1;
}
当前代码是放到了构造函数,此时这个Widget对象正在构造,还没有被加入到window frame中,因此,此时还看不到window frame的影响。
代码:
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// QRect rect = this->geometry();
// QRect rect1 = this->frameGeometry();
// qDebug()<< rect;
// qDebug()<< rect1;
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
QRect rect = this->geometry();
QRect rect1 = this->frameGeometry();
qDebug()<< rect;
qDebug()<< rect1;
}
四、QWidget的QWindowsTitle属性
当前的QWindowsTitle属性,是从属于QWidget,QWidget是一个广泛的概念,windwsTitle属性只能针对顶层窗口这样的QWidget才有效。当前不应该给按钮设置window Title,但是实际设置了之后,没有任何效果,也没有报错。此时没有有报错这样的设定是不太科学的。
这一点更希望当代码写出不科学的代码的时候,能够给一些报错提示。
五、QWidget的windowIcon属性
windowIcon表示窗口的图标。
Qt把各种涉及到的相关概念,都封装了类,QIcon就表示一个图标。
这两api类似于windowTitle只能针对于顶层窗口使用。需要先准备一个图片。
包含头文件QIcon,Qt封装的这些类,都会有同名的头文件。路径不要带中文,使用了/作为路径的分隔符,但是不能使用 \ 作为分割符。
使用qrc导入
代码:
通过绝对路径的方式引入图片是不科学的,你写的程序,最终要发布到用户的电脑上的。你无法确保你开发机上图片的路径和用户电脑上图片的路径完全一致。因此相比于使用绝对路径的方式,使用相对路径是更好的。相对路径,是以给定目录为基准,以. 或者 .. 的方式开头。
qrc机制这个机制就是从根本上解决上述的两个问题:
1、确保你的图片所在的路径在目标用户机器上存在。
2、确保你的图片不会被用户搞没了。
给Qt项目引入一个额外的xml文件,在这个xml中把要使用的图片资源给导入进来,并且在xml中进行记录。Qt在编译项目的时候,就会根据qrc中描述的图片信息,找到图片内容,并且提取出图片的二进制数据,把这些二进制数据转换成C++代码,最终编译到exe中。
qrc缺点:无法导入太大的资源文件,比如搞几个GB这种视频文件,qrc无能为力了。
qrc使用方式
1、在项目中创建一个qrc文件。文件名和文件路径不要带中文和特殊符号。
2、把图片导入到qrc文件中
1)先创建一个前缀,所谓的前缀可以理解成虚拟目录 ,这个目录没有在你的电脑上真是存在,是Qt自己抽象出来的,qrc机制本质上就是把图片的二进制数据转成C++代码,(最终就会在代码中看到很大的char数组,里面就是图片的二进制数据)为了方便Qt代码中访问到这个图片,Qt就自己抽象出了虚拟的目录。
把prefix的名字就改成 / 即可
2)把刚才使用的图片导入到资源文件中
Add Files按钮在创建prefix之前是禁用的。创建好prefix之后就可以使用了,添加的文件就是添加到prefix下面的。点击Add Files 得到的目录就是当前代码所在的目录。
导入图片的时候,需要确保你导入的图片必须在resource.qrc文件的同级目录,或者同级目录中的子目录里。
看到这个效果就证明导入成功
当代码中需要访问qrc中管理的文件时,就需要在路径上带有冒号前缀。qrc中导入的图片资源,就会被转成这个qrc_resource.cpp这个C++代码。
这里的字节内容就是图片里的每个字节的数据。当Qt项目进行编译的时候,这个cpp文件就被一起编译到了exe中。当exe程序运行的时候,上述图片的数据也就被加载到内存中了。
六、QWidget的windowOpacity属性
设置窗口的透明度
半透明效果。opacity相比于叫做透明度,可能叫做不透明度更适合。数值越大,越不透明。
代码:
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
float opacity = this->windowOpacity();
this->setWindowOpacity(opacity+0.1);
qDebug() << opacity;
}
void Widget::on_pushButton_2_clicked()
{
float opacity = this->windowOpacity();
this->setWindowOpacity(opacity-0.1);
qDebug() << opacity;
}
两个问题:
1、窗口的不透明度,变化并非是精确的!
C语言中 数据在内存中的存储,整数在内存中的存储,浮点数在内存中的存储。
IEEE 754规定了浮点数要使用二进制科学计数法的方法来表示!浮点数被分为三个部分,符号位,有效数字,指数部分。有效数字使用二进制表示的有效数字,而且这里是小于1的小数部分(默认整数是1)。由于float和double有效数字部分长度都是有限的,导致无法凑出一个非常接近0.1这样的数字。很多编程语言C/C++,Java都是这套体系。这么做的优点是:运算速度快,占用空间小。(CPU制造的时侯针对这种运算专门优化的)。缺点:对于有些小数无法精确表示。千万不能把两个浮点数直接使用 == 做比较。正确写法,做差判定差的绝对值小于预期误差范围。
2、这里加了判定条件,实际上如果不加这样的判定条件,代码也是ok的。超过1.0的数字设置不进去(setWindowOpacity内部也进行了判定)。在上述代码中,在进行设置之前,先判定了opacity的范围,然后再决定是否要设置。这个判定其实可以不屑,为啥我们仍然要写上??
代码大全这本书。进行了详细的讨论,我们要进行防御性编程,很多时候写代码的时候,往往是要把一个大的项目拆分成几个模块,有不同的人负责分别完成。模块之间要进行交互,你在使用别人提供的API来进行调用的时候,你是否要对传入的参数进行检查呢。比如你要传一个实参,你也不知道这个参数是否是一个NULL指针。是否应该再调用函数之前就对这个指针进行判空呢。答案是需要进行判空的。代码大全中,给出的结论是要进行双重判定的。函数的定义和调用时两伙人。万一对方失误了,没有判定呢?使用double check,意味着任何一方出错,都不会产生严重的后果!
代码:
cpp
void Widget::on_pushButton_clicked()
{
float opacity = this->windowOpacity();
if(opacity < 1.0)
{
this->setWindowOpacity(opacity+0.1);
}
qDebug() << opacity;
}
void Widget::on_pushButton_2_clicked()
{
float opacity = this->windowOpacity();
if(opacity >= 0.2)
{
this->setWindowOpacity(opacity-0.1);
}
qDebug() << opacity;
}
七、QWidget的cursor属性
设置鼠标样式
设置全局光标程序内的全局,而不是系统级别的全局。
通过图形化进行修改。
通过代码创建:
代码:
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QCursor cursor(Qt::WaitCursor);
ui->pushButton->setCursor(cursor);
}
Qt内置的图标形状。
Qt允许我们通过自定义的图片来设置光标。先准备一个图片,把图导入到项目中(qrc管理)在代码中访问到这个图片,基于这个图片构造出光标对象并设置。
代码:
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPixmap>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPixmap pixmap(":/jiantou.png");
pixmap = pixmap.scaled(100,100);
QCursor cursor(pixmap);
ui->pushButton->setCursor(cursor);
}
Widget::~Widget()
{
delete ui;
}
QPixmap通过这个对象代表一个图片。访问到图片文件,构造鼠标形式。这个默认情况下,鼠标点击时,相当于图片的左上角在进行点击。传入参数,热点所在的位置,以图片左上角0,0原点,找到10,10这个位置作为鼠标真正点击的位置,对图片进行缩放。通过这个函数对图片进行缩放。注意缩放不是修改图片对象本身,而是返回一个新的图片对象副本。阿里巴巴矢量图标库,免费下载图片。
八、QWidget的font属性
修改字体
Qfont对象的属性
上述属性具体怎么设置,实际开发中,往往有专门的美工/设计/UED 来确定的。但是如果有一些小公司,没有美工甚至没有产品经历。这个情况,就可以参考一下别人的网页,程序进行设计。Qt Designer能够对界面的属性设置支持"实时预览"改了啥属性,这边就可以立即显示出来。通过属性编辑这样的方式,虽然能够快速方便的修改文字相关的属性,但是还不够灵活。如果程序运行过程中,需要修改文字相关的属性就需要通过代码来操作了。
代码:
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QLabel>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QFont font;
QLabel* label = new QLabel(this);
label->setText("你好");
font.setFamily("仿宋");
font.setPixelSize(30);
font.setItalic(true);
font.setUnderline(true);
font.setBold(true);
font.setStrikeOut(true);
label->setFont(font);
}
Widget::~Widget()
{
delete ui;
}
九、QWidget的toolTip属性
一个GUI程序,界面比较复杂,按钮啥的很多。当你把鼠标悬停到这个控件上的时候,就能弹出一个提示。
代码:
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* button = new QPushButton(this);
button->setText("按钮");
button->setToolTip("请你按下");
button->setToolTipDuration(3000);
}
Widget::~Widget()
{
delete ui;
}
十、QWidget的属性focusPolicy属性
设置控件获取焦点的策略,比如某个控件能否用鼠标选中或者能否通过tab键选中。
计算机中的"焦点",对于键盘操作非常明显。界面上有一个输入框,此时必须要选中这个输入框,接下来键盘按键才会输入到输入框中。如果选中的是别的控件,或者别的窗口,此时键盘的输入就不会进入到这个输入框中。GUI中, 窗口/控件焦点是非常关键的。线上笔试,笔试链接,打开一个网站,就可以在线做题,如果我遇到不会的题,能不能切到百度这里来搜索一下。窗口焦点的问题。你在人家的网页中做题,网页是属于始终获取到焦点的状态的。一旦你切到百度或者其他程序。立刻人家的网页就能感知到失去焦点。我的理解是焦点是判断鼠标在哪个控件中。
一般来说,一个控件获取到焦点,主要是两种方式
1、鼠标点击。
2、键盘的tab方式。
十一、QWidget的属性stylesheet属性
通过CSS设置widget的样式
样式:描述了界面具体是啥样子的。
CSS:层叠样式表。在进行往后也开发的时候,设置网页样式的方式。这个东西发展很多年了,非常成熟了。能够提供非常丰富的功能,把网页能设置成非常好看的样式。
Qt就把CSS给参考过来了,搞了一套QSS。虽然QSS相比于CSS来说,功能上确实很多。但是如此也能够帮我们完成不少的效果。通过styleSheet属性初步演示:
和CSS类似,QSS设置的样式也是通过键值对的格式。键和值之间用冒号分割,键值对和键值对之间使用分号分割。Qt的文档中会详细介绍。也可以直接在Qt文档中搜索Qt stylesheet。
代码:通过代码来设置样式。实现一个夜间模式,文字是白色,背景是黑色。
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setStyleSheet("background-color:white;");
ui->pushButton_light->setStyleSheet("color:black;");
ui->pushButton_dark->setStyleSheet("color:black;");
}
Widget::~Widget()
{
delete ui;
}
首先需要认识计算机中的颜色是如何表示的:我们的QSS中是可以直接使用单词来设置颜色的。颜色有多少种呢?无数种。在计算机种使用RGB的方式来表示颜色。R:red G:green B:blue,这是三原色,按照这三种颜色进行不同比例的混合,就能构成不同的颜色。屏幕上的像素就是一个包含RGB的小灯泡。计算机中,通常会使用一个字节来表示R,一个字节表示G,一个字节表示B。最终表示一个颜色,就是使用 rgb(255,0,255)。或者#FF00FF,
Widget的初始背景色是多少数值呢?
取色器(PS)QQ上的截图,内置了取色器。此处借助取色器就能看到背景色。
十二、QWidget的属性总结
辅助技术(屏幕阅读器),这类属性,是属于盲人来使用的。现在的电脑手机都提供了无障碍功能。屏幕阅读器(Windows中叫做讲述人),甚至盲人也可以写代码,当程序员。