2015-05-20 5 views
8

Fast jede QtWidgets-Klasse kann Eltern haben. Und normalerweise ist es optional, bei der Objektinitialisierung Eltern festzulegen. Zum Beispiel, wenn ich eine Klasse erstellen, die QWidget Klasse erbt, tut ich folgendes auf dem Konstruktor:Was ist für Eltern in Qt?

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); 
} 

I gesetzt oder nicht gesetzt Elternteil kann. Ich kann cancel_button ein Kind von hbox sein. Ich kann auch cancel_button setzen, um ein Kind von yes_button zu sein, aber ich denke, es ist eine schlechte Sache zu tun.

Was ist der Sinn dieses? Und, ist es wirklich notwendig, Eltern für jede QWidget basierte Klasse zu setzen, die ich erstelle?

+0

Haben Sie bemerkt, dass 'QWidget' von' QObject' abgeleitet ist? – cmannett85

+0

Ja, natürlich ist es. Aber ich kann nicht wirklich verstehen, was der Sinn dieser Elternbeziehung ist. –

Antwort

9

Neben der Unterstützung der Zeichenreihenfolge in GUI-Objekten hilft es auch bei der Speicherverwaltung, sodass bei der Zerstörung eines QObject alle untergeordneten Elemente ebenfalls zerstört werden. Weitere Informationen finden Sie unter http://doc.qt.io/qt-4.8/objecttrees.html. Wenn sich etwas im Elternteil ändert (dh seine Größe ändert sich), kann es seine Kinder benachrichtigen, sich selbst zu aktualisieren.

Um Ihre Frage zu beantworten, müssen Sie nicht das übergeordnete Element für alles festlegen (deshalb ist es ein optionaler Parameter), aber die meiste Zeit ist es besser, es richtig einzustellen.

+0

Gibt es ein Beispiel, dass ich eine übergeordnete QWidget-basierte Klasse festlegen sollte. Ein Fall, in dem das Programm nicht ordnungsgemäß funktioniert, ohne das übergeordnete Element festzulegen? –

+2

Wenn Sie das übergeordnete Element nicht korrekt festlegen, funktionieren Layout-Manager nicht. Ein weiteres Problem ist, dass in Ihrem Beispiel, wenn Sie Ihr QWidget zerstören, Sie yes_button manuell zerstören müssen, oder es wird undicht. Wenn Eltern festgelegt wurde, würde Qt das für Sie übernehmen. – Vitor

+1

Beachten Sie, dass es einen Sonderfall mit 'QWidget' gibt. Wenn es kein Elternteil hat, wird es in ein Fenster der obersten Ebene konvertiert (siehe [hier] (http://doc.qt.io/qt-4.8/qobject.html#QObject)). – rbaleksandar

3

Zuerst ist ein QWidget ein QObject, und QObject sind Knoten in einem QObject Baum. Die untergeordneten Knoten werden vom übergeordneten Speicher verwaltet, es sei denn, Sie heben die Zuordnung auf, bevor der übergeordnete Knoten die Möglichkeit dazu hat. Daher ist die Speicherverwaltung ein Grund dafür, dass Widgets oder andere Elemente einen Elternteil haben.

Zweitens sind sichtbare elternlose Widgets immer Top-Level-Windows. Umgekehrt ist es unmöglich, ein Widget ohne Top-Level zu haben, das ohne Eltern ist. Wenn Sie ein Widget ohne Eltern anzeigen, erhält es ein eigenes Fenster. Das Gegenteil ist nicht unbedingt wahr - es ist möglich, einem Kind-Widget ein Qt::Window-Flag zu geben, und es wird auch ein Top-Level-Fenster.

Die logische Folge ist, dass jede Widget in andere Widgets enthalten ein Elternteil hat - sonst wäre es ein Fenster der obersten Ebene sein. Dieser Parent wird möglicherweise nicht explizit von Ihnen festgelegt, aber trotzdem gesetzt.

Ich denke, dass Ihre Frage wie neu formuliert werden kann: Wann muss ich Widget-Konstruktor explizit Eltern geben? Die Antwort lautet:

  1. Jedes Mal, wenn das Widget ein Top-Level-Fenster, das Sie beabsichtigen, einen Elternteil zu haben. Solche Fenster unterliegen nicht der Layout-Verwaltung, daher gibt es keinen Mechanismus, um dieses übergeordnete Element für Sie festzulegen. Transiente Dialoge auf höchster Ebene müssen Eltern haben, damit sie in Bezug auf das Elternfenster richtig positioniert sind.

  2. Immer wenn Sie ein Kind-Widget haben, das nicht der Layoutverwaltung unterliegt.

Widgets unterliegen Layout-Management werden beim Einfügen in ein Layout parented:

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(); 
} 

schließlich nicht alle Widgets oder QObject s müssen explizit auf dem Heap angelegt werden. Da alle QObject-abgeleitete Klassen in Qt (und viele andere Klassen auch!) Verwenden Sie das PIMPL-Idiom, wenn Sie sie einzeln auf dem Heap zuweisen, tun Sie wirklich die Heap-Zuweisung zweimal. Zuerst weisen Sie die Instanz der Klasse zu - manchmal ist die Instanz so klein wie ein oder zwei Zeiger - und dann weist der Konstruktor der Klasse seine PIMPL zu. Die explizite Heap-Zuweisung ist ein Fall von vorzeitiger Pessimierung.

dieses Pessimierung zu vermeiden, Ihre Widget wie folgt aussehen:

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); 
} 

Wenn Sie wollte the PIMPL idiom verwenden, können Sie das auch tun:

// 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! 

Natürlich sollten Sie sein mit QDialogButtonBox statt all dem :)

Verwandte Themen