web-dev-qa-db-ja.com

Qt-レイアウトからすべてのウィジェットを削除しますか?

これは簡単ではないようです。基本的に、私はQPushButtonsを関数を介してレイアウトに追加します。関数が実行されるとき、最初にレイアウトをクリアします(すべてのQPushButtonsおよびそこにある他のものをすべて削除します)。

ヘッダ

QVBoxLayout* _layout;

cpp

void MainWindow::removeButtonsThenAddMore(const QString &item) {

//remove buttons/widgets

QVBoxLayout* _layout = new QVBoxLayout(this);

QPushButton button = new QPushButton(item);
_layout->addWidget(button);

QPushButton button = new QPushButton("button");
_layout->addWidget(button);

QWidget* widget = new QWidget();
widget->setLayout(_layout);

QScrollArea* scroll = new QScrollArea();
scroll->setWidget(widget);
scroll->show();

}
56
user375566

同じ問題がありました。メインウィンドウクラスがQMainWindowを継承するゲームアプリがあります。そのコンストラクタは部分的に次のようになります。

_m_scene = new QGraphicsScene;
m_scene->setBackgroundBrush( Qt::black );
...
m_view = new QGraphicsView( m_scene );
...
setCentralWidget( m_view );
_

ゲームのレベルを表示したいときは、QLabelを追加するQGridLayoutをインスタンス化し、それらのピックスマップを特定の画像(透明部分を持つピックスマップ)に設定します。最初のレベルは正常に表示されますが、2番目のレベルに切り替えると、最初のレベルからのピックスマップが新しいものの後ろにまだ見える可能性があります(ピックスマップは透明でした)。

古いウィジェットを削除するためにいくつかのことを試しました。 (a)QGridLayoutを削除して新しいものをインスタンス化しようとしましたが、レイアウトを削除しても追加されたウィジェットは削除されないことがわかりました。 (b)新しいピックスマップでQLabel :: clear()を呼び出してみましたが、それはもちろん新しいものにのみ影響し、ゾンビのものには効果がありませんでした。 (c)m_viewとm_sceneを削除し、新しいレベルを表示するたびに再構築しようとしましたが、まだ運がありません。

次に、(d)上記の解決策の1つを試しました。

_QLayoutItem *wItem;
while (wItem = widget->layout()->takeAt(0) != 0)
    delete wItem;
_

しかし、それでもうまくいきませんでした。

しかし、さらにグーグルで、私は働いた答えを見つけました。 (d)に欠けていたのは、delete item->widget()の呼び出しでした。次は私のために今働く:

_// THIS IS THE SOLUTION!
// Delete all existing widgets, if any.
if ( m_view->layout() != NULL )
{
    QLayoutItem* item;
    while ( ( item = m_view->layout()->takeAt( 0 ) ) != NULL )
    {
        delete item->widget();
        delete item;
    }
    delete m_view->layout();
}
_

そして、最初のレベルと同様に新しいQGridLayoutをインスタンス化し、新しいレベルのウィジェットを追加します。

Qtは多くの点で優れていますが、この問題は、ここでの作業が少し簡単になる可能性があることを示していると思います。

41
Teemu Leisti

レイアウト管理 Qtのヘルプ状態のページ:

レイアウトは自動的にウィジェットの親を変更し(QWidget :: setParent()を使用)、レイアウトがインストールされているウィジェットの子になります。

私の結論:ウィジェットは手動で、またはレイアウトではなく親のWIDGETを破棄することで破棄する必要があります

レイアウト内のウィジェットは、レイアウト自体ではなく、レイアウトがインストールされているウィジェットの子です。ウィジェットは、レイアウトではなく、親として他のウィジェットのみを持つことができます。

私の結論:上記と同じ

「矛盾」の@Muelnerへ「アイテムの所有権はレイアウトに移され、それを削除するのはレイアウトの責任です。」 -これはWIDGETを意味するのではなく、レイアウトの親になり、後でレイアウトによって削除されるITEM。ウィジェットは、レイアウトがインストールされているウィジェットの子であり、手動で削除するか、親ウィジェット全体を削除することで削除する必要があります。

レイアウトからすべてのウィジェットとアイテムを本当に削除する必要がある場合、完全に空のままにするには、次のような再帰関数を作成する必要があります。

