2016-11-08 14 views
0

.uiファイルから作成された出力は、常にレイアウトと子ウィジェットを既存のウィジェットにインストールします。setupUiまたはuic出力を使用して親ウィジェットのない独立したレイアウトを作成するにはどうすればいいですか?

#include "ui_form.h" 

QWidget widget; 
Ui::Form ui; 
ui.setupUi(&widget); 

悲しいかなは、時々介在ウィジェットは不要である:あなたが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を視察したのだが、トップレベルのウィジェットなしでコードを発するサポートしていません:次のように我々はそれを使用することができます。必要なパッチがほんのわずかであることは事実です。

しかし、Qtのにパッチを適用せずにsetupUiを-accepting nullptrの効果に何かを実装する方法はありますか?

誰かがこれがメークアップ問題だと思わないように、この質問には部分的にthis code from YUViewが表示されました。そこで、setupUiに渡されたparentWidgetには既にレイアウトがあり、目的はsetupUiの後に別のレイアウトにフォームのレイアウトを追加することです。 QWidget::setLayoutsetupUiの内部から呼び出され、新しいレイアウトの設定を拒否し、警告を発し、それにもかかわらず動作します。コードは、Qtコードが目的の結果につながる正しい方法を壊しているため、脆いです。

+0

これは素晴らしいです。残念ですが、このウィジェットが必要でないということ以外に、 'setContentsMargins(0,0,0,0); 'で介在するウィジェットを使用する場合の短所はありますか?これが介在するウィジェットを作成するオーバーヘッドについては、答えは基本的に一時ウィジェットを作成し、そのレイアウトを取るか?だから、私はそれがパフォーマンスに関するものではないと思う。したがって、介在するウィジェット(余白を '0'に設定)でこれを設定し、代わりにこのウィジェットをレイアウトに追加する際の短所は何ですか? – Mike

+0

私は通常、このような問題に対処する際に私が介入するウィジェットのアプローチを使用するため、これを求めています。 – Mike

+0

不要な迷惑メールを含まないようにアイテムの階層が好きです。一時ウィジェットは永遠に保持して再利用することができます。唯一の追加料金については、一時的なラッパーのレイアウトが作成され、すぐに破棄されます。それは何のためにも大したことではないかもしれない、ああ。 –

答えて

1

はい。そうするために私たちを可能にする重要な所見は、以下のとおりです。

  1. childLayoutが親をゼロにすることによって、その親のレイアウトから削除することができます。

    childLayout->setParent(nullptr); 
    
  2. 親をレイアウトがします、それを追加するときにウィジェットまたは非nullのレイアウトparentWidget()、その子をreparentします。

したがって、我々は、フォームに追加のトップレベルのラッパーのレイアウトを追加し、それから、目標のトップレベルのレイアウトをunparent、そしてラッパーを削除する必要があります。 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関数を実装して、ウィジェットのレイアウトを切り出し、それをウィジェットに接続しないで返すことができます。プライベート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に見えます:

<?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> 
関連する問題