一系列模态对话框

时间:2017-08-29 12:39:41

标签: qt modal-dialog qml qtquick2

在命令式C ++中,我可以使用for(my.list in c("a", "b")) { assign(my.list, `names<-`(get(my.list), c("element1", "element2"))) } a #$element1 #[1] 1 #$element2 #[1] 2 b #$element1 #[1] 3 #$element2 #[1] 4 来连续询问用户几个问题。根据用户的答案(“是”,“否”,“确定”,“中止”),我可以继续前进。

但是在QML中,由于其声明性,它似乎是一项艰巨的任务。仍然可以创建一堆对话框,但依赖于答案的状态(信息)应该存储在QDialog / onAccepted / ...事件之间。如果与C ++代码(模型/单例)有很多交互,那就特别难了。

是否有任何一般的方法/技巧/习惯用法只使用一系列模态弹出窗口轻松创建类似“安装程序”的东西?

1 个答案:

答案 0 :(得分:1)

首先,和其他人之前提到的一样,对于更长的事件链,你真的需要使用类似状态机的东西。

话虽如此,在实践中,与对话相关的大多数事情涉及最多2-3个级别的问题。 回调 非常适合:

你正在寻找的基本Api是:

g_dialogs.confirm({
                     title: 'Discard unsaved changes?',
                     message: 'You have unsaved changes here.\n' +
                              'Are you sure you want to open a new file?'
                  },
                  function(result){
                    if(result) {
                      g_dialogs.fileDialog({title: 'Select an Image'},
                                           function(fileUrl){
                                             loadFile(fileUrl)
                                           })
                    }
                 })

其中g_dialogs指向Dialogs项的全局实例。您甚至可以使用QML单例。

<强> Dialogs.qml

import QtQuick 2.0
import QtQuick.Dialogs 1.2

Item
{
    property var dialogs: []
    property var callbacks: []

    /**
     * Creates a modal dialog pointed by the dialogSource qml file.
     * For this functioanlity to work, the dialogSource MUST be an instance of Dialog component
     * properties are the objects that will be passed on to the dialogSource' component
     * property must not contain a key called 'dialogId'
     * callback is the callback function that will be called with the result,
     * once the user finishes interacting with the dialog. (eg. confirms/dismisses and event)
     **/
    function showDialog(dialogSource, properties, callback) {
        if (properties === undefined)
            properties = {}

        if (properties['dialogId']) {
            console.error('You cannot use the property name dialogId')
            return -1
        }

        properties["dialogId"] = dialogs.length

        var dialog
        var component = Qt.createComponent(dialogSource)
        var result = -1

        if (component.status === Component.Ready) {
            dialog = component.createObject(parent, properties)
            callbacks.push(callback)
            dialogs.push(dialog)
            result = dialogs.length - 1
        }
        else {
            console.error(component.errorString())
        }

        return result
    }

    /**
     * Internal function used by Dialog
     * to return the result to the original caller via. the callback
     */
    function returnDialog(index, result) {
        if (index < 0 || index >= callbacks.length)
            return

        var callback = callbacks[index]
        var dialog = dialogs[index]

        callbacks.splice(index, 1)
        dialogs.splice(index, 1)

        if (dialog !== undefined)
           dialog.destroy()

        if (callback !== undefined)
            callback(result)
    }

    /**
     * Convenience method to create a confirmation dialog where the user is presented with a yes/no question
     * Asks the user whether he wants to confirm/reject an action.
     * properties can be an Object of {title: 'Window title', text: 'Text', icon: ''}
     * Once the user finishes interacting with dialog, the callback will be called with the result
     */
    function confirm(properties, callback) {
        properties["title"] = properties["title"]
                              ? properties["title"]
                              : qsTr("Warning")
        properties["text"] =  properties["text"]
                              ? properties["text"]
                              : qsTr("Are you sure?")

        properties["standardButtons"] = StandardButton.Yes | StandardButton.No

        return showDialog("MessageDialog.qml", properties, callback)
    }

    // Convenience wrapper around FileDialog.qml
    function fileDialog(properties, callback) {
        return showDialog("FileDialog.qml", properties, callback)
    }

}

其中,每个对话框都是 Dialog.qml 的实例:

import QtQuick 2.0

Item
{
    id: modalDialog

    readonly property int dialogId: 0

    signal done(var result)

    onDone: g_dialogs.returnDialog(dialogId, result)
}

例如。的 MessageDialog.qml

import QtQuick 2.0
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import '.'

Dialog
{
    property alias title: dialog.title
    property alias text: dialog.text
    property alias icon: dialog.icon
    property alias standardButtons: dialog.standardButtons

    MessageDialog
    {
        id: dialog

        modality: Qt.ApplicationModal
        standardButtons: StandardButton.Yes | StandardButton.No
        icon: StandardIcon.Warning

        onAccepted: done(true)
        onYes: done(true)
        onNo: done(false)
        onDiscard: done(false)
        onRejected: done(false)
    }

    //Variables in other windows don't seem to reflect the property changes in here.
    //So show the window only after all the properties are set.
    Component.onCompleted: dialog.visible = true
}

例如。的 FileDialog.qml

import QtQuick 2.0
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import '.'

Dialog
{
    property alias title: dialog.title
    property alias fileUrl: dialog.fileUrl
    property alias fileUrls: dialog.fileUrls
    property alias folder: dialog.folder
    property alias modality: dialog.modality
    property alias nameFilters: dialog.nameFilters
    property alias selectExisting: dialog.selectExisting
    property alias selectFolder: dialog.selectFolder
    property alias selectMultiple: dialog.selectMultiple
    property alias selectedNameFilter: dialog.selectedNameFilter
    property alias shortcuts: dialog.shortcuts
    property alias sidebarVisible: dialog.sidebarVisible

    FileDialog
    {
        id: dialog
        onAccepted: {
            if (selectMultiple) done(fileUrls)
            else done(fileUrl)
        }

        onRejected: done(null)
    }

    Component.onCompleted: dialog.visible = true
}

P.S关于这个api的好处是这些回调函数是很好的Javascript闭包,因此在ListView / Repeater委托中使用这种方法时,你不必传递讨厌的引用。