如何使用setupUi或uic输出创建一个没有父窗口小部件的独立布局?

时间:2016-11-08 22:14:35

标签: qt qwidget qlayout uic

uic从.ui文件生成的输出始终在现有窗口小部件上安装布局和子窗口小部件。当您打算直接在widget上添加布局/子项时,这非常有用:

#include "ui_form.h"

QWidget widget;
Ui::Form ui;
ui.setupUi(&widget);
唉,有时干预小部件是不必要的。首先,小部件上的布局具有非零边距,因此如果您打算将Ui::Form的内容插入布局,则必须重置边距:

QWidget top;
QVBoxLayout layout{&top};
QWidget widget;
Ui::Form ui;
ui.setupUi(&widget);
widget.layout()->setContentsMargins(0,0,0,0);
layout.addWidget(&widget);

此外,在这种情况下,干预小部件是完全没有必要的。假设我们有setupUi不需要顶级小部件(从Qt 5.7开始,它在传递nullptr时崩溃)。我们可以按如下方式使用它:

QWidget top;
QVBoxLayout layout{&top};
Ui::Form ui;
ui.setupUi(nullptr);
layout.addLayout(ui.layout); // assuming `layout` is the top-level layout there

我已经检查过uic并且它不支持在没有顶级窗口小部件的情况下发出代码。确实,所需的补丁是微不足道的。

然而,有没有办法实现nullptr的效果 - 接受setupUi而不修补Qt?

以免有人认为这是一个已解决的问题:此问题部分由this code from YUView提示。在那里,传递给parentWidget的{​​{1}}已经有了一个布局,目的是在setupUi完成后将表单的布局添加到另一个布局。从setupUi内部调用的QWidget::setLayout拒绝设置新布局,发出警告,事情恰好在其中发挥作用。代码是脆弱的,因为它依赖于Qt代码打破了导致期望结果的正确方法。

1 个答案:

答案 0 :(得分:1)

是。能够让我们这样做的关键观察是:

  1. 可以通过将其父级:

    归零来从其父级布局中删除setupUi
    childLayout
  2. 将无父布局添加到窗口小部件或具有非空childLayout->setParent(nullptr); 的布局后,将重新显示其子项。

  3. 因此,我们需要在表单中添加一个额外的顶级包装器布局,然后从中取消目标顶层布局,并删除包装器。 parentWidget()报告的表单结构为:

    dumpObjectTree

    测试用例:

    QWidget::Form 
        QVBoxLayout::wrapper 
            QVBoxLayout::layout 
                QHBoxLayout::horizontalLayout 
        QLabel::label 
        QLabel::label_2 
        QLabel::label_3
    

    如果您希望将此功能考虑在内,则可以使用模板// https://github.com/KubaO/stackoverflown/tree/master/questions/layout-take-40497358 #include <QtWidgets> #include "ui_form.h" QLayout * takeLayout(QWidget *); int main(int argc, char ** argv) { QApplication app{argc, argv}; QWidget parent; QHBoxLayout layout{&parent}; Ui::Form ui; { QWidget w; ui.setupUi(&w); w.dumpObjectTree(); if (true) { ui.layout->setParent(nullptr); delete ui.wrapper; layout.addLayout(ui.layout); } else { layout.addLayout(takeLayout(&w)); } } parent.show(); return app.exec(); } 来代替setupLayout。上面的测试用例将成为:

    setupUi

    QWidget parent; QHBoxLayout layout{&parent}; Ui::Form ui; layout.addLayout(setupLayout(&ui)); parent.show(); 是:

    setupLayout

    如果您不能或不愿意改变表格的结构怎么办?您可以使用Qt的内部实现//*** Interface QLayout * setupLayout_impl(void * ui, void(*setupUi)(void * ui, QWidget * widget)); // Extracts the top layout from a Ui class generated by uic. The top layout must // be enclosed in a wrapper layout. template <typename Ui> QLayout * setupLayout(Ui * ui) { struct Helper { static void setupUi(void * ui, QWidget * widget) { reinterpret_cast<Ui*>(ui)->setupUi(widget); } }; return setupLayout_impl(static_cast<void*>(ui), &Helper::setupUi); } //*** Implementation static void unparentWidgets(QLayout * layout) { const int n = layout->count(); for (int i = 0; i < n; ++i) { QLayoutItem * item = layout->itemAt(i); if (item->widget()) item->widget()->setParent(0); else if (item->layout()) unparentWidgets(item->layout()); } } QLayout * setupLayout_impl(void * ui, void(*setupUi)(void * ui, QWidget * widget)) { QWidget widget; setupUi(ui, &widget); QLayout * wrapperLayout = widget.layout(); Q_ASSERT(wrapperLayout); QObjectList const wrapperChildren = wrapperLayout->children(); Q_ASSERT(wrapperChildren.size() == 1); QLayout * topLayout = qobject_cast<QLayout*>(wrapperChildren.first()); Q_ASSERT(topLayout); topLayout->setParent(0); delete wrapperLayout; unparentWidgets(topLayout); Q_ASSERT(widget.findChildren<QObject*>().isEmpty()); return topLayout; } 函数,该函数会删除窗口小部件的布局并将其返回到窗口小部件。请注意私有takeLayout是不够的,因为它返回一个设置了QWidget::takeLayout标志的布局:这样的布局只能直接安装在小部件上,并且当一个人试图将它们添加到一个小部件时,它将失败一个断言布局(作为子布局)。方法如下:

    topLevel

    #include <private/qwidget_p.h> #include <private/qlayout_p.h> class WidgetHelper : private QWidget { struct LayoutHelper : private QLayout { static void resetTopLevel(QLayout * l) { auto d = static_cast<QLayoutPrivate*>(static_cast<LayoutHelper*>(l)->d_ptr.data()); d->topLevel = false; } }; public: static QLayout * takeLayout(QWidget * w) { auto d = static_cast<QWidgetPrivate*>(static_cast<WidgetHelper*>(w)->d_ptr.data()); auto l = w->layout(); if (!l) return nullptr; d->layout = 0; l->setParent(nullptr); LayoutHelper::resetTopLevel(l); return l; } }; QLayout * takeLayout(QWidget * w) { return WidgetHelper::takeLayout(w); } 如下所示:

    form.ui
相关问题