2016-06-07 16 views
11

Abbiamo un'applicazione QtQuick abbastanza grande, con molte finestre di dialogo modali. Tutte queste modali condividono un aspetto e un comportamento coerente e hanno lasciato Pulsanti, rightButtons, un contenuto e ulteriori widget di avviso. Usiamo la seguente classe di base (PFDialog.qml):Blocco in QQuickItem destructor/changeListeners durante la chiusura dell'applicazione (Qt 5.6)

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

e dichiarare dialoghi nel modo seguente (main.qml):

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

Il problema è che, quando viene chiuso l'applicazione, a segfault accade nel distruttore QQuickItem. Questo segfault è difficile da riprodurre, ma questo è un modo sicuro per farlo accadere: con Visual Studio in modalità di debug, la memoria liberata è piena di 0xDDDDDDD con trigger di segfault ogni volta.

applicazione di esempio completo può essere trovato qui: https://github.com/wesen/testWindowCrash

L'incidente avviene 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); 
} 

La ragione di questo è che il contenuto del nostro dialogo (l'elemento di testo nell'esempio di cui sopra) è un figlio QObject della finestra principale, ma un elemento visivo della finestra di dialogo. Quando si chiude l'applicazione, la finestra di dialogo viene prima distrutta e al momento dell'eliminazione della voce di testo, la finestra di dialogo (ancora registrata come changeListener) è obsoleta.

Ora la mia domanda è:

  • è presente un bug QtQuick? Se la finestra di dialogo si cancella da sola come ChangeListener per i suoi figli quando viene distrutta (penso che dovrebbe)
  • il nostro modello property alias content: layout.children è corretto oppure esiste un modo migliore per farlo? Ciò accade anche quando si dichiara un alias di proprietà predefinito.

Per motivi di completezza, ecco come l'abbiamo risolto nella nostra applicazione. Quando il contenuto cambia, ripariamo tutti gli articoli all'elemento di layout. A di eleganza, come concorderete tutti.

function reparentTo(objects, newParent) { 
    for (var i = 0; i < objects.length; i++) { 
     qmlHelpers.qml_SetQObjectParent(objects[i], newParent) 
    } 
} 
onContentChanged: reparentTo(content, contentLayout) 
+0

Puoi modificare la domanda con un caso di test autonomo, per favore? –

+0

Mi dispiace, non sono sicuro che ti stia seguendo. Questo è autonomo quanto posso andare (le 8 righe in alto sono l'intera applicazione). Puoi clonare il repository se vuoi avere qualcosa costruito fuori dalla scatola. –

+0

Oh, OK, scusa non ho capito che era tutto. Grazie! –

risposta

5

ho avuto questo problema un sacco di volte, io don Penso che sia un bug, più come una limitazione del design. Il comportamento più implicito si ottiene, minore è il controllo che si ha, che porta a ordini inappropriati di distruzione dell'oggetto e l'accesso a riferimenti ciondolanti.

Ci sono numerose situazioni in cui questo può accadere "da solo" quando si superano i confini di una banale applicazione "per libro" qml, ma nel tuo caso sei tu a farlo.

Se si desidera una corretta proprietà, non utilizzare questo:

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

Utilizzare invece questo:

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

Ecco una buona ragione per cui:

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

Un bambino non è la stessa cosa di una proprietà. Le proprietà sono ancora raccolte ma non sono parentali.

buoni risultati come per raggiungere l'thingie "contenuti dinamici", che ho avuto con 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 
    } 
} 

Poi:

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

Infine, per il gusto di fare pulizie espliciti prima della chiusura l'applicazione, sul tuo file QML principale è possibile implementare un gestore:

onClosing: { 
    if (!canExit) doCleanup() 
    close.accepted = true 
} 

Ciò garantisce che la finestra non venga distrutta senza eseguire prima la pulizia.

Infine:

è la nostra proprietà content alias: modello layout.children corretto, o è c'è un modo migliore per fare questo? Ciò si verifica anche quando si dichiara un alias di proprietà predefinito .

Non era l'ultima volta che l'ho guardato, ma era indietro di almeno un paio d'anni. Sarebbe certamente bello avere oggetti dichiarati come bambini che diventano figli di qualche altro oggetto, ma al momento non era possibile, e potrebbe anche non esserlo. Quindi la necessità di una soluzione leggermente più verbosa che coinvolge il modello di oggetto e la vista elenco. Se studi la questione e trovi qualcosa di diverso, lascia un commento per farmelo sapere.

2

Credo che non sia possibile dichiarare un oggetto finestra in una var. Nei miei test, la finestra secondaria non si apre mai e talvolta si interrompe all'avvio.

Una finestra può essere dichiarata all'interno di un oggetto o all'interno di un'altra finestra; in tal caso il finestra interna diventerà automaticamente "transitorio per" la finestra esterna Vedi: http://doc.qt.io/qt-5/qml-qtquick-window-window.html

modificare il codice per questo:

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

sì, questo è il nostro comportamento desiderato, e infatti usiamo Dialog come classe nella nostra app principale. Penso che questo non sia il problema qui, anzi puoi sostituire Window con un semplice Item, stavo solo cercando di imitare la nostra applicazione principale. Grazie per l'intuizione. –

Problemi correlati