Qt——常用Widget(控件)
常用控件 Widget
需要说明,此处说明的控件都继承于QWiget
,因此之前所说的控件属性,和相关API,在这里的控件都适用
文章目录
- 常用控件 Widget
- 按钮类控件
- QPushButton
- QRadioButton
- QCheckBox
- 显示类控件
- QLabel
- 初识事件
- LCD Number
- ProgressBar
- CalendarWidget
- 输入类控件
- LineEdit
- TextEdit
- ComboBox
- SpinBox & DoubleSpinBox
- Dial
- Slider
- 多元素控件
- ListWidget
- TableWidget
- TreeWidget
- 容器类控件
- GroupBox
- TabWidget
- 布局管理器
- VBoxLayout
- HBoxLayout
- GridLayout
- FormLayout
- Spacer
按钮类控件
QPushButton
QPushButton
继承于QAbstractButton
,QAbstractButton
是一个抽象类
-
其派生出类其他按钮,其内部实现了这些按钮包含的公共功能
-
其中包含了纯虚函数,无法创建出实例对象
QAbstractButton
中,和QPushButton
相关的重要属性
属性 | 说明 |
---|---|
text | 按钮中的文本 |
icon | 按钮中的图标 |
iconSize | 按钮中图标的尺寸 |
shortCut | 按钮对应的快捷键 |
autoRepeat | 按钮是否会重复触发. 当鼠标左键按住不放时, 如果设为 true, 则会持续产生鼠标点击事件; 如果设为 false, 则必须释放鼠标, 再次按下鼠标时才能产生点击事件. (相当于游戏手柄上的 “连发” 效果) |
autoRepeatDelay | 重复触发的延时时间. 按住按钮多久之后, 开始重复触发. |
autoRepeatInterval | 重复触发的周期. |
例如:
设置按钮图标:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QIcon icon(":/avatar");ui->pushButton->setIcon(icon);ui->pushButton->setIconSize(QSize(50, 50));
}
QSize
是一个用于描述大小的类,两个参数分别表示宽和高
设置快捷键:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QIcon iconTarget(":/avatar");QIcon iconLeft(":/left");QIcon iconRight(":/right");QIcon iconUp(":/up");QIcon iconDown(":/down");ui->pushButtonTarget->setIcon(iconTarget);ui->pushButtonTarget->setIconSize(QSize(70, 70));ui->pushButtonUp->setIcon(iconUp);ui->pushButtonUp->setIconSize(QSize(50, 50));ui->pushButtonDown->setIcon(iconDown);ui->pushButtonDown->setIconSize(QSize(50, 50));ui->pushButtonLeft->setIcon(iconLeft);ui->pushButtonLeft->setIconSize(QSize(50, 50));ui->pushButtonRight->setIcon(iconRight);ui->pushButtonRight->setIconSize(QSize(50, 50));// // 可以通过按键名字的方式,直接设置快捷键// ui->pushButtonUp->setShortcut(QKeySequence("w"));// ui->pushButtonDown->setShortcut(QKeySequence("s"));// ui->pushButtonLeft->setShortcut(QKeySequence("a"));// ui->pushButtonRight->setShortcut(QKeySequence("d"));// 可以通过枚举的方式,设置快捷键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));// // 也可以设置组合键// ui->pushButtonUp->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_W));// ui->pushButtonDown->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));// ui->pushButtonLeft->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_A));// ui->pushButtonRight->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_D));// 设置鼠标的自动连发ui->pushButtonUp->setAutoRepeat(true);ui->pushButtonDown->setAutoRepeat(true);ui->pushButtonLeft->setAutoRepeat(true);ui->pushButtonRight->setAutoRepeat(true);
}
QKeySequence
:表示按键序列,说明快捷键可以不是一个按键,也可以是一组按键- 键盘快捷键默认是支持连发的,但是鼠标默认不支持;如果要鼠标支持连发,就需要启用
autoRepeat
属性
效果:
QRadioButton
单选按钮
radioButton
同样继承于QAbstractButton
,上面介绍道的属性和方法同样适用
下面为QAbstractButton
和radioButton
关联较大的属性:
属性 | 说明 |
---|---|
checkable | 是否能选中 |
checked | 是否已经被选中. checkable 是 checked 的前提条件. |
autoExclusive | 是否排他. 选中一个按钮之后是否会取消其他按钮的选中. 对于 QRadioButton 来说默认就是排他的. |
例如:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 设置默认选项ui->radioButtonMale->setChecked(true);ui->label->setText("性别:男");// 禁用 其他 选项ui->radioButtonOther->setCheckable(false);
}Widget::~Widget()
{delete ui;
}void Widget::on_radioButtonMale_clicked()
{ui->label->setText("性别:男");
}void Widget::on_radioButtonFemale_clicked()
{ui->label->setText("性别:女");
}void Widget::on_radioButtonOther_clicked()
{ui->label->setText("性别:其他");
}
效果
可以看到,尽管按钮其他
被禁用了无法被选中,但是其对应的点击事件却可以触发。
即:**setCheckable(false)
**只能让按钮无法被选中,但是仍可以触发对应的点击事件
又例如,我们可以利用QRadioButton
来实现一个点餐系统:
直接运行:
可以看到,由于QRadioButton
具有排他性,这就导致了我们想分别选择主食,荤菜,素材,但是只能在所有的菜品中选择一个。
为了解决这个问题,就需要用到QButtonGroup
。QButtonGroup
可以针对单选按钮进行分组,我们将上面的按钮分成三组,就可以解决问题了
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QButtonGroup* buttonGroup1 = new QButtonGroup(this);QButtonGroup* buttonGroup2 = new QButtonGroup(this);QButtonGroup* buttonGroup3 = new QButtonGroup(this);buttonGroup1->addButton(ui->radioButton_1);buttonGroup1->addButton(ui->radioButton_2);buttonGroup1->addButton(ui->radioButton_3);buttonGroup2->addButton(ui->radioButton_4);buttonGroup2->addButton(ui->radioButton_5);buttonGroup2->addButton(ui->radioButton_6);buttonGroup3->addButton(ui->radioButton_7);buttonGroup3->addButton(ui->radioButton_8);buttonGroup3->addButton(ui->radioButton_9);
}
效果:
QCheckBox
表示复选按钮,可以选择多个
其关注的属性,同样是:
属性 | 说明 |
---|---|
checkable | 是否能选中 |
checked | 是否已经被选中. checkable 是 checked 的前提条件. |
例如:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{QString str("今日计划: ");if (ui->checkBox->isChecked()) {str += "写代码 ";}if (ui->checkBox_2->isChecked()) {str += "打游戏 ";}if (ui->checkBox_3->isChecked()) {str += "锻炼 ";}ui->label->setText(str);
}
效果:
显示类控件
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 就会被选中. |
-
文本格式
textFormat
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 文本"); }
效果:
-
填充图片
pixmax
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);QLabel* label = new QLabel(this);label->setGeometry(0, 0, this->geometry().width(), this->geometry().height()); // 设置Label充满整个窗口label->setPixmap(QPixmap(":/up"));label->setScaledContents(true); // 让图片填充到整个Lable }
效果:
- 可以看到,虽然程序运行后图片确实填充到了整个窗口,但是如果我们之后缩放窗口,图片就不能继续填充了
- 这是因为,图片被放在了
Label
中,而这又是在构造函数就初始化确定好了 - 我们之后移动窗口,并不能改变
Label
的属性,如果我们想要在移动窗口的同时,让图片保持填充,就需要做到Label
跟随窗口大小的变化,自行适配
初识事件
为了解决这个问题,就会涉及到”事件“这一概念
Qt中表示用户的操作,有两类概念:信号、事件
当用户拖拽修改窗口大小的时候,就会触发resize
事件
- 向
resize
这样的事件,是连续变化的 - 例如,把窗口尺寸从A拖动到B的这个过程中,会触发一系列的
resize
事件
了解了这些,就可以借助resize
事件来完成上述功能
- 可以让
Widget
窗口类,重写父类QWidget
的resizeEvent
虚函数 - 每次触发
resize
事件时,就会调用这个虚函数,从而重新指定Label
尺寸
// widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QResizeEvent>
#include <QLabel>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void resizeEvent(QResizeEvent* event);private:Ui::Widget *ui;QLabel* label = nullptr;
};
#endif // WIDGET_H// widget.cpp
#include "widget.h"
#include "./ui_widget.h"#include <QLabel>
#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);label = new QLabel(this);label->setGeometry(0, 0, this->geometry().width(), this->geometry().height()); // 设置Label充满整个窗口label->setPixmap(QPixmap(":/up"));label->setScaledContents(true); // 让图片填充到整个Lable
}Widget::~Widget()
{delete ui;
}void Widget::resizeEvent(QResizeEvent *event)
{// event 包含的 size()方法,得到的就是出发事件这一时刻,窗口的大小qDebug() << event->size();label->setGeometry(0, 0, event->size().width(), event->size().height());
}
效果:
-
设置对齐方式、自动换行、缩进、边距
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);// 设置对齐方式ui->label->setAlignment(Qt::AlignRight | Qt::AlignBottom); // 右下方对齐ui->label->setText("aaaaaaaaaaaaa");// 设置自动换行ui->label_2->setAlignment(Qt::AlignLeft | Qt::AlignTop);ui->label_2->setWordWrap(true);ui->label_2->setText("aaaaaaaaaaaaaaaaaaa aaaaaaaaaaaa aaaaaaaaaaaaaaa aaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaa");// 设置自动缩进ui->label_3->setWordWrap(true);ui->label_3->setIndent(30); // 30pxui->label_3->setText("aaaaaaaaaaaaaaaaaaa aaaaaaaaaaaa aaaaaaaaaaaaaaa aaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaa");// 设置边距ui->label_4->setWordWrap(true);ui->label_4->setMargin(50); // 50pxui->label_4->setText("aaaaaaaaaaaaaaaaaaa aaaaaaaaaaaa aaaaaaaaaaaaaaa aaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaa"); }
- 需要注意:
WordWrap
通常依赖空格或连字符等字符作为换行点。如果文本是连续的长字符串(如"aaaaaaaaaa..."
),Qt 可能找不到合适的换行位置。
效果:
- 需要注意:
-
设置
QLabel
伙伴- Qt中,
QLabel
中写的文本,时可以指定快捷键的 - 可以在文本中使用
&
后面跟上一个字符来表示 - 例如
&A
,就表示Alt + A
- 绑定了伙伴关系之后,通过快捷键就可以选中对应的单选按钮/复选按钮
例如:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);ui->labelA->setBuddy(ui->radioButton);ui->labelB->setBuddy(ui->checkBox); }
效果:
- Qt中,
LCD Number
QLCDNumber
是一个专门用来显示数字的控件。类似于”老式计算机“
相关属性:
属性 | 说明 |
---|---|
intValue | QLCDNumber 显示的数字值(int)。 |
value | QLCDNumber 显示的数字值(double)。 和 intValue 是联动的。 例如给 value 设为 1.5, intValue 的值就是 2。 另外, 设置 value 和 intValue 的方法名字为 display , 而不是 setValue 或者 setIntValue 。 |
digitCount | 显示几位数字。 |
mode | 数字显示形式。 1. QLCDNumber::Dec :十进制模式,显示常规的十进制数字。 2. QLCDNumber::Hex :十六进制模式,以十六进制格式显示数字。 3. QLCDNumber::Bin :二进制模式,以二进制格式显示数字。 4. QLCDNumber::Oct :八进制模式,以八进制格式显示数字。 只有十进制的时候才能显示小数点后的内容。 |
segmentStyle | 设置显示风格。 1. QLCDNumber::Flat :平面的显示风格,数字呈现在一个平坦的表面上。 2. QLCDNumber::Outline :轮廓显示风格,数字具有清晰的轮廓和阴影效果。 3. QLCDNumber::Filled :填充显示风格,数字被填充颜色并与背景区分开。 |
smallDecimalPoint | 设置比较小的小数点。 |
制作一个简单的倒计时:
#include "widget.h"
#include "./ui_widget.h"#include <QTimer>
#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->lcdNumber->display(6);QTimer* timer = new QTimer(this);connect(timer, &QTimer::timeout, this, [this](){int number = ui->lcdNumber->intValue();if (number <= 0){return;}ui->lcdNumber->display(number - 1);});timer->start(1000); // 1s后触发
}Widget::~Widget()
{delete ui;
}
QTimer
时Qt的一个定时器类,通过方法start()
设置其触发事件- 当
QTimer
触发时,就会发出一个timeout
信号。此时就可以用一个槽函数来关联这个信号,从而更新倒计时
效果:
可能有同学会疑惑:可不可以直接通过休眠和循环来实现这个计数器呢?我们可以试一下:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->lcdNumber->display(6);std::thread thread([this](){int number = this->ui->lcdNumber->intValue();while (true) {if (number <= 0) {return;}this->ui->lcdNumber->display(number);--number;std::this_thread::sleep_for(std::chrono::seconds(1));}});// thread.detach();
}
- 注意,这里之所以要使用线程,原因为:
- 如果不使用线程,而是直接在原有的线程里直接使用循环,那么就会阻塞
Widget
构造函数的进行。Widget
控件窗口都没有构造好,循环对LCDNumber
的修改肯定也是没用的
此时,运行程序会抛出异常:
terminate called without an active exception
这是因为:
为了保证线程安全,Qt要求所有 GUI 操作必须在主线程(GUI 线程)中进行
尽管上述代码在加上thread.detach();
后可以正常运行,但仍然建议使用信号槽机制,来对GUI界面进行操作:
ProgressBar
进度条
相关属性:
以下是合并后的 Markdown 表格,整合了进度条相关属性及说明:
属性 | 说明 |
---|---|
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 :表示总时间(以毫秒为单位) |
例如:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QTimer* timer = new QTimer(this);connect(timer, &QTimer::timeout, this, [this](){int val = this->ui->progressBar->value();if (val >= 100) {return;}this->ui->progressBar->setValue(val + 5);});timer->start(100);
}
效果:
CalendarWidget
日历
核心属性:
属性 | 说明 |
---|---|
selectDate | 当前选中的日期 |
minimumDate | 最小日期 |
maximumDate | 最大日期 |
firstDayOfWeek | 每周的第一天(也就是日历的第一列)是周几。 |
gridVisible | 是否显示表格的边框 |
selectionMode | 是否允许选择日期 |
navigationBarVisible | 日历上方标题是否显示 |
horizontalHeaderFormat | 日历上方标题显示的日期格式 |
verticalHeaderFormat | 日历第一列显示的内容格式 |
dateEditEnabled | 是否允许日期被编辑 |
重要信号
信号 | 说明 |
---|---|
selectionChanged(const QDate&) | 当选中的日期发生改变时发出 |
activated(const QDate&) | 当双击一个有效的日期或者按下回车键时发出,形参是一个 QDate 类型,保存了选中的日期 |
currentPageChanged(int, int) | 当年份月份改变时发出,形参表示改变后的新年份和月份 |
例如:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(ui->calendarWidget, &QCalendarWidget::clicked, this, [this](){this->ui->label->setText(this->ui->calendarWidget->selectedDate().toString());});
}
效果:
输入类控件
LineEdit
单行输入框,可以输入一段文字,但是不能换行
核心属性:
属性 | 说明 |
---|---|
text | 输入框中的文本 |
inputMask | 输入内容格式约束(对输入内容进行简单校验) |
maxLength | 最大长度 |
frame | 是否添加边框 |
echoMode | 显示方式。 - QLineEdit::Normal :默认值,文本框显示输入文本- QLineEdit::Password :输入字符隐藏,常用*或=代替- QLineEdit::NoEcho :不显示任何输入字符 |
cursorPosition | 光标所在位置 |
alignment | 文字对齐方式,设置水平和垂直方向的对齐 |
dragEnabled | 是否允许拖拽 |
readOnly | 是否是只读的(不允许修改) |
placeHolderText | 输入框内容为空时,显示的提示信息 |
clearButtonEnabled | 是否自动显示“清除按钮” - 如果输入框没有内容,没有变化 - 如果输入框有内容,那么就会在输入框右侧出现一个按钮,如果点击,就会清空输入框所有内容 |
信号:
属性(信号) | 说明 |
---|---|
void cursorPositionChanged(int old, int new) | 鼠标移动时发出信号,old 是先前位置,new 是新位置 |
void editingFinished() | 按返回 / 回车键、行编辑失去焦点时发出信号 |
void returnPressed() | 返回 / 回车键按下时发信号;若有验证器,需验证通过才触发 |
void selectionChanged() | 选中文本改变时发出信号 |
void textChanged(const QString &text) | 文本改变(包括代码修改)时发信号,text 为新文本 |
void textEdited(const QString &text) | 文本改变(仅用户编辑,代码修改不触发 )时发信号,text 为新文本 |
例如:
#include "widget.h"
#include "./ui_widget.h"#include <QDebug>
#include <QLineEdit>
#include <QRegularExpressionValidator>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->pushButton->setEnabled(false); // 手机号码正确之前,按钮不允许被按下ui->lineEditName->setPlaceholderText("请输入姓名");ui->lineEditName->setClearButtonEnabled(true);ui->lineEditPassword->setPlaceholderText("请输入密码");ui->lineEditPassword->setClearButtonEnabled(true);ui->lineEditPassword->setEchoMode(QLineEdit::Password);ui->lineEditRePassword->setPlaceholderText("请确认密码");ui->lineEditRePassword->setClearButtonEnabled(true);ui->lineEditRePassword->setEchoMode(QLineEdit::Password);ui->lineEditPhone->setPlaceholderText("请输入手机号");ui->lineEditPhone->setClearButtonEnabled(true);QRegularExpression regExp("^1[3-9]\\d{9}$");ui->lineEditPhone->setValidator(new QRegularExpressionValidator(regExp, this));//使用ToolButton作为是否显示密码的开关ui->toolButton->setIcon(QIcon(":/closeEyes"));ui->toolButton_2->setIcon(QIcon(":/closeEyes"));ui->toolButton->setCheckable(true);ui->toolButton_2->setCheckable(true);ui->toolButton->setChecked(false);ui->toolButton_2->setChecked(false);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{QString sex = ui->radioButton->isChecked()? "男" : "女";qDebug() << "姓名:" << ui->lineEditName->text() <<"性别:" << sex <<"密码:" << ui->lineEditPassword->text() <<"手机号:" << ui->lineEditPhone->text();
}void Widget::on_lineEditPhone_textEdited(const QString &arg1)
{QString str = arg1;int pos;QValidator::State state = ui->lineEditPhone->validator()->validate(str, pos);if (state == QValidator::State::Acceptable) {ui->pushButton->setEnabled(true);} else {ui->pushButton->setEnabled(false);}
}void Widget::on_lineEditPassword_textEdited(const QString &arg1)
{compare();
}void Widget::on_lineEditRePassword_textEdited(const QString &arg1)
{compare();
}void Widget::compare()
{QString str1 = ui->lineEditPassword->text();QString str2 = ui->lineEditRePassword->text();if (str1 == str2) {ui->labelCheck->setText("密码一致");} else {ui->labelCheck->setText("密码不一致,重新输入");}
}void Widget::on_toolButton_toggled(bool checked)
{if (checked) {ui->lineEditPassword->setEchoMode(QLineEdit::Normal);ui->toolButton->setIcon(QIcon(":/openEyes"));} else {ui->lineEditPassword->setEchoMode(QLineEdit::Password);ui->toolButton->setIcon(QIcon(":/closeEyes"));}
}void Widget::on_toolButton_2_toggled(bool checked)
{if (checked) {ui->lineEditRePassword->setEchoMode(QLineEdit::Normal);ui->toolButton_2->setIcon(QIcon(":/openEyes"));} else {ui->lineEditRePassword->setEchoMode(QLineEdit::Password);ui->toolButton_2->setIcon(QIcon(":/closeEyes"));}
}
注意:
-
这里的手机号部分,用到了正则表达式:
^1[3-9]\\d{9}$
-
正则表达式,本质上就是一个带有特殊字符的字符串,特殊字符用来表示另一个字符串的特征
-
例如上面的正则表达式意味:
^1
:以1开头[3-9]
:第二个数字为 3~9之间的一个数字\\d
:\d
表示任意数字,并用\
进行转义{9}
:表示前面的元素重复9次
-
在Qt中,可以使用
QRegularExpressionValidator
正则表达式验证器进行正则表达式验证-
其构造函数需要传入一个
QRegularExpression
,即一个正则表达式字符串 -
之后可以调用它的方法,来获取传入的数据是否匹配:
[override virtual] QValidator::State QRegularExpressionValidator::validate(QString &input, int &pos) const
input
:即为要被校验的字符串pos
:输出型参数,如果校验不通过,则会将pos设置为第一次校验失败的位置QValidator::State
:枚举类型。Acceptable
- 校验通过,Intermediate
- 部分匹配(例如对于上面的手机号,用户输入13
,后面还没输入,就属于部分匹配,因为用户之后输入的内容可能完全匹配),Invalid
- 无效(例如,用户输入0
,10
,输入的内容已经完全不匹配规则,故为无效)
-
效果:
TextEdit
多行输入框,也是一个 富文本/markdown 编辑器
除了TextEidt
外,还有一个多行输入框PlainTextEdit
,但是PlainTextEdit
只支持纯文本编辑
属性:
属性 | 说明 |
---|---|
markdown | 输入框内持有的内容,支持 markdown 格式,能够自动的对 markdown 文本进行渲染成 html |
html | 输入框内持有的内容,可以支持大部分 html 标签,包括 img 和 table 等 |
placeHolderText | 输入框为空时提示的内容 |
readOnly | 是否是只读的 |
undoRedoEnable | 是否开启 undo/redo 功能 按下 ctrl + z 触发 undo 按下 ctrl + y 触发 redo |
autoFormatting | 开启自动格式化 |
tabstopWidth | 按下缩进占多少空间 |
overwriteMode | 是否开启覆盖写模式 |
acceptRichText | 是否接收富文本内容 |
verticalScrollBarPolicy | 垂直方向滚动条的出现策略 - Qt::ScrollBarAsNeeded :根据内容自动决定是否需要滚动条,这是默认值- Qt::ScrollBarAlwaysOff :总是关闭滚动条- Qt::ScrollBarAlwaysOn :总是显示滚动条 |
horizontalScrollBarPolicy | 水平方向滚动条的出现策略 - Qt::ScrollBarAsNeeded :根据内容自动决定是否需要滚动条,这是默认值- Qt::ScrollBarAlwaysOff :总是关闭滚动条- Qt::ScrollBarAlwaysOn :总是显示滚动条 |
信号:
信号 | 说明 |
---|---|
textChanged() | 文本内容改变时触发 |
selectionChanged() | 选中范围改变时触发 |
cursorPositionChanged() | 光标移动时触发 |
undoAvailable(bool) | 可以进行 undo 操作时触发 |
redoAvailable(bool) | 可以进行 redo 操作时触发 |
copyAvailable(bool) | 文本被选中 / 取消选中时触发 |
例如:
#include "widget.h"
#include "./ui_widget.h"#include <QDebug>
#include <QTextCursor>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()
{QTextCursor cursor = ui->textEdit->textCursor();qDebug() << "selectionChanged: " << cursor.selectedText();
}void Widget::on_textEdit_cursorPositionChanged()
{QTextCursor cursor = ui->textEdit->textCursor();qDebug() << "cursorPositionChanged: " << cursor.position();
}void Widget::on_textEdit_copyAvailable(bool b)
{qDebug() << "copyAvailable: " << b;
}void Widget::on_textEdit_redoAvailable(bool b)
{qDebug() << "redoAvailable: " << b;
}void Widget::on_textEdit_undoAvailable(bool b)
{qDebug() << "undoAvailable: " << b;
}
效果:
ComboBox
下拉框
属性:
属性 | 说明 |
---|---|
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"#include <QDebug>
#include <fstream>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);std::ifstream file("G:/QtProject/study/edit/source/food.txt");std::string str;while (std::getline(file, str)) {ui->comboBox->addItem(QString::fromStdString(str));}
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{qDebug() << ui->comboBox->currentText();
}
效果:
SpinBox & DoubleSpinBox
微调框
属性:
属性 | 说明 |
---|---|
value | 存储的数值. |
singleStep | 每次调整的 “步长”. 按下一次按钮数据变化多少. |
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 为新的text,含前缀、后缀 |
valueChanged(int) | 微调框数值改变时触发,参数 int 是新的数值 |
例如:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);std::ifstream file("G:/QtProject/study/edit/source/food.txt");std::string str;while (std::getline(file, str)) {ui->comboBox->addItem(QString::fromStdString(str));}ui->spinBox->setValue(1);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{ui->label->setText(ui->comboBox->currentText() + ": " +QString::number(ui->spinBox->value()));
}
效果:
Dial
旋钮
属性:
属性 | 说明 |
---|---|
value | 持有的数值. |
minimum | 最小值 |
maximum | 最大值 |
singleStep | 按下方向键的时候改变的步长. |
pageStep | 按下 pageUp / pageDown 的时候改变的步长. |
sliderPosition | 界面上旋钮显示的初始位置 |
tracking | 外观是否会跟踪数值变化. 默认值为 true. 一般不需要修改. |
wrapping | 是否允许循环调整. 即数值如果超过最大值, 是否允许回到最小值.(调整过程能否 “套圈”) |
notchesVisible | 是否显示刻度线 |
notchTarget | 刻度线之间的相对位置. 数字越大, 刻度线越稀疏. |
例如,实现一个旋钮,用来调节窗口透明度:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->dial->setWrapping(true);ui->dial->setMinimum(10);ui->dial->setMaximum(1000);ui->dial->setNotchesVisible(true);
}Widget::~Widget()
{delete ui;
}void Widget::on_dial_valueChanged(int value)
{this->setWindowOpacity((double)value / 1000);
}
效果:
Slider
滑动条
QSlider
和QDial
都继承于QAbstractSlider
,因此用法基本相同
属性 | 说明 |
---|---|
value | 持有的数值. |
minimum | 最小值 |
maximum | 最大值 |
singleStep | 按下方向键的时候改变的步长. |
pageStep | 按下 pageUp /pageDown 的时候改变的步长. |
sliderPosition | 滑动条显示的初始位置 |
tracking | 外观是否会跟踪数值变化. 默认值为 true. 一般不需要修改. |
orientation | 滑动条的方向是水平还是垂直 |
invertedAppearance | 是否要翻转滑动条的方向 |
tickPosition | 刻度的位置. |
tickInterval | 刻度的密集程度. |
例如:实现两个滑动条,调节窗口宽度和高度
#include "widget.h"
#include "./ui_widget.h"#include <QDebug>
#include <QDateTime>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->horizontalSlider->setMinimum(100);ui->horizontalSlider->setMaximum(2000);ui->horizontalSlider->setValue(200);ui->horizontalSlider->setSingleStep(50);ui->verticalSlider->setMinimum(100);ui->verticalSlider->setMaximum(2000);ui->verticalSlider->setValue(200);ui->verticalSlider->setSingleStep(50);
}Widget::~Widget()
{delete ui;
}void Widget::on_verticalSlider_valueChanged(int value)
{QRect rect = this->geometry();this->setGeometry(rect.x(), rect.y(), rect.width(), value);
}void Widget::on_horizontalSlider_valueChanged(int value)
{QRect rect = this->geometry();this->setGeometry(rect.x(), rect.y(), value, rect.height());
}
效果:
同时学习一下,Qt快捷键的使用:
#include "widget.h"
#include "./ui_widget.h"#include <QDebug>
#include <QDateTime>
#include <QShortcut>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->horizontalSlider->setMinimum(100);ui->horizontalSlider->setMaximum(2000);ui->horizontalSlider->setValue(200);ui->label->setText("当前值为:"+ QString::number(ui->horizontalSlider->value()));QShortcut* shortcut_add = new QShortcut(this);shortcut_add->setKey(QKeySequence("="));QShortcut* shortcut_sub = new QShortcut(this);shortcut_sub->setKey(QKeySequence("-"));connect(shortcut_add, &QShortcut::activated, this, [this](){int val = this->ui->horizontalSlider->value() + 50;if (val < this->ui->horizontalSlider->maximum()) {this->ui->horizontalSlider->setValue(val);} else {this->ui->horizontalSlider->setValue(this->ui->horizontalSlider->maximum());}});connect(shortcut_sub, &QShortcut::activated, this, [this](){int val = this->ui->horizontalSlider->value() - 50;if (val > this->ui->horizontalSlider->minimum()) {this->ui->horizontalSlider->setValue(val);} else {this->ui->horizontalSlider->setValue(this->ui->horizontalSlider->minimum());}});
}Widget::~Widget()
{delete ui;
}void Widget::on_horizontalSlider_valueChanged(int value)
{ui->label->setText("当前值为:"+ QString::number(ui->horizontalSlider->value()));
}
QShortCut
:快捷键类,可以为其设置QKeySequence
,即快捷键字符串QShortcut::activated
:当快捷键被按下时触发该信号
多元素控件
ListWidget
能够显示一个纵向显示的列表
属性:
属性 | 说明 |
---|---|
currentRow | 当前被选中的是第几行 |
count | 一共有多少行 |
sortingEnabled | 是否允许排序 |
isWrapping | 是否允许换行 |
itemAlignment | 元素的对齐方式 |
selectRectVisible | 被选中的元素矩形是否可见 |
spacing | 元素之间的间隔 |
方法:
方法 | 说明 |
---|---|
addItem(const QString& label) addItem(QListWidgetItem *item) | 向列表添加元素(支持字符串标签或 QListWidgetItem 对象) |
currentItem() | 返回当前选中元素的 QListWidgetItem* |
currentRow() | 返回当前选中元素的 行号 |
setCurrentItem(QListWidgetItem* item) | 设置选中指定 QListWidgetItem 元素 |
setCurrentRow(int row) | 设置选中指定行的元素 |
insertItem(const QString& label, int row) insertItem(QListWidgetItem *item, int row) | 在指定行插入元素(支持字符串标签或 QListWidgetItem 对象) |
item(int row) | 返回第 row 行元素的 QListWidgetItem* |
takeItem(int row) | 删除指定行元素,返回被删除的 QListWidgetItem* |
信号:
方法(信号) | 说明 |
---|---|
currentItemChanged(QListWidgetItem* current, QListWidgetItem* old) | 选中元素变化时触发,参数为当前选中、之前选中的 QListWidgetItem |
currentRowChanged(int) | 选中元素变化时触发,参数为当前选中元素的行号 |
itemClicked(QListWidgetItem* item) | 点击列表项(元素)时触发,参数是被点击的 QListWidgetItem |
itemDoubleClicked(QListWidgetItem* item) | 双击列表项时触发,参数是被双击的 QListWidgetItem |
itemEntered(QListWidgetItem* item) | 鼠标进入列表项区域时触发,参数是对应的 QListWidgetItem |
例如:
#include "widget.h"
#include "./ui_widget.h"#include <QDebug>
#include <QDateTime>
#include <QShortcut>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->listWidget->addItem("C++");ui->listWidget->addItem(new QListWidgetItem("Linux"));ui->listWidget->addItem(new QListWidgetItem("Qt"));
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButtonAdd_clicked()
{QString text = ui->lineEdit->text();if (!text.isEmpty()) {ui->listWidget->addItem(text);}
}void Widget::on_pushButtonRemove_clicked()
{int row = ui->listWidget->currentRow();if (row >= 0) {ui->listWidget->takeItem(row);}
}void Widget::on_listWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
{// 一定要检查指针有效性if (current) {qDebug() << "现在的元素为" << current->text();}if (previous) {qDebug() << "之前的元素为:" << previous->text();}
}
效果:
TableWidget
表格
方法:
方法 | 说明 |
---|---|
item(int row, int column) | 根据行数列数获取指定的 QTableWidgetItem* |
setItem(int row, int column, QTableWidgetItem* item) | 根据行数列数设置表格中的元素 |
currentItem() | 返回被选中的元素 QTableWidgetItem* |
currentRow() | 返回被选中元素是第几行 |
currentColumn() | 返回被选中元素是第几列 |
row(QTableWidgetItem* item) | 获取指定 item 是第几行 |
column(QTableWidgetItem* item) | 获取指定 item 是第几列 |
rowCount() | 获取行数 |
columnCount() | 获取列数 |
insertRow(int row) | 在第 row 行处插入新行 |
insertColumn(int column) | 在第 column 列插入新列 |
removeRow(int row) | 删除第 row 行 |
removeColumn(int column) | 删除第 column 列 |
setHorizontalHeaderItem(int column, QTableWidgetItem* item) | 设置指定列的表头 |
setVerticalHeaderItem(int row, QTableWidgetItem* item) | 设置指定行的表头 |
信号:
信号签名 | 说明 |
---|---|
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) | 设置单元格是否选中 |
setSizeHint(const QSize&) | 设置单元格尺寸提示 |
setFont(const QFont&) | 设置单元格文本字体 |
例如:
#include "widget.h"
#include "./ui_widget.h"#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->tableWidget->insertRow(0);ui->tableWidget->insertRow(1);ui->tableWidget->insertRow(2);ui->tableWidget->insertColumn(0);ui->tableWidget->insertColumn(1);ui->tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem("姓名"));ui->tableWidget->setHorizontalHeaderItem(1, new QTableWidgetItem("性别"));ui->lineEdit->setPlaceholderText("请输入列名");ui->pushButtonPushLeft->setEnabled(false);ui->pushButtonPushRight->setEnabled(false);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButtonPushUp_clicked()
{int row = std::max(ui->tableWidget->currentRow(), 0);ui->tableWidget->insertRow(row);}void Widget::on_pushButtonPushDown_clicked()
{int row = ui->tableWidget->currentRow();ui->tableWidget->insertRow(row + 1);
}void Widget::on_pushButtonPushLeft_clicked()
{QString text = ui->lineEdit->text();int col = std::max(ui->tableWidget->currentColumn(), 0);ui->tableWidget->insertColumn(col);ui->tableWidget->setHorizontalHeaderItem(col, new QTableWidgetItem(text));ui->lineEdit->clear();}void Widget::on_pushButtonPushRight_clicked()
{QString text = ui->lineEdit->text();int col = ui->tableWidget->currentColumn();ui->tableWidget->insertColumn(col + 1);ui->tableWidget->setHorizontalHeaderItem(col + 1, new QTableWidgetItem(text));ui->lineEdit->clear();
}void Widget::on_pushButtonPopCol_clicked()
{int col = ui->tableWidget->currentColumn();if (col >= 0) {ui->tableWidget->removeColumn(col);}
}void Widget::on_pushButtonPopRow_clicked()
{int row = ui->tableWidget->currentRow();if (row >= 0) {ui->tableWidget->removeRow(row);}
}void Widget::on_lineEdit_textChanged(const QString &arg1)
{if (arg1.isEmpty()){ui->pushButtonPushLeft->setEnabled(false);ui->pushButtonPushRight->setEnabled(false);} else {ui->pushButtonPushLeft->setEnabled(true);ui->pushButtonPushRight->setEnabled(true);}
}
效果:
TreeWidget
树形控件
这里的树,不考虑顶层的根节点,而是从根节点的下一层开始计算的
TreeWidget
方法:
方法 | 说明 |
---|---|
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) | 设置 TreeWidget 的 header 名称. |
setHeaderHidden(bool) | 设置header是否隐藏 |
setColumnCount | 设置列数 |
TreeWidgetItem
属性:
属性 | 说明 |
---|---|
text | 持有的文本 |
textAlignment | 文本对齐方式 |
icon | 持有的图表 |
font | 文本字体 |
hidden | 是否隐藏 |
disabled | 是否禁用 |
expand | 是否展开 |
sizeHint | 尺寸大小 |
selected | 是否选中 |
TreeWidgetItem
方法:
方法 | 说明 |
---|---|
addChild(QTreeWidgetItem* child) | 新增子节点 |
childCount() | 子节点的个数 |
child(int index) | 获取指定下标的子节点,返回 QTreeWidgetItem* |
takeChild(int index) | 删除对应下标的子节点 |
removeChild(QTreeWidgetItem* child) | 删除对应的子节点 |
parent() | 获取该元素的父节点 |
例如:
#include "widget.h"
#include "./ui_widget.h"#include <QTreeWidgetItem>
#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->lineEdit->setPlaceholderText("请输入节点名称");ui->pushButtonAddNext->setEnabled(false);ui->pushButtonAddRoot->setEnabled(false);// 设置标题栏名称// ui->treeWidget->setHeaderHidden("true");// 添加两列ui->treeWidget->setColumnCount(2);ui->treeWidget->setHeaderLabels({"动物", "数量"});// 添加顶层节点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);// 为顶层节点添加子节点QTreeWidgetItem* item4 = new QTreeWidgetItem();item4->setText(0, "哈士奇");item1->addChild(item4);QTreeWidgetItem* item5 = new QTreeWidgetItem();item5->setText(0, "暹罗猫");item2->addChild(item5);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButtonAddRoot_clicked()
{QString text = ui->lineEdit->text();ui->lineEdit->clear();QTreeWidgetItem* item = new QTreeWidgetItem();item->setText(0, text);ui->treeWidget->addTopLevelItem(item);
}void Widget::on_pushButtonAddNext_clicked()
{// 获取当前选中的节点QTreeWidgetItem* root = ui->treeWidget->currentItem();if (root) {QString text = ui->lineEdit->text();ui->lineEdit->clear();QTreeWidgetItem* item = new QTreeWidgetItem();item->setText(0, text);root->addChild(item);}
}void Widget::on_pushButtonRemove_clicked()
{QTreeWidgetItem* root = ui->treeWidget->currentItem();if (root) {QTreeWidgetItem* parent = root->parent();if (parent == nullptr) {int index = ui->treeWidget->indexOfTopLevelItem(root);ui->treeWidget->takeTopLevelItem(index);} else {parent->removeChild(root);}}
}void Widget::on_lineEdit_textChanged(const QString &arg1)
{if (arg1.isEmpty()) {ui->pushButtonAddNext->setEnabled(false);ui->pushButtonAddRoot->setEnabled(false);} else {ui->pushButtonAddNext->setEnabled(true);ui->pushButtonAddRoot->setEnabled(true);}
}
效果:
容器类控件
多元素空间,包含的内容是,一个个的自定义好的Item
对象
容器类控件, 包含的内容是,前面所讲的各种控件,如PushButton
,LineEdit
GroupBox
属性:
属性 | 说明 |
---|---|
title | 分组框的标题 |
alignment | 分组框内部内容的对齐方式 |
flat | 是否是 “扁平” 模式 |
checkable | 是否可选择。 设为 true,则在 title 前方会多出一个可勾选的部分。 |
checked | 描述分组框的选择状态(前提是 checkable 为 true) |
TabWidget
标签页
属性:
属性 | 说明 |
---|---|
tabPosition | 标签页所在的位置,可选值:North(上方)、South(下方)、West(左侧)、East(右侧) |
currentIndex | 当前选中了第几个标签页(从 0 开始计算) |
currentTabText | 当前选中的标签页的文本 |
currentTabName | 当前选中的标签页的名字 |
currentTabIcon | 当前选中的标签页的图标 |
currentTabToolTip | 当前选中的标签页的提示信息 |
tabsCloseable | 标签页是否可以关闭 |
movable | 标签页是否可以移动 |
信号:
属性 | 说明 |
---|---|
currentChanged(int) | 标签页切换时触发,参数是被点击选项卡编号 |
tabBarClicked(int) | 点击选项卡标签条时触发,参数是被点击选项卡编号 |
tabBarDoubleClicked(int) | 双击选项卡标签条时触发,参数是被点击选项卡编号 |
tabCloseRequest(int) | 标签页关闭时触发,参数是被关闭选项卡编号 |
例如:
#include "widget.h"
#include "./ui_widget.h"#include <QTreeWidgetItem>
#include <QDebug>
#include <QLabel>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QLabel* label1 = new QLabel(ui->tab);label1->setText("标签页 1");label1->resize(100, 50);QLabel* label2 = new QLabel(ui->tab_2);label2->setText("标签页 2");label2->resize(100, 50);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButtonAdd_clicked()
{int count = ui->tabWidget->count();QWidget* w = new QWidget();ui->tabWidget->addTab(w, QString("Tab ") + QString::number(count + 1));QLabel* label = new QLabel(w);label->setText("标签页 " + 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);
}void Widget::on_tabWidget_currentChanged(int index)
{qDebug() << "当前所在标签页为:" << index + 1;
}
注意:
- 对于
TabWidget
中的每一个标签页,其都有对应的一个objectName
,也就是其标签名 - 如果要访问到这个标签页,使用
ui->[objectName]
即可
效果:
布局管理器
我们之前把控件放到界面上,都是靠“手动”的方式来布局的,这种方式是很不科学的
- 手动布局的方式非常复杂,而且不精准
- 无法对窗口大小进行自适应
此时就需要用到布局管理器
- 垂直布局
- 水平布局
- 网格布局
- 表单布局
注意:每个**Widget
**只能设置一个布局管理器
- 如果是在代码中创建
layout
,那么就只是创建了一个layout
- 如果是在
Qt Designer
放置了一个layout
,那么会先创建一个widget
,再在widget
里面创建layout
VBoxLayout
垂直布局管理器
属性
属性 | 说明 |
---|---|
layoutLeftMargin | 左侧边距 |
layoutRightMargin | 右侧边距 |
layoutTopMargin | 上方边距 |
layoutBottomMargin | 下方边距 |
layoutSpacing | 相邻元素之间的间距 |
VBoxLayout
只用于布局,不提供信号
例如:
使用图形化界面的方式,使用垂直布局管理器:
-
先创建
VerticalLayout
,再放置元素: -
先放置元素,再设置垂直布局:
查看效果:
通过代码的方式,使用垂直布局管理器:
#include "widget.h"
#include "./ui_widget.h"#include <QVBoxLayout>
#include <QDebug>
#include <QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button1 = new QPushButton("按钮1");QPushButton* button2 = new QPushButton("按钮2");QPushButton* button3 = new QPushButton("按钮3");QVBoxLayout* layout = new QVBoxLayout();layout->addWidget(button1);layout->addWidget(button2);layout->addWidget(button3);this->setLayout(layout);
}Widget::~Widget()
{delete ui;
}
效果:
可以发现,如果用代码的方式,往窗口添加VBoxLayout
,那么由于其不会创建额外的widget
,因此此时的VBoxLayout
就可以适配其父窗口(主窗口)的大小
但用Qt Designer
进行创建,由于会产生额外的widget
,因此此时的layout
就不能适配主窗口大小
HBoxLayout
水平布局管理器
属性:
属性 | 说明 |
---|---|
layoutLeftMargin | 左侧边距 |
layoutRightMargin | 右侧边距 |
layoutTopMargin | 上方边距 |
layoutBottomMargin | 下方边距 |
layoutSpacing | 相邻元素之间的间距 |
HBoxLayout
和VBoxLayout
用法雷同,这里不过多赘述
这里来展示一个VBoxLayout
和HBoxLayout
嵌套的例子:
#include "widget.h"
#include "./ui_widget.h"#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QDebug>
#include <QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 创建 垂直布局管理器QVBoxLayout* vlayout = new QVBoxLayout();this->setLayout(vlayout);// 将按钮添加到垂直布局QPushButton* button1 = new QPushButton("按钮1");QPushButton* button2 = new QPushButton("按钮2");vlayout->addWidget(button1);vlayout->addWidget(button2);// 创建 水平布局管理器QHBoxLayout* hlayout = new QHBoxLayout();this->setLayout(hlayout);// 将按钮添加到水平布局QPushButton* button3 = new QPushButton("按钮3");QPushButton* button4 = new QPushButton("按钮4");hlayout->addWidget(button3);hlayout->addWidget(button4);// 将水平布局管理器 添加到 垂直布局管理器中vlayout->addLayout(hlayout);
}Widget::~Widget()
{delete ui;
}
效果:
GridLayout
网格布局
属性:
属性 | 说明 |
---|---|
layoutLeftMargin | 左侧边距 |
layoutRightMargin | 右侧边距 |
layoutTopMargin | 上方边距 |
layoutBottomMargin | 下方边距 |
layoutHorizontalSpacing | 相邻元素水平方向间距 |
layoutVerticalSpacing | 相邻元素垂直方向间距 |
layoutRowStretch | 行方向拉伸系数 |
layoutColumnStretch | 列方向拉伸系数 |
例如:
#include "widget.h"
#include "./ui_widget.h"#include <QGridLayout>
#include <QDebug>
#include <QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QGridLayout* layout = new QGridLayout();this->setLayout(layout);QPushButton* button1 = new QPushButton("按钮1");QPushButton* button2 = new QPushButton("按钮2");QPushButton* button3 = new QPushButton("按钮3");QPushButton* button4 = new QPushButton("按钮4");// 网格布局layout->addWidget(button1, 0, 0);layout->addWidget(button2, 0, 1);layout->addWidget(button3, 1, 0);layout->addWidget(button4, 1, 1);// 水平布局layout->addWidget(button1, 0, 0);layout->addWidget(button2, 0, 1);layout->addWidget(button3, 0, 2);layout->addWidget(button4, 0, 3);// 垂直布局layout->addWidget(button1, 0, 0);layout->addWidget(button2, 1, 0);layout->addWidget(button3, 2, 0);layout->addWidget(button4, 3, 0);// 对角线布局layout->addWidget(button1, 0, 0);layout->addWidget(button2, 1, 1);layout->addWidget(button3, 2, 2);layout->addWidget(button4, 3, 3);
}Widget::~Widget()
{delete ui;
}
效果:
刚刚创建的布局管理器,控件的尺寸都是均等的
当需要创建出尺寸不同的控件的时候,就可以通过拉伸系数来设置。 拉伸系数就相当于设置控件之间尺寸的比例
例如:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QGridLayout* layout = new QGridLayout();this->setLayout(layout);QPushButton* button1 = new QPushButton("按钮1");QPushButton* button2 = new QPushButton("按钮2");QPushButton* button3 = new QPushButton("按钮3");QPushButton* button4 = new QPushButton("按钮4");QPushButton* button5 = new QPushButton("按钮5");QPushButton* button6 = new QPushButton("按钮6");// 按钮大小自适应,水平方向和垂直方向的sizePolicy属性button1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);button2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);button3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);button4->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);button5->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);button6->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);// 网格布局 2 * 3layout->addWidget(button1, 0, 0);layout->addWidget(button2, 0, 1);layout->addWidget(button3, 0, 2);layout->addWidget(button4, 1, 0);layout->addWidget(button5, 1, 1);layout->addWidget(button6, 1, 2);// 设置垂直比例为 1 : 2layout->setRowStretch(0, 1); // 第一行为 1layout->setRowStretch(1, 2); // 第二行为 2// 设置水平比例为 1 : 2 : 3layout->setColumnStretch(0, 1); // 第一列为 1layout->setColumnStretch(1, 2); // 第二列为 2layout->setColumnStretch(2, 3); // 第三列为 3
}
注意:
-
如果拉伸系数设置为0,说明该行/列不参与拉伸,窗口大小为固定值
-
关于
QWidget
属性sizePolicy
:-
QSizePolicy::Ignored
: 忽略控件的尺寸,不对布局产生影响。 -
QSizePolicy::Minimum
: 控件的最小尺寸为固定值,布局时不会小于该值。 -
QSizePolicy::Maximum
: 控件的最大尺寸为固定值,布局时不会超过该值。 -
QSizePolicy::Preferred
: 控件的理想尺寸为固定值,布局时会尽量接近该值。 -
QSizePolicy::Expanding
: 控件的尺寸可以根据空间调整,尽可能占据更多空间。 -
QSizePolicy::Shrinking
: 控件的尺寸可以根据空间调整,尽可能缩小以适应空间。
-
-
按钮的水平布局是默认拉伸的,但是垂直布局不是,所以垂直布局不会受拉伸比例影响
-
所以需要在拉伸之前设置按钮的
sizePolicy
属性
效果:
FormLayout
表单
FormLayout
属于GridLayout
的特殊情况,专门用于实现N
行2
列的表单布局
例如
#include "widget.h"
#include "./ui_widget.h"#include <QFormLayout>
#include <QDebug>
#include <QPushButton>
#include <QLabel>
#include <QLineEdit>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QFormLayout* layout = new QFormLayout();this->setLayout(layout);layout->addRow(new QLabel("姓名"), new QLineEdit());layout->addRow(new QLabel("学号"), new QLineEdit());layout->addRow(nullptr, new QPushButton("提交"));}Widget::~Widget()
{delete ui;
}
效果:
Spacer
使用布局管理器的时候,可能需要在控件之间添加一段空白,就可以使用QSpacerItem
表示
属性:
属性 | 说明 |
---|---|
width | 宽度 |
height | 高度 |
hData | 水平方向的 sizePolicy |
vData | 垂直方向的 sizePolicy |
例如:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QHBoxLayout* layout = new QHBoxLayout();this->setLayout(layout);QPushButton* button1 = new QPushButton("按钮1");QPushButton* button2 = new QPushButton("按钮2");button1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);button2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);QSpacerItem* spacer = new QSpacerItem(200, 50, QSizePolicy::Expanding, QSizePolicy::Expanding);layout->addWidget(button1);layout->addSpacerItem(spacer);layout->addWidget(button2);
}
效果: