在之前博主主要是围绕QWidget中涉及到的各种属性/函数/使用方法,那么从这里开始,将介绍Qt中的各种控件,QWidget中的各种属性/函数,在接下来中的控件中也是有效的.
[2:LCD Number](#2:LCD Number)
[4:Calendar Widget](#4:Calendar Widget)
[1:Line Edit](#1:Line Edit)
[2:Text Edit](#2:Text Edit)
[3:Combo Box](#3:Combo Box)
[4:Spin Box](#4:Spin Box)
[5:Date Edit & Time Edit](#5:Date Edit & Time Edit)
一:按钮类控件
1:PushButton
- 使用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 和 audoDefault 影响的是按下 enter 时自动点击哪个按钮的行为; flat 把按钮设置为扁平的样式. 这里我们暂时都不做过多关注.
1.1:代码示例1
创建 resource.qrc 文件, 并导入图片

在界面上创建⼀个按钮

修改 widget.cpp, 给按钮设置图标.
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QIcon>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QIcon icon(":/Cat.png");
//设置图标
ui->pushButton->setIcon(icon);
//设置图标大小
ui->pushButton->setIconSize(QSize(50,50));
}
Widget::~Widget()
{
delete ui;
}

1.2:代码案例2
- 在界⾯中拖五个按钮.五个按钮的 objectName 分别为 pushButton_target , pushButton_up ,ushButton_down , pushButton_left , pushButton_right
- 五个按钮的初始位置随意, 其中 pushButton_target 尺⼨设置为 100 * 100, 其余按钮设为 50 * 50. ⽂本内容均清空

创建 resource.qrc , 并导入5个图片

修改 widget.cpp, 设置图标资源和快捷键
- 使用setShortcut 给按钮设置快捷键. 参数是⼀个 QKeySequence 对象. 表示⼀个按键序列. ⽀持组合键 (ctrl + c 这种).
- QKeySequence 的构造函数参数, 可以直接使⽤ "ctrl+c" 这样的按键名字符串表示, 也可以使用预定义好的常量 (形如 Qt::CTRL + Qt::Key_C ) 表示
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QRect>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置图标
ui->pushButton_target->setIcon(QIcon(":/image/Cat.png"));
ui->pushButton_target->setIconSize(QSize(50,50));
ui->pushButton_up->setIcon(QIcon(":/image/Up.png"));
ui->pushButton_up->setIconSize(QSize(30,30));
ui->pushButton_down->setIcon(QIcon(":/image/Down.png"));
ui->pushButton_down->setIconSize(QSize(30,30));
ui->pushButton_left->setIcon(QIcon(":/image/Left.png"));
ui->pushButton_left->setIconSize(QSize(20,20));
ui->pushButton_right->setIcon(QIcon(":/image/Right.png"));
ui->pushButton_right->setIconSize(QSize(20,20));
//设置快捷键
ui->pushButton_up->setShortcut(QKeySequence("w"));
ui->pushButton_down->setShortcut(QKeySequence("s"));
ui->pushButton_left->setShortcut(QKeySequence("a"));
ui->pushButton_right->setShortcut(QKeySequence("d"));
// 设置快捷键也可以写作
// ui->pushButton_up->setShortcut(QKeySequence(Qt::Key_W));
// ui->pushButton_down->setShortcut(QKeySequence(Qt::Key_S));
// ui->pushButton_left->setShortcut(QKeySequence(Qt::Key_A));
// ui->pushButton_right->setShortcut(QKeySequence(Qt::Key_D));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_up_clicked()
{
QRect rect = ui->pushButton_target->geometry();
ui->pushButton_target->setGeometry(rect.x(),rect.y() - 5,rect.width(),rect.height());
qDebug()<<"Up";
}
void Widget::on_pushButton_left_clicked()
{
QRect rect = ui->pushButton_target->geometry();
ui->pushButton_target->setGeometry(rect.x() - 5,rect.y(),rect.width(),rect.height());
qDebug()<<"left";
}
void Widget::on_pushButton_right_clicked()
{
QRect rect = ui->pushButton_target->geometry();
ui->pushButton_target->setGeometry(rect.x() + 5,rect.y(),rect.width(),rect.height());
qDebug()<<"rigt";
}
void Widget::on_pushButton_down_clicked()
{
QRect rect = ui->pushButton_target->geometry();
ui->pushButton_target->setGeometry(rect.x(),rect.y() + 5,rect.width(),rect.height());
qDebug()<<"down";
}

按钮的重复触发
在上述案例中, 按住快捷键, 是可以进⾏重复触发的. 但是⿏标点击则不能.
修改 widget.cpp, 在构造函数中开启重复触发.
cpp
ui->pushButton_up->setAutoRepeat(true);
ui->pushButton_down->setAutoRepeat(true);
ui->pushButton_left->setAutoRepeat(true);
ui->pushButton_right->setAutoRepeat(true);
此时, 按住鼠标时, 即可让狗头连续移动
2:RadioButtion
QRadioButton 是单选按钮. 可以让我们在多个选项中选择⼀个.
作为 QAbstractButton 和 QWidget 的子类, 上⾯介绍的属性和用法, 对于 QRadioButton
同样适.
QAbstractButton中和 QRadioButton关系较大的属性.
|---------------|--------------------------------------------------------|
| 属性 | 说明 |
| checkable | 是否能选中 |
| checked | 是否已经被选中. checkable 是 checked 的前提条件. |
| autoExclusive | 是否排他. 选中⼀个按钮之后是否会取消其他按钮的选中. 对于 QRadioButton 来说默认就是排他的. |
2.1:代码示例1
在界⾯上创建⼀个 label, 和 3 个 单选按钮设置的文本如下图. 3 个单选按钮的 objectName 分别为 radioButton_male , radioButton_female , radioButton_other

修改 widget.cpp, 编辑三个 QRadioButton 的 slot 函数.
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_radioButton_male_clicked()
{
ui->label->setText("你选择的性别为:男");
}
void Widget::on_radioButton_female_clicked()
{
ui->label->setText("你选择的性别为:女");
}
void Widget::on_radioButton_other_clicked()
{
ui->label->setText("你选择的性别为:其他");
}
运⾏程序, 可以看到随着选择不同的单选按钮, label 中的提示文字就会随之变化.

当前代码中, 如果程序启动, 则不会选择任何选项.可以修改代码, 让程序启动默认选中性别男.
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->radioButton_male->setChecked(true);
ui->label->setText("你选择的性别为:男");
}

此时运行程序, 即可看到性别男已经被选中了.
当前代码中, 也可以禁用 "其他" 被选中.
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->radioButton_other->setCheckable(false);
ui->radioButton_other->setEnabled(false);
}

2.2:代码示例2
click, press, release, toggled 的区别
- clicked 表示一次 "点击".
- pressed 表示鼠标 "按下".
- released 表示鼠标 "释放".
- toggled 表示按钮状态切换.
objectName 分别为 radioButton , radioButton_2 , radioButton_3 , radioButton_4.
给 1 创建 clicked 槽函数, 给 2 创建 pressed 槽函数, 给 3 创建 released 槽函数, 给 4 创建 toggled 槽函数.
2.2.1:widget.cpp
cpp
#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_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 checked true";
else
qDebug() << "toggled checked false";
}

运⾏程序,可以看到
- clicked 是⼀次鼠标按下+⿏标释放触发的.
- pressed 是鼠标按下触发的.
- released 是鼠标释放触发的.
- toggled 是 checked 属性改变时触发的.
总的来说, toggled 是最适合 QRadioButton 的.
2.3:代码示例3
在界⾯上创建 6 个单选框, 用来模拟⻨当劳点餐界面. objectName 分别为 radioButton 到 radioButton_6.

此时直接运⾏程序, 可以看到, 这六个 QRadioButton 之间都是排他的. 我们希望每⼀组内部来控制排他, 但是组和组之间不能排他.

引入QButtonGroup进行分组.
2.3.1:widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QButtonGroup>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建三个组
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:CheckBox
- QCheckBox表示复选按钮. 可以允许选中多个.
- 和 QCheckBox 最相关的属性也是 checkable 和 checked , 都是继承自QAbstractButton
3.1:代码示例
在界⾯上创建三个复选按钮, 和⼀个普通按钮. objectName 分别为 checkBox_eat , checkBox_sleep , checkBox_play , 以及pushButton.

给 pushButton 添加 slot 函数
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
QString Result = "今天你的安排是:";
if(ui->checkBox_eat->isChecked())
{
Result +=ui->checkBox_eat->text() + " ";
}
if(ui->checkBox_sleep->isChecked())
{
Result +=ui->checkBox_sleep->text() + " ";
}
if(ui->checkBox_play->isChecked())
{
Result +=ui->checkBox_play->text() + " ";
}
ui->label->setText(Result);
}
运行程序, 可以看到点击确认按钮时, 就会在控制台中输出选中的内容.

二:显示类控件
1:Label
QLabel 可以用来显示文本和图片.
|-------------------|----------------------------------------------------------------------------------------------------------------------|
| 属性 | 说明 |
| text | QLabel 中的⽂本 |
| textFormat | ⽂本的格式 * Qt::PlainText 纯⽂本 * Qt::RichText 富⽂本(支持html 标签) * Qt::MarkdownText markdown 格式 * Qt::AutoText 根据⽂本内容⾃动决定⽂本格式 |
| pixmap | QLabel 内部包含的图片. |
| scaledContents | 设为 true 表⽰内容⾃动拉伸填充 QLabel. 设为 false 则不会⾃动拉伸. |
| alignment | 对⻬⽅式. 可以设置水平和垂直⽅向如何对⻬. |
| wordWrap | 设为 true 内部的⽂本会⾃动换行. 设为 false 则内部⽂本不会自动换行. |
| indent | 设置⽂本缩进. 水平和垂直⽅向都生效. |
| margin | 内部⽂本和边框之间的边距. 不同于于 indent, 但是是上下左右四个⽅向都同时有效. 而indent 最多只是两个⽅向有效(具体哪两个方向有效取决于 alignment ) |
| openExternalLinks | 是否允许打开⼀个外部的链接. (当 QLabel ⽂本内容包含 url 的时候涉及到) |
| buddy | 给 QLabel 关联⼀个 "伙伴" , 这样点击 QLabel 时就能激活对应的伙伴. 例如伙伴如果是⼀个 QCheckBox, 那么该 QCheckBox 就会被选中. |

1.1:代码示例1
widget.cpp
cpp
#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;
}

1.2:代码示例2
虽然 QPushButton 也可以通过设置图标的⽅式设置图片, 但是并非是⼀个好的选择. 更多的时候还是希望通过 QLabel 来作为⼀个更单纯的显示图片的⽅式
在界⾯上创建⼀个 QLabel, objectName 为 label

创建 resource.qrc ⽂件, 并把图片导入到 qrc 中.

widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QResizeEvent>
#include <qDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setGeometry(0,0,800,600);
QPixmap pixmap(":/Cat.png");
ui->label->setPixmap(pixmap);
}
Widget::~Widget()
{
delete ui;
}

