【问题标题】:Qt: How to force a hidden widget to calculate its layout?Qt:如何强制隐藏的小部件计算其布局?
【发布时间】:2011-01-26 11:50:33
【问题描述】:

我正在尝试将 qwidget 渲染到不同的窗口(手动使用 QPainter)

我有一个带有布局和一堆子控件的 QWidget (w)。 w 被隐藏。在显示 w 之前,不会发生布局计算,这是预期的。

当我调用w->render(painter, w->mapToGlobal(QPoint(0,0)) 时,我得到了一堆相互重叠的控件。
w->layout()->activate();w->layout()->update() 似乎没有做任何事情。

有没有办法强制布局发生而不显示 w?

【问题讨论】:

    标签: c++ user-interface qt


    【解决方案1】:

    尝试使用QWidget::sizeHint() 方法,该方法应该在布局后返回小部件的大小。

    【讨论】:

    • 我试过了,它确实给了我合理的尺寸..但它没有更新位置!所以尺寸看起来是对的,但它们仍然相互重叠。
    【解决方案2】:

    在不显示在屏幕上的小部件上强制进行布局计算:

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

    show() 调用将强制布局计算,Qt::WA_DontShowOnScreen 确保不显式显示小部件。

    【讨论】:

    • 请注意,只有当它的所有祖先都可见时,小部件才会变得可见。如果不是这种情况,调用show() 可能无法解决这里的问题。请参阅下面的答案,了解应该始终有效的不同方法。
    • @MathiasKunter 不过,应该注意的是,绘画事件完美地流向“显示”(但在屏幕上不可见)小部件,如果你说,给它一些鼠标点击(不像普通的无父小部件)!
    • @mlvljr 你是对的,这个解决方案可能会导致不正确的事件调度。因此,不要使用它,因为它可能会导致细微的错误,尤其是在相关小部件中重新实现事件处理程序时。
    • @MathiasKunter 好吧,我的意思是,如果确实想要一个不可见的小部件,这就是(可能)要走的路。但是由于这个原因,也可能确实会发生棘手的情况(尽管在一个简单的演示中对我来说效果很好):)
    【解决方案3】:

    当我使用 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)

    【讨论】:

      【解决方案4】:

      通过在w->layout()->activate() 之前先调用w->layout()->update(),我在类似的问题上取得了一些成功。这似乎迫使 activate() 实际做某事,而不是认为它很好,因为无论如何都没有显示窗口。

      【讨论】:

        【解决方案5】:

        小部件的布局计算可以通过在其布局上调用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();
        }
        

        【讨论】:

        • layout-&gt;invalidate(); layout-&gt;activate(); 为我工作了一个稍微不同的应用程序:在添加/删除小部件后强制调整已经可见的布局的大小。
        • 如果layout-&gt;invalidate(); layout-&gt;activate(); 没有刷新几何,请尝试添加QApplication::processEvents(); 这可能不是正确的方法,但它解决了我的问题
        【解决方案6】:

        在浏览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();
        }
        

        【讨论】:

          猜你喜欢
          • 2013-05-25
          • 1970-01-01
          • 2014-12-17
          • 1970-01-01
          • 1970-01-01
          • 2010-12-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多