// shallowly tested, seems to work, but apply the logic

void clearLayout(QLayout* layout, bool deleteWidgets = true)
{
    while (QLayoutItem* item = layout->takeAt(0))
    {
        if (deleteWidgets)
        {
            if (QWidget* widget = item->widget())
                widget->deleteLater();
        }
        if (QLayout* childLayout = item->layout())
            clearLayout(childLayout, deleteWidgets);
        delete item;
    }
}
37

私はこの質問が古くて答えられていることを知っていますが、QtAlgorithmsはqDeleteAllを提供しているので、ワンライナーですべての子を削除するなど、レイアウトを削除することができます。このコードは、レイアウト、そのすべての子、およびレイアウト内のすべてが「消える」を削除します。

qDeleteAll(yourWidget->children());

オーバーロードされた関数の説明は次のとおりです。

void qDeleteAll(ForwardIterator begin、ForwardIterator end)

C++のdelete>演算子を使用して、範囲[begin、end]のすべてのアイテムを削除します。項目タイプはポインタータイプ(QWidget *など)でなければなりません。

QDeleteAllには、そのレイアウトからではなく、そのウィジェットからのコンテナーを供給する必要があることに注意してください。また、qDeleteAllはyourWidget-その子だけを削除しないことに注意してください。

これで、新しいレイアウトを設定できます。

12
m.w.

未試行:新しいレイアウトを作成し、古いレイアウトと交換して、古いレイアウトを削除してみませんか?これにより、レイアウトが所有していたすべてのアイテムが削除され、他のアイテムは残ります。

編集:私の答え、ドキュメント、およびQtソースへのコメントを調べた後、より良い解決策を見つけました。

まだQt3サポートを有効にしている場合は、QLayout :: takeAtのドキュメントのヒントと基本的に同じであるQLayout :: deleteAllItems()を使用できます。

次のコードは、レイアウトからすべてのアイテムを削除する安全な方法を示しています。

QLayoutItem *child;
while ((child = layout->takeAt(0)) != 0) {
  ...
  delete child;
}

編集:さらなる調査の後、上記の両方のバージョンは同等であるように見えます:親のないサブレイアウトとウィジェットのみが削除されます。親を持つウィジェットは特別な方法で処理されます。 TeLのソリューションが機能するように見えます。トップレベルのウィジェットを削除しないように注意してください。別の方法は、ウィジェット階層を使用してウィジェットを削除することです。親なしで特別なウィジェットを作成し、この特別なウィジェットの子としてすべての削除可能なウィジェットを作成します。レイアウトをクリーニングした後、この特別なウィジェットを削除します。

8
hmuelner

また、スペーサーとQWidgetではないものを必ず削除する必要があります。レイアウト内の唯一のものがQWidgetsであると確信している場合、前の答えは問題ありません。それ以外の場合は、これを行う必要があります。

QLayoutItem *wItem;
while (wItem = widget->layout()->takeAt(0) != 0)
      delete wItem;

クリアするレイアウトがより大きなレイアウトの一部である場合、レイアウトを破棄したくないため、これを行う方法を知ることが重要です。レイアウトがウィンドウの残りの場所との関係と関係を維持することを保証したい。

また、このメソッドを呼び出すたびに大量のオブジェクトを作成していることに注意してください。オブジェクトはクリーンアップされていません。まず、おそらくQWidgetとQScrollAreaを別の場所に作成し、参照用にそれらを指すメンバー変数を保持する必要があります。次に、コードは次のようになります。

QLayout *_layout = WidgetMemberVariable->layout();

// If it is the first time and the layout has not been created
if (_layout == 0)
{
  _layout = new QVBoxLayout(this);
  WidgetMemberVariable->setLayout(_layout);
}

// Delete all the existing buttons in the layout
QLayoutItem *wItem;
while (wItem = widget->layout()->takeAt(0) != 0)
    delete wItem;

//  Add your new buttons here.
QPushButton button = new QPushButton(item);
_layout->addWidget(button);

QPushButton button = new QPushButton("button");
_layout->addWidget(button);
4
Liz

既存の回答はどれも私のアプリケーションでは機能しませんでした。これまでのところ、Darko Maksimovicの変更が機能しているようです。ここにあります:

