Удаление виджетов из QGridLayout - PullRequest
11 голосов
/ 22 марта 2011

Я пытаюсь удалить виджеты из указанной строки в QGridLayout следующим образом:

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

Но когда я вызываю его, приложение падает с SIGSEGV на delete item в первой итерации.Есть идеи?

Ответы [ 2 ]

22 голосов
/ 08 октября 2013

Краткий ответ: используйте код, указанный ниже

Удалить строку или столбец (или даже одну ячейку) из QGridLayout сложно. Используйте код, указанный ниже.

Длинный ответ: копание в деталях QGridLayout

Во-первых, обратите внимание, что QGridLayout::rowCount() и QGridLayout::columnCount() всегда возвращают количество внутренних и выделенных строк и столбцов в сетке. Например, если вы вызовете QGridLayout::addWidget(widget,5,7) в только что построенном макете сетки, количество строк будет равно 6, а количество столбцов будет равно 8, и все ячейки макета сетки, кроме ячейки с индексом (5,7), будут иметь вид пустой и, следовательно, невидимый в графическом интерфейсе.

Обратите внимание, что, к сожалению, невозможно удалить такую ​​внутреннюю строку или столбец из компоновки сетки. Другими словами, количество строк и столбцов макета сетки всегда может только увеличиваться, но никогда не уменьшаться.

Что вы можете сделать - это удалить содержимое строки или столбца, что будет иметь тот же визуальный эффект, что и удаление самой строки или столбца. Но это, конечно, означает, что все количество строк и столбцов и индексы будут оставаться неизменными .

Так как же очистить содержимое строки или столбца (или ячейки)? К сожалению, это не так просто, как может показаться.

Во-первых, вам нужно подумать, хотите ли вы только удалить виджеты из макета или хотите, чтобы они стали удалены . Если вы удаляете только виджеты из макета, вы должны впоследствии вернуть их в другой макет или вручную придать им разумную геометрию. Если виджеты также будут удалены, они исчезнут из графического интерфейса. Предоставленный код использует логический параметр для управления удалением виджета.

Далее необходимо учитывать, что ячейка макета может содержать не только виджет, но также вложенный макет , который сам может содержать вложенные макеты и т. Д. Кроме того, вам нужно обрабатывать элементы макета, которые охватывают несколько строк и столбцов . И, наконец, есть некоторые атрибуты строки и столбца , такие как минимальная ширина и высота, которые не зависят от фактического содержимого, но о которых нужно позаботиться.

код

#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();
  }
};
3 голосов
/ 22 марта 2011

QGridLayout сам управляет QLayoutItem.Я полагаю, что в тот момент, когда вы позвоните removeWidget, элемент будет удален.Таким образом, у вас есть неверный указатель в этой точке.Попытка сделать что-нибудь с этим, не только delete, потерпит неудачу.

Таким образом, просто не удаляйте это, все будет в порядке.

...