这个图片本⾝并没有把 QLabel 填充满.
修改代码, 设置 scaledContents 属性
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QResizeEvent>
#include <qDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setGeometry(0,0,800,600);
QPixmap pixmap(":/Cat.png");
ui->label->setPixmap(pixmap);
//设置拉伸
ui->label->setScaledContents(true);
}
Widget::~Widget()
{
delete ui;
}
再次运⾏, 观察效果, 可以看到图⽚已经被拉伸, 可以把窗⼝填满了.

此时, 如果拖动窗口大小, 可以看到图⽚并不会随着窗口大小的改变⽽同步变化.

为了解决这个问题, 可以在 Widget 中重写 resizeEvent 函数.
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QResizeEvent>
#include <qDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setGeometry(0,0,800,600);
QPixmap pixmap(":/Cat.png");
ui->label->setPixmap(pixmap);
//设置拉伸
ui->label->setScaledContents(true);
}
Widget::~Widget()
{
delete ui;
}
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 ).
此处属于是 多态 机制的⼀种经典用法. 通过上述过程, 就可以把⾃定义的代码, 插入到框架内部执行. 相当于 "注册回调函数" .

1.3:代码示例3
widget.cpp
文本对齐, 自动换行, 缩进, 边距创建四个 label, objectName 分别是 label 到 label_4 并且在 QFrame 中设置 frameShape为WinPanel (设置边框之后看起来会更清晰⼀些)


