Qt (按钮/显示/输入/容器类控件 布局管理器)

文章目录

按钮类控件

QPushButton

QWidget中涉及到的各种属性/函数,针对继承QWidget类的控件都有效:

相关的属性:

text 按钮中的⽂本
icon 按钮中的图标
iconSize 按钮中图标的尺⼨
shortCut 按钮对应的快捷键
autoRepeat 按钮是否会重复触发。当⿏标左键按住不放时, 如果设为true,则会持续产⽣⿏标点击事件; 如果设为false,则必须释放⿏标,再次按下⿏标时才能产⽣点击事件。 (相当于游戏⼿柄上的"连发"效果)
autoRepeatDelay 重复触发的延时时间。按住按钮多久之后,开始重复触发。
autoRepeatInterval 重复触发的周期。

给按钮夹图标:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 创建图标对象
    QIcon icon(":/picture.png");
    // 设置图标
    ui->pushButton->setIcon(icon);
    // 设置图标的尺寸
    ui->pushButton->setIconSize(QSize(100,100));
}

Widget::~Widget()
{
    delete ui;
}

效果:

给QpushButton添加快捷键:

示例:现在用wasd去控制图片的移动。

首先,从阿里巴巴矢量图库中找到箭头图片,并自己准备一个图片。

然后添加到qrc文件中,关联槽函数等,点击上下左右能移动图片,后使用setShortcyt()函数与键盘关联:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //把图像写入
    ui->pushButton_Target->setIcon(QIcon(":/image/picture.png"));
    ui->pushButton_Target->setIconSize(QSize(300,300));
    //四个箭头写入
    ui->pushButton_UP->setIcon(QIcon(":/image/up.png"));
    ui->pushButton_UP->setIconSize(QSize(125,125));

    ui->pushButton_down->setIcon(QIcon(":/image/down.png"));
    ui->pushButton_down->setIconSize(QSize(125,125));

    ui->pushButton_left->setIcon(QIcon(":/image/left.png"));
    ui->pushButton_left->setIconSize(QSize(125,125));

    ui->pushButton_right->setIcon(QIcon(":/image/right.png"));
    ui->pushButton_right->setIconSize(QSize(125,125));
    //关联键盘去移动
    ui->pushButton_UP->setShortcut(QKeySequence("w"));
    ui->pushButton_down->setShortcut(QKeySequence("s"));
    ui->pushButton_left->setShortcut(QKeySequence("a"));
    ui->pushButton_right->setShortcut(QKeySequence("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());
}

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());
}


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());
}


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());
}

如果我们直接通过按键的名字来设置,虽然简单,但容易写错。

可用通过按键的枚举来设置按键的快捷键:

cpp 复制代码
	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));

组合键:

cpp 复制代码
   ui->pushButton_UP->setShortcut(QKeySequence("ctrl + w"));
   ui->pushButton_UP->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_W));

在实际运行中发现,快捷键可以点着不动,图片会一直移动,但用鼠标点击不松手,图片不能移动。

开启鼠标点击的的连发功能:

cpp 复制代码
ui->pushButton_UP->setAutoRepeat(true);

Radio Button 单选

QRadioButton是单选按钮,多个选项中选择一个。

checkable 是否可以选中。
checked 是否已经被选中。checkable是checked的前提条件。
autoExclusive 是否排他。 选中⼀个按钮之后是否会取消其他按钮的选中。 对于QRadioButton 来说默认就是排他的。

示例:性别选择,默认为男,禁用其它选项

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //添加一个默认的选项
    ui->radioButton_boy->setChecked(true);
    ui->label->setText("性别为:男");
    //禁用 其它 选项
    ui->radioButton_other->setCheckable(false);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_radioButton_boy_clicked()
{
    ui->label->setText("性别为:男");
}


void Widget::on_radioButton_girl_clicked()
{
    ui->label->setText("性别为:女");
}


void Widget::on_radioButton_other_clicked()
{
    ui->label->setText("性别为:其它");
}

我们发现禁用 其它 选项,只是按钮不能被选择,但仍然可以响应点击事件:

需要用到setEnabled:

例子:做一个选餐的界面

用上面的方法做出来的效果,每次只能选择一个,而我们现实中都是汉堡+小食+饮料:

用QButtonGroup去分组来解决:

cpp 复制代码
#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);

    //把按钮分配到各个组中
    group1->addButton(ui->radioButton);
    group1->addButton(ui->radioButton_2);
    group1->addButton(ui->radioButton_3);

    group2->addButton(ui->radioButton_4);
    group2->addButton(ui->radioButton_5);
    group2->addButton(ui->radioButton_6);

    group3->addButton(ui->radioButton_8);
    group3->addButton(ui->radioButton_9);
}

Widget::~Widget()
{
    delete ui;
}

Check Box 多选

QCheck Box 表示复选按钮,可以选择多个。

示例:

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_study->isChecked()){
        result += ui->checkBox_study->text();
    }
    if(ui->checkBox_eat->isChecked()){
        result += ui->checkBox_eat->text();
    }
    if(ui->checkBox_work->isChecked()){
        result += ui->checkBox_work->text();
    }
    ui->label->setText(result);
}

显示类控件

Label

QLabel 可以用来显示文本和图片。

