はい。これを可能にする主な観察事項は次のとおりです。
AchildLayout
は、その親を null にすることで親レイアウトから削除できます。
childLayout->setParent(nullptr);
親のないレイアウトは、それをウィジェットまたは null 以外のレイアウトに追加すると、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
:
//*** 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;
}
フォームの構造を変更できない、または変更したくない場合はどうしますか? Qt の内部構造を使用takeLayout
して、ウィジェットのレイアウトを取り出し、それをウィジェットに関連付けずに返す関数を実装できます。privateQWidget::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
ように見えます。
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<layout class="QVBoxLayout" name="wrapper">
<item>
<layout class="QVBoxLayout" name="layout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Top</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Left</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Right</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</ui>