首页 文章

Qt 5 QML应用程序,包含许多Windows或复杂的UI

提问于
浏览
5

在使用QtQuick控件的QtQuick 2中,您可以创建复杂的桌面应用程序 . 然而,在我看来,必须在应用程序开始时声明整个UI并立即创建所有UI . 您仍然不想使用任何您不想使用的部分(例如文件 - >打开对话框),但它们是隐藏的,如下所示:

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.

现在,对于简单的应用程序来说这可能没问题,但对于复杂的应用程序或具有许多对话框的应用程序,这肯定是一件疯狂的事情吗?在传统的QtWidgets模型中,您可以在需要时动态创建对话框 .

我知道有一些解决方法,例如您可以使用 Loader 甚至直接在javascript中动态创建QML对象,但它们非常难看,您将失去优质QML语法的所有好处 . 你也不可能真的"unload"组件 . 那么 Loader 声称你可以,但我试过了,我的应用程序崩溃了 .

这个问题有优雅的解决方案吗?或者我只需要咬紧牙关并立即为我的应用创建所有潜在的用户界面,然后隐藏其中的大部分内容?

注意:this page有关于使用 Loader 来解决这个问题的信息,但是你可以看到它不是一个非常好的解决方案 .

编辑1 - 为什么Loader不是最理想的?

好的,为了告诉你 Loader 为什么不那么令人愉快,请考虑这个开始一些复杂任务并等待结果的例子 . 假设 - 与人们通常给出的所有琐碎的例子不同 - 任务有很多输入和几个输出 .

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

如果我在没有 Loader 的情况下这样做,我可能会有这样的事情:

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

这显然更简单 . 我明确想要的是 ComplexTask 被加载并且当 componentLoaded 设置为true时激活其所有声明性关系的某种方式,然后在 componentLoaded 设置为false时断开关系并卸载组件 . 我很确定目前在Qt中没有办法制作这样的东西 .

3 回答

  • -2

    动态地从JS创建QML组件与动态创建小部件一样丑陋(如果不是这样,实际上更灵活) . 没有什么可丑的,你可以在单独的文件中实现你的QML组件,使用Creator在创建时提供的每个帮助,并在你需要的地方尽可能多地实例化那些组件 . 让一切都隐藏起来是非常丑陋的,它也更加沉重,它无法预测动态组件实例化可能发生的所有事情 .

    这是一个简约的自包含示例,它甚至不使用加载器,因为该对话框是本地可用的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()
            }
        }
    }
    

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

    此外,上面的示例非常准确,只是为了说明,考虑使用堆栈视图,您自己的实现或自5.1库存 StackView 以来可用 .

  • 0

    这里's a slight alternative to ddriver'的答案每次创建该组件的实例时都不会调用 Qt.createComponent() (这将非常慢):

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

    我认为加载和卸载元素不再是实际的,因为每个用户都有超过2GB的RAM .

    你认为你的应用程序甚至可以使用超过512 MB的内存吗?我对此表示怀疑 .

    你应该加载qml元素,不要卸载它们,不会发生崩溃,只需存储所有指针并操纵qml帧 .

    如果您只是将所有QML元素保存在RAM中并存储它们的状态,它将更快地运行并且看起来更好 .

    示例是我以这种方式开发的项目:https://youtube.com/watch?v=UTMOd2s9Vkk

    我已经制作了所有窗口继承的基础框架 . 这个框架确实有hide / show和resetState方法 . 基本窗口确实包含所有子帧,因此通过信号/插槽,其他帧显示/隐藏下一个所需的帧 .

相关问题