- QFrame 是 QLabel 的父类. 其中 frameShape 属性用来设置边框性质.
- QFrame::Box :矩形边框
- QFrame::Panel :带有可点击区域的⾯板边框
- QFrame::WinPanel :Windows风格的边框
- QFrame::HLine :⽔平线边框
- QFrame::VLine :垂直线边框
- QFrame::StyledPanel :带有可点击区域的面板边框,但样式取决于窗口主题
cpp
#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, 四个方向均有间距(图上仅体现出三个方向, 下方看不出来).
1.4:代码示例4
创建两个 label 和 两个 radioButton. objectName 分别问 label , label_2 , radioButton , radioButton_2.

此处把 label 中的⽂本设置为 "快捷键 &A" 这样的形式. 其中 & 后⾯跟着的字符, 就是快捷键. 可以通过 alt + A 的⽅式来触发该快捷键. 但是注意, 这里的快捷键和 QPushButton 的不同. 需要搭配 alt 和 单个字⺟的方式才能触发.
widget.cpp
cpp
#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;
}
运行程序, 可以看到, 按下快捷键 alt + a 或者 alt + b, 即可选中对应的选项.

2:LCD Number
QLCDNumer 是⼀个专门用来显示数字的控件. 类似于 "老式计算器" 的效果.

|-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| 属性 | 说明 |
| intValue | QLCDNumber 显示 的数字值(int). |
| value | QLCDNumber显示 的数字值(double). 和 intValue 是联动的. 例如给 value 设为 1.5, intValue 的值就是 2. |
| digitCount | 显示几位数字. |
| mode | 数字显示形式. QLCDNumber::Dec :十进制模式,显示常规的十进制数字。 QLCDNumber::Hex :十六进制模式,以⼗六进制格式显示数字。 QLCDNumber::Bin :⼆进制模式,以⼆进制格式显示数字。 QLCDNumber::Oct :八进制模式,以⼋进制格式显示数字。 |
| segmentStyle | 设置显示风格. * QLCDNumber::Flat :平⾯的显示风格,数字呈现在⼀个平坦的表面上。 * QLCDNumber::Outline :轮廓显示风格,数字具有清晰的轮廓和阴影效果。 * QLCDNumber::Filled :填充显示风格,数字被填充颜⾊并与背景区分开。 |
| smallDecimalPoint | 设置比较小的小数点. |
2.1:代码示例1
使用QLCDNumber显示一个初始的数值,比如10,每间隔一秒钟,数字就-1,一直到0,就会停止.
- 此处的关键点在于要实现"每秒钟 - 1"这个效果即周期性地执行某个逻辑.
- C++标准库中,没有提供定时器的实现但Boost库里面提供了对应的功能.
- Qt中也同样封装了对应的定时器(结合了信号槽机制的).
- QTimer通过这个类创建出来的对象,就会产生一个timeout这样的信号,可以通过start方法来开启定时器,并且参数中设定触发timeout信号的周期.
- 结合Connect,把这个timeout信号绑定到需要的槽函数中,就可以执行逻辑,修改LCDNumber中的数字了.
在界面上创建⼀个 QLCDNumber, 初始值设为 10.objectName 为 lcdNumber.
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void updateTime();
private:
Ui::Widget *ui;
QTimer * timer;
};
#endif // WIDGET_H
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QTimer>
#include <qDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建QTimer实例
timer = new QTimer(this);
//链接信号槽,QTimer会每间隔一定的时间触发一个timeout信号.所以将timeout信号与updatetime连接起来
connect(timer,&QTimer::timeout,this,&Widget::updateTime);
//启动 QTimer,并且规定每隔 1000ms 触发⼀次 timeout 信号.
timer->start(1000);
}
Widget::~Widget()
{
delete ui;
}
void Widget::updateTime()
{
qDebug() << "updateTime";
int value = ui->lcdNumber->intValue();
if(value <= 0)
{
//如果时间到,停止定时器
timer->stop();
return;
}
ui->lcdNumber->display(value - 1);
}
- 修改 widget.h 代码, 创建⼀个 QTimer 成员, 和⼀个 updateTime 函数.
- 修改 widget.cpp, 在构造函数中初始化 QTimer
- QTimer表示定时器,通过start方法启动定时器之后,就会每隔一定周期,触发一次QTimer::timeout信号.
- 使用connect把QTimer::timeout信号和Widget::updateTime连接起来,意味着每一个触发QTimer::timeout都会执行Widget::updateTime.
- 修改 widget.cpp, 实现 updateTime
- 通过intValue获取到QLCDNumber内部的数值.
- 如果value的值归0了,就停止QTimer.接下来QTimer也就不会触发timeout信号了.

针对上述代码,存在两个问题.
上述代码如果直接在Widget构造函数中,通过一个循环 + sleep的方式是否可以实现呢?
cpp
#include "widget.h"
#include "ui_widget.h"
#include <thread>
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::~Widget()
{
delete ui;
}

显然, 这个代码是不行的. 循环会使 Widget 的构造函数无法执行完毕, 此时界⾯是不能正确构造和显示的.

上述代码如果是在 Widget 构造函数中, 另起⼀个线程, 在新线程中完成 循环 + sleep 是否可以呢?
cpp
#include "widget.h"
#include "ui_widget.h"
#include <thread>
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);
}
});
}
Widget::~Widget()
{
delete ui;
}

