Dynamic addition and deletion of Qt controls in the layout (QFormLayout, QGridLayout, etc.)

Project scenario:

In the project development process, such as the display of alarm information, if all the alarm information is added to the layout, and then the alarm is hidden and displayed by the control, this method is undoubtedly the simplest, but if there are too many types of alarms , but it often needs to be displayed very little. It is too troublesome to drag controls or add codes one by one in the interface, which requires dynamic addition and deletion!


Problem Description:

Take the following picture as an example:
because the battery cell is not fixed, it may be 8 cells or 16 cells, so parts 1, 2, and 3 are all dynamically updated.
And both use the groupbox container, the difference is that 1 and 2 place the formlayout layout, and 3 place the gridlayout layout.

battery picture


code:

Dynamic addition
Adding code is very simple, just add as needed:
void QFormLayout::addRow(const QString &labelText, QWidget *field)

void QGridLayout::addWidget(QWidget *widget, int row, int column, Qt::Alignment alignment = Qt::Alignment())
However, the controls in the layout must be deleted before adding each time, and it is very easy to make mistakes during the operation .

delete dynamically

  • QFormLayout:
  1. Method 1: (recommended)

There are two functions in QFormLayout

void QFormLayout::removeRow(int row)
//Delete and delete the corresponding widgets from the layout without risk of memory leak

QFormLayout::TakeRowResult QFormLayout::takeRow(int row)
//Remove from the layout and return the corresponding widgets, there is a risk of memory leaks

We must use the removeRow method here.
The following is the wrong usage

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

Cause of error: ui->formLayout->rowCount() is dynamically updated
Correction:

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

or

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

Or the last line starts to delete! ! ! ! !

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

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

ui->formLayout->This can be compared to the dynamic update of tablewidget data~

  1. Method 2:
    Delete through 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. Method 3: Delete
    by searching the sub-controls of the Groupbox container . Yes, this is an error-prone place. Do not search for the sub-controls of the formLayout layout . After testing, this will not have the deletion effect (compared to the effect presented by QGridLayout method 3. ).
    For the reason, refer to the explanation in the forum:
    Layout requires a QWidget as a container, and all controls added to the layout are child controls of the corresponding container, so it is the most appropriate method to find child controls from the container and delete them. You originally deleted QLayoutItem, not QPushButton, so there is a memory leak, that is to say, you just removed QPushButton from the layout, so it is not displayed, but it does not mean that this QPushButton is completely deleted
  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 does not have the removeRow method in QFormLayout, so it provides two methods:
    1. Delete through 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. Delete by finding the child controls of the Groupbox container

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

If you look for the deletion of the child controls of the formLayout layout , there will only be a deletion effect (this is different from the QFormLayout rendering effect above) , but there is no real deletion. Check the memory usage and you will find that the memory leaks!
For the reason, refer to the explanation in the forum:
Layout requires a QWidget as a container, and all controls added to the layout are child controls of the corresponding container, so it is the most appropriate method to find child controls from the container and delete them. You originally deleted QLayoutItem, not QPushButton, so there is a memory leak, that is to say, you just removed QPushButton from the layout, so it is not displayed, but it does not mean that this QPushButton is completely deleted


Summarized as follows:

1. QFormLayout dynamically deletes controls, please pay attention to the scope of the loop body, not variable;
2. When deleting through QLayoutItem, you must remember to setParent(0);
3. By searching for the control class, remember not to search (layout ) The controls in the layout must be controls in the container (widgets)!

Reference article: http://www.qtcn.org/bbs/read-htm-tid-33202.html

Guess you like

Origin blog.csdn.net/qq_41750806/article/details/120220109