Qt 5 QML application with many Windows or complex user interfaces

In QtQuick 2, with QtQuick controls, you can create complex desktop applications. However, it seems to me that the whole user interface should be declared and create everything at once at the beginning of the application. Any parts that you do not want to use yet (for example, the File-> Open dialog) should be created, but they are hidden, for example:

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. 

Now this may be good for simple applications, but for complex applications or applications with many dialogs, is this certainly a crazy thing? In the traditional QtWidgets model, you dynamically create your own dialogue when necessary.

I know there are some workarounds for this, for example. you can use Loader or even create QML objects dynamically directly in javascript, but they are very ugly and you lose all the benefits of the pretty QML syntax. Also you cannot "unload" components. Well Loader claims you can, but I tried and my app crashed.

Is there an elegant solution to this problem? Or do I just need to bite the bullet and create the entire potential user interface for my application right away, and then hide most of it?

Note: this page contains information on using Loader to get around this, but as you can see, this is not a very nice solution.

Edit 1 - Why a Suboptimal Bootloader?

Well, to show you why Loader is actually not so nice, consider this example, which launches a complex task and awaits the result. Suppose that - unlike all the trivial examples that people usually give - a task has many inputs and several outputs.

This is the Loader solution:

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

If I did this without Loader , I could have something like this:

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

This is obviously simpler. What I explicitly want is the incorrect way to load ComplexTask and activate all of its declarative relationships when componentLoaded set to true, and then disconnect the relations and unload the component if componentLoaded set to false. I am sure there is no way to do something like this in Qt right now.

+5
source share
3 answers

Creating QML components from JS is dynamically as ugly as creating widgets from C ++ dynamically (if not less, since it is actually more flexible). There is nothing ugly about this, you can implement your QML components in separate files, use every help that Creator provides when creating them, and create instances of those components where you need them as much as you need. It is much uglier so that everything is hidden from the transition, it is also much harder, and he could not foresee everything that could happen, as well as the possibility of creating dynamic components.

Here's a minimalistic stand-alone example, it doesn't even use the bootloader, since the dialog box is a locally accessible QML file.

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

In addition, the above example is very simple and just to illustrate, consider using the stack view, either your own implementation, or available from the StackView stock.

+6
source

Here's a small alternative to ddriver's answer that doesn't call Qt.createComponent() every time you instantiate this component (which will be pretty slow):

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

I think that the loading and unloading elements are no longer relevant, because each user has more than 2 GB of RAM.

And do you think your application can take up more than 512 MB of RAM? I doubt it.

You must load the qml elements and not unload them, no crashes will happen, just save all the pointers and process the qml frames.

If you just save all your QML elements in RAM and save their states, it will work faster and look better.

An example of my project that was designed this way: https://youtube.com/watch?v=UTMOd2s9Vkk

I created a basic frame inherited by all windows. There are hide / show and resetState methods in this frame. The base window contains all the child frames, therefore, through the signal / slots, other frames show / hide the next required frame.

-2
source

Source: https://habr.com/ru/post/1206337/


All Articles