- 这个代码同样是不行的,Qt里,里面有个专门的线程去负责维护更新的(主线程即main函数所在的线程),对于GUI来说,内部包含了很多的隐藏状态.
- Qt为了保证修改界面的过程中,线程安全是不会受到影响的,Qt禁止了其他线程直接修改界面 .像上面代码的ui->lcdNumber->display(value);这种操作就是在修改界面.
- 因此,Qt为了确保线程安全,直接要求所有的对界面的修改操作,必须在主线程中完成.对于Qt的槽函数来说,默认情况下,槽函数都是由主线程调用的,在槽函数中修改界面是没有任何问题的.
3:ProgressBar
使用QProgressBar表示⼀个进度条.

|------------------|----------------------------------------------------------------------------------------------------------|
| 属性 | 说明 |
| minimum | 进度条最小值 |
| maximum | 进度条最大值 |
| value | 进度条当前值 |
| alignment | 文本在进度条中的对齐方式 Qt::AlignLeft : 左对⻬. Qt::AlignRight : 右对⻬. Qt::AlignCenter : 居中对⻬. Qt::AlignJustify : 两端对⻬. |
| textVisible | 进度条的数字是否可见. |
| orientation | 进度条的方向是水平还是垂直. |
| invertAppearance | 是否是朝反方向增长进度. |
| textDirection | 文本的朝向. |
| format | 展示的数字格式. %p :表示进度的百分⽐(0-100) %v :表示进度的数值(0-100) %m :表示剩余时间(以毫秒为单位) %t :表示总时间(以毫秒为单位) |
设置进度条按时间增⻓
在界⾯上创建进度条, objectName 为 progressBar.

3.1:代码示例1
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
QTimer * timer;
void updateProgressBar();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QTimer>
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::updateProgressBar);
//启动QTimer,并且规定每间隔0.1s触发一次timeout信号
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);
}
运行程序, 可以看到进度条中的进度在快速增长.

- 在实际开发中, 进度条的取值, 往往是根据当前任务的实际进度来进行设置的.
- 比如需要读取⼀个很大的文件, 就可以获取文件的总的大小, 和当前读取完毕的大小, 来设置进度条的比例.
- 由于上⾯我们介绍了 Qt 禁止在其他线程修改界⾯, 因此进度条的更新往往也是需要搭配定时器来完成的.
- 通过定时器周期触发信号, 主线程调⽤对应的 slot 函数. 再在 slot 函数中对当前的任务进度进行计算, 并更新进度条的界面效果.
3.2:代码示例2
上述的进度条使用绿色表⽰的, 但是考虑到有部分uu可能不喜欢绿⾊, 因此我们改成⼀个红色的进度条.
不要忘了,QProgressBar 同样也是 QWidget 的子类, 因此我们可以使用styleSheet 通过样式来修改进度条的颜色
在界面上创建⼀个进度条.

- 在 Qt Designer 右侧的属性编辑器中, 找到 QWidget 的 styleSheet 属性.
- 同时把 QProcessBar 的 alignment 属性设置为垂直水平居中.
- 此处如果不设置 alignment , 进度条中的数字会跑到左上⻆. 这个怀疑是 Qt 本⾝的 bug, 暂时只能先使⽤ alignment 来⼿动调整下.

widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
QTimer * timer;
void UpdateTime();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QTimer>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建QTimer实例
timer = new QTimer(this);
//链接信号槽,QTimer会每隔一定的时间触发一个timeout信号,现在将timeout信号与updatetime连接起来,此时意味着每次触发 timeout
connect(timer,&QTimer::timeout,this,&Widget::UpdateTime);
//启动QTimer,并且规定每间隔0.1s触发一次timeout信号
timer->start(100);
}
Widget::~Widget()
{
delete ui;
}
void Widget::UpdateTime()
{
int value = ui->progressBar->value();
if(value >= 100)
{
timer->stop();
return;
}
ui->progressBar->setValue(value + 1);
}

4:Calendar Widget
QCalendarWidget 表示 "日历" , 形如

|------------------------|-----------------------|
| 属性 | 说明 |
| selectDate | 当前选中的⽇期 |
| minimumDate | 最小日期 |
| maximumDate | 最大日期 |
| firstDayOfWeek | 每周的第⼀天(也就是⽇历的第⼀列) 是周几 |
| gridVisible | 是否显示表格的边框 |
| selectionMode | 是否允许选择⽇期 |
| navigationBarVisible | 日历上方标题是否显示 |
| horizontalHeaderFormat | ⽇历上方标题显示的⽇期格式 |
| verticalHeaderFormat | ⽇历第⼀列显示的内容格式 |
| dateEditEnabled | 是否允许⽇期被编辑 |
|---------------------------------|--------------------------------------------|
| 信号 | 说明 |
| selectionChanged(const QDate&) | 当选中的日期发⽣改变时发出. |
| activated(const QDate&) | 当双击⼀个有效的⽇期或者按下回⻋键时发出,形参是⼀个QDate类型,保存了选中的⽇期 |
| currentPageChanged(int, int) | 当年月份变时发出,形参表示改变后的新年份和月份. |
4.1:代码示例1
在界面上创建⼀个 QCalendarWidget和一个 labelobjectName 为 calendarWidget , label

给 QCalendarWidget 添加 slot 函数.
widget.cpp
cpp
#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_calendarWidget_selectionChanged()
{
QDate date = ui->calendarWidget->selectedDate();
qDebug()<<date;
ui->label->setText(date.toString());
}

