web-dev-qa-db-ja.com

Qtの親は何ですか?

ほとんどすべてのQtWidgetsクラスは親を持つことができます。そして通常、オブジェクトの初期化時に親を設定することはオプションです。たとえば、QWidgetクラスを継承するクラスを作成する場合、コンストラクターで次のようにします。

Widget::Widget(QWidget* parent): QWidget(parent) {
    hbox = new QHBoxLayout(this);
    yes_button = new QPushButton("&Yes");
    no_button = new QPushButton("&No", this);
    cancel_button = new QPushButton("&Cancel", hbox);
}

親を設定してもしなくてもかまいません。 cancel_buttonhboxの子に設定できます。 cancel_buttonyes_buttonの子に設定することもできますが、それは悪いことだと思います。

これの意味は何ですか?そして、私が作成するすべてのQWidgetベースのクラスの親を設定することは本当に必要ですか?

17
Mas Bagol

GUIオブジェクトの描画順序に加えて、メモリ管理にも役立ちます。そのため、QObjectを破棄すると、その子もすべて破棄されます。詳細は http://doc.qt.io/qt-4.8/objecttrees.html を参照してください。親で何かが変更されたとき(たとえば、サイズが変更されたとき)は、子にも通知して自分自身を更新することができます。

あなたの質問に答えるために、すべての親を設定する必要はありません(結局、それがオプションのパラメーターである理由です)が、ほとんどの場合、正しく設定する方が良いです。

18
Vitor

まず、QWidgetQObjectであり、QObjectsはQObjectツリー内のノードです。子ノードは、親が割り当てを解除する前に割り当てを解除しない限り、親によってメモリ管理されます。したがって、メモリ管理は、ウィジェットや他のQObjectsが親を持つ理由の1つです。

第2に、表示される親のないウィジェットは常にトップレベルのウィンドウです。逆に、親のないトップレベル以外のウィジェットを作成することは不可能です。親のないウィジェットを表示すると、独自のウィンドウが取得されます。逆は必ずしも真ではありません-子ウィジェットにQt::Windowフラグを与えることが可能であり、それもトップレベルのウィンドウになります。

当然の結果として、他のウィジェットに含まれているウィジェットウィジェットは親を持っています-それ以外の場合は、トップレベルウィンドウになります。この親は明示的に設定されていない場合がありますが、それでも設定されています。

あなたの質問は次のように言い換えることができると思います:ウィジェットコンストラクターを親に明示的に指定する必要があるのはいつですか?答えは:

  1. ウィジェットが、親を持つつもりである最上位ウィンドウであるときはいつでも。このようなウィンドウはレイアウト管理の対象ではないため、親を設定するメカニズムはありません。トップレベルの一時的なダイアログは、親ウィンドウに対して適切に配置されるように、親を持つ必要があります。

  2. レイアウト管理の対象とならない子ウィジェットがあるときはいつでも。

レイアウト管理の対象となるウィジェットは、レイアウトへの挿入時にペアレント化されます。

int main(int argc, char ** argv) {
  QApplication app(argc, argv);
  QWidget window;
  QVBoxLayout layout(&window);
  QLabel label("Hello");
  QPushButton button("Goodbye");
  layout.addWidget(&label);
  layout.addWidget(&button);
  QObject::connect(&button, &QPushButton::clicked, [&app]{ app.quit(); });
  window.show();
  return app.exec();
}

最後に、すべてのウィジェットまたはQObjectsをヒープ上に明示的に作成する必要はありません。 QtのすべてのQObject派生クラス(および他の多くのクラスも!)はPIMPLイディオムを使用するため、ヒープに個別に割り当てると、実際にヒープ割り当てtwice。最初にクラスのインスタンスを割り当てます-場合によってはインスタンスが1つまたは2つのポインタと同じくらい小さいことがあります-次にクラスのコンストラクタがそのPIMPLを割り当てます。明示的なヒープ割り当ては、時期尚早の悲観化の例です。

この悲観化を回避するには、Widgetを次のようにします。

class Widget : public QWidget {
  Q_OBJECT
  QHBoxLayout m_layout;
  QPushButton m_yesButton, m_noButton, m_cancelButton;
public:
  Widget(QWidget * parent = 0);
};

Widget::Widget(QWidget * parent) : 
  QWidget(parent),
  m_layout(this),
  m_yesButton("&Yes"),
  m_noButton("&No"),
  m_cancelButton("&Cancel")
{
  m_layout.addWidget(&m_yesButton);
  m_layout.addWidget(&m_noButton);
  m_layout.addWidget(&m_cancelButton);
}

PIMPLイディオム を使用したい場合は、次のようにすることもできます。

// Widget.h - Interface
class WidgetPrivate;
class Widget : public QWidget {
{
  Q_OBJECT
  Q_DECLARE_PRIVATE(Widget)
  QScopedPointer<WidgetPrivate> const d_ptr;
public:
  Widget(QWidget * parent = 0);
  ~Widget();
};

// Widget.cpp - Implementation 
class WidgetPrivate {
  Q_DISABLE_COPY(WidgetPrivate)
  Q_DECLARE_PUBLIC(Widget)
  Widget * const q_ptr;
  QHBoxLayout layout;
  QPushButton yesButton, noButton, cancelButton;
public:
  WidgetPrivate(Widget * q);
};

WidgetPrivate::WidgetPrivate(Widget * q) {
  q_ptr(q),
  layout(q),
  yesButton("&Yes"),
  noButton("&No"),
  cancelButton("&Cancel")
{
  layout.addWidget(&yesButton);
  layout.addWidget(&noButton);
  layout.addWidget(&cancelButton);
}

Widget::Widget(QWidget * parent) :
  QWidget(parent),
  d_ptr(new WidgetPrivate(this))
{}

Widget::~Widget() {}
// necessary, since WidgetPrivate is unknown to the interface!

もちろん、これだけではなく QDialogButtonBox を使用する必要があります:)

10