2015-05-20 22 views
8

Prawie każda klasa QtWidgets może mieć element nadrzędny. Zwykle ustawianie obiektu nadrzędnego podczas inicjalizacji obiektu jest opcjonalne. Na przykład, jeśli utworzyć klasę, która dziedziczy QWidget klasę, zrobię na następujące konstruktora:Co jest dla rodziców w 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); 
} 

mogę ustawić lub nie jest ustawiona rodzica. Mogę ustawić cancel_button jako dziecko o numerze hbox. Mogę też ustawić cancel_button jako dziecko o yes_button, ale myślę, że to źle.

Jaki jest sens tego? I czy naprawdę konieczne jest ustawienie rodzica dla każdej klasy opartej na QWidget, którą tworzę?

+0

Czy zauważyłeś, że 'QWidget' pochodzi od' QObject'? – cmannett85

+0

Tak, oczywiście, że tak. Ale nie mogę naprawdę zrozumieć, jaki jest sens tego rodzicielskiego związku. –

Odpowiedz

9

Oprócz pomocy przy porządkowaniu rysowania w obiektach GUI, pomaga również w zarządzaniu pamięcią, więc po zniszczeniu obiektu QObject wszystkie jego dzieci są niszczone. Aby uzyskać więcej informacji, patrz http://doc.qt.io/qt-4.8/objecttrees.html. Kiedy coś zmienia się w rodzicu (tj. Jest zmieniany), może powiadomić swoje dzieci, aby również się zaktualizowały.

Aby odpowiedzieć na twoje pytanie, nie musisz ustawiać rodzica dla wszystkiego (dlatego jest to parametr opcjonalny), ale w większości przypadków lepiej jest ustawić go poprawnie.

+0

Czy istnieje przykład, że powinienem ustawić macierzystą klasę opartą na 'QWidget'. Przypadek, że program nie będzie działał poprawnie bez ustawienia rodzica? –

+2

Jeśli nie ustawisz prawidłowo rodzica, menedżery układu nie będą działać. Innym problemem jest to, że w twoim przykładzie, jeśli zniszczysz swój QWidget, będziesz musiał zniszczyć ręcznie przycisk yes_ lub wycieknie. Jeśli ustawiono opcję parent, Qt zadba o to dla Ciebie. – Vitor

+1

Należy zauważyć, że istnieje specjalny przypadek z 'QWidget'. Jeśli nie ma elementu nadrzędnego, jest konwertowany na okno najwyższego poziomu (patrz [tutaj] (http://doc.qt.io/qt-4.8/qobject.html#QObject)). – rbaleksandar

3

Po pierwsze, QWidget to QObject i QObject s są węzłami w drzewie QObject. Węzły potomne są zarządzane przez pamięć przez rodzica, chyba że zwolni się je przed rodzicem ma szansę to zrobić. W ten sposób zarządzanie pamięcią jest jednym z powodów, dla których widżety lub jakiekolwiek inne QObject s mają rodzica.

Po drugie, widoczne widgety bez rodziców to zawsze okien najwyższego poziomu. I odwrotnie, niemożliwe jest posiadanie widgetu innego niż najwyższego poziomu, który jest bez rodziców. Gdy wyświetlasz widget bez rodziców, uzyskuje on własne okno. Odwrotna sytuacja niekoniecznie jest prawdą - możliwe jest nadanie dziecku widżetu flagi Qt::Window, która również staje się oknem najwyższego poziomu.

Następstwem jest to, że każdywidget zawarte w innych widżetów ma rodzica - w przeciwnym razie byłoby to okno najwyższego poziomu. Ten rodzic może nie być jawnie ustawiony przez ciebie, ale mimo to jest ustawiony.

Myślę, że twoje pytanie może zostać zmienione jako: Kiedy muszę jawnie nadać konstruktorowi widżetów rodzic? Odpowiedź brzmi:

  1. Zawsze, gdy widget jest oknem najwyższego poziomu, w którym zamierzasz mieć rodzica. Takie okna nie podlegają zarządzaniu układem, więc nie ma mechanizmu, który ustawiłby dla ciebie tego rodzica. Transientowe okna najwyższego poziomu muszą mieć rodziców, aby były poprawnie umieszczone w stosunku do okna nadrzędnego.

  2. Zawsze, gdy widget dziecka nie podlega zarządzaniu układem.

Widgety podlega zarządzaniu układu są wychowywane po włożeniu w układzie:

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

Wreszcie, nie wszystkie widżety lub QObject s muszą być wyraźnie tworzone na stercie. Ponieważ wszystkie klasy QObject-pochodne w Qt (i wiele innych klas też!) używaj idiomu PIMPL, gdy przydzielasz je pojedynczo na stercie, tak naprawdę robisz przydziałdwa razy. Najpierw przydziel instancję klasy - czasami instancja jest tak mała jak wskaźnik lub dwa - a następnie konstruktor klasy przydziela swój PIMPL. Jawne przydzielanie sterty to przypadek przedwczesnej pesymizacji.

Aby uniknąć tego pessimization, twój Widget powinien wyglądać następująco:

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

jeśli chce korzystać the PIMPL idiom, można to zrobić, too:

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

Oczywiście, powinno być przy użyciu QDialogButtonBox zamiast tego wszystkiego :)