三:输入类控件
1:Line Edit
QLineEdit用来表示单行输入框.可以输入一段文件,但是不能换行.
核心属性
|--------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
| 属性 | 说明 |
| text | 输入框中的文本 |
| inputMask | 输入内容格式约束 |
| maxLength | 最大长度 |
| frame | 是否添加边框 |
| echoMode | 显示方式. QLineEdit::Normal :这是默认值,⽂本框会显示输入的文本。 QLineEdit::Password :在这种模式下,输⼊的字符会被隐藏,通常⽤星号(*)或等号(=)代替。 QLineEdit::NoEcho :在这种模式下,⽂本框不会显示任何输入的字符。 |
| cursorPosition | 光标所在位置 |
| alignment | ⽂字对齐方式, 设置水平和垂直⽅向的对齐. |
| dragEnabled | 是否允许拖拽. |
| readOnly | 是否是只读的(不允许修改). |
| placeHolderText | 当输入框内容为空的时候, 显示什么样的提示信息. |
| clearButtonEnabled | 是否会自动显示出 "清除按钮" . |
上面的text属性不仅仅可以通过代码来设置.用户在输入框中进行编辑,也会影响到text值的变化.
- 通过代码中设置text,界面上的文本会发生变化.
- 直接操作修改界面上的文本,text属性的值也会发生变化.


核心信号
|---------------------------------------------|-----------------------------------------------------|
| 属性 | 说明 |
| void cursorPositionChanged(int old,int new) | 当⿏标移动时发出此信号,old为先前的位置,new为新位置。 |
| void editingFinished() | 当按返回或者回车键时,或者行编辑时失去焦点时,发出此信号。 |
| void returnPressed() | 当返回或回⻋键按下时发出此信号. 如果设置了验证器, 必须要验证通过, 才能触发. |
| void selectionChanged() | 当选中的⽂本改变时,发出此信号. |
| void textChanged(const QString &text) | 当QLineEdit中的文本改变时,发出此信号,text是新的文本。代码对⽂本的修改能够触发这个信号. |
| void textEdited(const QString &text)) | 当QLineEdit中的文本改变时,发出此信号,text是新的文本。代码对文本的修改不能触发这个信号. |
1.1:代码示例1
- 在界面上创建三个输入框和两个单选按钮, ⼀个普通按钮.
- 三个输入框的 objectName 为 lineEdit_name , lineEdit_password,lineEdit_phone.
- 两个单选按钮的 objectName 为 radioButton_male , radioButton_female,按钮的objectName为pushButton.

编写 widget.cpp.
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QString>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//第一个输入框
ui->lineEdit_Name->setPlaceholderText("请输入姓名");
ui->lineEdit_Name->setClearButtonEnabled(true);
//第二个输入框
ui->lineEdit_PassWord->setPlaceholderText("请输入密码");
ui->lineEdit_PassWord->setClearButtonEnabled(true);
//设置显示方式,QLineEdit::Password:在这种模式下,输入的字符会被隐藏,通常⽤星号(*)或等号(=)代替。
ui->lineEdit_PassWord->setEchoMode(QLineEdit::Password);
//第三个输入框
ui->lineEdit_Phone->setPlaceholderText("请输入电话号码");
ui->lineEdit_Phone->setClearButtonEnabled(true);
//对输入内容进行格式约束,验证手机号码必须是11位数字,并且按照"344"的格式来输入,
ui->lineEdit_Phone->setInputMask("000-0000-0000");
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
QString gender = ui->radioButton_male->isChecked() ? "男" : "女";
qDebug() << "姓名" <<ui->lineEdit_Name->text();
qDebug() << "密码" <<ui->lineEdit_PassWord->text();
qDebug() << "性别" <<gender;
qDebug() << "手机" << ui->lineEdit_Phone->text();
}
执行程序, 可以看到, 随着用户输入内容之后, 点击按钮, 就能打印到输入的信息.

inputMask只能进行简单的输入格式校验. 实际开发中, 基于正则表达式的方式是更核心的方法.
1.2:代码示例2
- 正则表达式是⼀种在计算机中常用的, 使用特殊字符描述⼀个字符串的特征的机制. 在进行字符串匹配时非常有用,正则表达式的语法还比较复杂, ⼀般都是随用随查, 不需要背下来.
- 正则表达式文档: 正则表达式语法 | Microsoft Learn
正则表达式在线⼯具: 正则表达式语法测试工具 - 在线工具
在界面上创建输入框和⼀个按钮.

编写widget.cpp, 把按钮初始 enabled 设为 false. 给输入框添加验证器.
- 使用QRegularExpression创建⼀个正则表达式对象. "^1\\d{10}$" 表示 "以 1 开头, 后⾯跟上任意的10个十进制数字".
- 使用QRegularExpressionValidator创建⼀个验证器对象. Qt 中内置了四个主要的验证器对象.