text QLabel 中的⽂本
textFormat Qt::PlainText 纯⽂本。 Qt::RichText 富⽂本(⽀持html标签,word就是一种富文本)。 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

pixmap 设置图片

我想把这个"滑稽"图片占满整个界面:

发现界面是800 x 600的,滑稽图片太小,做不到全面覆盖:

用到setScaledContents:

但是又有一个新问题:我们在拖拽Widget的时候,他不会覆盖全部

原因:在Qt 中,表示用户的操作,有两类概念:1.信号。2.事件。

当用户拖拽窗口的大小时,就会触发 resize 事件 (resizeEvent)。

我们把窗口尺寸从A拖拽到B,还可以拖拽到C,说明可以是连续变化的,触发一系列的resizeEvent。

用鼠标拖拽Wiget窗口类,会重写父类(QWidget)的resizeEvent 虚函数。

代码:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QResizeEvent>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 让label铺满整个界面
    QRect rect = this->geometry();
    ui->label->setGeometry(0,0,rect.width(),rect.height());
    // 获取图片
    QPixmap pixmap(":/picture.png");
    ui->label->setPixmap(pixmap);
    // 打开自动拉伸
    ui->label->setScaledContents(true);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::resizeEvent(QResizeEvent *event)
{
    qDebug() << event->size();
    ui->label->setGeometry(0,0,event->size().width(),event->size().height());
}

效果:当拉伸的时候,左边的日志显示了界面的尺寸

文本对齐方式

当我们把标签拖进界面时,希望能看清边框:

这里选了box作为效果:

水平居中 + 垂直居中:

自动换行

第一个标签没有自动换行,第二个标签自动换行:

设置缩进

设置边距

可以看到上下左右都缩进:

QLabel 伙伴

先看样例:

效果:

由上述可知:Qt 中,QLabel 中写的文本,可以指定"快捷键",但快捷键的规则功能上比QPushButon弱很多。

语法:在文本中使用 &字符 来表示快捷键。绑定了伙伴关系后,通过快捷键就能选中对应的单选框/复选框。

LCD Number 显示数字

QLCD Number 是一个专门用来显示数字的控件。

intValue QLCDNumber显⽰的数字值(int)。
value QLCDNumber显⽰的数字值(double)。 例如给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 设置⽐较⼩的⼩数点。

示例:倒计时,使用QLCDNumber 显示一个初始的数值,每隔一秒,数字-1

Qt 中封装了对应的定时器:QTimer 通过这个类创建出来的对象,会产生timeout的信号,通过start开始,并且能在start中参数设定多少ms触发一次timeout信号。后通过connect来连接信号与槽。

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);
    // 设置初始值
    ui->lcdNumber->display(10);
    // 把Qtime函数与timeout 信号与槽函数相关联
    timer = new QTimer(this);
    connect(timer,&QTimer::timeout,this,&Widget::handle);
    // 启动定时器 单位:ms
    timer->start(1000);

}

Widget::~Widget()
{
    delete ui;
}

void Widget::handle()
{
    //先拿到LCDNumber的数字
    int value = ui->lcdNumber->intValue();
    if(value <= 0){
        timer->stop();
        return;
    }
    ui->lcdNumber->display(value - 1);
}

效果这里就不再演示了。

想一个问题,如果不用QTimer能不能实现,用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){
        // 休眠1s
        std::this_thread::sleep_for(std::chrono::seconds(1));
        if(value <=0){
            break;
        }
        value -= 1;
        ui->lcdNumber->display(value);
    }
}

Widget::~Widget()
{
    delete ui;
}

运行后发现,等了10才弹窗,且并没显示成功。

原因:

想了第二个方案,自己搞一个线程,在线程里循环 + 更新,就避免了直接在主线程的构造函数中等待:

cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    std::thread t([this] (){
        int value = ui->lcdNumber->intValue();
        while(true){
            std::this_thread::sleep_for(std::chrono::seconds(1));
            if(value <= 0){
                break;
            }
            value -= 1;
            ui->lcdNumber->display(value);
        }
    });
}

发现依旧不行,还有报错:

原因:在Qt 中,里面有一个专门的线程去负责回鹘更新的主线程(main函数所在的线程)。

对于GUI来说,内部包含了很多的隐藏状态,Qt 为了保证修改界面的过程中,线程的安全会受到影响,Qt禁止了其它线程直接修改见面。

这种操作,就是在修改界面:

因此,所有的对界面修改操作,必须在主线程中完成。槽函数是主线程调用的,所以我们会用槽函数修改界面。

return a.exec();

重点说一下:

cpp 复制代码
return a.exec();

a.exec() 就是这个无限循环的启动命令。它会启动一个 QEventLoop,并阻塞在 a.exec() 这一行代码上。这个循环会一直运行,直到你关闭所有窗口或主动退出,a.exec()返回一个退出码,开始执行 return a.exec(),整个main函数结束。

在循环期间,他会做:

1.监听和收集所有事件:

用户输入事件:鼠标点击、移动、键盘按键。

系统事件:定时器超时、窗口重绘请求、文件读写完成、网络数据到达。

2.分发事件:

将收集到的事件分发给相应的目标对象(QObject)。比如:把鼠标点击事件发给被点击的按钮。把键盘事件发给获得焦点的输入框。

3.处理事件并触发槽函数(Slots)。

ProgressBar 进度条

