5.常用控件-QWidget|enabled|geometry|window frame(C++)
控件概述
实现图形化界面的程序.
Qt中已经给我们提供了很多的“控件"
就需要学习和了解这些控件,学会如何使用这些控件
编程讲究的是“站在巨人的肩膀上”,而不是“从头发明轮子"
一个图形化界面上的内容,不需要咱们全都从零去实现.Qt中已经提供了很多内置的控件了(按钮,文本框,单选按钮,复选按钮,下拉框.) 咱们拿过来就能直接使用.
QWidget核⼼属性
在Qt中,使⽤QWidget类表⽰"控件"。像按钮,视图,输⼊框,滚动条等具体的控件类,都是继承⾃QWidget.
可以说,QWidget中就包含了Qt整个控件体系中,通⽤的部分.
在QtDesigner中,随便拖⼀个控件过来,选中该控件,即可在右下⽅看到QWidget中的属性
这些属性既可以通过QtDesigner会直接修改,也可以通过代码的⽅式修改.
这些属性的具体含义,在Qt Assistant中均有详细介绍.
在Qt Assistant中搜索QWidget,即可找到对应的⽂档说明。(或者在Qt Creator代码中,选中QWidget,按F1也可).
核⼼属性概览
属性 | 作⽤ |
---|---|
enabled | 设置控件是否可使⽤. true 表⽰可⽤, false 表⽰禁⽤. |
geometry | 位置和尺⼨.包含x,y,width,height四个部分. 其中坐标是以⽗元素为参考进⾏设置的. |
windowTitle | 设置widget标题 |
windowIcon | 设置widget图标 |
windowOpacity | 设置widget透明度 |
cursor | ⿏标悬停时显⽰的图标形状. 是普通箭头,还是沙漏,还是⼗字等形状. 在Qt Designer界⾯中可以清楚看到可选项. |
font | 字体相关属性. 涉及到字体家族,字体⼤⼩,粗体,斜体,下划线等等样式. |
toolTip | ⿏标悬停在widget上会在状态栏中显⽰的提⽰信息. |
toolTipDuring | toolTip显⽰的持续时间. |
statusTip | Widget状态发⽣改变时显⽰的提⽰信息(⽐如按钮被按下等). |
whatsThis | ⿏标悬停并按下alt+F1时,显⽰的帮助信息(显⽰在⼀个弹出的窗⼝中). |
styleSheet | 允许使⽤CSS来设置widget中的样式. Qt中⽀持的样式⾮常丰富,对于前端开发⼈员上⼿是⾮常友好的. |
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 | 指定窗⼝是否具有"模态"⾏为. |
sizeIncrement | 拖动窗⼝⼤⼩时的增量单位. |
baseSize | 窗⼝的基础⼤⼩,⽤来搭配sizeIncrement调整组件尺⼨是计算组件应该调整到的合适的值. |
palette | 调⾊板.可以设置widget的颜⾊⻛格. |
mouseTracking | 是否要跟踪⿏标移动事件. 如果设为true,表⽰需要跟踪,则⿏标划过的时候该widget就能持续收到⿏标移动事件. 如果设为false,表⽰不需要跟踪,则⿏标划过的时候,widget不会收到⿏标移动事件,只能收到⿏标按下或者释放的事件. |
tabletTracking | 是否跟踪触摸屏的移动事件. 类似于mouseTracking.Qt5.9中引⼊的新属性. |
layoutDirection | 布局⽅向. • Qt::LeftToRight:⽂本从左到右排列,也是默认值。 • Qt::RightToLeft:⽂本从右到左排列。 • Qt::GlobalAtomics:部件的布局⽅向由全局原⼦性决定(这个翻译其实有点尴尬.其实就是根据应⽤程序中的其他widget布局⽅向确的). |
autoFillBackground | 是否⾃动填充背景颜⾊. |
windowFilePath | 能够把widget和⼀个本地⽂件路径关联起来. |
accessibleName | 设置widget的可访问名称.这个名称可以被辅助技术(像屏幕阅读器)获取到.这个属性⽤于实现⽆障碍程序的场景中(也就是给盲⼈写的程序). |
accessibleDescription | 设置widget的详细描述.作⽤同accessibleName |
inputMethodHints | 针对输⼊框有效,⽤来提⽰⽤⼾当前能输⼊的合法数据的格式.⽐如只能输⼊数字,只能输⼊⽇期等. |
enabled
API | 说明 |
---|---|
isEnabled() | 获取到控件的可⽤状态. |
setEnabled | 设置控件是否可使⽤. true 表⽰可⽤, false 表⽰禁⽤. |
- 所谓"禁⽤"指的是该控件不能接收任何⽤⼾的输⼊事件,并且外观上往往是灰⾊的.
- 如果⼀个widget被禁⽤,则该widget的⼦元素也被禁⽤
代码⽰例:使⽤代码创建⼀个禁⽤状态的按钮
#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->setEnabled(false);
}Widget::~Widget()
{delete ui;
}
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("按钮");//按钮处于禁用状态button->setEnabled(false);connect(button, &QPushButton::clicked, this, &Widget::handle);
}Widget::~Widget()
{delete ui;
}void Widget::handle()
{qDebug() << "handle";
}
加上槽函数,同样用不了
切换禁用状态
创建两个按钮
转到槽,分别给两个按钮插入clicked()
#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_clicked()
{qDebug() << "执行了槽函数";
}void Widget::on_pushButton_2_clicked()
{// 切换第一个按钮的禁用状态// 1.先获取到第一个按钮当前的状态bool enable = ui->pushButton->isEnabled();if (enable) {ui->pushButton->setEnabled(false);}else {ui->pushButton->setEnabled(true);}
}
运⾏程序,可以看到,初始情况下,上⾯的按钮是可⽤状态.
点击下⽅按钮,即可使上⽅按钮被禁⽤;再次点击下⽅按钮,上⽅按钮就会解除禁⽤.(禁⽤状态的按钮为灰⾊,且不可点击).
QObject的 objectName 属性介绍:
QObject是QWidget的⽗类.⾥⾯最主要的属性就是 objectName .
在⼀个Qt程序中, objectName 相当于对象的⾝份标识,彼此之间不能重复.
在使⽤Qt Designer时,尤其是界⾯上存在多个widget的时候,可以通过 objectName 获取到指定的widget对象.
Qt Designer⽣成的ui⽂件,本⾝是xml格式的.qmake会把这个xml⽂件转换成C++的.h⽂件(这个⽂件⽣成在build⽬录中),构成⼀个ui_widget类.
每个widget的 objectName 最终就会成为ui_widget类的属性名字.
最终这个类的实例,就是 Ui::Widget *ui
,因此就可以通过形如 ui->pushButton 或者 ui->pushButton_2 这样的代码获取到界⾯上的widget对象了.
在Qt Designer中创建按钮的时候,可以设置按钮的初始状态是"可⽤"还是"禁⽤".
如果把enabled这⼀列的对钩去掉,则按钮的初始状态就是"禁⽤"状态.
geometry
位置和尺⼨.其实是四个属性的统称:
- x 横坐标
- y 纵坐标
- width 宽度
- height ⾼度
但是实际开发中,我们并不会直接使⽤这⼏个属性,⽽是通过⼀系列封装的⽅法来获取/修改.
对于Qt的坐标系,不要忘记是⼀个"左⼿坐标系".其中坐标系的原点是当前元素的⽗元素的左上⻆
API | 说明 |
---|---|
geometry() | 获取到控件的位置和尺⼨.返回结果是⼀个QRect,包含了x,y,width,height.其中x,y是左上⻆的坐标. |
setGeometry(QRect) setGeometry(int x,int y,int width,int height) | 设置控件的位置和尺⼨.可以直接设置⼀个QRect,也可以分四个属性单独设置 |
Rect就是矩形
Qt中针对一些几何上的概念也进行了封装
QPoint表示一个点 QRect表示一个矩形
属于是小对象,里面的属性非常少,占用空间也小.
C++中使用上述对象,通常就会按照值的方式来传递参数了
move只是修改位置
setGeometry既可以修改位置,又可以修改尺寸~
代码⽰例:控制按钮的位置
创建一个按钮target
修改objectName为pushButton_target
修改这些属性的时候,一定要先确认好你当前选中的是哪个控件
1)在界⾯中拖五个按钮.
五个按钮的objectName分别为 pushButton_target , pushButton_up ,pushButton_down , pushButton_left , pushButton_right
五个按钮的初始位置和⼤⼩都随意
期望通过点击这几个按钮,就能够修改target按钮的geometry
#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_up_clicked()
{// 获取到target本身的geometryQRect rect = ui->pushButton_target->geometry();qDebug() << rect;
}
单位都是像素
void Widget::on_pushButton_up_clicked()
{// 获取到target本身的geometryQRect rect = ui->pushButton_target->geometry();qDebug() << rect;rect.setY(rect.y() - 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_left_clicked()
{QRect rect = ui->pushButton_target->geometry();qDebug() << rect;rect.setX(rect.x() - 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);
}
当前代码实际执行的效果,是在调整左上角位置.左上角位置改变的同时,高度和宽度也同时发生了改变
如果想要让这个按钮能够平移.(宽度高度不变,整个按钮的位置都发生改变)
刚才的代码,修改的是QRect对象的×和y.这样的修改就会使QRect宽度高度发生改变
如何才能实现“平移”的效果,保持尺寸不变,整个按钮位置变化?
不再修改QRect,而是通过QRect基于setGeometry第二个版本的函数重新设置位置即可.
void Widget::on_pushButton_up_clicked()
{// 获取到target本身的geometryQRect rect = ui->pushButton_target->geometry();qDebug() << rect;
// rect.setY(rect.y() - 5);
// ui->pushButton_target->setGeometry(rect);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();qDebug() << rect;
// rect.setY(rect.y() + 5);
// ui->pushButton_target->setGeometry(rect);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();qDebug() << rect;
// rect.setX(rect.x() - 5);
// ui->pushButton_target->setGeometry(rect);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();qDebug() << rect;
// rect.setX(rect.x() + 5);
// ui->pushButton_target->setGeometry(rect);ui->pushButton_target->setGeometry(rect.x() + 5, rect.y(), rect.width(), rect.height());
}
代码⽰例:⼀个表⽩程序
1)往界⾯上拖拽两个按钮和⼀个Label.
PushButton的objectName为 pushButton_accept 和 pushButton_reject ,label的objectName为 label
控件中⽂本如下图所⽰
void Widget::on_pushButton_accept_clicked()
{ui->label->setText("女神,嘴一个");
}
#include "widget.h"
#include "ui_widget.h"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("女神,嘴一个");
}void Widget::on_pushButton_reject_clicked()
{// 如果点击了这个按钮,就把这个按钮挪走// 通过生成随机数的方式来确定按钮新的位置// 获取到当前程序窗口的尺寸int width = this->geometry().width();int height = this->geometry().height();// 重新生成按钮的位置int x = rand() % width;int y = rand() % height;// 移动按钮的位置ui->pushButton_reject->move(x, y);
}
rand(是c标准库中的函数
能够生成一个随机的整数.这个数字范围很大~~
上述代码就类似于之前写猜数字,要生成一个1-100之间的整数
rand0 % 100 + 1
[0, 99] + 1 => [1, 100]
rand函数使用之前要设置随机种子~^
C语言中通过time可以获取到秒级时间戳~~
按钮提供的信号不止有点击
一下一上是点击,
不再使用clicked信号,换成pressed.鼠标按下的时候触发
void Widget::on_pushButton_reject_pressed()
{// 如果点击了这个按钮,就把这个按钮挪走// 通过生成随机数的方式来确定按钮新的位置// 获取到当前程序窗口的尺寸int width = this->geometry().width();int height = this->geometry().height();// 重新生成按钮的位置int x = rand() % width;int y = rand() % height;// 移动按钮的位置ui->pushButton_reject->move(x, y);
}
也可以做到,鼠标不点击,只要挪到按钮上,就会让按钮移动~~(需要使用到Qt中的事件机制)
window frame
如果widget作为⼀个窗⼝(带有标题栏,最⼩化,最⼤化,关闭按钮),那么在计算尺⼨和坐标的时候就有两种算法.包含window frame和不包含window frame.
其中x(),y(),frameGeometry(),pos(),move()都是按照包含window frame的⽅式来计算的.
其中geometry(),width(),height(),rect(),size()则是按照不包含window frame的⽅式来计算的.
当然,如果⼀个不是作为窗⼝的widget,上述两类⽅式得到的结果是⼀致的
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相当于QPoint和QSize的结合体.可以获取x,y, width,size. 计算时包含window frame对象. |
width() | 获取宽度 计算时不包含window frame |
height() | 获取⾼度 计算时不包含window frame |
size() | 返回QSize对象,⾥⾯包含width(),height(),setWidth(),setHeight()等⽅法. 计算时不包含window frame |
rect() | 返回QRect对象.QRect相当于QPoint和QSize的结合体.可以获取并设置x, y,width,size. 计算时不包含window frame对象. |
geometry() | 返回QRect对象.QRect相当于QPoint和QSize的结合体.可以获取x,y, width,size. 计算时不包含window frame对象. |
setGeometry() | 直接设置窗⼝的位置和尺⼨.可以设置x,y,width,height,或者QRect对象. 计算时不包含window frame对象. |
在Qt中,关于位置尺寸,提供了很多的API. | |
有的API的位置信息是以Widget本体左上角为原点的(不考虑Windowframe) 有的API的位置信息是以windowframe左上角为原点的. | |
geometry() | |
setGeometry()都是不考虑windowframe | |
frameGeometry() | |
setFrameGeometry()考虑windowframe | |
其实这⾥的API有frameGeometry和geometry两个就⾜够完成所有的需求了. |
代码⽰例:感受geometry和frameGeometry的区别
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>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;
}Widget::~Widget()
{delete ui;
}
当前代码是放到了构造函数中. 此时这个Widget对象正在构造还没有被加入到windowframe中
因此,此时还看不到windowframe的影响,
#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* button = new QPushButton(this);button->setText("按钮");button->move(100, 100);connect(button, &QPushButton::clicked, this, &Widget::handle);
}Widget::~Widget()
{delete ui;
}void Widget::handle()
{QRect rect1 = this->geometry();QRect rect2 = this->frameGeometry();qDebug() << rect1;qDebug() << rect2;
}