Qt: Как заставить скрытый виджет рассчитать свой макет?

Я пытаюсь сделать qwidget в другое окно (вручную используя QPainter)

У меня есть QWidget (w) с макетом и кучей дочерних элементов управления. w скрыт. Пока не будет показано w, не происходит никаких расчетов компоновки, что ожидается.

Когда я звоню w->render(painter, w->mapToGlobal(QPoint(0,0)), я получаю кучу элементов управления, которые перекрывают друг друга.
w->layout()->activate();w->layout()->update() ничего не делает.

Есть ли способ заставить макет произойти, не показывая w?

Ответы

Ответ 1

Принуждение вычисления компоновки виджета, не отображая его на экране:

widget->setAttribute(Qt::WA_DontShowOnScreen);
widget->show();

Вызов show() заставит вычислять макет, а Qt::WA_DontShowOnScreen гарантирует, что виджет явно не показан.

Ответ 2

Расчет макета виджета может быть принудительным, вызвав invalidate(), а затем activate() на его макете, даже если виджет скрыт. Это также приводит к тому, что функции виджетов size() и sizeHint() возвращают правильные и обновленные значения, даже если show() еще не был вызван этим виджетами.

Однако необходимо заботиться обо всех дочерних виджетах и ​​макетах рекурсивно, поскольку запрос перерасчета макета не распространяется автоматически на дочерние элементы.

В следующем коде показано, как это сделать.

/**
 * Forces the given widget to update, even if it hidden.
 */
void forceUpdate(QWidget *widget) {
    // Update all child widgets.
    for (int i = 0; i < widget->children().size(); i++) {
        QObject *child = widget->children()[i];
        if (child->isWidgetType()) {
            forceUpdate((QWidget *)child);
        }
    }

    // Invalidate the layout of the widget.
    if (widget->layout()) {
        invalidateLayout(widget->layout());
    }
}

/**
 * Helper function for forceUpdate(). Not self-sufficient!
 */
void invalidateLayout(QLayout *layout) {
    // Recompute the given layout and all its child layouts.
    for (int i = 0; i < layout->count(); i++) {
        QLayoutItem *item = layout->itemAt(i);
        if (item->layout()) {
            invalidateLayout(item->layout());
        } else {
            item->invalidate();
        }
    }
    layout->invalidate();
    layout->activate();
}

Ответ 3

Попробуйте использовать метод QWidget::sizeHint(), который должен возвращать размер виджета, который был выложен.

Ответ 4

Это работало для меня при использовании sizeHint() плюс перевод живописца, однако я делаю это внутри метода paint().

void ParentWidget::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
  painter->save();
  painter->translate(option.rect.topLeft());
  w->render(painter);
  painter->restore();
}

В этом случае option.rect.topLeft() дает мне правильное размещение. Вы должны попробовать более разумную координату вместо w->mapToGlobal(QPoint(0,0).

Ответ 5

У меня были некоторые успехи в аналогичной проблеме, сначала позвонив w->layout()->update() до w->layout()->activate(). Кажется, что заставить activ() фактически что-то делать, а не думать, что это нормально, потому что окно пока не отображается.