属性:

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.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 handle();

private:
    Ui::Widget *ui;
    QTimer *timer;
};
#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);

    timer = new QTimer();
    connect(timer,&QTimer::timeout,this,&Widget::handle);
    timer->start(100);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::handle()
{
    int value = ui->progressBar->value();
    if(value >= 100){
        timer->stop();
        return;
    }
    ui->progressBar->setValue(value+1);
}

样子:

发现了一个问题:.h文件中并没有加头文件QTimer,成员变量居然没有报错

原因如下:(不用过多深究原理,只需要知道有这回事就行)

而这个包含机制:

cpp 复制代码
我们写一个普通Qt头文件 → 比如 <QWidget>
    ↓
这个头文件可能包含模块级头文件 → 比如 <QtWidgets/qtwidgetsglobal.h>
    ↓
模块级头文件可能包含前置声明头文件 → 比如间接包含 <QtCore/QtCoreFwd>
    ↓
前置声明头文件包含了所有类声明 → 包括 class QTimer;

更具体一点:

cpp 复制代码
// 代码:
#include <QWidget>  // 看似只包含一个类

// 实际上编译器看到:
#include <QtCore/QtCoreFwd>  // 所有 Core 类前置声明
#include <QtGui/QtGuiFwd>    // 所有 GUI 类前置声明  
#include <QtWidgets/QtWidgetsFwd> // 所有 Widgets 类前置声明
// 加上 QWidget 的具体实现

Qt 为什么要有这种技巧?

主要是解决编译速度问题,横向对比中,C/C++的编译速度非常慢,原因跟头文件有直接关系。include头文件关系错综复杂,减少个数可以有效减少编译时间,Qt就用class前置声明来减少头文件的包含。

但在实际开发中,该包含头文件还是包含。提升硬件能力换取高效的编译比用特殊技巧更好。

Calendar Widget 日历

QCalendarWidget 表⽰⼀个⽇历。

属性:

selectDate 当前选中的⽇期
minimumDate 最⼩⽇期
maximumDate 最⼤⽇期
firstDayOfWeek 每周的第⼀天(也就是⽇历的第⼀列)是周⼏.
gridVisible 是否显⽰表格的边框
selectionMode 是否允许选择⽇期
navigationBarVisible ⽇历上⽅标题是否显⽰
horizontalHeaderFormat ⽇历上⽅标题显⽰的⽇期格式
verticalHeaderFormat ⽇历第⼀列显⽰的内容格式
dateEditEnabled 是否允许⽇期被编辑

信号:

selectionChanged(constQDate&) 当选中的⽇期发⽣改变时发出
activated(const QDate&) 当双击⼀个有效的⽇期或者按下回⻋键时发出,形参是⼀个QDate类型,保存了选中的⽇期
currentPageChanged(int,int) 当年份⽉份改变时发出,形参表⽰改变后的新年份和⽉份。

例子:

输入类控件

Line Edit

QLineEdit ⽤来表⽰单⾏输⼊。可以输⼊⼀段⽂本,但是不能换⾏。

属性:

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) 当QLineEdit中的⽂本改变时,发出此信号,text是新的⽂本。 代码对⽂本的修改能够触发这个信号。
void textEdited(const QString &text)) 当QLineEdit中的⽂本改变时,发出此信号,text是新的⽂本。 代码对⽂本的修改不能触发这个信号。

例子:让用户输入个人信息,输入后点提交按钮,日志打印出来

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#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);
    //显示模式转成密码模式
    ui->lineEdit_password->setEchoMode(QLineEdit::Password);

    //手机号
    ui->lineEdit__telephone->setPlaceholderText("请输入手机号");
    ui->lineEdit__telephone->setClearButtonEnabled(true);
    //手机号的固定格式,0代表数字
    ui->lineEdit__telephone->setInputMask("000-0000-0000");
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    QString gender = ui->radioButton_male->isChecked() ? "男" : "女";
    qDebug() << "姓名:" << ui->lineEdit_name->text()
             << "密码:" << ui->lineEdit_password->text()
             << "性别:" << gender
             << "电话号码:" << ui->lineEdit__telephone->text();
}

效果:

在上面代码中,限制手机号用到了inputMask,功能有限,只能进行简单的验证。需要"正则表达式"。

"正则表达式":本质上是一个带有特殊字符的字符串,特殊字符用来表示另一个字符串的特征。

就比如我要找个女朋友,条件:1.白;2.富;3.身材好。我就要拿着这三个条件去对比,然后找到心仪的女生。正则表达式就是拿着条件去匹配。

这里用一下就懂了:

例子:输入框检查输入的内容是否是合法的手机号,如果是,则按钮为可用状态;不是,则为禁用状态。

cpp 复制代码
    //给单行输入框设置验证器,基于正则表达式来完成
    QRegularExpression regExp("^1\\d{10}$");
    //注册验证器
    ui->lineEdit->setValidator(new QRegularExpressionValidator(regExp));

注册完验证器,后使用验证器,使用的方法

由其它代码来完成。

比如这里的使用规则为:验证输入框的内容是否合法,只要输入框中的内容发生改变,验证器操作就应被执行。

