目录
控件概述
Widget是Qt中的核心概念,英文原义是"小部件",此处将其翻译为"控件"。控件是构成一个图形化界面的基本要素,如按钮、列表视图、树形视图、单行输入框、多行输入框、滚动条、下拉框等都可以称为"控件"。
Qt作为一个成熟的GUI开发框架,内置了大量的常用控件。Qt也提供了"自定义控件"的能力,可以在现有控件不能满足需求时,对现有控件做出扩展,或者自定义出新的控件。
控件体系的发展
控件是 GUI 开发中的通用概念,不仅仅局限在 Qt 中。
第一阶段:
完全没有控件,此时需要通过⼀些绘图 API 手动的绘制出按钮或者输入框等内容,代码编写繁琐,例如文曲星的 Lava 平台开发。
第二阶段:
- 只包含粗略的控件,只是提供了按钮,输⼊框,单选框,复选框等最常用的控件.
- 例如 html 的原生控件
第三阶段:
- 更完整的控件体系,基本可以覆盖到 GUI 开发中的⼤部分场景
- 例如早期的 MFC,VB,C++ Builder,Qt,Delphi,后来的 Android SDK,Java FX,前端的各种 UI 库等
按钮类控件
QPushButton
Qt中使用QPushButton表示一个按钮。QPushButton继承自QAbstractButton,该类是一个抽象类,是其他按钮的父类。
在Qt Designer中也能够看到这里的继承关系
QAbstractButton中和QPushButton相关性较大的属性
属性 | 说明 |
text | 按钮中的文本 |
icon | 按钮中的图标 |
iconSize | 按钮中图标的尺寸 |
shortCut | 按钮对应的快捷键 |
autoRepeat | 按钮是否会重复触发,当鼠标左键按住不放时 若设为true,则会持续产生鼠标点击事件 若设为false,则必须释放鼠标后,再次点击鼠标才能产生点击事件 (类似于游戏手柄上的"连发效果") |
autoRepeatDelay | 重复触发的延时时间(按住按钮多久,开始重复触发) |
autoRepeatInterval | 重复触发的周期 |
注意:
- QAbstractButton作为QWidget的子类,继承了QWidget的属性。QWidget里的各种属性用法,对于QAbstractButton同样适用,因此表格仅列出QAbstractButton独有的属性
- Qt的api设计风格非常清晰,此处列出的属性都是可以获取和设置的。如:使用text()获取按钮文本,使用setText()设置文本
事实上,QPushButton的核心功能都是QAbstractButton提供的,自身提供的属性都较为简单,其中default和autoDefault影响的是按下enter时自动点击哪个按钮的行为,flat把按钮设置为扁平的样式。暂时不做过多关注。
代码示例:带有图标的按钮
创建resource.qrc文件并导入图片
在界面上创建一个按钮
修改widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建图标
QIcon icon(":/001.png");
//设置图标
ui->pushButton->setIcon(icon);
//设置图标大小
ui->pushButton->setIconSize(QSize(66, 66));
}
Widget::~Widget()
{
delete ui;
}
运行代码
代码示例:带有快捷键的按钮
在界面中拖五个按钮。五个按钮的objectName分别为pushButtonTarget 、pushButtonUp、pushButtonDown、pushButtonLeft、pushButtonRight,五个按钮的初始位置随意,其中pushButtonTarget尺寸设为100*100,其余按钮设为50*50,文本内容均清空。
创建resource.qrc并导入5个图片
修改widget.cpp,设置图标资源和快捷键,设置四个方向键的slot函数
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置图标
ui->pushButtonTarget->setIcon(QIcon(":/image/dog.png"));
ui->pushButtonUp->setIcon(QIcon(":/image/Up.png"));
ui->pushButtonDown->setIcon(QIcon(":/image/Down.png"));
ui->pushButtonLeft->setIcon(QIcon(":/image/Left.png"));
ui->pushButtonRight->setIcon(QIcon(":/image/Right.png"));
//设置快捷键1
ui->pushButtonUp->setShortcut(QKeySequence(Qt::Key_W));
ui->pushButtonDown->setShortcut(QKeySequence(Qt::Key_S));
ui->pushButtonLeft->setShortcut(QKeySequence(Qt::Key_A));
ui->pushButtonRight->setShortcut(QKeySequence(Qt::Key_D));
//设置快捷键2
// ui->pushButtonUp->setShortcut(QKeySequence("W"));
// ui->pushButtonDown->setShortcut(QKeySequence("S"));
// ui->pushButtonLeft->setShortcut(QKeySequence("A"));
// ui->pushButtonRight->setShortcut(QKeySequence("D"));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButtonUp_clicked()
{
const QRect& rect = ui->pushButtonTarget->geometry();
ui->pushButtonTarget->setGeometry(rect.x(), rect.y() - 5, rect.height(), rect.width());
}
void Widget::on_pushButtonDown_clicked()
{
const QRect& rect = ui->pushButtonTarget->geometry();
ui->pushButtonTarget->setGeometry(rect.x(), rect.y() + 5, rect.height(), rect.width());
}
void Widget::on_pushButtonLeft_clicked()
{
const QRect& rect = ui->pushButtonTarget->geometry();
ui->pushButtonTarget->setGeometry(rect.x() - 5, rect.y(), rect.height(), rect.width());
}
void Widget::on_pushButtonRight_clicked()
{
const QRect& rect = ui->pushButtonTarget->geometry();
ui->pushButtonTarget->setGeometry(rect.x() + 5, rect.y(), rect.height(), rect.width());
}
运行程序,此时点击按钮,或者使用wasd均可让狗头移动
代码示例:按钮的重复触发
在上述案例中,按住快捷键是可以重复触发的,但是鼠标点击则不能
修改widget.cpp,在构造函数中开启重复触发
//开启鼠标重复触发
ui->pushButtonUp->setAutoRepeat(true);
ui->pushButtonDown->setAutoRepeat(true);
ui->pushButtonLeft->setAutoRepeat(true);
ui->pushButtonRight->setAutoRepeat(true);
QRadioButton
QRadioButton是单选按钮,可以在多个选项中选择一个。作为QAbstractButton和QWidget的子类,上面介绍的属性和用法对于QRadioButton同样适用
QAbstractButton中和QRadioButton关系较大的属性
属性 | 说明 |
checkable | 是否能被选中 |
checked | 是否已经被选中,checkable时checked的前提条件 |
autoExclusive | 是否排它 选中一个按钮之后是否会取消其他按钮的选中 对于QRadioButton而言,默认就是排它的 |
代码示例:选择性别
在界面上创建一个label和三个单选按钮。设置的文本如下图,三个单选按钮的objectName分别为radioButtonMale 、radioButtonFemale、radioButtonOther
修改widget.cpp,编辑三个QRadioButton的slot函数
void Widget::on_radioButtonMale_clicked()
{
ui->label->setText("你选择的性别为:男");
}
void Widget::on_radioButtonFemale_clicked()
{
ui->label->setText("你选择的性别为:女");
}
void Widget::on_radioButtonOther_clicked()
{
ui->label->setText("你选择的性别为:其他");
}
修改代码,让程序启动默认选中性别男
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置默认选中该按钮
ui->radioButtonMale->setChecked(true);
ui->label->setText("你选择的性别为:男");
}
禁用"其他"选项
// 禁用other选项
ui->radioButtonOther->setCheckable(false);
点击"其他"按钮时,虽然不会被选中,但是可以触发点击事件,使label显示性别为其他
使用setEnabled可以更彻底的禁用按钮,此时该按钮无法被选中,也无法响应任何输入
// 禁用other选项
ui->radioButtonOther->setEnabled(false);
代码示例:clicked、pressed、released、toggled区别
- clicked表示一次"点击"(一次按下 + 一次释放)
- pressed表示鼠标"按下"
- released表示鼠标"释放"
- toggled表示按钮状态切换
在界面上创建四个单选按钮,objectName分别为radioButton、radioButton_2、radioButton_3、radioButton_4
给1创建clicked槽函数,给2创建pressed槽函数,给3创建released槽函数,给4创建toggled槽函数
void Widget::on_radioButton_clicked()
{
qDebug() << "clicked";
}
void Widget::on_radioButton_2_pressed()
{
qDebug() << "pressed";
}
void Widget::on_radioButton_3_released()
{
qDebug() << "released";
}
void Widget::on_radioButton_4_toggled(bool checked)
{
if(checked) qDebug() << "toggled true";
else qDebug() << "toggled false";
}
代码示例:单选按钮分组
在界面上创建6个单选框,用来模拟麦当劳点餐界面。objectName分别为radioButton到radioButton_6
此时直接运行程序,可以发现这六个QRadioButton之间都是排它的。但是按照正常的逻辑,应该每⼀组内部来控制排它,但是组和组之间不能排它。
引入QButtonGroup进行分组
#include "widget.h"
#include "ui_widget.h"
#include <QButtonGroup>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建三个QButtonGroup
QButtonGroup* group1 = new QButtonGroup(this);
QButtonGroup* group2 = new QButtonGroup(this);
QButtonGroup* group3 = new QButtonGroup(this);
//将QRadioButton两两一组放入QButtonGroup中
group1->addButton(ui->radioButton);
group1->addButton(ui->radioButton_2);
group2->addButton(ui->radioButton_3);
group2->addButton(ui->radioButton_4);
group3->addButton(ui->radioButton_5);
group3->addButton(ui->radioButton_6);
}
Widget::~Widget()
{
delete ui;
}
再次执行程序,就可以按照正确的分组方式来进行排它了
QCheckBox
QCheckBox表示复选按钮,可以允许选中多个。和QCheckBox最相关的属性是checkable和checked,都是继承自QAbstractButton
至于QCheckBox独有的属性tristate用来实现"三态复选框",不过较为冷门,暂时不做讨论
代码示例:获取复选按钮的取值
在界面上创建三个复选按钮和一个普通按钮,objectName分别为checkBoxEat、checkBoxSleep、checkBoxPlay以及pushButton
给pushButton添加slot函数
void Widget::on_pushButton_clicked()
{
QString result;
if(ui->checkBoxEat->isChecked())
result += ui->checkBoxEat->text();
if(ui->checkBoxSleep->isChecked())
result += ui->checkBoxSleep->text();
if(ui->checkBoxPlay->isChecked())
result += ui->checkBoxPlay->text();
qDebug() << "选中的内容:" << result;
}
QToolButton
QToolButton大部分功能和QPushButton一致,但是QToolButton主要应用在工具栏、菜单等场景。
显示类控件
QLabel
QLabel可以用来显示文本和图片
核心属性
属性 | 说明 |
text | QLabel中的文本 |
textFormat | 文本的格式 Qt::PlainText 纯文本 Qt::Rich Text 富文本(支持html标签) Qt::MarkdownText markdown格式 Qt::AutoText 根据文本内容自动决定文本格式 |
pixmap | QLabel内部包含的图片 |
scaledContents | 设为true表示内容自动拉伸填充QLabel 设为false则不会自动拉伸 |
alignment | 对齐方式(可以设置水平方向和垂直方向如何对齐) |
wordWrap | 设为true内部文本会自动换行 设为false则内部文本不会自动换行 |
indent | 设置文本缩进(水平方向和垂直方向都生效) |
margin | 内部文本与边框之间的边距 |
openExternalLinks | 是否允许打开一个外部链接 (当QLabel的文本中包含url时涉及) |
buddy | 给QLabel关联一个"伙伴",按下QLabel指示的快捷键时就能激活对应的伙伴 若伙伴是一个QCheckBox,那么该QCheckBox就会被选中 |
代码示例:显示不同格式的文本
在界面上创建三个QLabel,objectName分别为label、label_2、label_3
修改widget.cpp,设置三个label的属性
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setTextFormat(Qt::PlainText);
ui->label->setText("这是一段纯文本");
ui->label_2->setTextFormat(Qt::RichText);
ui->label_2->setText("<b> 这是一段富文本 </b>");
ui->label_3->setTextFormat(Qt::MarkdownText);
ui->label_3->setText("## 这是一段MarkDown文本");
}
Widget::~Widget()
{
delete ui;
}
运行程序,观察效果
代码示例:显示图片
虽然QPushButton也可以通过设置图标的方式设置图片,但是并非是一个好的选择,更多的时候还是希望通过QLabel来作为一个更单纯的显示图片的方式
在界面上创建一个QLabel,objectName为label
创建resource.qrc文件并把图片导入到qrc中
修改widget.cpp,给QLabel设置图片
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置label大小与窗口大小相同
ui->label->setGeometry(0, 0, 800, 600);
//设置label图片
QPixmap pixmap(":/huaji.png");
ui->label->setPixmap(pixmap);
//设置label内容拉伸
ui->label->setScaledContents(true);
}
Widget::~Widget()
{
delete ui;
}
运行程序,观察效果,可以看到图片已经被拉伸,可以把窗口填满了。但是若拖动窗口大小,可以看到图片并不会随着窗口大小的改变而同步变化
为了解决该问题,可以在Widget中重写resizeEvent函数
void Widget::resizeEvent(QResizeEvent *event)
{
ui->label->setGeometry(0, 0, event->size().width(), event->size().height());
qDebug() << event->size();
}
注意:
- 此处的resizeEvent函数没有手动调用,但是能在窗口大小变化时被自动调用。这个过程就是依赖C++中的多态来实现的,Qt框架内部管理着QWidget对象表示窗口,在窗口大小发生改变时,Qt就会自动调用resizeEvent函数
- 但是实际上这个窗口的并非是QWidget,而是QWidget的子类,也就是自主编写的Widget。此时虽然是通过父类调用函数,但是实际上执行的是子类的函数(即重写后的resizeEvent )
- 此处属于是多态机制的一种经典用法。通过上述过程,就可以把自定义的代码插入到框架内部执行,相当于"注册回调函数"
代码示例:文本对齐、自动换行、缩进、边距
创建四个label,objectName分别是label到label_4,并且在QFrame中设置frameShape为Box(设置边框后看起来会更清晰一些)
QFrame是QLabel的父类,其中frameShape属性用来设置边框性质
- QFrame::Box:矩形边框
- QFrame::Panel:带有可点击区域的面板边框
- QFrame::WinPanel:Windows风格的边框
- QFrame::HLine:水平线边框
- QFrame::VLine:垂直线边框
- QFrame::StyledPanel:带有可点击区域的面板边框,但样式取决于窗口主题
编写widget.cpp,给这四个label设置属性
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置文字居中对齐
ui->label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
ui->label->setText("垂直水平居中的文本");
//设置自动换行
ui->label_2->setAlignment(Qt::AlignTop | Qt::AlignLeft);
ui->label_2->setWordWrap(true);
ui->label_2->setText("这是一个很长的文本这是一个很长的文本这是一个很长的文本这是一个很长的文本这是一个很长的文本这是一个很长的文本");
//设置首行缩进
ui->label_3->setAlignment(Qt::AlignTop | Qt::AlignLeft);
ui->label_3->setIndent(20);
ui->label_3->setText("这是一个很长的文本这是一个很长的文本这是一个很长的文本这是一个很长的文本这是一个很长的文本这是一个很长的文本");
//设置边距
ui->label_4->setAlignment(Qt::AlignTop | Qt::AlignLeft);
ui->label_4->setMargin(20);
ui->label_4->setText("这是一个很长的文本这是一个很长的文本这是一个很长的文本这是一个很长的文本这是一个很长的文本这是一个很长的文本");
}
Widget::~Widget()
{
delete ui;
}
- 第一个label垂直水平居中
- 第二个label设置了wordWrap,能够自动动换行
- 第三个label设置了Indent,左侧和上方和边框有间距,右侧则没有
- 第四个label设置了margin,四个方向均有间距(图上仅体现出三个方向,下方看不出来)
代码示例:设置伙伴
创建两个label和两个radioButton,objectName分别为label、label_2、radioButton、radioButton_2
注意:
- 此处把label中的文本设置为"快捷键 &A"这样的形式,其中&后面跟着的字符就是快捷键,可以通过alt+A的方式来触发该快捷键
- 但是这里的快捷键和QPushButton不同,需要搭配alt和单个字母的方式才能触发
编写widget.cpp,设置buddy属性
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setBuddy(ui->radioButton);
ui->label_2->setBuddy(ui->radioButton_2);
}
Widget::~Widget()
{
delete ui;
}
QLCDNumber
QLCDNumer是一个专门用来显示数字的控件,类似于"老式计算器"的效果