2014-11-06 11 views
5

In QtQuick 2 mit den QtQuick Controls können Sie komplexe Desktop-Anwendungen erstellen. Es scheint mir jedoch, dass die gesamte UI beim Start der App deklariert und gleichzeitig erstellt werden muss. Alle Teile, die Sie nicht wollen, noch verwenden (zum Beispiel die File-> Öffnen-Dialog) noch erstellt werden müssen, aber sie sind versteckt, wie folgt aus:Qt 5 QML App mit vielen Windows oder komplexen UIs

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. 

Nun, dies kann für einfache Anwendungen in Ordnung sein, aber für komplexe oder Apps mit vielen Dialogen ist das sicher eine verrückte Sache? Im traditionellen QtWidgets-Modell würden Sie Ihren Dialog bei Bedarf dynamisch erstellen.

Ich weiß, dass es einige Problemumgehungen dafür gibt, z. Sie können ein Loader verwenden oder sogar QML-Objekte dynamisch direkt in Javascript erstellen, aber sie sind sehr hässlich und Sie verlieren alle Vorteile der netten QML-Syntax. Außerdem können Sie die Komponenten nicht wirklich "entladen". Nun, Loader Ansprüche können Sie, aber ich habe es ausprobiert und meine App abgestürzt.

Gibt es eine elegante Lösung für dieses Problem? Oder muss ich einfach in den sauren Apfel beißen und alle möglichen UI für meine App auf einmal erstellen und dann die meisten davon verstecken?

Hinweis: this page hat Informationen über die Verwendung von Loader s, um dies zu umgehen, aber wie Sie sehen können, ist es keine sehr nette Lösung.

Edit 1 - Warum ist Loader suboptimal?

Ok, um Ihnen zu zeigen, warum Loader nicht wirklich so angenehm ist, betrachten Sie dieses Beispiel, das eine komplexe Aufgabe startet und auf ein Ergebnis wartet. Nehmen wir an, dass - im Gegensatz zu allen trivialen Beispielen, die normalerweise von Leuten gegeben werden - die Aufgabe viele Eingaben und mehrere Ausgaben hat.

Dies ist die Loader Lösung:

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

Wenn ich es ohne Loader tun war, habe ich so etwas wie dieses haben könnte:

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

die offensichtlich Weg ist einfacher. Was ich will eindeutig ist eine Möglichkeit für die ComplexTask geladen werden und haben alle seine deklarativen Beziehungen aktiviert, wenn componentLoaded auf True festgelegt ist, und dann die Beziehungen getrennt haben und die Komponente entladen, wenn componentLoaded auf false festgelegt ist. Ich bin mir ziemlich sicher, dass es momentan keine Möglichkeit gibt, etwas in Qt zu machen.

+0

Warum ist die Loader Lösung nicht schön? Warum ist es hässlich? Wenn Ihre App beim Setzen von "aktiv" auf "false" abstürzt, handelt es sich entweder um einen Fehler in Ihrer App oder über den Qt-Code und ist kein Argument gegen die Verwendung von Loader. Warum ist die Sichtbarkeit von Dingen in einer großen Anwendung verrückt? – Mitch

+0

Das hört sich ein bisschen so an, als würde ich dich verhören, aber es sind nur wirklich neugierige Fragen. :) – Mitch

+0

Siehe Updates. Und die Sichtbarkeit in großen Apps auf false zu setzen, ist verrückt, weil die Komponente immer noch geladen ist und Ressourcen verwendet. Es ist wie diese "eine Seite Websites", die alles vorladen. Sie sind in Ordnung, wenn Sie nur ein paar Seiten haben, aber Sie können diese Technik nicht verwenden, wenn Sie ein eBay oder Amazon machen. – Timmmm

Antwort

6