全部代码:

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);

    //给单行输入框设置验证器,基于正则表达式来完成
    QRegularExpression regExp("^1\\d{10}$");
    //注册验证器
    ui->lineEdit->setValidator(new QRegularExpressionValidator(regExp));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_lineEdit_textChanged(const QString &text)
{
    QString content = text;
    int pos = 0;
    if(ui->lineEdit->validator()->validate(content,pos) == QValidator::Acceptable){
        //验证通过
        ui->pushButton->setEnabled(true);
    }else{
        //验证失败
        ui->pushButton->setEnabled(false);
    }
}

效果:

正则表达式的语法:语法

正则表达式的在线检测工具:工具

textEdited() 与 textChanged()的区别

信号 触发时机
textEdited() 用户手动编辑时触发
textChanged() 文本内容改变就触发(包括代码设置)

例子:验证两次输入的密码一致

代码:

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::compare()
{
    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_textEdited(const QString &arg1)
{
    this->compare();
}


void Widget::on_lineEdit_2_textEdited(const QString &arg1)
{
    this->compare();
}

效果:

我们平常会遇到这样的问题:有用不到的参数,会警告

可用骗过编译器,不让警告发生:

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::ScrollBarAlwaysOn:总是显⽰滚动条。

信号:

textChanged() ⽂本内容改变时触发
selectionChanged() 选中范围改变时触发
cursorPositionChanged() 光标移动时触发
undoAvailable(bool) 可以进⾏undo操作时触发
redoAvailable(bool) 可以进⾏redo操作时触发
copyAvaiable(bool) ⽂本被选中/取消选中时触发

示例:用法很简单

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()
{
    qDebug() << "textChange: " << ui->textEdit->toPlainText();
}

void Widget::on_textEdit_selectionChanged()
{
    qDebug() << "textChange: " << ui->textEdit->toPlainText();
}

void Widget::on_textEdit_cursorPositionChanged()
{
    qDebug() << "cursorPositionChanged: " << ui->textEdit->toPlainText();
}

用法简单,效果很显然易见,这里就不演示了。

Combox Box 下拉框

Combox Box 表示下拉框。

属性 说明
currentText 当前选中的⽂本
currentIndex 当前选中的条⽬下标。从0开始计算。如果当前没有条⽬被选中,值为-1。
editable 是否允许修改设为true时,QComboBox的⾏为就⾮常接近QLineEdit,也可以设置validator。
iconSize 下拉框图标(⼩三⻆)的⼤⼩
maxCount 最多允许有多少个条⽬
方法 说明
addItem(constQString&) 添加⼀个条⽬
currentIndex() 获取当前条⽬的下标 从0开始计算.如果当前没有条⽬被选中,值为-1。
currentText() 获取当前条⽬的⽂本内容。
⽅法 说明
activated(int) activated(constQString&text) 当⽤⼾选择了⼀个选项时发出。 这个时候相当于⽤⼾点开下拉框,并且⿏标划过某个选项。 此时还没有确认做出选择。
currentIndexChanged(int) currentIndexChanged(constQString&text) 当前选项改变时发出。 此时⽤⼾已经明确的选择了⼀个选项。 ⽤⼾操作或者通过程序操作都会触发这个信号。
editTextChanged(constQString&text) 当编辑框中的⽂本改变时发出(editable为true时有效)

例子:点餐

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("雪碧");
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    qDebug() << ui->comboBox->currentText()
             << ui->comboBox_2->currentText()
             << ui->comboBox_3->currentText();
}

效果:

也可以在界面中添加条目:

下拉框里面的内容,很多时候不是代码写死的,而是通过文件/网络加载数据得到的。

例子:把下面文件的内容做成下拉框

代码:

cpp 复制代码
#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("C:/Program1/config.txt");
    if(!file.is_open()){
        qDebug() << "文件打开失败";
        return;
    }
    //按行来读取内容
    std::string line;
    while(std::getline(file,line)){
        //每取到一行,放到下拉框中
        ui->comboBox->addItem(QString::fromStdString(line));
    }
    file.close();

}

Widget::~Widget()
{
    delete ui;
}

效果:

Spin Box

使⽤QSpinBox 或者QDoubleSpinBox 表示"微调框",它是带有按钮的输⼊框,可以⽤来输⼊整数/浮点数,通过点击按钮来修改数值⼤⼩。

属性 说明
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带有前缀和后缀.
valueChanged(int) 微调框的⽂本发⽣改变时会触发. 参数int,表⽰当前的数值.

例子:结合上面的点餐服务,用 微调框 选择数量

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->spinBox->setRange(1,5);
    ui->spinBox_2->setRange(1,5);
    ui->spinBox_3->setRange(1,5);

    //初始化数量
    ui->spinBox->setValue(1);
    ui->spinBox_2->setValue(1);
    ui->spinBox_3->setValue(1);
}

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();
}

效果:

DateEdit &TimeEdit

这⼏个控件⽤法⾮常相似,我们以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表示秒 注意:这⾥的格式化符号的含义,不要记忆。不同语⾔/库的设定规则是存在差异的。⼀定是⽤的时候再去查。
minimumDateTime 最⼩时间⽇期
maximumDateTime 最⼤时间⽇期
timeSpec Qt::LocalTime:显⽰本地时间。北京时间"东八区",LocalTime = UTC + 8h Qt::UTC:显⽰协调世界时(UTC)。是标准时间,通过原子钟 Qt::OffsetFromUTC:显⽰相对于UTC的偏移量(时差)。
信号 说明
dateChanged(QDate) ⽇期改变时触发.
timeChanged(QTime) 时间改变时触发.
dateTimeChanged(QDateTime) 时间⽇期任意⼀个改变时触发.

