从 QGridLayout 中删除小部件

2021-12-09 00:00:00 qt qgridlayout c++

我尝试从 QGridLayout 中的指定行中删除小部件,如下所示:

I try to remove widgets from a specified row in a QGridLayout like this:

void delete_grid_row(QGridLayout *layout, int row)
{
    if (!layout || row < 0) return;

    for (int i = 0; i < layout->columnCount(); ++i) {
        QLayoutItem* item = layout->itemAtPosition(row, i);
        if (!item) continue;

        if (item->widget()) {
            layout->removeWidget(item->widget());
        } else {
            layout->removeItem(item);
        }
        delete item;
    }
}

但是当我调用它时,应用程序在第一次迭代中在 delete item 上崩溃并显示 SIGSEGV.有什么想法吗?

But when I call it, the app crashes with SIGSEGV on delete item in the first iteration. Any ideas?

推荐答案

简短回答:使用下面提供的代码

QGridLayout 中删除一行或一列(甚至单个单元格)是很棘手的.使用下面提供的代码.

Short answer: Use the code provided below

Removing a row or column (or even a single cell) from a QGridLayout is tricky. Use the code provided below.

首先,请注意QGridLayout::rowCount()QGridLayout::columnCount() 总是返回内部分配 的行数和网格布局中的列.例如,如果您在新构建的网格布局上调用 QGridLayout::addWidget(widget,5,7),则行数将为 6,列数将为 8,并且所有单元格除了索引 (5,7) 上的单元格之外的网格布局将为空,因此在 GUI 中不可见.

First, note that QGridLayout::rowCount() and QGridLayout::columnCount() always return the number of internally allocated rows and columns in the grid layout. As an example, if you call QGridLayout::addWidget(widget,5,7) on a freshly constructed grid layout, the row count will be 6 and the column count will be 8, and all cells of the grid layout except the cell on index (5,7) will be empty and thus invisible within the GUI.

请注意,很遗憾不可能从网格布局中删除这样的内部行或列.换句话说,网格布局的行数和列数总是只会增长,而不会缩小.

Note that it's unfortunately impossible to remove such an internal row or column from the grid layout. In other words, the row and column count of a grid layout can always only grow, but never shrink.

您可以做的是删除行或列的内容,这将有效地具有与删除行或列本身相同的视觉效果.但这当然意味着所有行和列计数和索引将保持不变.

What you can do is to remove the contents of a row or column, which will effectively have the same visual effect as removing the row or column itself. But this of course means that all row and column counts and indices will remain unchanged.

那么如何清除一行或一列(或单元格)的内容呢?不幸的是,这也并不像看起来那么容易.

So how can the contents of a row or column (or cell) be cleared? This unfortunately also isn't as easy as it might seem.

首先,您需要考虑您是否只想从布局中删除小部件,或者您是否还希望它们被删除.如果您只从布局中删除小部件,则必须在之后将它们放回不同的布局中,或者手动为它们提供合理的几何形状.如果小部件也被删除,它们将从 GUI 中消失.提供的代码使用布尔参数来控制小部件删除.

First, you need to think about if you only want to remove the widgets from the layout, or if you also want them to become deleted. If you only remove the widgets from the layout, you must put them back into a different layout afterwards or manually give them a reasonable geometry. If the widgets also become deleted, they will disappear from the GUI. The provided code uses a boolean parameter to control widget deletion.

接下来,您必须考虑到,布局单元不仅可以包含小部件,还可以包含嵌套布局,它本身可以包含嵌套布局,等等.您还需要处理跨越多行和多列的布局项.最后,还有一些行和列的属性,比如最小宽度和高度,它们不依赖于实际内容,但仍然需要注意.

Next, you have to consider that a layout cell can not just only contain a widget, but also a nested layout, which itself can contain nested layouts, and so on. You further need to handle layout items which span over multiple rows and columns. And, finally, there are some row and column attributes like minimum widths and heights which don't depend on the actual contents but still have to be taken care of.

#include <QGridLayout>
#include <QWidget>

/**
 * Utility class to remove the contents of a QGridLayout row, column or
 * cell. If the deleteWidgets parameter is true, then the widgets become
 * not only removed from the layout, but also deleted. Note that we won't
 * actually remove any row or column itself from the layout, as this isn't
 * possible. So the rowCount() and columnCount() will always stay the same,
 * but the contents of the row, column or cell will be removed.
 */
class GridLayoutUtil {

public:

  // Removes the contents of the given layout row.
  static void removeRow(QGridLayout *layout, int row, bool deleteWidgets = true) {
    remove(layout, row, -1, deleteWidgets);
    layout->setRowMinimumHeight(row, 0);
    layout->setRowStretch(row, 0);
  }

  // Removes the contents of the given layout column.
  static void removeColumn(QGridLayout *layout, int column, bool deleteWidgets = true) {
    remove(layout, -1, column, deleteWidgets);
    layout->setColumnMinimumWidth(column, 0);
    layout->setColumnStretch(column, 0);
  }

  // Removes the contents of the given layout cell.
  static void removeCell(QGridLayout *layout, int row, int column, bool deleteWidgets = true) {
    remove(layout, row, column, deleteWidgets);
  }

private:

  // Removes all layout items which span the given row and column.
  static void remove(QGridLayout *layout, int row, int column, bool deleteWidgets) {
    // We avoid usage of QGridLayout::itemAtPosition() here to improve performance.
    for (int i = layout->count() - 1; i >= 0; i--) {
      int r, c, rs, cs;
      layout->getItemPosition(i, &r, &c, &rs, &cs);
      if (
          (row == -1 || (r <= row && r + rs > row)) &&
          (column == -1 || (c <= column && c + cs > column))) {
        // This layout item is subject to deletion.
        QLayoutItem *item = layout->takeAt(i);
        if (deleteWidgets) {
          deleteChildWidgets(item);
        }
        delete item;
      }
    }
  }

  // Deletes all child widgets of the given layout item.
  static void deleteChildWidgets(QLayoutItem *item) {
    QLayout *layout = item->layout();
    if (layout) {
      // Process all child items recursively.
      int itemCount = layout->count();
      for (int i = 0; i < itemCount; i++) {
        deleteChildWidgets(layout->itemAt(i));
      }
    }
    delete item->widget();
  }
};

相关文章