Подтвердить что ты не робот

Очистка макета в Qt

Я создаю приложение в Qt, которое позволяет пользователям перетаскивать различные "модули" в QGraphicsView. Всякий раз, когда один из этих модулей выбран, он выдает сигнал, который затем выбирается "ConfigurationWidget", который является боковой панелью, которая должна отображать все соответствующие параметры выбранного модуля:

class ConfigurationWidget : public QWidget
{
  Q_OBJECT

  public:
    ConfigurationWidget(QWidget *parent) : QWidget(parent) {}  

  public slots:
    void moduleSelected(Module* m)
    {
      if(layout())
      { 
        while (itsLayout->count()>0) 
        { 
          delete itsLayout->takeAt(0); 
        }
      }
      delete layout();

      itsLayout = new QFormLayout(this);
      itsLayout->addRow(QString(tr("Type:")),     new QLabel(m->name()));
      itsLayout->addRow(QString(tr("Instance:")), new QLabel(m->instanceID()));
      // ... Display a whole bunch of other fields that depends on the module
    }
};

Проблема заключается в том, что ConfigurationWidget никогда не очищается при выборе модуля. Новые поля просто нарисованы поверх старых. Я пробовал различные комбинации hide() и show(), invalidate(), update() и т.д. Безрезультатно.

Какой правильный способ сделать виджет, который может менять свои поля, как это на лету?

4b9b3361

Ответ 1

Похоже, что лучший способ сделать это - использовать QStackedLayout, как намекнул armonge:

void ConfigurationWidget::moduleSelectedSlot(Module* m)
{
  QStackedLayout *stackedLayout = qobject_cast<QStackedLayout*>(layout());

  QWidget *layoutWidget = new QWidget(this);
  QFormLayout *formLayout = new QFormLayout(layoutWidget);
  formLayout->addRow(QString(tr("Type:")),     new QLabel(m->name()));
  formLayout->addRow(QString(tr("Instance:")), new QLabel(m->instanceID()));
  // ... Display a whole bunch of other fields that depends on the module

  delete stackedLayout->currentWidget();
  stackedLayout->addWidget(layoutWidget);
  stackedLayout->setCurrentWidget(layoutWidget);
}

Ответ 2

Цикл кода, который я использовал ранее, выглядит следующим образом:

void clearLayout(QLayout *layout) {
    QLayoutItem *item;
    while((item = layout->takeAt(0))) {
        if (item->layout()) {
            clearLayout(item->layout());
            delete item->layout();
        }
        if (item->widget()) {
           delete item->widget();
        }
        delete item;
    }
}

Надеюсь, это будет полезно для вас!

Ответ 3

Если вы переносите макет на другой виджет, выделенный в стеке, виджеты в этом макете становятся дочерними элементами нового виджета. Когда временный объект выходит из области действия, он автоматически уничтожает макет и все его виджеты.

void moduleSelected(Module* m)
{
    if(layout())
        QWidget().setLayout(layout());

    itsLayout = new QFormLayout(this);
    itsLayout->addRow(QString(tr("Type:")),     new QLabel(m->name()));
    itsLayout->addRow(QString(tr("Instance:")), new QLabel(m->instanceID()));
    // ... Display a whole bunch of other fields that depends on the module
}

Ответ 4

Использование while((item = layout->takeAt(0))) приведет к предупреждению "Недопустимый индекс принимает значение 0". Вместо этого я использую count()

void clearLayout(QLayout *layout) 
{
    if (layout) {
        while(layout->count() > 0){
            QLayoutItem *item = layout->takeAt(0);
            QWidget* widget = item->widget();
            if(widget)
                delete widget;
            delete item;
        }
    }
}

Ответ 5

Недавно я столкнулся с той же проблемой с QFormLayout. Что сработало для меня (и также в документации Qt: http://qt-project.org/doc/qt-5/layout.html) был методом takeAt (int).

void clearLayout(QLayout *layout)
{
     QLayoutItem *item;
     while ((item = layout->takeAt(0)))
         delete item;
}

Ответ 6

Восстановите элементы до NULL, когда вы хотите, чтобы они исчезли из окна. Недавно у меня были подобные проблемы, и репарация решила их.

Ответ 7

Я не нашел ни одного другого ответа до сих пор работать. Они либо вылетают, либо не полностью очищают макет. Итак, вот что я нашел для работы, я надеюсь, что другие найдут это полезным.

void eraseLayout(QLayout * layout)
{
    while(layout->count() > 0)
    {
        QLayoutItem *item = layout->takeAt(0);

        QWidget* widget = item->widget();
        if(widget)
        {
            delete widget;
        }
        else
        {
            QLayout * layout = item->layout();
            if (layout)
            {
                eraseLayout(layout);
            }
            else
            {
                QSpacerItem * si = item->spacerItem();
                if (si)
                {
                    delete si;
                }
            }
        }
        delete item;
    }
}

Ответ 8

Благодаря комментарию Vaidotas Strazdas я нашел свое решение для правильного сброса макета. Использование deleteLater() работает очень хорошо, в противном случае ключевое слово delete вызывает массовые ошибки отображения в обновленном макете.

void resetLayout(QLayout* apLayout)
{
    QLayoutItem *vpItem;
    while ((vpItem = apLayout->takeAt(0)) != 0)  {
        if (vpItem->layout()) {
            resetLayout(vpItem->layout());
            vpItem->layout()->deleteLater();
        }
        if (vpItem->widget()) {
            vpItem->widget()->deleteLater();
        }
        delete vpItem;
    }
}