目录
一、控件概述
二、QWidget
三、Buttons类控件
3.1 QPushButton
3.2 QRadioButton
3.3 QCheckBox
3.4 QToolButton
四、Display Widgets(显示类控件)
4.1 QLabel
4.2 QLCDNumber
4.3 QProgressBar
4.4 QCalendarWidget
五、Input Widgets(输入类控件)
5.1 QLineEdit
5.2 QTextEdit
5.3 QComboBox
5.4 QSpinBox
5.5 QDateEdit & QTimeEdit
5.6 QDial
5.7 QSlider
六、多元素控件
6.1 QListWidget
6.2 QTableWidget
6.3 QTreeWidget
七、Containers(容器类控件)
7.1 QGroupBox
7.2 QTabWidget
八、Layouts(布局管理器)
8.1 QVBoxLayout
8.2 QHBoxLayout
8.3 QGridLayout
8.4 QFormLayout
九、Spacers(间隔控件)
一、控件概述
Widget是Qt中的核心概念,英文原义是"小部件",此处将其翻译为"控件"。控件是构成一个图形化界面的基本要素,如按钮、列表视图、树形视图、单行输入框、多行输入框、滚动条、下拉框等都可以称为"控件"
Qt作为一个成熟的GUI开发框架,内置了大量的常用控件。Qt也提供了"自定义控件"的能力,可以在现有控件不能满足需求时,对现有控件做出扩展,或者自定义出新的控件
控件体系的发展
第一阶段:完全没有控件。此时需要通过一些绘图API手动的绘制出按钮或者输入框等内容,代码编写繁琐。如文曲星的Lava平台开发
第二阶段:只包含粗略的控件,只提供了按钮、输入框、单选框、复选框等最常用的控件。如html的原生控件
第三阶段:更完整的控件体系,基本可以覆盖到GUI开发中的大部分场景。如早期的MFC、VB、C++ Builder、Qt、Delphi,后来的Android SDK、Java FX、前端的各种UI库等
二、QWidget
具体参考博主下面这篇文章
【Qt】Qwidget的常见属性-CSDN博客https://blog.csdn.net/GG_Bruse/article/details/136476973
三、Buttons类控件
3.1 QPushButton
Qt中使用QPushButton表示一个按钮。QPushButton继承自QAbstractButton,该类是一个抽象类,是其他按钮的父类
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(":/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,文本内容均清空
创建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);
3.2 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;
}
再次执行程序,就可以按照正确的分组方式来进行排它了
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可以用来显示文本和图片
核心属性
属性 | 说明 |
---|---|
text | QLabel中的文本 |
textFormat | 文本的格式
|
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;
}
4.2 QLCDNumber
QLCDNumer是一个专门用来显示数字的控件,类似于"老式计算器"的效果
核心属性
属性 | 说明 |
---|---|
intValue | QLCDNumber显示的数字(int) |
value | QLCDNumber显示的数字(double) 和intValue是联动的(如:给value设置为1.5,intValue的值就是2) |
digCount | 显示几位数字 |
mode | 数字显示形式
只有十进制时才能显示小数点后面的数字 |
segmentStyle | 设置显示的风格 QLCDNumber::Flat:平面的显示风格,数字呈现在一个平坦的表面上 QLCDNumber::Outline:轮廓显示风格,数字具有清晰的轮廓和阴影效果 QLCDNumber::Filled:填充显示风格,数字被填充颜色并于背景区分开 |
smallDecimalPoint | 设置比较小的小数点 |
代码示例:倒计时
在界面上创建一个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);
}
存在问题1
上述代码若直接在Widget构造函数中,通过一个循环+sleep的方式是否可以呢?
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
int value = ui->lcdNumber->intValue();
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
if (value <= 0) break;
ui->lcdNumber->display(value - 1);
}
}
显然这个代码是不行的,循环会使Widget的构造函数无法执行完毕,此时界面是不能正确构造和显示的
存在问题2
上述代码若是在Widget构造函数中另起⼀个线程,在新线程中完成循环+sleep是否可以呢?
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
std::thread t([this]() {
int value = this->ui->lcdNumber->intValue();
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
if (value <= 0) break;
this->ui->lcdNumber->display(value - 1);
}
});
}
这个代码同样是不行的。Qt中规定,任何对于GUI上内容的操作必须在主线程中完成。像Widget构造函数,以及connect连接的slot函数,都是在主线程中调用的,而自主创建的线程则不是。当自主创建的线程中尝试对界面元素进行修改时,Qt程序往往会直接崩溃
注意:
这样的约定主要是因为GUI中的状态往往是牵一发动全身的,修改一个地方就需要同步的对其他内容进行调整。如调整了某个元素的尺寸,就可能影响到内部的文字位置,或者其他元素的位置。这里一连串的修改,都是需要按照一定的顺序来完成的。由于多线程执行的顺序无法保障,因此Qt从根本上禁止了其他线程修改GUI状态,避免后续的一系列问题
4.3 QProgressBar
Qt中使用QProgressBar表示一个进度条
核心属性
属性 | 说明 |
---|---|
minimum | 进度条最小值 |
maximum | 进度条最大值 |
value | 进度条当前值 |
alignment | 文本在进度条中的对齐方式
|
textVisible | 进度条的数字是否可见 |
orientation | 进度条的方向是水平还是垂直 |
invertAppearance | 是否朝反方先增长进度 |
format | 展示的数字格式
|
代码示例:设置进度条按时间增长
在界面上创建进度条,objectName为progressBar
其中最小值设为0,最大值设为100,当前值设为0
修改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);
}
代码示例:创建以个红色的进度条
QProgressBar也是QWidget的子类,因此可以使用styleSheet通过样式来修改进度条的颜色。在Qt Designer右侧的属性编辑器中,找到QWidget的styleSheet属性
QProgressBar::chunk {background-color: #FF0000;}
同时把QProcessBar的alignment属性设置为垂直水平居中
4.4 QCalendarWidget
QCalendarWidget表示一个"日历"
核心属性
属性 | 说明 |
---|---|
selectDate | 当前选中的日期 |
minimumDate | 最小日期 |
maximumDate | 最大日期 |
firstDayOfWeek | 日历的第一列是周几 |
gridVisible | 是否显示表格的边框 |
selectionMode | 是否允许选择日期 |
navigationBarVisible | 日历上方标题是否显示 |
horizontalHeaderFormat | 日历上方标题显示的日期格式 |
verticalHeaderFormat | 日历第一列显示的内容格式 |
dateEditEnabled | 是否允许日期被编辑 |
重要信号
信号 | 说明 |
---|---|
selectionChanged(const QDate&) | 当选中的日期发生改变时触发 |
activated(const QDate&) | 当双击一个日期或者按下回车键时触发 |
currentPageChanged(int, int) | 当年份月份发生改变时触发,形参表示改变后的新年份和新月份 |
代码示例:获取当前选中的日期
在界面上创建一个QCalendarWidget和一个label,objectName为calendarWidget、label
给QCalendarWidget添加slot函数
void Widget::on_calendarWidget_selectionChanged()
{
QDate date = ui->calendarWidget->selectedDate();
qDebug() << date;
ui->label->setText(date.toString());
}
五、Input Widgets(输入类控件)
5.1 QLineEdit
QLineEdit用来表示单行输入框,可以输入一段文本,但是不能换行
核心属性
属性 | 说明 |
---|---|
text | 输入框中的文本 |
inputMask | 输入内容格式约束 |
maxLength | 最大长度 |
frame | 是否添加边框 |
echoMode | 显示方式
|
cursorPosition | 光标所在的位置 |
alignment | 文字对齐的方式,设置水平和垂直方向的对齐 |
dragEnabled | 是否允许拖拽 |
readOnly | 是否是只读的 |
placeHolderText | 当输入框为空时,显示什么样的提示信息 |
clearButtonEnabled | 是否会自动显示出"清除按钮" |
核心信号
信号 | 说明 |
---|---|
void cursorPositionChanged(int old, int new) | 当鼠标移动时发出此信号,old为先前的位置,new为新位置 |
void editingFinished() | 当按下返回或者回车键时,或行编辑失去焦点时,发出此信号 |
void returnPress() | 当返回或回车键按下时发出此信号 若设置了验证器,必须要通过验证,才能触发 |
void selectionChanged() | 当选中的文本改变时,发出此信号 |
void textChanged(const QString& text) | 当QLineEdit中的文本改变时,发出此信号,text是新的文本 代码对文本的修改可以触发此信号 |
void textEdited(const QString& text) | 当QLineEdit中的文本改变时,发出此信号,text是新文本 代码对文本的修改不可以触发此信号 |
代码示例:录入个人信息
在界面上创建三个输入框和两个单选按钮,一个普通按钮。三个输入框的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只能进行简单的输入格式校验。实际开发中,基于正则表达式的方式是更核心的方法
代码示例:使用正则表达式验证输入框数据
正则表达式是一种在计算机中常用的,使用特殊字符描述一个字符串的特征的机制。在进行字符串匹配时非常有用。正则表达式的语法较为复杂,一般都是随用随查,不需要背下来
正则表达式语法 | Microsoft Learnhttps://learn.microsoft.com/zh-cn/previous-versions/visualstudio/visual-studio-2008/ae5bf541(v=vs.90)?redirectedfrom=MSDN正则表达式语法测试工具 - 在线工具正则表达式语法在线测试工具,支持文本字符串正则替换。https://regextester.buyaocha.com/在界面上创建输入框和一个按钮
#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编辑器,并且能在内容超出编辑框范围时自动提供滚动条
核心属性
属性 | 说明 |
---|---|
markdown | 输入框内的内容支持markdown,能自动对markdown文本进行渲染 |
html | 可以支持大部分html标签,包括img、table等 |
placeHolderText | 输入框为空时提示的内容 |
readOnly | 是否是只读的 |
undoRedoEnable | 是否支持undo、redo功能 按下ctrl+z触发undo 按下ctrl+y触发redo |
autoFormating | 开启自动格式化 |
tabstopWidth | 缩进占用多少空间 |
overwriteMode | 是否开辟复写模式 |
acceptRichText | 是否接收富文本内容 |
verticalScrollBarPolicy | 垂直方向滚动条的出现策略
|
horizontalScrollBarPolicy | 水平方向滚动条的出现策略 |
核心信号
信号 | 说明 |
---|---|
textChanged() | 文本内容改变时触发 |
selectionChanged() | 选中范围改变时触发 |
cursorPositionChanged() | 光标移动时触发 |
undoAvailable(bool) | 进行undo操作时触发 |
redoAvailable(bool) | 进行redo操作时触发 |
copyAvailable(bool) | 文本被选中/取消选中时触发 |
代码示例:获取多行输入框的内容
创建一个多行输入框和一个label
通过toPlainText方法获取到内部的文本。QTextEdit还提供了toMarkdown和toHtml,根据需要调整不同的获取方式
#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表示下拉框
核心属性
属性 | 说明 |
---|---|
currentText | 当前选中的文本 |
currentIndex | 当前选中的条目下标 从0开始计算,如果当前没有条目被选中,值为1 |
editable | 是否允许修改 设为true时,QComboBox的行为就十分接近QLineEdit,也可以设置validator |
iconSize | 下拉框图标(小三角)的大小 |
maxCount | 最多允许有多少个条目 |
核心方法
方法 | 说明 |
---|---|
addItem(const QString&) | 添加一个条目 |
currentIndex() | 获取当前条目的下标 从0开始计算,若当前没有条目被选中,值为-1 |
currentText() | 获取当前条目的文本内容 |
核心信号
方法 | 说明 |
---|---|
activated(int) activated(const QString& text) | 当用户选择了一个选项时发出 这时相当于用户点开下拉框,并且鼠标划过某个选项 此时还没确认做出选择 |
currentIndexChanged(int) currentIndexChanged(const QString& text) | 当前选项改变时发出 此时用户已经明确的选择了一个选项 用户操作或者通过程序操作都会触发这个信号 |
editTextChanged(const QString& text) | 当编辑框中的文本改变时发出 (editable为true时有效) |
代码示例:使用下拉框模拟麦当劳点餐
在界面上创建三个下拉框和一个按钮
#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;
}
Qt中也提供了QFile实现读写文件的功能。使用C++标准库的std::fstream也是完全可以的
之所以存在两套,是因为Qt诞生较早(1991年左右)此时C++还没有完成"标准化"的工作,C++标准库这样的概念自然也没有诞生。因此Qt就自己打造了⼀套库,实现了字符串、容器、文件操作、多线程、网络操作、定时器、正则表达式等内容
(由于C++标准委员会的不作为,至今仍然有些Qt提供的功能是标准库不具备的)
5.4 QSpinBox
使用QSpinBox或者QDoubleSpinBox表示"微调框",它是带有按钮的输入框,可以用来输入整数/浮点数,通过点击按钮来修改数值大小
核心属性
属性 | 说明 |
---|---|
value | 存储的数值 |
singleStep | 每次调整的步长(按下一次按钮数据变化多少) |
displayInteger | 数字的进制 |
minimum | 最小值 |
maximum | 最大值 |
suffix | 后缀 |
prefix | 前缀 |
wrapping | 是否允许换行 |
frame | 是否带边框 |
alignment | 文字对齐方式 |
readOnly | 是否允许修改 |
buttonSymbol | 按钮上的图标
|
accelerated(加速的) | 按下按钮时是否为快速调整模式 |
correctionMode | 输入有误时如何调整
|
keyboardTrack | 是否开启键盘跟踪
|
核心信号
信号 | 说明 |
---|---|
textChanged(QString) | 微调框的文本发生改变时触发 参数QString带有前缀和后缀 |
valueChanged(int) | 微调框的文本发生改变时触发 参数int代表当前的数值 |
代码示例:调整麦当劳购物车中的份数
#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核心属性
属性 | 说明 |
---|---|
dateTime | 时间日期的值,形如 2000/1/1 0:00:00 |
date | 单纯日期的值,形如 2001/1/1 |
time | 单纯时间的值,形如 0:00:00 |
displayFormat | 时间日期格式,形如 yyyy/M/d H:mm:ss
|
minimumDateTime | 最小时间日期 |
maximumDateTime | 最大时间日期 |
timeSpec | Qt::LocalTime:显示本地时间 Qt::UTC 显示协调世界时 Qt::OffsetFromUTC:显示相对于UTC的偏移量(时差) |
关于本地时间(LocalTime)和协调世界时(UTC)
UTC时间是一个基于原子钟的标准时间,不受地球的自转周期影响,和格林威治时间(GMT)是
非常接近的,科学家会通过精密的设备来测量并维护
计算机内部使用的时间就是基于UTC时间,本地时间则是基于不同的时区,对UTC时间做出了一些调整。如北京时间,位于"东八区",就需要在UTC时间基础上+8个小时的时差
核心信号
信号 | 说明 |
---|---|
dateChanged(QDate) | 日期改变时触发 |
timeChanged(QTime) | 时间改变时触发 |
dateTimeChanged(QDateTime) | 日期时间任意一个改变时触发 |
代码示例:时间日期计算器
#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
使用QDial表示一个旋钮
核心属性
属性 | 说明 |
---|---|
value | 持有的数值 |
minimum | 最小值 |
maximum | 最大值 |
singleStep | 按下方向键时改变的步长 |
pageStep | 按下pageUp/pageDown时改变的步长 |
sliderPosition | 界面上旋钮显示的初始位置 |
tracking | 外观是否会跟踪数值变化 默认值为true,一般不需要修改 |
wrapping | 是否允许循环调整 即数值如果超过最大值,是否允许回到最小值 (调整过程能否"套圈") |
notchesVisible | 是否显示刻度线 |
notchTarget | 刻度线之间的相对位置 数字越大,刻度线越稀疏 |
核心信号
信号 | 说明 |
---|---|
valueChanged(int) | 数值改变时触发 |
rangedChanged(int,int) | 范围改变时触发 |
代码示例:调整透明度
#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,因此用法上基本相同
核心属性
属性 | 说明 |
---|---|
value | 数值 |
minimum | 最小值 |
maximum | 最大值 |
singleStep | 按下方向键时改变的步长 |
pageStep | 按下pageUp/pageDown时改变的步长 |
sliderPosition | 滑动条显示的初始位置 |
tracking | 外观是否会跟踪数值变化 默认值为true,一般不需要修改 |
orientation | 滑动条的方向是水平还是垂直 |
invertedAppearance | 是否要翻转滑动条的方向 |
tickPosition | 刻度的位置 |
tickInterval | 刻度的密集程度 |
核心信号
信号 | 说明 |
---|---|
valueChanged(int) | 数值改变时触发 |
rangedChanged(int,int) | 范围变换时触发 |
代码示例:调整窗口大小
在界面上创建两个滑动条,分别是水平和垂直滑动条。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之间的区别
以QTableWidget和QTableView为例
- QTableView是基于MVC设计的控件,QTableView自身不持有数据,使用QTableView时需要用户创建一个Model对象(如QStandardModel),并且把Model和QTableView关联起来。后续修改Model中的数据就会影响QTableView的显示;修改QTableView的显示也会影响到Model中的数据(双向绑定)
- QTableWidget则是QTableView的子类,对Model进行了封装,不需要用户手动创建Model对象,直接就可以往QTableWidget中添加数据了
6.1 QListWidget
使用QListWidget能够显示一个纵向的列表,每个选项都可以被选中
核心属性
属性 | 说明 |
---|---|
currentRow | 当前被选中的是第几行 |
count | 一共有多少行 |
sortingEnabled | 是否允许排序 |
isWrapping | 是否允许换行 |
itemAlignment | 元素的对齐方式 |
selectRectVisible | 被选中的元素矩形是否可见 |
spacing | 元素之间的间隔 |
核心方法
方法 | 说明 |
---|---|
addItem(const QString& label) addItem(QListWidgetItem* item) | 列表中添加元素 |
currentItem() | 返回QListWidgetItem*表示当前选中的元素 |
setCurrentItem(QListWidgetItem* item) | 设置选中哪个元素 |
setCurrentRow(int row) | 设置选中第几行的元素 |
insertItem(const QString& label,int row) insertItem(QListWidgetItem* item,int row) | 在指定行插入元素 |
item(int row) | 返回QListWidgetItem*表示第row行的元素 |
takeItem(int row) | 删除指定行的元素,返回QListWidgetItem*表示是哪个元素被删除了 |
核心信号
信号 | 说明 |
---|---|
currentItemChanged(QListWidgetItem* current,QListWidgetItem* old) | 选中不同元素时会触发,参数是当前选中的元素和之前选中的元素 |
currentRowChanged(int) | 选中不同元素时会触发,参数是当前选中元素的行数 |
itemClicked(QListWidgetItem* item) | 点击某个元素时触发 |
itemDoubleClicked(QListWidgetItem* item) | 双击某个元素时触发 |
itemEntered(QListWidgetItem* item) | 鼠标进入元素时触发 |
QListWidgetItem
在上述介绍中,涉及到一个关键的类,QListWidgetItem。这个类表示QListWidget中的一个元素。本质上就是一个"文本+图标"构成的
方法 | 说明 |
---|---|
setFont | 设置字体 |
setIcon | 设置图标 |
setHidden | 设置隐藏 |
setSizeHint | 设置尺寸 |
setSelected | 设置是否选中 |
setText | 设置文本 |
setTextAlignment | 设置文本对齐方式 |
代码示例:使用QListWidget
在界面上创建一个ListView,右键=>变形为=>QListWidget,再创建一个QLineEdit和两个按钮
注意:ListWidget是ListView的子类,功能比QListView更丰富,下面使用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核心方法
方法 | 说明 |
---|---|
item(int row, int column) | 根据行列获取指定的QTableWidgetItem* |
setItem(int row, int column, QTableWidget*) | 根据行数列数设置表格中的元素 |
currentItem() | 返回被选中的元素QTableWidgetItem* |
currentRow() | 返回被选中元素是第几行 |
currentColumn() | 返回被选中元素是第几列 |
row(QTableWidgetItem*) | 获取指定item是第几行 |
column(QTableWidgetItem*) | 获取指定item是第几列 |
rowCount() | 获取行数 |
columnCount() | 获取列数 |
insertRow(int row) | 在第row行处插入新行 |
insertColumn(int column) | 在第column列插入新列 |
removeRow(int row) | 删除第row行 |
removeColumn(int column) | 删除第column列 |
setHorizontalHeaderItem(int column, QTableWidgetItem*) | 设置指定列的表头 |
setVerticalHeaderItem(int row, QTableWidgetItem*) | 设置指定行的表头 |
QTableWidgetItem核心信号
信号 | 说明 |
---|---|
cellClicked(int row, int column) | 点击单元格时触发 |
cellDoubleClicked(int row, int column) | 双击单元格时触发 |
cellEntered(int row, int column) | 鼠标进入单元格时触发 |
currentCellChanged(int row, int column, int previousRow, int previousColumn) | 选中不同单元格时触发 |
QTableWidgetItem核心方法
方法 | 说明 |
---|---|
row() | 获取当前是第几行 |
column() | 获取当前是第几列 |
setText(const QString&) | 设置文本 |
setTextAlignment(int) | 设置文本对齐 |
setIcon(const QIcon&) | 设置图标 |
setSelected(bool) | 设置被选中 |
setSizeHints(const QSize&) | 设置尺寸 |
setFont(const QFont&) | 设置字体 |
代码示例:使用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核心方法
方法 | 说明 |
---|---|
clear | 清空所有子节点 |
addTopLevelItem(QTreeWidgetItem* item) | 新增顶层节点 |
topLevelItem(int index) | 获取指定下标的顶层节点 |
topLevelItemCount() | 获取顶层节点个数 |
indexOfTopLevelItem(QTreeWidgetItem* item) | 查询指定节点在顶层下标中的节点 |
takeTopLevelItem(int index) | 删除指定的顶层节点,返回QTreeWidgetItem*表示被删除的元素 |
currentItem() | 获取当前选中的节点,返回QTreeWidgetItem* |
setCurrentItem(QTreeWidgetItem* item) | 选中指定节点 |
setExpanded(bool) | 展开/关闭节点 |
setHeaderLabel(const QString& text) | 设置QTreeWidget的header名称 |
QTreeWidget核心信号
信号 | 说明 |
---|---|
currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* old) | 切换选中元素时触发 |
itemClicked(QTreeWidgetItem* item, int col) | 点击元素时触发 |
itemDoubleClicked(QTreeWidgetItem* item, int col) | 双击元素时触发 |
itemEntered(QTreeWidgetItem* item, int col) | 鼠标进入时触发 |
itemExpanded(QTreeWidgetItem* item) | 元素被展开时触发 |
itemCollapsend(QTreeWidgetItem* item) | 元素被折叠时触发 |
QTreeWidgetItem核心属性
属性 | 说明 |
---|---|
text | 持有的文本 |
textAlignment | 文本对齐方式 |
icon | 持有的图表 |
font | 文本字体 |
hidden | 是否隐藏 |
disabled | 是否禁用 |
expand | 是否展开 |
sizeHint | 尺寸大小 |
selected | 是否选中 |
QTreeWidgetItem核心方法
方法 | 说明 |
---|---|
addChlid(QTreeWidgetItem* child) | 新增子节点 |
childCount() | 子节点的个数 |
child(int index) | 获取指定下标的子节点,返回QTreeWidgetItem* |
takeChild(int index) | 删除对应下标的子节点 |
removeChild(QTreeWidgetItem* child) | 删除对应的子节点 |
parent() | 获取该元素的父节点 |
代码示例:使用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)
核心属性
属性 | 说明 |
---|---|
title | 分组框的标题 |
alignment | 分组框内部内容的对齐方式 |
flat | 是否为"扁平"模式 |
checkable | 是否可选择 设为true,则在title前方会多出一个可勾选的部分 |
checked | 描述分组框的选择状态(前提时checkable为true) |
分组框只是一个用来"美化界面"的组件,并不涉及到用户交互和业务逻辑,属于"锦上添花"
代码示例:给麦当劳案例加上分组框
在界面上创建三个分组框并且在分组框内部创建下拉框和微调框
注意:在复制粘贴控件时,一定要先选中对应的父控件再粘贴
#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,进一步就可以通过标签页来切换
核心属性
属性 | 说明 |
---|---|
tabPosition | 标签页所在的位置 |
currentIndex | 当前选中了第几个标签页(从0开始计算) |
currentTabText | 当前选中的标签页的文本 |
currentTabName | 当前选中的标签页的名字 |
currentTabIcon | 当前选中的标签的图标 |
currentTabToolTip | 当前选中的标签页的提示信息 |
tabsCloseable | 标签页是否可以关闭 |
moveable | 标签页是否可以移动 |
核心信号
属性 | 说明 |
---|---|
currentChanged(int) | 在标签页发生切换时触发,参数为被点击的选项卡编号 |
tabBarClicked(int) | 在点击选项卡的标签条时触发,参数为被点击的选项卡编号 |
tabBarDoubleClicked(int) | 在双击选项卡的标签条时触发,参数为被点击的选项卡编号 |
tabCloseRequest(int) | 在标签页关闭时触发,参数为被关闭的选项卡编号 |
代码示例:使用标签页管理多组控件
在界面上创建一个QTabWidget和两个按钮
QTabWidget中的每个标签页都是一个QWidget;点击标签页就可以直接切换;右键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的缩写
核心属性
属性 | 说明 |
---|---|
layoutLeftMargin | 左侧边距 |
layoutRightMargin | 右侧边距 |
layoutTopMargin | 上方边距 |
layoutBottomMargin | 下方边距 |
layoutSpacing | 相邻元素之间的间距 |
Layout只用于界⾯布局,并没有提供信号
代码示例:使用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;
}
通过上述代码的方式,只能给这个widget设定一个布局管理器,实际上也可以通过Qt Design在一个窗口中创建多个布局管理器
代码示例:创建两个QVBoxLayout
在界面上创建两个QVBoxLayout,每个QVBoxLayout各放三个按钮
运行程序可以看到这些按钮已经自动排列好,只不过这些按钮的位置不能随着窗口大小自动变化
通过Qt Designer创建的布局管理器,其实是先创建了一个widget,设置过geometry属性的,再把这个layout设置到这个widget中
实际上,一个widget只能包含一个layout,打开ui文件的原始xml,可以看到其中的端倪
这种情况下layout并非是窗口widget的布局管理器,因此不会随着窗口大小改变
<?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的缩写
核心属性与QVBoxLayout一致
代码示例:嵌套的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的网格效果
核心属性
属性 | 说明 |
---|---|
layoutLeftMargin | 左侧边距 |
layoutRightMargin | 右侧边距 |
layoutTopMargin | 上方边距 |
layoutBottomMargin | 下方边距 |
layoutHorizontalSpacing | 相邻元素之间水平方向的间距 |
layoutVerticalSpacing | 相邻元素之间垂直方向的间距 |
layoutRowStretch | 行方向的拉伸系数 |
layoutColumnStretch | 列方向的拉伸系数 |
代码示例:使用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;
}
执行代码,观察效果,可以看到当前的这几个按钮是按照2行2列的方式排列的
若调整行列坐标为下列代码
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 0, 1);
layout->addWidget(btn3, 0, 2);
layout->addWidget(btn4, 0, 3);
执行代码,可以看到这几个按钮都在同一行了,相当于QHBoxLayout
若调整行列坐标为下列代码
layout->addWidget(btn1, 1, 0);
layout->addWidget(btn2, 2, 0);
layout->addWidget(btn3, 3, 0);
layout->addWidget(btn4, 4, 0);
执行代码,可以看到这几个按钮都在同一列了,相当于QVBoxLayout
注意:设置行和列时,若设置的是一个很大的值,但是这个值和上一个值之间并没有其他的元素,那么并不会在中间腾出额外的空间
代码示例:设置QGridLayout中元素的大小比例
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建 6 个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
QPushButton* btn5 = new QPushButton("按钮5");
QPushButton* btn6 = new QPushButton("按钮6");
// 创建⽹格布局管理器, 并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 0, 1);
layout->addWidget(btn3, 0, 2);
layout->addWidget(btn4, 1, 0);
layout->addWidget(btn5, 1, 1);
layout->addWidget(btn6, 1, 2);
// 第0列拉伸⽐例设为1
layout->setColumnStretch(0, 1);
// 第1列拉伸⽐例设为0, 即为固定⼤⼩, 不参与拉伸
layout->setColumnStretch(1, 0);
// 第2列拉伸⽐例设为3, 即为第2列的宽度是第0列的3倍
layout->setColumnStretch(2, 3);
// 设置layout到窗⼝中
this->setLayout(layout);
}
Widget::~Widget()
{
delete ui;
}
使用QGridLayout能够代替很多QHBoxLayout和QVBoxLayout嵌套的场景,毕竟嵌套的代码写起来是比较麻烦的。QGridLayout里面也能嵌套QHBoxLayout和QVBoxLayout,QHBoxLayout和QVBoxLayout里面也能嵌套QGridLayout。灵活使用上述布局管理器,就可以实现出任意的复杂界面
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来表示
核心属性
属性 | 说明 |
---|---|
width | 宽度 |
height | 高度 |
hData | 水平方向的sizePolicy
|
vData | 垂直方向的sizePolicy |
代码示例:创建一组左右排列的按钮
在界面上创建一个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;
}