【Qt】常用控件
> 作者:დ旧言~
> 座右铭:松树千年终是朽,槿花一日自为荣。> 目标:了解 QT 常用的控件。
> 毒鸡汤:有些事情,总是不明白,所以我不会坚持。早安!
> 专栏选自:QT从基础到入门_დ旧言~的博客-CSDN博客
> 望小伙伴们点赞👍收藏✨加关注哟💕💕
一、控件概述
控件概念:
Widget是Qt中的核心概念,英文原义是"小部件",此处将其翻译为"控件"。控件是构成一个图形化界面的基本要素,如按钮、列表视图、树形视图、单行输入框、多行输入框、滚动条、下拉框等都可以称为"控件"。
Qt作为一个成熟的GUI开发框架,内置了大量的常用控件。Qt也提供了"自定义控件"的能力,可以在现有控件不能满足需求时,对现有控件做出扩展,或者自定义出新的控件。
控件体系的发展:
第一阶段:完全没有控件。此时需要通过一些绘图API手动的绘制出按钮或者输入框等内容,代码编写繁琐。如文曲星的Lava平台开发
第二阶段:只包含粗略的控件,只提供了按钮、输入框、单选框、复选框等最常用的控件。如html的原生控件
第三阶段:更完整的控件体系,基本可以覆盖到GUI开发中的大部分场景。如早期的MFC、VB、C++ Builder、Qt、Delphi,后来的Android SDK、Java FX、前端的各种UI库等
二、QWidget
概念:
- 在 Qt 中, 使⽤ QWidget 类表⽰ "控件". 像按钮, 视图, 输⼊框, 滚动条等具体的控件类, 都是继承⾃QWidget.
- 可以说, QWidget 中就包含了 Qt 整个控件体系中, 通⽤的部分.
- 在 Qt Designer 中, 随便拖⼀个控件过来, 选中该控件, 即可在右下⽅看到 QWidget 中的属性
三、Buttons类控件
3.1、QPushButton
Qt中使用QPushButton表示一个按钮:
QPushButton继承自QAbstractButton,该类是一个抽象类,是其他按钮的父类
QAbstractButton中和QPushButton相关性较大的属性:
注意:
- QAbstractButton作为QWidget的子类,继承了QWidget的属性。QWidget里的各种属性用法,对于QAbstractButton同样适用,因此表格仅列出QAbstractButton独有的属性。
- Qt的api设计风格非常清晰,此处列出的属性都是可以获取和设置的。如:使用text()获取按钮文本,使用setText()设置文本。。
事实上,QPushButton的核心功能都是QAbstractButton提供的,自身提供的属性都较为简单,其中default和autoDefault影响的是按下enter时自动点击哪个按钮的行为,flat把按钮设置为扁平的样式。暂时不做过多关注。
代码示例:带有图标的按钮
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建图标
QIcon icon(":/dog.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,文本内容均清空
#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());
}
代码示例:按钮的重复触发
在上述案例中,按住快捷键是可以重复触发的,但是鼠标点击则不能,修改widget.cpp,在构造函数中开启重复触发
//开启鼠标重复触发
ui->pushButtonUp->setAutoRepeat(true);
ui->pushButtonDown->setAutoRepeat(true);
ui->pushButtonLeft->setAutoRepeat(true);
ui->pushButtonRight->setAutoRepeat(true);
3.2、QRadioButton
QRadioButton是单选按钮:
可以在多个选项中选择一个。作为QAbstractButton和QWidget的子类,上面介绍的属性和用法对于QRadioButton同样适用。
QAbstractButton中和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;
}
3.3、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;
}
3.4、QToolButton
QToolButton大部分功能和QPushButton一致,但是QToolButton主要应用在工具栏、菜单等场景
四、Display Widgets(显示类控件)
4.1、QLabel
作用:
- QLabel可以用来显示文本和图片。
核心属性:
代码示例:显示不同格式的文本
在界面上创建三个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;
}
4.2、QLCDNumber
作用:
QLCDNumer是一个专门用来显示数字的控件,类似于"老式计算器"的效果
核心属性:
代码示例:倒计时
在界面上创建一个QLCDNumber,初始值设为10,objectName为lcdNumber
修改widget.cpp:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建QTimer实例
timer = new QTimer(this);
//QTimer会每隔⼀定的时间触发⼀个timeout信号,将timeout信号和updateTime连接起来
//意味着每次触发timeout信号都会伴随updateTime函数的执⾏
connect(timer, &QTimer::timeout, this, &Widget::updateTime);
//启动QTimer,每个1000ms触发一次信号
timer->start(1000);
}
Widget::~Widget()
{
delete ui;
}
void Widget::updateTime()
{
int value = ui->lcdNumber->intValue();
if(value <= 0) {
timer->stop();
return;
}
ui->lcdNumber->display(value - 1);
}
4.3、QProgressBar
核心属性:
代码示例:设置进度条按时间增长
在界面上创建进度条,objectName为progressBar
修改widget.h:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTimer>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void updateProgressBar();
private:
Ui::Widget *ui;
QTimer* timer;
};
#endif // WIDGET_H
修改widget.cpp:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &Widget::updateProgressBar);
timer->start(100);
}
Widget::~Widget()
{
delete ui;
}
void Widget::updateProgressBar()
{
int value = ui->progressBar->value();
if(value >= 100) {
timer->stop();
return;
}
ui->progressBar->setValue(value + 1);
}
4.4、QCalendarWidget
核心属性:
重要信号:
代码示例:获取当前选中的日期
在界面上创建一个QCalendarWidget和一个label,objectName为calendarWidget、label
五、Input Widgets(输入类控件)
5.1、QLineEdit
作用:
QLineEdit用来表示单行输入框,可以输入一段文本,但是不能换行。
核心属性:
核心信号:
代码示例:录入个人信息
在界面上创建三个输入框和两个单选按钮,一个普通按钮。三个输入框的objectName为lineEditName、lineEditPassword、lineEditPhone,两个单选按钮的objectName为radioButtonMale、radioButtonFemale,按钮的objectName为pushButton
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//初始化第一个输入框
ui->lineEditName->setPlaceholderText("请输入姓名");
ui->lineEditName->setClearButtonEnabled(true);
//初始化第二个输入框
ui->lineEditPassword->setPlaceholderText("请输入密码");
ui->lineEditPassword->setClearButtonEnabled(true);
ui->lineEditPassword->setEchoMode(QLineEdit::Password);
//初始化第三个输入框
ui->lineEditPhone->setPlaceholderText("请输入电话号码");
ui->lineEditPhone->setClearButtonEnabled(true);
//验证手机号必须是11位数字,并且按照"334"的格式输入
ui->lineEditPhone->setInputMask("000-0000-0000");
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
QString gender = ui->radioButtonMale->isChecked()? "男" : "女";
qDebug() << "姓名:" << ui->lineEditName->text()
<< "密码:" << ui->lineEditPassword->text()
<< "性别:" << gender
<< "电话:" << ui->lineEditPhone->text();
}
inputMask只能进行简单的输入格式校验。实际开发中,基于正则表达式的方式是更核心的方法。
代码示例:使用正则表达式验证输入框数据
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置按钮默认是禁用状态
ui->pushButton->setEnabled(false);
//注册一个validator
ui->lineEdit->setValidator(new QRegExpValidator(QRegExp("^1\\d{10}$")));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_lineEdit_textEdited(const QString &arg1)
{
qDebug() << arg1;
QString content = arg1;
int pos = 0;
if(ui->lineEdit->validator()->validate(content, pos) == QValidator::Acceptable)
ui->pushButton->setEnabled(true);
else
ui->pushButton->setEnabled(false);
}
- 使用QRegExp创建一个正则表达式对象,"^1\\d{10}$"表示"以1开头,后面跟上任意的10个十进制数字"
- 使用QRegExpValidator创建一个验证器对象,Qt中内置了四个主要的验证器对象
- QRegularExpressionValidator在匹配性能上做出了一定优化,但是从使用角度讲,和QRegExpValidator差别不大
- 通过lineEdit->validator()获取到内置的验证器
- 通过validate方法验证文本是否符合要求
- 第一个参数填写的是要验证的字符串。第二个参数是一个int&,是输出型参数,当验证的字符串不匹配时,返回这个字符串的长度。返回值是一个枚举,QValidator::Acceptable表示验证通过,QValidator::Invalid表示验证不通过
代码示例:验证两次输入密码一致
在界面上创建两个输入框和一个label。
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->lineEdit->setEchoMode(QLineEdit::Password);
ui->lineEdit_2->setEchoMode(QLineEdit::Password);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_lineEdit_textEdited(const QString &arg1) {
(void)arg1;
Compare();
}
void Widget::on_lineEdit_2_textEdited(const QString &arg1) {
(void)arg1;
Compare();
}
void Widget::Compare()
{
const QString str1 = ui->lineEdit->text();
const QString str2 = ui->lineEdit_2->text();
if(str1.isEmpty() && str2.isEmpty())
ui->label->setText("密码为空");
else if(str1 == str2)
ui->label->setText("两次输入密码相同");
else
ui->label->setText("两次输入密码不同");
}
5.2、QTextEdit
作用:
QTextEdit表示多行输入框,也是⼀个富文本&markdown编辑器,并且能在内容超出编辑框范围时自动提供滚动条。
核心属性:
核心信号:
代码示例:获取多行输入框的内容
创建一个多行输入框和一个label
#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_textEdit_textChanged()
{
const QString& text = ui->textEdit->toPlainText();
qDebug() << text;
ui->label->setText(text);
}
代码示例:验证输入框的各种信号
QTextEdit中包含了一个QTextCursor对象,通过这个对象可以获取到当前光标位置和选中的内容。
#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_textEdit_textChanged()
{
qDebug() << "[textChanged]" << ui->textEdit->toPlainText();
}
void Widget::on_textEdit_selectionChanged()
{
const QTextCursor& cursor = ui->textEdit->textCursor();
qDebug() << "[selectionChanged]" << cursor.selectedText();
}
void Widget::on_textEdit_cursorPositionChanged()
{
const QTextCursor& cursor = ui->textEdit->textCursor();
qDebug() << "[cursorPositionChanged]" << cursor.position();
}
void Widget::on_textEdit_undoAvailable(bool b)
{
qDebug() << "[undoAvailable]" << b;
}
void Widget::on_textEdit_redoAvailable(bool b)
{
qDebug() << "[redoAvailable]" << b;
}
void Widget::on_textEdit_copyAvailable(bool b)
{
qDebug() << "[copyAvailable]" << b;
}
5.3、QComboBox
作用:
QComboBox表示下拉框。
核心属性:
核心方法:
核心信号:
代码示例:使用下拉框模拟麦当劳点餐
在界面上创建三个下拉框和一个按钮。
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->comboBox->addItem("巨无霸");
ui->comboBox->addItem("麦辣鸡腿堡");
ui->comboBox_2->addItem("薯条");
ui->comboBox_2->addItem("麦辣鸡翅");
ui->comboBox_3->addItem("可乐");
ui->comboBox_3->addItem("雪碧");
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
qDebug() << "汉堡选择:" << ui->comboBox->currentText();
qDebug() << "小食选择:" << ui->comboBox_2->currentText();
qDebug() << "饮料选择:" << ui->comboBox_3->currentText();
}
代码示例:从文件中加载下拉框的选项
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
std::ifstream file("D:\\repositorys\\Qt\\QComboBox_2\\config.txt");
std::string line;
while(std::getline(file, line))
ui->comboBox->addItem(QString::fromStdString(line));
file.close();
}
Widget::~Widget()
{
delete ui;
}
5.4、QSpinBox
作用:
使用QSpinBox或者QDoubleSpinBox表示"微调框",它是带有按钮的输入框,可以用来输入整数/浮点数,通过点击按钮来修改数值大小。
核心属性:
核心信号:
代码示例:调整麦当劳购物车中的份数
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->comboBox->addItem("巨无霸");
ui->comboBox->addItem("麦辣鸡腿堡");
ui->comboBox_2->addItem("薯条");
ui->comboBox_2->addItem("麦辣鸡翅");
ui->comboBox_3->addItem("可乐");
ui->comboBox_3->addItem("雪碧");
ui->spinBox->setValue(1);
ui->spinBox->setRange(1, 5);
ui->spinBox_2->setValue(1);
ui->spinBox_2->setRange(1, 5);
ui->spinBox_3->setValue(1);
ui->spinBox_3->setRange(1, 5);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
qDebug() << "当前下单内容:"
<< ui->comboBox->currentText() << ":" << ui->spinBox->value()
<< ui->comboBox_2->currentText() << ":" << ui->spinBox_2->value()
<< ui->comboBox_3->currentText() << ":" << ui->spinBox_3->value();
}
5.5、QDateEdit & QTimeEdit
使用:
使用QDateEdit作为日期的微调框;使用QTimeEdit作为时间的微调框;使用QDateTimeEdit作为时间日期的微调框。这几个控件用法非常相似,下面以QDateTimeEdit为例进行介绍
QDateTimeEdit核心属性:
关于本地时间(LocalTime)和协调世界时(UTC):
- UTC时间是一个基于原子钟的标准时间,不受地球的自转周期影响,和格林威治时间(GMT)是非常接近的,科学家会通过精密的设备来测量并维护
- 计算机内部使用的时间就是基于UTC时间,本地时间则是基于不同的时区,对UTC时间做出了一些调整。如北京时间,位于"东八区",就需要在UTC时间基础上+8个小时的时差
核心信号:
代码示例:时间日期计算器
#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()
{
//获取到两个时间框的时间日期
QDateTime timeOld = ui->dateTimeEdit->dateTime();
QDateTime timeNew = ui->dateTimeEdit_2->dateTime();
//计算日期差值
int days = (timeOld.secsTo(timeNew) / 3600) / 24;
int hours = (timeOld.secsTo(timeNew) / 3600) % 24;
//设置label的内容
QString text = "计算结果为:" + QString::number(days) + "天 零 " + QString::number(hours) + "小时";
ui->label->setText(text);
}
5.6、QDial
核心属性:
核心信号:
代码示例:调整透明度
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置可以循环旋转
ui->dial->setWrapping(true);
//设置刻度线可见
ui->dial->setNotchesVisible(true);
//设置最大值为100
ui->dial->setMaximum(100);
//设置最小值为0
ui->dial->setMinimum(0);
//设置当前为100
ui->dial->setValue(100);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_dial_valueChanged(int value)
{
ui->label->setText(QString("当前不透明度为:") + QString::number(value));
this->setWindowOpacity((double)value / 100);
}
5.7、QSlider
作用:
使用QSlider表示一个滑动条,QSlider和QDial都是继承自QAbstractSlider,因此用法上基本相同
核心属性:
核心信号:
代码示例:调整窗口大小
在界面上创建两个滑动条,分别是水平和垂直滑动条。objectName分别为horizontalSlider和verticalSlider
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->horizontalSlider->setMinimum(500);
ui->horizontalSlider->setMaximum(2000);
ui->horizontalSlider->setSingleStep(100);
ui->horizontalSlider->setValue(800);
ui->verticalSlider->setMinimum(500);
ui->verticalSlider->setMaximum(1500);
ui->verticalSlider->setSingleStep(100);
ui->verticalSlider->setValue(600);
//翻转
ui->verticalSlider->setInvertedAppearance(true);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_horizontalSlider_valueChanged(int value)
{
QRect rect = this->geometry();
this->setGeometry(rect.x(), rect.y(), value, rect.height());
qDebug() << value;
}
void Widget::on_verticalSlider_valueChanged(int value)
{
QRect rect = this->geometry();
this->setGeometry(rect.x(), rect.y(), rect.width(), value);
qDebug() << value;
}
六、多元素控件
- QListWidget
- QListView
- QTableWidget
- QTableView
- QTreeWidget
- QTreeView
xxWidget和xxView之间的区别:
- QTableView是基于MVC设计的控件,QTableView自身不持有数据,使用QTableView时需要用户创建一个Model对象(如QStandardModel),并且把Model和QTableView关联起来。后续修改Model中的数据就会影响QTableView的显示;修改QTableView的显示也会影响到Model中的数据(双向绑定)
- QTableWidget则是QTableView的子类,对Model进行了封装,不需要用户手动创建Model对象,直接就可以往QTableWidget中添加数据了
6.1、QListWidget
作用:
使用QListWidget能够显示一个纵向的列表,每个选项都可以被选中
核心属性:
核心方法:
核心信号:
QListWidgetItem:
在上述介绍中,涉及到一个关键的类,QListWidgetItem。这个类表示QListWidget中的一个元素。本质上就是一个"文本+图标"构成的。
代码示例:使用QListWidget
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->listWidget->addItem("C++");
ui->listWidget->addItem("Java");
ui->listWidget->addItem("Python");
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_listWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
{
if(current != nullptr && previous != nullptr)
qDebug() << "当前选中:" << current->text() << ",之前选中:" << previous->text();
}
void Widget::on_pushButtonAdd_clicked()
{
const QString& text = ui->lineEdit->text();
if(!text.isEmpty())
ui->listWidget->addItem(text);
}
void Widget::on_pushButtonDel_clicked()
{
int row = ui->listWidget->currentRow();
ui->listWidget->takeItem(row);
}
6.2、QTableWidget
作用:
使用QTableWidget表示一个表格控件,一个表格中包含若干行,每一行又包含若干列。表格中的每个单元格是一个QTableWidgetItem对象。
QTableWidget核心方法:
QTableWidgetItem核心信号:
QTableWidgetItem核心方法:
代码示例:使用QTableWidget
在界面上创建QTableWidget和三个按钮,一个输入框
注意:QTableWidget是QTableView的子类,功能比QTableView更丰富,使用QTableWidget即可
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建3行
ui->tableWidget->insertRow(0);
ui->tableWidget->insertRow(1);
ui->tableWidget->insertRow(2);
//创建3列
ui->tableWidget->insertColumn(0);
ui->tableWidget->insertColumn(1);
ui->tableWidget->insertColumn(2);
//给3列设定列名
ui->tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem("学号"));
ui->tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem("姓名"));
ui->tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem("年龄"));
//设置初始数据
ui->tableWidget->setItem(0, 0, new QTableWidgetItem("1001"));
ui->tableWidget->setItem(0, 1, new QTableWidgetItem("张三"));
ui->tableWidget->setItem(0, 2, new QTableWidgetItem("20"));
ui->tableWidget->setItem(1, 0, new QTableWidgetItem("1002"));
ui->tableWidget->setItem(1, 1, new QTableWidgetItem("李四"));
ui->tableWidget->setItem(1, 2, new QTableWidgetItem("21"));
ui->tableWidget->setItem(2, 0, new QTableWidgetItem("1003"));
ui->tableWidget->setItem(2, 1, new QTableWidgetItem("王五"));
ui->tableWidget->setItem(2, 2, new QTableWidgetItem("22"));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButtonAddRow_clicked()
{
int rowCount = ui->tableWidget->rowCount();
ui->tableWidget->insertRow(rowCount);
}
void Widget::on_pushButtonAddColumn_clicked()
{
int colCount = ui->tableWidget->columnCount();
ui->tableWidget->insertColumn(colCount);
const QString& name = ui->lineEdit->text();
ui->tableWidget->setHorizontalHeaderItem(colCount, new QTableWidgetItem(name));
}
void Widget::on_pushButtonDeleteRow_clicked()
{
int currentRow = ui->tableWidget->currentRow();
ui->tableWidget->removeRow(currentRow);
}
void Widget::on_pushButtonDeleteColumn_clicked()
{
int currentCol = ui->tableWidget->currentColumn();
ui->tableWidget->removeColumn(currentCol);
}
6.3、QTreeWidget
作用:
- 使用QTreeWidget表示一个树形控件,里面的每个元素都是一个QTreeWidgetItem,每个QTreeWidgetItem可以包含多个文本和图标,每个文本/图标为一个列。
- 可以给QTreeWidget设置顶层节点(顶层节点可以有多个),然后再给顶层节点添加子节点,从⽽构成树形结构。。
QTreeWidget核心方法:
QTreeWidget核心信号:
QTreeWidgetItem核心属性:
QTreeWidgetItem核心方法:
代码示例:使用QTreeWidget
在界面上创建一个QTreeView,右键=>变形为=>QTreeWidget,再创建一个lineEdit和两个按钮
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->treeWidget->setHeaderLabel("动物");
QTreeWidgetItem* item1 = new QTreeWidgetItem();
item1->setText(0, "猫");
ui->treeWidget->addTopLevelItem(item1);
QTreeWidgetItem* item2 = new QTreeWidgetItem();
item2->setText(0, "狗");
ui->treeWidget->addTopLevelItem(item2);
QTreeWidgetItem* item3 = new QTreeWidgetItem();
item3->setText(0, "鸟");
ui->treeWidget->addTopLevelItem(item3);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
const QString& text = ui->lineEdit->text();
if(text.isEmpty()) return;
QTreeWidgetItem* item = new QTreeWidgetItem();
item->setText(0, text);
ui->treeWidget->addTopLevelItem(item);
}
void Widget::on_pushButton_2_clicked()
{
const QString& text = ui->lineEdit->text();
if(text.isEmpty()) return;
QTreeWidgetItem* currentItem = ui->treeWidget->currentItem();
if(currentItem == nullptr) return;
QTreeWidgetItem* item = new QTreeWidgetItem();
item->setText(0, text);
currentItem->addChild(item);
currentItem->setExpanded(true);
}
void Widget::on_pushButton_3_clicked()
{
QTreeWidgetItem* currentItem = ui->treeWidget->currentItem();
if(currentItem == nullptr) return;
QTreeWidgetItem* parent = currentItem->parent();
if(parent == NULL) {//顶层节点
int index = ui->treeWidget->indexOfTopLevelItem(currentItem);
ui->treeWidget->takeTopLevelItem(index);
}
else parent->removeChild(currentItem);
}
七、Containers(容器类控件)
7.1、QGroupBox
作用:
使用QGroupBox实现一个带有标题的分组框,可以把其他的控件放到里面作为一组,这样看起来更好看一些
注意:不要将QGroupBox和QButtonGroup混淆(之前在介绍QRadionButton时提到了QButtonGroup)
核心属性:
代码示例:给麦当劳案例加上分组框
在界面上创建三个分组框并且在分组框内部创建下拉框和微调框
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->comboBox->addItem("巨无霸");
ui->comboBox->addItem("麦辣鸡腿堡");
ui->comboBox_2->addItem("薯条");
ui->comboBox_2->addItem("麦辣鸡翅");
ui->comboBox_3->addItem("可乐");
ui->comboBox_3->addItem("雪碧");
}
Widget::~Widget()
{
delete ui;
}
7.2、QTabWidget
作用:
使用QTabWidget实现一个带有标签页的控件,可以往里面添加一些widget,进一步就可以通过标签页来切换。
核心属性:
核心信号:
代码示例:使用标签页管理多组控件
在界面上创建一个QTabWidget和两个按钮
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QLabel* label = new QLabel(ui->tab);
label->setText("标签页1");
label->resize(100, 50);
QLabel* label2 = new QLabel(ui->tab_2);
label2->setText("标签页2");
label->resize(100, 50);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButtonAdd_clicked()
{
//获取当前标签页的数量
int count = ui->tabWidget->count();
//创建新的widget
QWidget* widget = new QWidget();
ui->tabWidget->addTab(widget, QString("Tab ") + QString::number(count + 1));
//往widget中添加label
QLabel* label = new QLabel(widget);
label->setText(QString("标签页") + QString::number(count + 1));
label->resize(100, 50);
//选中这个新标签页
ui->tabWidget->setCurrentIndex(count);
}
void Widget::on_pushButtonDel_clicked()
{
int index = ui->tabWidget->currentIndex();
ui->tabWidget->removeTab(index);
}
八、Layouts(布局管理器)
概念:
- 之前使用Qt在界面中创建的控件都是通过"绝对定位"的方式来设定的,即每个控件所在的位置都需要计算坐标,最终通过setGeometry或者move方式摆放过去。
- 这种设定方式其实并不方便,尤其是界面内容较多的情况,不好计算,而且一个窗口大小往往是可以调整的,按照绝对定位的方式,也无法自适应窗口大小。
- 因此Qt引入"布局管理器"(Layout)机制来解决上述问题,布局管理器并非Qt独有,其他的GUI开发框架,如Android、前端等也有类似的机制。
8.1、QVBoxLayout
作用:
使用QVBoxLayout表示垂直的布局管理器,V是vertical的缩写。
核心属性:
代码示例:使用QVBoxLayout管理多个控件
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建三个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
//创建布局管理器,并将按钮添加进去
//若创建时指定父元素为this,则后面不需要setLayout了
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(btn1);
layout->addWidget(btn2);
layout->addWidget(btn3);
//将布局管理器设置到widget中
this->setLayout(layout);
}
Widget::~Widget()
{
delete ui;
}
代码示例:创建两个QVBoxLayout
在界面上创建两个QVBoxLayout,每个QVBoxLayout各放三个按钮,运行程序可以看到这些按钮已经自动排列好,只不过这些按钮的位置不能随着窗口大小自动变化
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Widget</class>
<widget class="QWidget" name="Widget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Widget</string>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>170</x>
<y>110</y>
<width>221</width>
<height>381</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="verticalLayoutWidget_2">
<property name="geometry">
<rect>
<x>400</x>
<y>110</y>
<width>221</width>
<height>381</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="pushButton_5">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_6">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_4">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
8.2、QHBoxLayout
作用:
使用QHBoxLayout表示垂直的布局管理器,H是horizontal的缩写。
代码示例:嵌套的layout
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建顶层layout
QVBoxLayout* layoutParent = new QVBoxLayout(this);
//添加两个按钮进去
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
layoutParent->addWidget(btn1);
layoutParent->addWidget(btn2);
//创建子layout
QHBoxLayout* layoutChild = new QHBoxLayout();
//添加两个按钮进去
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
layoutChild->addWidget(btn3);
layoutChild->addWidget(btn4);
//将子layout添加到父layout中
layoutParent->addLayout(layoutChild);
}
Widget::~Widget()
{
delete ui;
}
8.3、QGridLayout
作用:
QGridLayout用来实现网格布局的效果,可以达到M*N的网格效果
核心属性:
代码示例:使用QGridLayout管理元素
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建四个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
//创建网格布局管理器,并添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 0, 1);
layout->addWidget(btn3, 1, 0);
layout->addWidget(btn4, 1, 1);
//设置到layout到窗口中
this->setLayout(layout);
}
Widget::~Widget()
{
delete ui;
}
8.4、QFormLayout
作用:
QFormLayout属于是QGridLayout的特殊情况,专门用于实现两列表单的布局。这种表单布局多用于让用户填写信息的场景,左侧列为提示,右侧列为输入框。
代码示例:使用QFormLayout创建表单
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建 layout
QFormLayout* layout = new QFormLayout();
this->setLayout(layout);
// 创建三个 label
QLabel* label1 = new QLabel("姓名");
QLabel* label2 = new QLabel("年龄");
QLabel* label3 = new QLabel("电话");
// 创建三个 lineEdit
QLineEdit* lineEdit1 = new QLineEdit();
QLineEdit* lineEdit2 = new QLineEdit();
QLineEdit* lineEdit3 = new QLineEdit();
// 创建⼀个提交按钮
QPushButton* btn = new QPushButton("提交");
// 把上述元素添加到 layout 中
layout->addRow(label1, lineEdit1);
layout->addRow(label2, lineEdit2);
layout->addRow(label3, lineEdit3);
layout->addRow(NULL, btn);
}
Widget::~Widget()
{
delete ui;
}
九、Spacers(间隔控件)
作用:
使用布局管理器时,可能需要在控件之间,添加一段空白,就可以使用QSpacerItem来表示
核心属性:
代码示例:创建一组左右排列的按钮
在界面上创建一个QVBoxLayout 并添加两个按钮
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QHBoxLayout* layout = new QHBoxLayout();
this->setLayout(layout);
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
// 创建 Spacer
QSpacerItem* spacer = new QSpacerItem(200, 20);
layout->addWidget(btn1);
// 在两个 widget 中间添加空⽩
layout->addSpacerItem(spacer);
layout->addWidget(btn2);
}
Widget::~Widget()
{
delete ui;
}
十、结束语
今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。