例子:时间计算器,计算两个时间中间的间隔是多少天/多少小时

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 dateOld = ui->dateTimeEdit->dateTime();
    QDateTime dateNew = ui->dateTimeEdit_2->dateTime();
    //做差
    int days = dateOld.daysTo(dateNew);
    int seconds = dateOld.secsTo(dateNew);
    // 把秒换成小时
    int hours = (seconds / 3600) %24;
    //计算结果放到label中
    ui->label->setText(QString::number(days) + QString("天") + QString::number(hours) + QString("小时") );
}

效果:

会有一个BUG:这里应该是0天7小时

查一下文档:他说23:55到0:05算一天

那就加上:

cpp 复制代码
void Widget::on_pushButton_clicked()
{
    //得到时间
    QDateTime dateOld = ui->dateTimeEdit->dateTime();
    QDateTime dateNew = ui->dateTimeEdit_2->dateTime();
    //做差
    int seconds = dateOld.secsTo(dateNew);
    // 把秒换成小时
    int hours = (seconds / 3600) %24;
    int days = (seconds / 3600) /24;
    //计算结果放到label中
    ui->label->setText(QString::number(days) + QString("天") + QString::number(hours) + QString("小时") );
}

Dial 旋钮

使⽤QDial 表示⼀个旋钮。

属性 说明
value 持有的数值。
minimum 最⼩值
maximum 最⼤值
singleStep 按下⽅向键的时候改变的步⻓。
pageStep 按下pageUp/pageDown的时候改变的步⻓。
sliderPosition 界⾯上旋钮显⽰的初始位置。
tracking 外观是否会跟踪数值变化. 默认值为true.⼀般不需要修改.
wrapping 是否允许循环调整。 即数值如果超过最⼤值,是否允许回到最⼩值。(调整过程能否"套圈")
notchesVisible 是否显⽰刻度线
notchTarget 刻度线之间的相对位置。 数字越⼤,刻度线越稀疏。

信号:

valueChanged(int) 数值改变时触发
rangeChanged(int, int) 范围变化时触发

Slider 滑动条

使⽤QSlider 表⽰⼀个滑动条。

属性 说明
value 持有的数值。
minimum 最⼩值
maximum 最⼤值
singleStep 按下⽅向键的时候改变的步⻓。
pageStep 按下pageUp/pageDown的时候改变的步⻓。
sliderPosition 滑动条显⽰的初始位置
tracking 外观是否会跟踪数值变化. 默认值为true.⼀般不需要修改
orientation 滑动条的⽅向是⽔平还是垂直
invertedAppearance 是否要翻转滑动条的⽅向
tickPosition 刻度的位置
tickInterval 刻度的密集程度.
信号 说明
valueChanged(int) 数值改变时触发
rangeChanged(int,int) 范围变化时触发

多元素控件

两两为一组:

xxWidget VS xxView

xxView更倾向于底层,xxWidget基于xxView封装而来的。

xxView 是MVC结构的一种典型实现:

M model 数据 ;V view 视图(界面);C controller 控制器 数据与试图之间的业务流程。

xxView只负责视图。如果程序员需要自己实现mdoel和controller的部分比较麻烦,所以xxWidget基于xxView同时把model和controller做好了,提供了API。

xxView的上限高,能实现复杂的功能,但麻烦。(CPU、内存等零件自己组装电脑)

xxWidget的下限高,使用方便,功能有限。(买整机,内存等零件可能会拉后腿,不兼容)

List Widget

使⽤QListWidget 能够显⽰⼀个纵向的列表。形如:每个选项可以被选中

属性 说明
currentRow 当前被选中的是第⼏⾏
count ⼀共有多少⾏
sortingEnabled 是否允许排序
isWrapping 是否允许换⾏
itemAlignment 元素的对⻬⽅式
selectRectVisible 被选中的元素矩形是否可⻅
spacing 元素之间的间隔
方法 说明
addItem(constQString&label) addItem(QListWidgetItem*item) 列表中添加元素
currentItem() 返回QListWidgetItem*表⽰当前选中的元素
setCurrentItem(QListWidgetItem*item) 设置选中哪个元素
setCurrentRow(introw) 设置选中第⼏⾏的元素
insertItem(constQString&label,introw) insertItem(QListWidgetItem*item,introw) 在指定的位置插⼊元素
item(introw) 返回QListWidgetItem*表⽰第row⾏的元素
takeItem(introw) 删除指定⾏的元素,返回QListWidgetItem*表⽰是哪个元素被删除了
信号 说明
currentItemChanged(QListWidgetItemcurrent,QListWidgetItemold) 选中不同元素时会触发.参数是当前选中的元素和之前选中的元素
currentRowChanged(int) 选中不同元素时会触发.参数是当前选中元素的⾏数
itemClicked(QListWidgetItem*item) 点击某个元素时触发
itemDoubleClicked(QListWidgetItem*item) 双击某个元素时触发
itemEntered(QListWidgetItem*item) ⿏标进⼊元素时触发

