Qt控件在布局内(QFormLayout、QGridLayout等)的动态添加与删除

项目场景:

在项目开发过程中,比如报警信息的显示,如果将所有的报警信息都添加至布局内,再以控件隐藏与显示的方式进行报警,这种方法无疑是最简单的,但是如果报警种类过多,但往往需要显示的很少,在界面里面去一个个拖控件或者代码添加都太麻烦,这就需要动态添加与删除了!


问题描述:

就以下图为例吧:
因为电池电芯不固定,可能是8芯可能是16芯,所以1、2、3部分都是动态更新的。
且都使用了groupbox容器,不同的是1、2放置formlayout布局,3放置gridlayout布局。

电池图片


代码:

动态添加
添加代码很简单,按需要添加即可:
void QFormLayout::addRow(const QString &labelText, QWidget *field)

void QGridLayout::addWidget(QWidget *widget, int row, int column, Qt::Alignment alignment = Qt::Alignment())
但是每次添加前必须将布局里面的控件删除,在操作过程中发现极易出错。

动态删除

  • QFormLayout:
  1. 方法一:(推荐)

QFormLayout里面有两个函数

void QFormLayout::removeRow(int row)
//从布局中删除并删除相应的widgets 无内存泄漏危险

QFormLayout::TakeRowResult QFormLayout::takeRow(int row)
//从布局中删除并返回相应的widgets 有内存泄漏危险

我们这里肯定采用removeRow方法了
下面是错误用法

  if(ui->formLayout->rowCount())
    {
    
    
        for (int i = 0; i <= ui->formLayout->rowCount(); ++i )
        {
    
    
            ui->formLayout->removeRow(0);
        }
    }

出错原因:ui->formLayout->rowCount()是动态更新的
更正:

	int count = ui->formLayout->rowCount();
    if(count)
    {
    
    
        for (int i = 0; i <= count; ++i )
        {
    
    
            ui->formLayout->removeRow(0);
        }
    }

或者

 while (ui->formLayout->rowCount())
    {
    
    
        ui->formLayout->removeRow(0);
    }

或者最后一行开始删除!!!!!

    if(ui->formLayout->rowCount())
    {
    
    
        for (int i = ui->formLayout->rowCount() - 1; i >= 0; --i )
        {
    
    

            ui->formLayout->removeRow(i);
        }
    }

ui->formLayout->这可以类比到tablewidget的数据动态更新~

  1. 方法二:
    通过QLayoutItem删除
    QLayoutItem* item;
    while((item = ui->formLayout_2->takeAt(0)) != 0)
    {
    
    
        if(item->widget())
        {
    
    
            ui->formLayout_2->removeWidget(item->widget());
            item->widget()->setParent(0);
            delete item->widget();
        }
    }
  1. 方法三:
    通过查找Groupbox容器的子控件进行删除,没错,这是个容易错误的地方,不要查找formLayout布局的子控件,经测试,这样并不会有删除效果(对比QGridLayout方法3呈现的效果不同)。
    原因参考论坛中的解释:
    layout是需要一个QWidget作为容器的,所有加入layout中的控件都是对应容器的子控件,因此从容器中查找子控件,然后删除是最合适的方法。你原来删除的是QLayoutItem,不是QPushButton,所以有内存泄露,也就是说你只是把QPushButton从layout里面移除了,所以它不显示,但是并不代表这个QPushButton被完全删除了
  QList<QLabel*> all_labs = ui->groupBox_3->findChildren<QLabel*>();
  QList<QLineEdit*> all_leds = ui->groupBox_3->findChildren<QLineEdit*>();
    for (auto* lab : all_labs)
    {
    
    
        delete lab;
    }
    for (auto* led : all_leds)
    {
    
    
        delete led;
    }
  • QGridLayout:
    QGridLayout不像QFormLayout里有removeRow方法,所以提供两种方法:
    1、通过QLayoutItem删除
 	QLayoutItem* item;
    while((item = ui->gridLayout->takeAt(0)) != 0)
    {
    
    
        if(item->widget())
        {
    
    
            ui->gridLayout->removeWidget(item->widget());
            item->widget()->setParent(0);
            delete item->widget();
        }
    }

2、 通过查找Groupbox容器的子控件进行删除

    QList<QLabel*> all_labs = ui->groupBox->findChildren<QLabel*>();//注意不是gridLayout
    for (auto* lab : all_labs)    
    {
    
    
       delete lab;
    }

如果查找formLayout布局的子控件删除,则只会有删除效果(这与上面的QFormLayout呈现效果不同),但是并没有真正的删除,查看内存占用情况会发现,内存泄漏了!
原因参考论坛中的解释:
layout是需要一个QWidget作为容器的,所有加入layout中的控件都是对应容器的子控件,因此从容器中查找子控件,然后删除是最合适的方法。你原来删除的是QLayoutItem,不是QPushButton,所以有内存泄露,也就是说你只是把QPushButton从layout里面移除了,所以它不显示,但是并不代表这个QPushButton被完全删除了


总结如下:

1、QFormLayout动态删除控件,请注意循环体内的范围,不要是可变的;
2、通过QLayoutItem删除时,一定要记得setParent(0);
3、通过查找控件类的方法,记得不要查找的(layout)布局中的控件,一定要是容器(widgets)内的控件!

参考文章:http://www.qtcn.org/bbs/read-htm-tid-33202.html

猜你喜欢

转载自blog.csdn.net/qq_41750806/article/details/120220109