Как заставить макет обновлять/изменять размер, когда ребенок управляет изменениями размера?

У меня есть пользовательский виджет QTextEdit, у которого в углу есть собственный размер. Я могу изменить размер текстового виджета, но макет, который он внутри, не изменяется при изменении размера текста.

Есть ли способ сообщить управляющему макету пересчитать себя на основе независимого изменения размера в одном из своих виджетов?

Ответ 1

Макет соответствует sizeHint() и minimalSizeHint() его дочерних элементов (а не их size()). Поэтому вы должны переопределить эти два виртуальных метода и вернуть размер, который хотите заставить. Всякий раз, когда меняется подсказка размера, , вы должны вызвать updateGeometry() в своем виджете, чтобы вызвать обновление макета, в котором размещен виджет.

См. документацию:

  • virtual QSize QWidget::sizeHint() const

    Это свойство содержит рекомендуемый размер для виджета.
    [...]

  • virtual QSize QWidget::minimumSizeHint() const

    Это свойство содержит рекомендуемый минимальный размер для виджета.
    [...]
    QLayout никогда не будет изменять размер виджета до размера, меньшего, чем подсказка минимального размера, если установлено значение minimumSize() или для политики размера задано значение QSizePolicy:: Ignore. Если установлено minimumSize(), подсказка минимального размера будет проигнорирована.

Итак, чтобы установить минимальный размер вашего виджета, выполните minimumSizeHint(), вернув минимальный размер; до принудительного фиксированного размера, реализуйте оба путем возврата одного и того же размера.

Я также рекомендую прочитать следующий раздел на странице документации "Управление макетами" :

Пользовательские виджеты в макетах

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

  • Повторите QWidget:: sizeHint(), чтобы вернуть предпочтительный размер виджета.
  • Повторить QWidget:: minimumSizeHint(), чтобы вернуть наименьший размер, который может иметь виджете.
  • Вызовите QWidget:: setSizePolicy(), чтобы указать требования к пространству виджета.

Вызовите QWidget:: updateGeometry(), когда меняются подсказки размера, политики подсказок минимального размера или размера. Это приведет к перерасчету компоновки. Несколько последовательных вызовов QWidget:: updateGeometry() будут вызывать только один пересчет макета.


Обновить (см. комментарии):

Вы также можете форсировать размер (или только высоту/ширину), вызывая:

QWidget::setFixedSize(QSize);
QWidget::setFixedHeight(int);
QWidget::setFixedWidth(int);

Эти методы просто устанавливают подсказки размера для данного значения и вызывают updateGeometry() AFAIK. Но, возможно, они немного улучшились с тех пор (как видно из комментариев), это был единственный способ найти проблему здесь.

Ответ 2

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

Необходимо было переопределить метод sizeHint() виджета:

QSize MultilineTextEdit::sizeHint() const
{
    return size();
}

Затем в конце mouseMoveEvent() моего виджета SizeGrip я добавил

void SizeGrip::mouseMoveEvent(QMouseEvent * e)
{
    // note that this is not the full code of the method
    ...
    // save the old minimum and maximum size of the widget (w) resized by the grip
    QSize oldMin = w->minimumSize();
    QSize oldMax = w->maximumSize();

    w->setFixedSize(nr.size()); // nr is the new rect of the widget

    w->setMinimumSize(oldMin);
    w->setMaximumSize(oldMax);
}

Виджет SizeGrip был вдохновлен классом QSizeGrip, который работает непосредственно с родительским окном верхнего уровня. Вместо этого SizeGrip работает со своим прямым родительским виджетом.