例子:在右边写入添加到列表中;或者选中列表的项目删除

代码:

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_insert_clicked()
{
    // 读取写入的内容
    const QString text = ui->textEdit->toPlainText();
    // 写入列表中
    ui->listWidget->addItem(text);
    // 删除输入框内的内容
    ui->textEdit->clear();
}

void Widget::on_pushButton_delete_clicked()
{
    // 选中要删除内容的行数
    int row = ui->listWidget->currentRow();
    // 删除
    ui->listWidget->takeItem(row);
}

也可以用QListWidgetItem添加,在QListWidgetItem中可用设置字体属性,图标,文字大小,被选中状态等:

cpp 复制代码
ui->listWidget->addItem(new QListWidgetItem("小草神"));

界面也能添加列表:

Table Widget

使⽤QTableWidget 表⽰⼀个表格控件。⼀个表格中包含若⼲⾏,每⼀⾏⼜包含若⼲列。表格中的每个单元格,是⼀个QTableWidgetItem 对象。(Table Widget是2维,List Widget是1维的 )

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(introw) 在第row⾏处插⼊新⾏
insertColumn(intcolumn) 在第column列插⼊新列
removeRow(introw) 删除第row⾏
removeColumn(intcolumn) 删除第column列
setHorizontalHeaderItem(int column,QTableWidgetItem*) 设置指定列的表头
setVerticalHeaderItem(introw,QTableWidgetItem*) 设置指定⾏的表头

QTableWidgetItem核⼼信号:

信号 说明
cellClicked(introw,intcolumn) 点击单元格时触发
cellDoubleClicked(introw,int column) 双击单元格时触发
cellEntered(introw,intcolumn) ⿏标进⼊单元格时触发
currentCellChanged(int row,int column,intpreviousRow,int previousColumn) 选中不同单元格时触发

QTableWidgetItem核⼼⽅法:

⽅法 说明
row() 获取当前是第⼏⾏
column() 获取当前是第⼏列
setText(const QString&) 设置⽂本
setTextAlignment(int) 设置⽂本对⻬
setIcon(const QIcon&) 设置图标
setSelected(bool) 设置被选中
setSizeHints(const QSize&) 设置尺⼨
setFont(const QFont&) 设置字体

图形化:

代码:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

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->insertColumn(2);

    // 给3个列起名字
    ui->tableWidget->setHorizontalHeaderItem(0,new QTableWidgetItem("姓名"));
    ui->tableWidget->setHorizontalHeaderItem(1,new QTableWidgetItem("性别"));
    ui->tableWidget->setHorizontalHeaderItem(2,new QTableWidgetItem("成绩"));

    // 给表格中添加数据 (行,列)
    ui->tableWidget->setItem(0,0,new QTableWidgetItem("张三"));
    ui->tableWidget->setItem(0,1,new QTableWidgetItem("男"));
    ui->tableWidget->setItem(0,2,new QTableWidgetItem("100"));
    ui->tableWidget->setItem(1,0,new QTableWidgetItem("李四"));
    ui->tableWidget->setItem(1,1,new QTableWidgetItem("男"));
    ui->tableWidget->setItem(1,2,new QTableWidgetItem("90"));
    ui->tableWidget->setItem(2,0,new QTableWidgetItem("王五"));
    ui->tableWidget->setItem(2,1,new QTableWidgetItem("男"));
    ui->tableWidget->setItem(2,2,new QTableWidgetItem("95"));


}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_add_row_clicked()
{
    // 获取目前行号
    int row = ui->tableWidget->rowCount();
    // 增加行
    ui->tableWidget->insertRow(row);
}

void Widget::on_pushButton_delete_row_clicked()
{
    // 获得选中的行号
    int row = ui->tableWidget->currentRow();
    // 删除
    ui->tableWidget->removeRow(row);
}


void Widget::on_pushButton_add_Column_clicked()
{
    // 获取目前列号
    int column = ui->tableWidget->columnCount();
    // 增加列
    ui->tableWidget->insertColumn(column);
    // 写入列的名字
    ui->tableWidget->setHorizontalHeaderItem(column,new QTableWidgetItem(ui->lineEdit->text()));
}


void Widget::on_pushButton_Delete_Column_clicked()
{
    // 获得选中的列号
    int column = ui->tableWidget->currentColumn();
    // 删除
    ui->tableWidget->removeColumn(column);
}

效果:

Tree Widget

使⽤QTreeWidget 表示⼀个树形控件。⾥⾯的每个元素,都是⼀个QTreeWidgetItem ,每个QTreeWidgetItem 可以包含多个⽂本和图标。

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) 设置TreeWidget的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 持有的⽂本
setText 设置文本
textAlignment ⽂本对⻬⽅式
icon 持有的图表
font ⽂本字体
hidden 是否隐藏
disabled 是否禁⽤
expand 是否展开
sizeHint 尺⼨⼤⼩
selected 是否选中

这里注意,setText(int column, const QString &text)column是列号。(是该结点的父节点往右数,第几列,从0开始数的)

QTreeWidgetItem核⼼⽅法:

⽅法 说明
addChild(QTreeWidgetItem* child) 新增⼦节点
childCount() ⼦节点的个数
child(int index) 获取指定下标的⼦节点.返回QTreeWidgetItem*
takeChild(int index) 删除对应下标的⼦节点
removeChild(QTreeWidgetItem* child) 删除对应的⼦节点
parent() 获取该元素的⽗节点