void clearLayout(QLayout* layout, bool deleteWidgets = true)
{
    while (QLayoutItem* item = layout->takeAt(0))
    {
        QWidget* widget;
        if (  (deleteWidgets)
              && (widget = item->widget())  ) {
            delete widget;
        }
        if (QLayout* childLayout = item->layout()) {
            clearLayout(childLayout, deleteWidgets);
        }
        delete item;
    }
}

少なくとも私のウィジェットとレイアウトの階層では、明示的にウィジェットを再帰および削除する必要がありました。

2
Alex S

他の方法については書きませんが、QStackedWidgetを使用して、必要なボタンの配置ごとに1つのビューを2つ追加することもできます。それらの2つの間を反転することは、問題ではなく、動的に作成されたボタンのさまざまなインスタンスをジャグリングするよりもはるかに少ないリスクです。

2

ウィジェットのテーマ自体も削除されている場合、私のボタンリストでのみ機能します。それ以外の場合、古いボタンは引き続き表示されます。

QLayoutItem* child;
while ((child = pclLayout->takeAt(0)) != 0)
{
    if (child->widget() != NULL)
    {
        delete (child->widget());
    }
    delete child;
}
2
Martin Wilde

QVBoxLayoutインスタンスを含む動的に作成されたQHBoxLayoutオブジェクトを含むQWidgetがある場合も同様です。何らかの理由で、最上位のQVBoxLayoutも個々のQHBoxLayoutsも削除しても、ウィジェットを取り除くことができませんでした。私が働いた唯一の解決策は、階層を調べて、具体的にすべてを削除および削除することでした:

while(!vbox->isEmpty()) {
    QLayout *hb = vbox->takeAt(0)->layout();
    while(!hb->isEmpty()) {
        QWidget *w = hb->takeAt(0)->widget();
        delete w;
    }
    delete hb;
}
1
teukkam

この問題の可能な解決策があります( Qt-QWidgetのレイアウト内からすべてのウィジェットをクリア を参照)。 2つの別々のステップですべてのウィジェットとレイアウトを削除します。

ステップ1:すべてのウィジェットを削除する

    QList< QWidget* > children;
    do
    {
       children = MYTOPWIDGET->findChildren< QWidget* >();
       if ( children.count() == 0 )
           break;
       delete children.at( 0 );
    }
    while ( true );

ステップ2:すべてのレイアウトを削除する

    if ( MYTOPWIDGET->layout() )
    {
        QLayoutItem* p_item;
        while ( ( p_item = MYTOPWIDGET->layout()->takeAt( 0 ) ) != nullptr )
            delete p_item;
        delete MYTOPWIDGET->layout();
    }

ステップ2の後、MYTOPWIDGETはきれいになります。

0
boto

ウィジェットをレイアウトに追加するとき、および他のレイアウトにレイアウトを追加するときに何もおかしくない場合は、親ウィジェットに追加するときにそれらすべての親を変更する必要があります。すべてのQObjectにはdeleteLater()スロットがあり、イベントループに制御が戻るとすぐにオブジェクトが削除されます。このマナーで削除されたウィジェットは、その子も削除します。したがって、ツリーの最上位の項目でdeleteLater()を呼び出すだけです。

hppで

QScrollArea * Scroll;

cppで

void ClearAndLoad(){
    Scroll->widget()->deleteLater();
    auto newLayout = new QVBoxLayout;
    //add buttons to new layout
    auto widget = new QWidget;
    widget->setLayout(newLayout);
    Scroll->setWidget(widget);
}

また、この例では、_layoutはローカル変数であり、ヘッダーファイルの_layoutと同じものではないことに注意してください(QVBoxLayout*部分を削除してください)。また、_で始まる名前は標準ライブラリ実装者用に予約されていることに注意してください。 var_のように末尾の_を使用してローカル変数を表示します。多くの好みがありますが、先行する_および__は技術的に予約されています。

0
odinthenerd

すべてのウィジェットを削除する場合は、次のようなことを行うことができます。

foreach (QObject *object, _layout->children()) {
  QWidget *widget = qobject_cast<QWidget*>(object);
  if (widget) {
    delete widget;
  }
}
0
BastiBen