编写 widget.cpp, 给 lineEdit 添加 textEdited 信号的 slot 函数.
on_lineEdit_textEdited 的参数是当前输入框的内容.
通过lineEdit->validator() 获取到内置的验证器.
通过validate方法验证文本是否符合要求.
- 第⼀个参数填写的是要验证的字符串. 由于参数要求是 QString&而 不是 const
QString& , 需要把这个变量复制⼀下.
- 第二个参数是int&,是输出型参数.当验证的字符串不匹配时,返回这个字符串的长度.
- 返回值是⼀个枚举. QValidator::Acceptable 表⽰验证通过, QValidator::Invalid 表示验证不通过.
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QRegularExpression>
#include <QRegularExpressionValidator>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//将按钮设置为禁用状态
ui->pushButton->setEnabled(false);
// 给 lineEdit 注册⼀个 validator
ui->lineEdit->setValidator(new QRegularExpressionValidator(QRegularExpression("^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);
}
执⾏程序, 观察效果. 可以看到此时尝试输入字⺟是⽆法输入的. 并且只有当输入的内容符合要求, 确定按钮才能被使用.

1.3:代码示例3
验证两次输⼊的密码⼀致在界⾯上创建两个输入框和⼀个 label.

编写代码, 设置两个输⼊框的 echoMode 为 Password.
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//QLineEdit::Password:在这种模式下,输⼊的字符会被隐藏,通常⽤星号(*)或等号(=)代替。
ui->lineEdit->setEchoMode(QLineEdit::Password);
ui->lineEdit_2->setEchoMode(QLineEdit::Password);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_lineEdit_textEdited(const QString &arg1)
{
const QString & s1 = ui->lineEdit->text();
const QString & s2 = ui->lineEdit_2->text();
if(s1.isEmpty() && s2.isEmpty())
{
ui->label->setText("密码为空!");
}
else if(s1 == s2)
{
ui->label->setText("两次输入的密码相同!");
}
else
{
ui->label->setText("两次输入的密码不同!");
}
}
void Widget::on_lineEdit_2_textEdited(const QString &arg1)
{
const QString & s1 = ui->lineEdit->text();
const QString & s2 = ui->lineEdit_2->text();
if(s1.isEmpty() && s2.isEmpty())
{
ui->label->setText("密码为空!");
}
else if(s1 == s2)
{
ui->label->setText("两次输入的密码相同!");
}
else
{
ui->label->setText("两次输入的密码不同!");
}
}


1.4:代码示例4
创建⼀个输⼊框和⼀个复选按钮.

widget.cpp
修改 widget.cpp, 设置输入框的 echoMode 为 Password.
cpp
#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);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_checkBox_toggled(bool checked)
{
if(checked)
ui->lineEdit->setEchoMode(QLineEdit::Normal);
else
ui->lineEdit->setEchoMode(QLineEdit::Password);
}
执行程序, 可以看到切换复选框的状态, 就可以控制输入框显示密码.


2:Text Edit
QTextEdit表示多行输入 框. 也是⼀个富文本 & markdown 编辑器. 并且能在内容超出编辑框范围时自动提供滚动条.
核心属性.
|---------------------------|---------------------------------------------------------------------------------------------------------------------------------|
| 属性 | 说明 |
| markdown | 输入框内持有的内容. ⽀持 markdown 格式. 能够自动的对markdown文本进行渲染成 html. |
| html | 输入框内持有的内容. 可以支持大部分 html 标签. 包括 img 和 table 等. |
| placeHolderText | 输入框为空时提示的内容. |
| readOnly | 是否是只读的. |
| undoRedoEnable | 是否开启 undo / redo 功能. 按下 ctrl + z 触发 undo 按下 ctrl + y 触发 redo |
| autoFormating | 开启自动格式化. |
| tabstopWidth | 按下缩进占多少空间. |
| overwriteMode | 是否开启覆盖写模式. |
| acceptRichText | 是否接收富文本内容. |
| verticalScrollBarPolicy | 垂直方向滚动条的出现策略. * Qt::ScrollBarAsNeeded:根据内容自动决定是否需要滚动条.这是默认值. * Qt::ScrollBarAlwaysOff:总是关闭滚动条. * Qt::ScrollBarAlwaysOn:总是显示滚动条. |
| horizontalScrollBarPolicy | 水平方向滚动条的出现策略 * Qt::ScrollBarAsNeeded:根据内容自动决定是否需要滚动条.这是默认值 * Qt::ScrollBarAlwaysOff:总是关闭滚动条. * Qt::ScollBarAlwaysOn::总是显示滚动条. |
核心信号.
|-------------------------|----------------|
| 信号 | 说明 |
| textChanged() | 文本内容改变时触发. |
| selectionChanged() | 选中范围改变时触发. |
| cursorPositionChanged() | 光标移动时触发. |
| undoAvailable(bool) | 可以进行undo操作时触发. |
| redoAvailable(bool) | 可以进行redo操作时触发. |
| copyAvaiable(bool) | 文本被选中/取消选中时触发. |
2.1:代码示例1
创建⼀个多行输入框和⼀个label.

给多行输入框添加 slot 函数. 处理 textChanged 信号.
- 通过 toPlainText 方法 获取到内部的文本.
- 类似的, QTextEdit 还提供了 toMarkdown 和 toHtml, 根据需要我们调整不同的获取方式.
widget.cpp
cpp
#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_textEdit_textChanged()
{
const QString & content = ui->textEdit->toPlainText();
qDebug() << content;
ui->label->setText(content);
}
执行程序, 可以看到当输入框中的内容发⽣变化时, label 中的内容同步发生改变.

2.2:代码示例2
widget.cpp
cpp
#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_textEdit_textChanged()
{
//利用toPlainText函数获取文本信息
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();
}
//可以进行undo操作时触发.
void Widget::on_textEdit_undoAvailable(bool b)
{
qDebug() << "[undoAvailable]"<<b;
}
//可以进行redo操作时触发.
void Widget::on_textEdit_redoAvailable(bool b)
{
qDebug() << "[redoAvailable]"<<b;
}
//文本被选中/取消选中时触发.
void Widget::on_textEdit_copyAvailable(bool b)
{
qDebug() << "[copyAvailable]"<<b;
}
编写内容时, textChanged 和 cursorPositionChanged会触发.

选中⼀段文本时, cursorPositionChanged , selectionChanged , copyAvailable会触发.

按下 ctrl + z 时, textChanged , undoAvailable, redoAvailable, cursorPositionChanged会触发

按下 ctrl + y, textChanged , undoAvailable , redoAvailable, cursorPositionChanged 会触发

3:Combo Box
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 时有效) |
3.1:代码示例1
在界⾯上创建三个下拉框, 和⼀个按钮.

widget.cpp
cpp
#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->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();
}
执行程序, 可以看到, 在点击确定按钮时, 就能获取到当前下拉框中选中的内容.

3.2:代码示例2
从文件中加载下拉框的选项 很多时候下拉框的选项并非是固定的, ⽽是通过读取文件/读取网络获取到的.
在界面上创建⼀个下拉框.
创建文件 d:/log.txt , 编写选项. 每个选项占一行.

widget.cpp
- 使用ifstream 打开文件.
- 使用getline 读取每一行.
- 使用QString::fromStdString 把 std::string 转成 QString.
cpp
#include "widget.h"
#include "ui_widget.h"
#include <iostream>
#include <fstream>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
std::ifstream file("D:/log.txt");
std::string line;
//从文件读取数据,每次读一行,读取到line中
while(std::getline(file,line))
{
//将std::string转换为QString.
ui->comboBox->addItem(QString::fromStdString(line));
}
file.close();
}
Widget::~Widget()
{
delete ui;
}
执行程序, 可以看到文件内容已经被加载到下拉框中.