Das dynamische Erstellen von QML-Komponenten aus JS ist genauso hässlich wie das dynamische Erstellen von Widgets aus C++ (wenn nicht weniger, da es tatsächlich flexibler ist). Es gibt nichts Hässliches daran, Sie können Ihre QML-Komponenten in separaten Dateien implementieren, jede Unterstützung verwenden, die Creator bei ihrer Erstellung bereitstellt, und diese Komponenten dort, wo Sie sie brauchen, so oft wie Sie benötigen, instanziieren. Es ist viel hässlicher, alles von Anfang an versteckt zu haben, es ist auch viel schwerer und es könnte unmöglich alles vorausahnen, was ebenso wie dynamische Komponenteninstanziierung passieren könnte.

Hier ist ein minimalistisches in sich geschlossenes Beispiel, es verwendet nicht einmal einen Loader, da der Dialog lokal verfügbare QML-Datei ist.

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

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

Auch das obige Beispiel ist sehr Barebone und nur aus Gründen der Veranschaulichung betrachten wir eine Stapelansicht, entweder eine eigene Implementierung oder die verfügbar seit 5.1 Lager StackView.

+0

Ok, dieses Beispiel ist schöner als 'Loader'. Warum macht die Tatsache, dass die QML-Datei lokal ist, einen Unterschied? – Timmmm

+0

@Timmmm - wenn Sie über das Internet laden, kann es eine Weile dauern. Sie müssen es aufteilen und 'component.statusChanged.connect (finishCreation)', so dass Sie 'createObject()' aufrufen, wenn die Komponente schließlich 'Component.Ready' ist (wenn sie nicht aus einem anderen Grund ausläuft oder ausfällt)) ... oder verwenden Sie einen Loader – dtech

+0

Der Unterschied zwischen der Verwendung dieses Ansatzes und Loadern ist, dass Sie Ihre QML-Dateien nicht mit Loadern verschmutzen, beide Ansätze machen effektiv dasselbe. Außerdem ist der Loader definiert und "einheitlicher", während Sie die manuelle dynamische Instanziierung auf verschiedene Arten verwenden können. – dtech

-2

Ich denke, Laden und Entladen von Elementen ist nicht mehr aktuell, weil jeder Benutzer mehr als 2 GB RAM haben.

Und denken Sie, Ihre App kann mehr als 512 MB RAM nehmen? Ich bezweifle das.

Sie sollten qml Elemente laden und nicht entladen, keine Abstürze passieren, nur alle Zeiger speichern und qml Frames manipulieren.

Wenn Sie nur alle Ihre QML-Elemente im RAM behalten und ihre Zustände speichern, wird es schneller arbeiten und sieht besser aus.

Beispiel ist mein Projekt, das auf diese Weise entwickelt: https://youtube.com/watch?v=UTMOd2s9Vkk

Ich habe Grundrahmen aus, dass von allen Fenstern geerbt. Dieser Rahmen verfügt über die Methoden hide/show und resetState. Das Basisfenster enthält alle untergeordneten Frames, also zeigen/verbergen andere Frames über Signal/Slots den nächsten erforderlichen Frame.

+2

Nun ist dies ein perfektes Beispiel für eine schlechte Antwort. Es ist nicht nur "nur Link", aber der Link enthält überhaupt keine Informationen, nur ein fertiges Produkt ohne Informationen über seine Implementierung. –

+0

@ user3735658 ok, bitte entfernen Sie Ihren Kommentar und ich lösche meine Antwort. – IGHOR

+0

Ich würde es vorziehen, dass Sie es verbessern, indem Sie einige nützliche Informationen darin zur Verfügung stellen, dann kann ich den Downvote entfernen. Ansonsten ist es keine Antwort, sondern nur Sie zeigen Ihr Produkt aus. –

0

Hier ist eine leichte Alternative Antwort auf ddriver ist, die nicht Qt.createComponent() Sie eine Instanz dieser Komponente erstellen, jedes Mal nicht nennen (was ziemlich langsam sein):

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