2016-06-07 8 views
11

Wir haben eine ziemlich große QtQuick-Anwendung, mit vielen modalen Dialogen. Alle diese Modalitäten haben ein einheitliches Aussehen und Verhalten und haben Links, Buttons, einen Inhalt und zusätzliche Warnungs-Widgets. Wir verwenden die folgende Basisklasse (PFDialog.qml):Crash in QQuickItem destructor/changeListeners beim Schließen der Anwendung (Qt 5.6)

Window { 
    property alias content: contentLayout.children 
    ColumnLayout { 
     id: contentLayout 
    } 
} 

und erklären Dialoge auf folgende Weise (main.qml):

Window { 
    visible: true 
    property var window: PFDialog { 
     content: Text { text: "Foobar" } 
    } 
} 

Das Problem ist, dass, wenn die Anwendung geschlossen ist, ein segfault passiert im QQuickItem-Destruktor. Dieser Segfault ist schwer zu reproduzieren, aber hier ist ein todsicherer Weg, dies zu ermöglichen: Mit Visual Studio im Debug-Modus wird der freigegebene Speicher mit 0xDDDDDDD gefüllt, wobei jedes Mal der Segfault ausgelöst wird. https://github.com/wesen/testWindowCrash

Der Absturz passiert in QQuickItem::~QQuickItem:

for (int ii = 0; ii < d->changeListeners.count(); ++ii) { 
    QQuickAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate(); 
    if (anchor) 
     anchor->clearItem(this); 
} 

Der Grund dafür ist, dass der Inhalt unseres Dialogs (das Text-Element in dem obigen Beispiel)

Vollbeispielanwendung finden Sie hier ist ein QObject-Kind des Hauptfensters, aber ein visuelles Kind des Dialogfensters. Beim Schließen der Anwendung wird zuerst das Dialogfenster zerstört, und zu dem Zeitpunkt, zu dem das Textelement gelöscht wird, ist das (noch als ChangeListener registrierte) Dialogfenster veraltet.

Nun meine Frage:

  • dieser Fehler ein QtQuick ist? Sollte der Dialog sich selbst als changeListener für seine Kinder abmelden, wenn er zerstört wird (ich denke es sollte)
  • ist unser property alias content: layout.children Muster korrekt, oder gibt es einen besseren Weg dies zu tun? Dies geschieht auch bei der Deklaration eines Standardeigenschaftenalias.

Der Vollständigkeit halber, hier ist, wie wir es in unserer Anwendung Hotfix. Wenn sich der Inhalt ändert, reparieren wir alle Elemente im Layoutelement. A von Eleganz, wie Sie alle zustimmen werden.

+0

Können Sie die Frage bitte mit einem in sich geschlossenen Testfall bearbeiten? –

+0

Es tut mir leid, nicht sicher, ich folge dir. Dies ist ungefähr so ​​eigenständig wie ich kann (die 8 Zeilen an der Spitze sind die ganze Anwendung). Sie können das Repository klonen, wenn Sie etwas aus der Box haben möchten. –

+0

Oh, OK, Entschuldigung, ich habe nicht verstanden, dass es alles war. Vielen Dank! –

Antwort

5

Ich habe dieses Problem viele Male hatte ich don Ich denke, es ist ein Fehler, mehr wie eine Design-Beschränkung. Je impliziter das Verhalten, das Sie erhalten, desto weniger Kontrolle haben Sie, was zu unangemessenen Befehlen zur Zerstörung von Objekten und zu unzugänglichen Referenzen führt.

Es gibt zahlreiche Situationen, in denen dies "für sich allein" passieren kann, wenn Sie die Grenzen einer trivialen "by the book" qml-Anwendung überschreiten, aber in Ihrem Fall sind Sie es, der es tut.

Wenn Sie die richtige Eigentum wollen, tun dies nicht verwenden:

property var window: PFDialog { 
    content: Text { text: "Foobar" } 
} 

Stattdessen verwenden diese:

property Window window: dlg // if you need to access it externally 
PFDialog { 
    id: dlg 
    content: Text { text: "Foobar" } 
} 

Hier ist ein guter Grund, warum:

property var item : Item { 
    Item { 
    Component.onCompleted: console.log(parent) // qml: QQuickItem(0x4ed720) - OK 
    } 
} 
// vs 
property var item : Item { 
    property var i: Item { 
    Component.onCompleted: console.log(parent) // qml: null - BAD 
    } 
} 

Ein Kind ist nicht das Gleiche wie eine Eigenschaft. Eigenschaften werden noch gesammelt, aber sie sind nicht parented.

Wie für die "dynamische Inhalte" thingie zu erreichen, ich habe gute Ergebnisse mit ObjectModel:

Window { 
    property ObjectModel layout 
    ListView {    
     width: contentItem.childrenRect.width // expand to content size 
     height: contentItem.childrenRect.height 
     model: layout 
     interactive: false // don't flick 
     orientation: ListView.Vertical 
    } 
} 

Dann:

PFDialog { 
    layout: ObjectModel { 
     Text { text: "Foobar" } 
     // other stuff 
    } 
} 

schließlich aus Gründen vor der Schließung ausdrücklich Cleanups tun die Anwendung, auf Ihrer Haupt-QML-Datei können Sie eine Handler implementieren:

Dadurch wird sichergestellt, dass das Fenster nicht ohne vorherige Säuberung zerstört wird.

Endlich:

sind unser Eigentum alias Inhalt: layout.children Muster richtig, oder ist es ein besserer Weg, dies zu tun? Dies geschieht auch, wenn Sie einen Standardeigenschaftenalias deklarieren.

Es war nicht das letzte Mal, dass ich mich darum kümmerte, aber es war mindestens ein paar Jahre zurück. Es wäre sicherlich schön, Objekte als Kinder deklarieren zu lassen, die tatsächlich zu Kindern eines anderen Objekts werden, aber zu der Zeit war das nicht möglich und kann es auch nicht sein. Daher die Notwendigkeit für die etwas ausführlichere Lösung, die das Objektmodell und die Listenansicht involviert. Wenn Sie die Angelegenheit untersuchen und etwas anderes finden, hinterlassen Sie einen Kommentar, um es mir mitzuteilen.

2

Ich glaube, dass Sie ein Window-Objekt in einem Var nicht deklarieren können. In meinen Tests wird das SubWindow niemals geöffnet und manchmal beim Start unterbrochen.

Ein Fenster kann in einem Element oder in einem anderen Fenster deklariert werden; werden „transient für“ das äußere Fenster in diesem Fall das innere Fenster automatisch Siehe: http://doc.qt.io/qt-5/qml-qtquick-window-window.html

Ändern Sie den Code dazu:

Window { 
    visible: true 
    PFDialog { 
     content: Text { text: "Foobar" } 
    } 
} 
+0

Ja, das ist unser gewünschtes Verhalten, und wir verwenden Dialog als eine Klasse in unserer Haupt-App. Ich denke, das ist nicht das Problem hier, in der Tat können Sie Fenster mit einem einfachen Artikel ersetzen, ich habe nur versucht, unsere Hauptanwendung zu imitieren. Danke für die Einsicht. –