- Qt 中也提供了 QFile 实现读写文件的功能. 当然使用C++ 标准库的 std::fstream 也是完全可以的.
- 之所以存在两套, 是因为 Qt 诞⽣较早 (1991 年左右), 此时 C++ 还没有完成 "标准化" 的工作,C++ 标准库这样的概念⾃然也没有诞生.
- 因此 Qt 就自己打造了⼀套库, 实现了字符串, 容器, 文件操作, 多线程, 网络操作, 定时器, 正则.
- 表达式等内容. (由于 C++ 标准委员会的不作为, 至今仍然有些 Qt 提供的功能, 是标准库不具备的).
4:Spin Box
使用QSpinBox 或者 QDoubleSpinBox表示 "微调框", 它是带有按钮的输入框. 可以用来输入整
数/浮点数. 通过点击按钮来修改数值大小.
由于 SpinBox 和 QDoubleSpinBox用法 基本相同, 就只介绍 SpinBox 的使用了.

QSpinBox关键属性
|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 属性 | 说明 |
| value | 存储的数值. |
| single | 每次调整的"步长".按下一次按钮数据变化多少. |
| displayInteger | 数字的进制. 例如 displayInteger 设为 10, 则是按照 10 进制表示. 设为 2 则为 2进制表示. |
| minimum | 最小值. |
| maximum | 最大值. |
| suffix | 后缀. |
| prefix | 前缀. |
| wrapping | 是否允许换⾏. |
| frame | 是否带边框. |
| alignment | 文字对齐方式. |
| readOnly | 是否允许修改 |
| buttonSymbol | 按钮上的图标 * UpDownArrows 上下箭头形式. * PlusMinus 加减号形式. * NoButtons 没有按钮. |
| accelerated (加速的) | 按下按钮时是否为快速调整模式 |
| correctionMode | 输入有误时如何修正 * QAbstractSpinBox::CorrectToPreviousValue:如果用户输入了一个无效的值(例如,在只能显示正整数的SpinBox中输入了负数),那么SpinBox会恢复为上一个有效值.例如,如果SpinBox的初始值是1,用户输入了-1(无效),然后SpinBox会恢复为1. * QAbstractSpinBox::CorrectToNearestValue:如果用户输入了一个无效的值,SpinBox会恢复为最接近的有效值.例如,如果SpinBox的初始值为1,用户输入了-1(无效),那么SpinBox会恢复为0. |
| keyboardTrack | 是否开启键盘跟踪. 设为true,每次在输入框输入一个数字,都会触发一次valueChanged()和textChanged()信号. 设为false,只有在最终按下enter或者输入框失去焦点,才会触发valueChanged()和textChanged()信号. |
核心信号
|----------------------|----------------------------------|
| 信号 | 说明 |
| textChanged(QString) | 微调框的文本发生改变时会触发 参数QString带有前缀和后缀. |
| valueChanged(int) | 微调框的文本发生改变时会触发. 参数int,表示当前的数值. |
4.1:代码示例1
在界面上创建下列内容
- 三个下拉框: objectName 为 comboBox 到 comboBox_3.
- 三个微调框: objectName 为 spinBox 到 spinBox_3.
- ⼀个按钮:objectName 为 pushButton.

widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//初始化下拉框
ui->comboBox->addItem("巨无霸");
ui->comboBox->addItem("香辣鸡腿堡");
ui->comboBox->addItem("劲脆鸡腿堡");
ui->comboBox_2->addItem("薯条");
ui->comboBox_2->addItem("麦辣鸡翅");
ui->comboBox_2->addItem("鸡米花");
ui->comboBox_3->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:Date Edit & Time Edit
使用QDateEdit 作为日期的微调框.

使用QTimeEdit作为时间 的微调框.

使用QDateTimeEdit 作为时间日期的微调框.

这几个控件用法非常相似, 博主以 QDateTimeEdit 为例进行介绍.
核心属性
|-----------------|----------------------------------------------------------------------------------------------------------------------------------------|
| 属性 | 说明 |
| dateTime | 时间日期的值. 形如 2000/1/1 0:00:00. |
| date | 单纯日期的值. 形如 2001/1/1. |
| time | 单纯时间的值. 形如 0:00:00. |
| displayFormat | 时间日期格式. 形如 yyyy/M/d H:mm * y 表示年份 * M 表示⽉份 * d 表示日期 * H 表示小时 * m 表示分钟 * s 表示秒 PS:这里的格式化符号的含义, 不要记忆. 不同语言/库的设定规则是存在差异的. ⼀定是用的时候再去查. |
| minimumDateTime | 最小时间日期. |
| maximumDateTime | 最大时间日期. |
| timeSpec | * Qt::LocalTime :显示本地时间。 * Qt::UTC :显示协调世界时间(UTC)。 * Qt::OffsetFromUTC :显示相对于UTC的偏移量(时差). |
核心信号
|----------------------------|----------------|
| 信号 | 说明 |
| dateChanged(QDate) | 日期改变时触发. |
| timeChanged(QTime) | 时间改变时触发. |
| dateTimeChanged(QDateTime) | 时间日期任意⼀个改变时触发. |
5.1:代码示例1
在界面上创建两个 QDateTimeEdit 和⼀个按钮, ⼀个 label QDateTimeEdit 的 objectName 为 dateTimeEdit_old 和 dateTimeEdit_new.

