[gelöst] Problem beim Löschen der Kinder eines Widgets

Alles rund um die Programmierung mit Qt
Antworten
Brunolein
Beiträge: 4
Registriert: 17. August 2008 15:56

[gelöst] Problem beim Löschen der Kinder eines Widgets

Beitrag von Brunolein »

Hallo,

ich bin Qt-Anfänger und habe ein Problem, das ich auf folgende Applikation reduziert habe:

Main-Datei:

Code: Alles auswählen

// Main
#include <QtGui>
#include "worker.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QWidget w;
    CWorker worker(&w);
    w.show();
    return app.exec();
}
Arbeitsklasse:

Code: Alles auswählen

#include <QtGui>

class CWorker : public QObject
{
    Q_OBJECT

    public:
        CWorker(QWidget* w) : widget(w) { f1(); }
        void f1(void);
        void f2(void);

    public slots:
        void slot1(void) { f1(); }
        void slot2(void) { f2(); }

    private:
        void empty(void);
        QWidget* widget;
};

inline void CWorker::f1(void)
{
    empty();

    // Das widget mit neuem Inhalt füllen.
    QVBoxLayout* layout = new QVBoxLayout(widget);
    layout->addWidget(new QPushButton("f1-Dummy"));
    QPushButton* button = new QPushButton("f1-Button1");
    QObject::connect(button, SIGNAL(clicked(bool)), this, SLOT(slot2()));
    layout->addWidget(button);
}

inline void CWorker::f2(void)
{
    empty();

    // Das widget mit neuem Inhalt füllen.
    QHBoxLayout* layout = new QHBoxLayout(widget);
    QPushButton* button = new QPushButton("f2-Button1");
    QObject::connect(button, SIGNAL(clicked(bool)), this, SLOT(slot1()));
    layout->addWidget(button);
}

inline void CWorker::empty(void)
{
    // Versuch, das widget zu leeren.
    QLayout* layout = widget->layout();
    if (layout == NULL) { return; }

    while (layout->count() > 0)
    {
        QLayoutItem* item = layout->itemAt(0);
        layout->removeItem(item);
        delete item;
    }
    delete layout;
}
Ich erzeuge ein QWidget-Objekt w, das dem worker übergeben wird. Dieser füllt w in der Funktion f1() mit einem Layout und zwei QPushButtons. Die Aktivierung eines dieser Buttons (f1-Button1) dient zum Umschalten in einen anderen Modus. In diesem soll w mit der Funktion f2() vom aktuellen Layout und den Buttons befreit und mit einem anderen Layout gefüllt werden. Der einzige Button in dem neuen Layout dient wieder zu Zurückschalten auf das erste Layout.

Eigentlich hätte ich gedacht, daß durch die Funktion empty() das widget geleert wird und dann mit neuem Layout und neuen Widgets gefüllt werden kann. Beim Benutzen der Buttons während des Programmlaufs werden diese aber nicht gelöscht. Sie sind nach wie vor im Widget enthalten.

Mein Problem ist jetzt: Wie kann eine Funktion, bevor sie eigene Widgets einbaut, das Widget richtig leeren, wenn sie dessen Kinder nicht kennt?
Zuletzt geändert von Brunolein am 20. August 2008 20:35, insgesamt 1-mal geändert.
Ginsengelf
Beiträge: 79
Registriert: 2. Mai 2007 10:21

Beitrag von Ginsengelf »

Moin, es kann die Kinder kennen lernen:

http://doc.trolltech.com/4.3/qobject.html#findChildren

Ginsengelf
Brunolein
Beiträge: 4
Registriert: 17. August 2008 15:56

Beitrag von Brunolein »

Hallo Ginsengelf,

vielen Dank für Deine Hilfe.
Ich habe meine empty-Funktion auf Grund Deines Hinweises geändert:

Code: Alles auswählen

