2014-11-06 17 views
5

In QtQuick 2 utilizzando il QtQuick Controls è possibile creare applicazioni desktop complesse. Tuttavia, mi sembra che l'intera interfaccia utente debba essere dichiarata e creare tutto in una volta all'inizio dell'app. Tutte le parti che non si desidera utilizzare ancora (ad esempio, il File-> Apri finestra di dialogo) devono ancora essere creati, ma sono nascosti, in questo modo:Qt 5 QML app con un sacco di finestre o di interfacce utente complesse

ApplicationWindow { 

    FileDialog { 
    id: fileOpenDialog 
    visible: false 
    // ... 
    } 
    FileDialog { 
    id: fileSaveDialog 
    visible: false 
    // ... 
    } 
    // And so on for every window in your app and every piece of UI. 

Ora, questo può andare bene per le applicazioni semplici, ma per quelli complessi o le app con molti dialoghi sicuramente questa è una cosa da pazzi? Nel modello tradizionale di QtWidgets creerai dinamicamente il tuo dialogo quando necessario.

So che ci sono alcune soluzioni per questo, ad esempio, puoi usare uno Loader o persino creare oggetti QML dinamicamente direttamente in javascript, ma sono molto brutti e perdi tutti i vantaggi della bella sintassi QML. Inoltre, non è possibile "scaricare" realmente i componenti. Bene Loader afferma che puoi ma l'ho provato e la mia app si è bloccata.

Esiste una soluzione elegante a questo problema? O devo semplicemente mordere il proiettile e creare tutta l'interfaccia utente potenziale per la mia app in una sola volta e poi nasconderne la maggior parte?

Nota: this page ha informazioni sull'utilizzo di Loader s per aggirare questo problema, ma come potete vedere non è una soluzione molto bella.

Edit 1 - Perché Loader non ottimale?

Ok, per mostrare il perché Loader non è poi così piacevole, considerare questo esempio che inizia un compito complesso e attende un risultato. Supponiamo che - a differenza di tutti gli esempi banali che di solito si danno - il compito abbia molti input e diversi output.

Questa è la soluzione Loader:

Window { 
    Loader { 
     id: task 
     source: "ComplexTask.qml" 
     active: false 
    } 
    TextField { 
     id: input1 
    } 
    TextField { 
     id: output1 
    } 
    Button { 
     text: "Begin complex task" 
     onClicked: { 
       // Show the task. 
       if (task.active === false) 
       { 
        task.active = true; 
        // Connect completed signal if it hasn't been already. 
        task.item.taskCompleted.connect(onTaskCompleted) 
       } 

       view.item.input1 = input1.text; 
       // And several more lines of that... 
      } 
     } 
    } 

    function onTaskCompleted() 
    { 
     output1.text = view.item.output1 
     // And several more lines... 

     // This actually causes a crash in my code: 
//  view.active = false; 
    } 
} 

Se lo facevo senza Loader, potrei avere qualcosa di simile:

Window { 
    ComplexTask { 
     id: task 
     taskInput1: input1.text 
     componentLoaded: false 
     onCompleted: componentLoaded = false 
    } 

    TextField { 
     id: input1 
    } 
    TextField { 
     id: output1 
     text: task.taskOutput1 
    } 

    Button { 
     text: "Begin complex task" 
     onClicked: task.componentLoaded = true 
    } 
} 

che ovviamente è modo più semplice. Quello che voglio chiaramente è un modo per caricare lo ComplexTask e avere tutte le sue relazioni dichiarative attivate quando componentLoaded è impostato su true, quindi avere le relazioni disconnesse e scaricare il componente quando componentLoaded è impostato su false. Sono abbastanza sicuro che non ci sia modo di fare qualcosa di simile a Qt al momento.

+0

Perché è la soluzione Loader non è bello? Perché è brutto? Se la tua app si arresta in modo anomalo quando l'impostazione è attiva su false, è un bug nell'app o nel codice Qt e non è un argomento contro l'utilizzo di Loader. Perché impostare la visibilità delle cose su false in una grande applicazione è pazzesca? – Mitch

+0

Sembra un po 'come ti sto interrogando, ma è solo una serie di domande davvero curiose. :) – Mitch

+0

Vedi gli aggiornamenti. E impostare la visibilità su false nelle app di grandi dimensioni è folle perché il componente è ancora caricato e utilizza le risorse. È come quei "siti web di una pagina" che precaricano tutto. Sono ok se hai solo poche pagine, ma non puoi usare quella tecnica se stai facendo un eBay o Amazon. – Timmmm

risposta

6