编写计算按钮的 slot 函数.
- 使用 daysTo 函数可以计算两个日期的天数.
- 使用secsTo 函数可以计算两个时间的秒数.
- 通过 (秒数 / 3600) 换算成小时数, 再余上 24 得到零几个小时.
- 使用QString::number 把整数转成 QString 进行拼接.
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
//获取两个时间框的日期
QDateTime timeOld = ui->dateTimeEdit_old->dateTime();
QDateTime timeNew = ui->dateTimeEdit_new->dateTime();
//计算日期差值,daysTo可计算两个日期的差值,如果当前得到的小时数不足24j,由于此处是整数除法,那么就会舍弃掉小数点后的内容
int days = (timeOld.secsTo(timeNew) / 3600) / 24;
int hours = (timeOld.secsTo(timeNew) / 3600) % 24;
QString text = QString("爱你已经持续了 ") + QString::number(days) +
QString(" 天 零 ")
+ QString::number(hours) + QString("个小时!");
ui->label->setText(text);
}
执行程序, 观察结果.


6:Dial
使用Dial表示一个按钮

核心属性.
|----------------|--------------------------------|
| 属性 | 说明. |
| value | 持有的数值. |
| minimum | 最小值. |
| maximum | 最大值. |
| singleStep | 按下方向键的时候改变的步长. |
| pageStep | 按下 pageUp / pageDown 的时候改变的步长. |
| sliderPosition | 界面上旋转显示的初始位置. |
| tracking | 外观是否会跟踪数值变化. 默认值为true.一般不需要修改. |
| wrapping | 是否允许循环调整 |
| notchesVisible | 是否显示刻度线 |
| notchTarget | 刻度线之间的相对位置. 数字越大, 刻度线越稀疏. |
核心信号.
|------------------------|---------|
| 属性 | 说明 |
| valueChanged(int) | 数值改变时触发 |
| rangeChanged(int, int) | 范围变化时触发 |
6.1:代码示例1
在界面上创建⼀个旋钮和⼀个 label.

编写 widget.cpp, 对旋钮初始化
widget.cpp
cpp
#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);
//设置最大值为
ui->dial->setMaximum(100);
//设置最小值
ui->dial->setMinimum(0);
//设置初始值为
ui->dial->setValue(100);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_dial_valueChanged(int value)
{
//透明度是从0~1,因此强制类型转换为doule并且除以100
ui->label->setText(QString("当前不透明度为:") + QString::number(value));
this->setWindowOpacity((double)value / 100);
}
运行程序, 观察效果. 可以看到随着拖动旋钮旋转, 不透明度发⽣明显变化.

7:Slider
使用QSlider表示一个滑动条

QSlider 和 QDial 都是继承自QAbstractSlider , 因此用法上基本相同.
核心属性
|--------------------|--------------------------------|
| 属性 | 说明. |
| value | 持有的数值. |
| minimum | 最小值. |
| maximum | 最大值. |
| singleStep | 按下方向键的时候改变的步长. |
| pageStep | 按下 pageUp / pageDown 的时候改变的步长. |
| sliderPosition | 界面上旋转显示的初始位置. |
| tracking | 外观是否会跟踪数值变化. 默认值为true.一般不需要修改. |
| orientation | 滑动条的方式是水平还是垂直. |
| invertedAppearance | 是否要翻转滑动条的⽅向 |
| tickPosition | 刻度的位置. |
| tickInterval | 刻度的密集程度. |
核心信号
|------------------------|---------|
| 属性 | 说明 |
| valueChanged(int) | 数值改变时触发 |
| rangeChanged(int, int) | 范围变化时触发 |
7.1:代码示例1
在界面上创建两个滑动条, 分别是水平和垂直滑动条.objectName 分别为 horizontalSlider 和 verticalSlider.

编写代码初始化滑动条与编写滑动条的 valueChanged slot 函数.
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置滑动条的最小值
ui->horizontalSlider->setMinimum(500);
//设置滑动条的最大值
ui->horizontalSlider->setMaximum(2000);
//设置改变的步长为100
ui->horizontalSlider->setSingleStep(100);
//设置初始值为800
ui->horizontalSlider->setValue(800);
//设置滑动条的最小值
ui->verticalSlider->setMinimum(500);
//设置滑动条的最大值
ui->verticalSlider->setMaximum(1500);
//设置改变的步长为100
ui->verticalSlider->setSingleStep(100);
//设置初始值为800
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()<<"Width:"<<value;
}
void Widget::on_verticalSlider_valueChanged(int value)
{
QRect rect = this->geometry();
this->setGeometry(rect.x(),rect.y(),rect.width(),value);
qDebug()<<"Height:"<<value;
}
执行程序, 可以看到调整滑动条, 窗口大小就会随之改变.

7.2:代码示例2
通过⾃定义快捷键调整滑动条位置.设置 - 减小 value, 设置 = 增加 value
在界面上创建滑动条和 label

编写初始化代码.
widget.cpp
- 使用QShortCut 类设置快捷键.
- 快捷键触发时, 会发出 QShortcut::activated 信号, 我们连接到⾃⼰写的 slot 函数.
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QShortcut>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setText("");
ui->horizontalSlider->setMinimum(0);
ui->horizontalSlider->setMaximum(100);
ui->horizontalSlider->setSingleStep(10);
ui->horizontalSlider->setValue(0);
//设置快捷键
QShortcut * ShortCut1 = new QShortcut(this);
ShortCut1->setKey(QKeySequence("-"));
connect(ShortCut1,&QShortcut::activated,this,&Widget::SubValue);
QShortcut * ShortCut2 = new QShortcut(this);
ShortCut2->setKey(QKeySequence("+"));
connect(ShortCut2,&QShortcut::activated,this,&Widget::AddValue);
}
Widget::~Widget()
{
delete ui;
}
void Widget::SubValue()
{
int value = ui->horizontalSlider->value();
ui->horizontalSlider->setValue(value - 20);
}
void Widget::AddValue()
{
int value = ui->horizontalSlider->value();
ui->horizontalSlider->setValue(value + 20);
}
void Widget::on_horizontalSlider_valueChanged(int value)
{
ui->label->setText(QString::number(value));
}
执行程序, 观察效果. 可以看到此时按下 - 和 = 就可以调整 value 的值了.



