Qt布局管理器之表单布局(QFormLayout)

QFormLayout顾名思义,主要用来做表单的输入及显示。和前面讲到的QBoxLayout、QGridLayout类似,QFormLayout可以插入widget、layout,直接用QGridLayout也可以做出QFormLayout的效果。

下面开始介绍QFormLayout用作表单应用时相关的接口函数。

基本方法

在末尾新增

左侧添加标签,右侧添加widget或layout

void addRow(QWidget *label, QWidget *field)
void addRow(QWidget *label, QLayout *field)

一般,左侧添加一个QLabel用来显示文本信息,右侧增加一个QLineEdit用来给用户输入。

左侧使用QFormLayout自带标签,右侧增加widget或layout

void addRow(const QString &labelText, QWidget *field)
void addRow(const QString &labelText, QLayout *field)

针对上面提到的接口函数,QFormLayout提供了一种更为便利的方法,该方法直接使用QFormLayout内部的QLabel,labelText所传参数为左侧label所显示的内容。

跨越两列增加widget或layout

void addRow(QWidget *widget)
void addRow(QLayout *layout)

该方法直接在末尾添加一个横跨两列的widget或layout,比如在末尾添加一个含有ok和clear按钮的layout。

在指定位置新增

void insertRow(int row, QWidget *label, QWidget *field)
void insertRow(int row, QWidget *label, QLayout *field)
void insertRow(int row, const QString &labelText, QWidget *field)
void insertRow(int row, const QString &labelText, QLayout *field)
void insertRow(int row, QWidget *widget)
void insertRow(int row, QLayout *layout)

只不过加了一个行号,其他含义和用法和上面一样,这里就不分条介绍了。

删除

删除某一行

void removeRow(int row)

删除某一widget或layout

void removeRow(QWidget *widget)
void removeRow(QLayout *layout)

获取

QLayoutItem *itemAt(int row, QFormLayout::ItemRole role) const

返回具有指定角色(列)的给定行中的布局项。如果没有这样的项,则返回nullptr。

QFormLayout::ItemRole参数列表:

QFormLayout::LabelRole    A label widget.
QFormLayout::FieldRole    A field widget.
QFormLayout::SpanningRole    A widget that spans label and field columns.

这里其实和前面add或insert时对应的label或field对应。

演示表单提交的代码

界面编写:

自定义Widget类的构造函数代码

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    //创建表单布局
    QFormLayout *flay = new QFormLayout();

    //创建QLabel作为表单标题显示
    QLabel *title = new QLabel("学生信息录入");
    //居中显示
    title->setAlignment(Qt::AlignHCenter);
    //设置字体大小
    title->setStyleSheet("font-size:20px");
    //放置标题到最前面
    flay->addRow(title);

    QLineEdit *nameLineEdit = new QLineEdit();
    //添加name
    flay->addRow("&Name:", nameLineEdit);

    //添加email
    QLineEdit *emailLineEdit = new QLineEdit();
    flay->addRow("&Email:", emailLineEdit);

    //添加age
    QSpinBox *ageSpinBox = new QSpinBox();
    flay->addRow("&Age:", ageSpinBox);

    //在最底部增加两个按钮
    QHBoxLayout *hlay = new QHBoxLayout();
    QPushButton *btOk = new QPushButton("ok");
    QPushButton *btClear = new QPushButton("clear");
    hlay->addWidget(btOk);
    hlay->addWidget(btClear);
    flay->addRow(hlay);

    //设置widget的主布局
    this->setLayout(flay);

}

main函数代码

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    Widget w;
    w.show();

    return a.exec();
}

运行效果:

其中"&Age:"、"&Email:"、"&Name:",前面都有加一个'&'符号,其目的是可以用快捷键"ALT+首字母"将光标切换到目标行进行输入。


表单的提交

 所谓表单的提交就是在用户按下ok按钮时,拿到用户输入的各项数据,并作出相应的处理。

既然和按钮按下有关,那就要先绑定按钮按下事件。

在widget构造函数中新增以下代码,用于信号与槽的绑定

connect(btOk,SIGNAL(clicked()),this,SLOT(OnOkBtClicked()));

槽函数的具体实现

void Widget::OnOkBtClicked()
{
    //得到QFormLayout指针
    QFormLayout *flay = static_cast<QFormLayout *>(this->layout());
    if(!flay)
    {
        qDebug() << "Get layout failed!";
        return;
    }
    //用来收集错误信息
    QString errCode = "";
    //用来收集正确的信息
    QString inputMessage = "";
    //遍历所有行,逐个取出所需内容
    for(int i = 0 ; i < flay->rowCount() ; i++)
    {
        //获取label
        QLayoutItem *itemLab = flay->itemAt(i, QFormLayout::LabelRole);
        if(!itemLab)
            continue;

        QLabel *lab = static_cast<QLabel*>(itemLab->widget());
        if(!lab)
            continue;

        //获取内容输入框
        QLayoutItem *item = flay->itemAt(i, QFormLayout::FieldRole);
        if(!item)
            continue;

        //获取widget
        QWidget *widget = item->widget();
        if(!widget)
            continue;

        //取到label的文字
        QString labelText = lab->text();
        //去掉前面的'&'
        if(labelText[0] == '&')
            labelText.remove(0,1);

        //widget类型为QLineEdit
        if(QString(widget->metaObject()->className()) == "QLineEdit")
        {
            //强制转换
            QLineEdit *edit = static_cast<QLineEdit*>(widget);
            if(!edit)
                continue;
            //如果是空的,证明此项没有输入,增加错误信息
            if(edit->text().isEmpty())
            {
                errCode += labelText + "null\n";
                continue;
            }
            //放到正确信息里
            inputMessage += labelText + edit->text() + "\n";
        }
        else if(QString(widget->metaObject()->className()) == "QSpinBox")
        {
            QSpinBox *spinbox = static_cast<QSpinBox*>(widget);
            if(!spinbox)
                continue;
            if(spinbox->text().isEmpty())
            {
                errCode += labelText + "null\n";
                continue;
            }

            inputMessage += labelText + spinbox->text() + "\n";
        }
        else {
            continue;
        }

    }
    //弹出MessageBox提示成功或失败
    if(!errCode.isEmpty())
        QMessageBox::warning(this,"Input Error",errCode);
    else {
        QMessageBox::information(this,"Input OK",inputMessage);
    }
}

整个代码的功能是逐行获取用户输入,如果发现输入为空时,弹出警告窗口,提醒用户哪些项为空。如果程序成功获取到用户输入的所有内容时,弹出information窗口,显示出用户的输入。

其中的关键点是itemAt的使用,当想要获取左侧label时,需要给它的第二个参数传递QFormLayout::LabelRole。当想要获取右侧的用户输入时,需要给它的第二个参数传递QFormLayout::FieldRole。除此之外,还要做类型的强转、空指针的判断、widget对应的ClassName的获取,这些缺一不可。本程序中没有用到任何全局可访问的变量,全部通过动态获取的方式获取目标指针。

程序运行结果:

正确输入

有空项时:

 

 


版权声明:本文为qq_14945437原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。