界面:

代码:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

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* item3 = new QTreeWidgetItem();
    item3->setText(0,"龙猫");
    item1->addChild(item3);

    // 增加顶层节点
    QTreeWidgetItem* item2 = new QTreeWidgetItem();
    item2->setText(0,"鹿");
    ui->treeWidget->addTopLevelItem(item2);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_add_top_clicked()
{
    // 获取输入文本
    const QString& message = ui->lineEdit->text();
    QTreeWidgetItem* item = new QTreeWidgetItem();
    item->setText(0,message);
    ui->treeWidget->addTopLevelItem(item);
}


void Widget::on_pushButton_add_select_clicked()
{
    QTreeWidgetItem* Current_item = ui->treeWidget->currentItem();
    if(Current_item == NULL){
        return;
    }
    // 获取输入文本
    const QString& message = ui->lineEdit->text();
    QTreeWidgetItem* item = new QTreeWidgetItem();
    item->setText(0,message);
    Current_item->addChild(item);
}


void Widget::on_pushButton_delete_clicked()
{
    QTreeWidgetItem* current_item = ui->treeWidget->currentItem();
    if(current_item == NULL){
        return;
    }
    // 删除选中的元素,需要找到父亲节点
    QTreeWidgetItem* item_parent = current_item->parent();
    if(item_parent == NULL){
        // 头节点
        int index = ui->treeWidget->indexOfTopLevelItem(current_item);
        ui->treeWidget->takeTopLevelItem(index);
    } else {
        // 普通节点
        item_parent->removeChild(current_item);
    }
}

容器类控件

GroupBox 分组框

使⽤QGroupBox 实现⼀个带有标题的分组框。可以把其他的控件放到⾥⾯作为⼀组。这样看起来能更好看⼀点。

核⼼属性:

属性 说明
title 分组框的标题
alignment 分组框内部内容的对⻬⽅式
flat 是否是"扁平"模式
checkable 是否可选择. 设为true,则在title前⽅会多出⼀个可勾选的部分.
checked 描述分组框的选择状态(前提是checkable为true)

界面操作:麦当劳点餐

可以看到,comboBox 和 spinBox 是 groupBox 的子元素:

QTableWidget 标签页

使⽤QTabWidget 实现⼀个带有标签⻚的控件,可以往⾥⾯添加⼀些widget.进⼀步的就可以通过标签⻚来切换。

属性 说明
tabPosition 标签⻚所在的位置。 North 上⽅ South 下⽅ West 左侧 East 右侧
currentIndex 当前选中了第⼏个标签⻚(从0开始计算)
currentTabText 当前选中的标签⻚的⽂本
currentTabName 当前选中的标签⻚的名字
currentTabIcon 当前选中的标签⻚的图标
currentTabToolTip 当前选中的标签⻚的提⽰信息
tabsCloseable 标签⻚是否可以关闭
movable 标签⻚是否可以移动
addTab 创建新的标签页
count 标签页的个数
removeTab 删除新的标签页

核⼼信号:

属性 说明
currentChanged(int) 在标签⻚发⽣切换时触发,参数为被点击的选项卡编号.
tabBarClicked(int) 在点击选项卡的标签条的时候触发.参数为被点击的选项卡编号.
tabBarDoubleClicked(int) 在双击选项卡的标签条的时候触发.参数为被点击的选项卡编号.
tabCloseRequest(int) 在标签⻚关闭时触发.参数为被关闭的选项卡编号.

代码:创建一个程序,带有一个QTableWidget作为标签页。提供两个按钮,创建于关闭标签页。

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#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(50,100);

    QLabel* label2 = new QLabel(ui->tab_2);
    label2->setText("标签 2");
    label2->resize(50,100);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    // 添加标签
    QWidget* w = new QWidget();
    int count = ui->tabWidget->count(); //获得标签页的个数
    const QString& name = QString("标签") + QString::number(count + 1);
    ui->tabWidget->addTab(w,name);
    QLabel* label = new QLabel(w);
    label->setText(QString("标签页" + QString::number(count + 1)));
    label->resize(50,100);
}


void Widget::on_pushButton_2_clicked()
{
    // 获取当前标签页
    int index = ui->tabWidget->currentIndex();
    // 删除
    ui->tabWidget->removeTab(index);
}

布局管理器

之前使⽤Qt在界⾯上创建的控件,都是通过"绝对定位"的⽅式来设定的。也就是每个控件所在的位置,都需要计算坐标,最终通过setGeometry 或者move ⽅式摆放过去。

这就造成了:

  1. 手动布局的方式非常复杂,不精确。
  2. 无法对窗口的大小进行自适应。

垂直布局

使⽤QVBoxLayout 表示垂直的布局管理器。V是vertical 的缩写。

核⼼属性:

属性 说明
layoutLeftMargin 左侧边距
layoutRightMargin 右侧边距
layoutTopMargin 上⽅边距
layoutBottomMargin 下⽅边距
layoutSpacing 相邻元素之间的间距

代码:创建三个按钮,使用垂直布局管理起来

首先,函数他有"认父"功能:

比如:并没有按钮的显示,这是因为没有设置父节点,通俗来讲QPushButton* button1 = new QPushButton(this);

