Qt: Как заставить скрытый виджет рассчитать его макет? - PullRequest
15 голосов
/ 11 марта 2010

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

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

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

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

Ответы [ 6 ]

17 голосов
/ 22 октября 2010

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

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

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

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

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

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

Следующий код показывает, как это сделать.

/**
 * Forces the given widget to update, even if it's 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();
}
1 голос
/ 14 сентября 2018

Проходя через QWidget::grab() я заметил эту часть:

if (r.width() < 0 || r.height() < 0) {
    // For grabbing widgets that haven't been shown yet,
    // we trigger the layouting mechanism to determine the widget's size.
    r = d->prepareToRender(QRegion(), renderFlags).boundingRect();
    r.setTopLeft(rectangle.topLeft());
}

QWidget::grab() была введена в Qt 5.0, и эта тестовая функция с QDialog, содержащим макет, похоже, работает на Qt 5.5.1

int layoutTest_2(QApplication& a)
{
    CLayoutTestDlg dlg; // initial dlg size can also be set in the constructor
        // https://stackoverflow.com/questions/21635427

    QPixmap pixmap = dlg.grab(); // must be called with default/negative-size QRect
    bool savedOK = pixmap.save("E:/temp/dlg_img.png");
        // saving is not necessary, but by now the layout should be done

    dlg.show();
    return a.exec();
}
1 голос
/ 12 марта 2010

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

0 голосов
/ 22 мая 2013

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

0 голосов
/ 23 ноября 2011

Это сработало для меня при использовании 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).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...