Creazione di componenti QML da JS è dinamicamente altrettanto brutto come la creazione di widget da C++ in modo dinamico (se non meno, come in realtà è più flessibile). Non c'è niente di brutto in questo, puoi implementare i tuoi componenti QML in file separati, usare ogni assistenza che il Creatore fornisce nella loro creazione e istanziare quei componenti ovunque ti serva quanto ne hai bisogno. È molto più brutto avere tutto nascosto dall'avvio, è anche molto più pesante e non è possibile anticipare tutto ciò che potrebbe accadere, così come l'istanziazione dinamica dei componenti.

Ecco un esempio minimalista autosufficiente, non ha nemmeno utilizzare un loader, dal momento che la finestra di dialogo è disponibile localmente file QML.

Dialog.qml

Rectangle { 
    id: dialog 
    anchors.fill: parent 
    color: "lightblue" 

    property var target : null 

    Column { 
     TextField { 
      id: name 
      text: "new name" 
     } 
     Button { 
      text: "OK" 
      onClicked: { 
       if (target) target.text = name.text 
       dialog.destroy() 
      } 
     } 
     Button { 
      text: "Cancel" 
      onClicked: dialog.destroy() 
     } 
    } 
} 

principale.QML

ApplicationWindow { 
    visible: true 
    width: 200 
    height: 200 

    Button { 
     id: button 
     text: "rename me" 
     width: 200 
     onClicked: { 
      var component = Qt.createComponent("Dialog.qml") 
      var obj = component.createObject(overlay) 
      obj.target = button 
     } 
    } 

    Item { 
     id: overlay 
     anchors.fill: parent 
    } 
} 

Inoltre, l'esempio di cui sopra è molto barebone e solo per il gusto di illustrazione, è consigliabile utilizzare una vista pila, sia la propria implementazione o disponibili dal 5.1 magazzino StackView.

+0

Ok quell'esempio è più carino dell'uso di 'Loader'. Perché il fatto che il file QML sia locale fa la differenza? – Timmmm

+0

@Timmmm - cuz se carichi su Internet potrebbe volerci un po 'di tempo. Dovrai dividerlo e 'component.statusChanged.connect (finishCreation)' così chiamerai 'createObject()' quando il componente è finalmente 'Component.Ready' (se non va in timeout o fallisce per qualche altro motivo) ... o utilizzare un caricatore – dtech

+0

La differenza tra l'utilizzo di tale approccio e caricatori è che non si inquina i file QML con i caricatori, entrambi gli approcci fanno effettivamente la stessa cosa. Anche il caricatore è definito e "più uniforme", mentre è possibile utilizzare l'istanziazione dinamica manuale in vari modi. – dtech

-2

Penso che gli elementi di caricamento e scaricamento non siano più attuali perché ogni utente ha più di 2 GB di RAM.

E pensi che la tua app possa richiedere più di 512 MB di RAM? Ne dubito.

È necessario caricare elementi qml e non scaricarli, non si verificheranno arresti anomali, è sufficiente memorizzare tutti i puntatori e manipolare i frame qml.

Se si conservano tutti gli elementi QML nella RAM e si memorizzano i loro stati, funzionerà più rapidamente e avrà un aspetto migliore.

Esempio è il mio progetto che ha sviluppato in questo modo: https://youtube.com/watch?v=UTMOd2s9Vkk

ho fatto telaio di base che ha ereditato da tutte le finestre. Questo frame ha metodi hide/show e resetState. La finestra di base contiene tutti i frame secondari, quindi via segnale/slot altri frame mostrano/nascondi il prossimo frame richiesto.

+2

Ora questo è un perfetto esempio di cattiva risposta. Non è solo "link only", ma il link non contiene nemmeno informazioni di alcun tipo, solo un prodotto pronto senza informazioni sulla sua implementazione. –

+0

@ user3735658 ok, si prega di rimuovere il tuo commento e cancellerò la mia risposta. – IGHOR

+0

Preferirei che migliorassi fornendo alcune informazioni utili al suo interno, quindi potrò rimuovere il downvote. Altrimenti non è una risposta, ma stai solo mostrando il tuo prodotto. –

0

Ecco una leggera alternativa risposta che non chiama Qt.createComponent() ogni volta che si crea un'istanza di tale componente (che sarà abbastanza lento) di per ddriver:

// Message dialog box component. 
Component { 
    id: messageBoxFactory 
    MessageDialog { 
    } 
} 
// Create and show a new message box. 
function showMessage(text, title, modal) 
{ 
    if (typeof modal === 'undefined') 
     modal = true; 

    // mainWindow is the parent. We can also specify initial property values. 
    var messageDialog = messageBoxFactory.createObject(mainWindow, { 
                  text: text, 
                  title: title, 
                  visible: true, 
                  modality: modal ? Qt.ApplicationModal : Qt.NonModal 
                 }); 

    messageDialog.accepted.connect(messageDialog.destroy); 
    messageDialog.rejected.connect(messageDialog.destroy); 
}