addWidget

首先:

cpp 复制代码
layout->addWidget(button1);
// 布局的内部列表中添加了 button1 的记录
// 布局现在"知道"要管理这个按钮
// 但按钮与布局都还不知道自己的父对象是谁

然后:

cpp 复制代码
this->setLayout(layout);  //this(Widget)的布局为layout
// 此时自动发生:
layout->setParent(this);  // 布局成为窗口的孩子

// 并且递归设置所有子控件:
// - button1->setParent(this)
// - button2->setParent(this)
// - button3->setParent(this)

递归后:

布局开始真正工作:

  • 计算所有控件的位置
  • 分配控件的大小
  • 响应窗口大小变化
  • 管理控件的显示/隐藏

我们有时候会用到:

cpp 复制代码
window->setLayout(layout);
// window,从现在开始,layout 就是你的布局主管了

最后我们要知道QWidget是显现能力,而QLayout提供布局能力。QLayout不能显现,只能由QWidget提供布局,所以要把按钮放在Widget上。

每个Widget 只能设置一个布局管理器。

如果在代码中创建一个layout,只是创建了一个layout。

如果在QtDesigner 中创建的layout,先创建一个 Widget,然后在这个新的Widget中添加一个layout:

刚才是先拖了 layout 过去,然后再往 layout 中拖其它控件。

也可以先拖其它控件,后给这些控件套上 layout:

水平布局

使⽤QHBoxLayout 表⽰垂直的布局管理器。H是horizontal 的缩写。

核⼼属性(和QVBoxLayout 属性是⼀致的)

代码:

布局管理器之间,也能嵌套:

网格布局

Qt 中还提供了核⼼属性QGridLayout ⽤来实现⽹格布局的效果.可以达到M*N的这种⽹格的效果。

核⼼属性整体和QVBoxLayout 以及QHBoxLayout 相似。但是设置spacing的时候是按照垂直⽔平两个⽅向来设置的:

属性 说明
layoutLeftMargin 左侧边距
layoutRightMargin 右侧边距
layoutTopMargin 上⽅边距
layoutBottomMargin 下⽅边距
layoutHorizontalSpacing 相邻元素之间⽔平⽅向的间距
layoutVerticalSpacing 相邻元素之间垂直⽅向的间距
layoutRowStretch ⾏⽅向的拉伸系数
layoutColumnStretch 列⽅向的拉伸系数

我们可以看到按钮5虽然是(100,100)但和按钮4没有拉的很远,说明此处设置的行数和列数,只是用来决定控件之间的相对位置。

以上创建的布局管理器,控件尺寸都是均等的。

当我们想要创建出尺寸不同的控件时,可以通过拉伸系数来设置(控件之间尺寸的比例)。

函数:

cpp 复制代码
layout->setColumnStretch(第几列,伸拉系数);

这里设置的比列是1:1:2:

如果伸拉系数为0,则会出现:无论如何拖动,第一列大小不变

如果想调整上下拉伸:发现并没有变化

是因为SizePolicy 起到的影响,按钮垂直方向默认没有拉伸开(水平方向默认是拉伸的),因此垂直方向不会受到拉伸系数的影响。要想让垂直方向的拉伸系数生效,就需要让按钮先能拉伸展开。

SizePolicy是QWidget的属性:

  • QSizePolicy::Ignored :忽略控件的尺⼨,不对布局产⽣影响。
  • QSizePolicy::Minimum :控件的最⼩尺⼨为固定值,布局时不会超过该值。
  • QSizePolicy::Maximum :控件的最⼤尺⼨为固定值,布局时不会⼩于该值。
  • QSizePolicy::Preferred :控件的理想尺⼨为固定值,布局时会尽量接近该值。
  • QSizePolicy::Expanding :控件的尺⼨可以根据空间调整,尽可能占据更多空间。
  • QSizePolicy::Shrinking :控件的尺⼨可以根据空间调整,尽可能缩⼩以适应空间。

设置可拉伸:

表单布局

除了上述的布局管理器之外,Qt还提供了门⽤于实现两列表单的布局。这QFormLayout ,属于是QGridLayout 的特殊情况。(只许N行2列)

这种表单布局多⽤于让⽤⼾填写信息的场景.左侧列为提示,右侧列为输⼊框。

示例:

Spacer

使⽤布局管理器的时候,可能需要在控件之间,添加⼀段空⽩。就可以使⽤QSpacerItem来表示。

比如:默认就用空白

现在想把空白变大:

相关推荐
bubiyoushang8881 小时前
OFDM系统信道估计MATLAB实现(LS、MMSE、DCT、LRMMSE方法)
开发语言·网络·matlab
Felven1 小时前
C. Dora and Search
c语言·开发语言
John Song3 小时前
Python创建虚拟环境的方式对比与区别?
开发语言·python
搞程序的心海3 小时前
Python面试题(一):5个最常见的Python基础问题
开发语言·python
MediaTea10 小时前
Python:collections.Counter 常用函数及应用
开发语言·python
LawrenceLan10 小时前
37.Flutter 零基础入门(三十七):SnackBar 与提示信息 —— 页面反馈与用户交互必学
开发语言·前端·flutter·dart
李昊哲小课10 小时前
Python json模块完整教程
开发语言·python·json