inline void CWorker::empty(void)
{
    QList<QWidget*> listW = widget->findChildren<QWidget*>();
    int countW = listW.count();
    foreach(QWidget* w, listW)
    {
        delete w;
    }
    // Überprüfung, ob widget noch Widgets enthält
    QList<QWidget*> listW2 = widget->findChildren<QWidget*>();
    countW = listW2.count();

    QList<QLayout*> listL = widget->findChildren<QLayout*>();
    int countL = listL.count();
    foreach(QLayout* l, listL)
    {
        delete l;
    }
    // Überprüfung, ob widget noch Layouts enthält
    QList<QLayout*> listL2 = widget->findChildren<QLayout*>();
    countL = listL2.count();
}
Jetzt funktioniert das Programm so, wie ich es wollte und bei der Überprüfung nach dem Löschen ist die Liste der Kinder leer. Allerdings fühle ich mich unwohl, wenn ich die gefundenen Kinder einfach so mit delete lösche. Mir ist noch nicht klar, wie das Eltern-Widget es mitkriegt, daß seine Kinder gelöscht wurden. Offensichtlich ist QObject auch nicht so ganz damit einverstanden, da es beim Aufruf von delete folgende Debug-Ausgabe macht: "QObject: Do not delete object, 'unnamed', during its event handler!".

Ich überblicke diesen Teil von Qt noch nicht so ganz, aber ich glaube Trolltech/Nokia könnte an dieser Stelle das Ganze noch etwas vereinfachen. Wenn es die Möglichkeit gibt, ein Widget mit anderen Objekten (QWidget, QLayout) mit add...() zu füllen, müßte es auch eine entsprechend einfache Möglichkeit geben, das Widget wieder zu leeren.

Ich habe als Alternative auch probiert, das gesamte widget zu löschen und damit auch seinen Inhalt. Das funktioniert auch. In meinem angegebenen Beispiel ist dies auch kein Problem. In meiner richtigen Anwendung ist dies aber mit dem Problem verbunden, daß das widget Teil eines anderen Layouts ist. Wenn ich das widget lösche, hat das auch Auswirkungen auf das Gesamt-Layout und dieses muß dann wieder neu aufgebaut werden. Aus diesem Grund ist die geschilderte Alternative schlecht.

Ich werde also die von Dir vorgeschlagenen Methode benutzen und versuchen, herauszukriegen, wie ich die mir jetzt bekannten Kinder des widget löschen kann, ohne daß sich irgendjemand beschwert.

Vielen Dank nochmal für Deine Hilfe

Brunolein
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag von upsala »

Ungetestet:

Code: Alles auswählen

QObjectList list(widget->children());
foreach(QObject *o, list) {
  o->deleteLater();
}
Ansonsten solltest du vielleicht über ein QStackedWidget nachdenken...
Brunolein
Beiträge: 4
Registriert: 17. August 2008 15:56

Beitrag von Brunolein »

Hallo upsala,

danke für die schnelle Reaktion. Auf Grund Deines Vorschlags habe ich meine empty-Funktion noch mal geändert:

Code: Alles auswählen

inline void CWorker::empty(void)
{
    QObjectList list(widget->children());
    int countO = list.count();
    foreach(QObject *o, list)
    {
        o->deleteLater();
    }

    // Überprüfung, ob widget noch Objects enthält
    QObjectList listb(widget->children());
    countO = listb.count();
}
Die Anzahl der Objekte vor und nach dem deleteLater() ist jeweils 3 (2 Buttons und 1 Layout). Gemäß Hilfe werden die Löschungen nicht vorgenommen, wenn die exec-Funktion der Applikation noch nicht gestartet wurde oder wenn eine andere Ereignisschleife gestartet wurde. Beide Bedingungen sind bei mir aber erfüllt, die Objekte sind aber noch nicht gelöscht , wenn ich die neuen Widgets und ein neues Layout einfügen will. Dementsprechend wird das neue Layout auch nicht angenommen und mein Fenster bleibt leer. Ich muß da wohl noch etwas Aufwand treiben, um den Fehler zu finden.

Der Vorschlag mit dem QStackedWidget ist eine Überlegung wert. Wenn aber, wie in meiner Testapplikation möglich, sehr oft hin- und hergeschaltet wird, könnte die Anzahl der QWidgets innerhalb des QStackedWidget wohl stark ansteigen. Ich werde aber auf jeden Fall diesen Gedanken auch weiterverfolgen.

Brunolein
Antworten