QML 中可重复使用的确认提示

Reusable confirmation prompt in QML

当用户按下按钮时,我需要一个确认或警告对话框。根据他们选择 'yes' 还是 'no',会触发不同的操作。挑战在于我有两个按钮可以弹出这样一个对话框,而在 QML 中如何做到这一点并不是很简单。这是代码(我的演示应用程序):

main.qml

import QtQuick 2.5
import QtQuick.Window 2.2

import QtQuick.Controls 1.4

Window {
    visible: true

    function areYouSure()
    {
        prompt.visible = true
    }

    MainForm {
        anchors.fill: parent

        Button {
            id: buttonA
            anchors.left: parent.left
            anchors.top: parent.top
            text: "Button A"

            onClicked: areYouSure() // based on yes or no, different actions but how to tell what was pressed?
        }

        Button {
            id: buttonB
            anchors.right: parent.right
            anchors.top: parent.top
            text: "Button B"

            onClicked: areYouSure() // based on yes or no, different actions but how to tell what was pressed?
        }
    }

    Prompt {
        anchors.fill: parent
        id: prompt
        visible: false

        onCancelled: {
            console.log("Cancel was pressed")
            // but how can I tell which button's cancel as pressed?
        }

        onAccepted: {
            console.log("Accept was pressed")
            // same for which button's Ok?
        }
    }
}

Prompt.qml

import QtQuick 2.5
import QtQuick.Window 2.2

import QtQuick.Controls 1.4

Rectangle
{
    id: root
    width: parent.width

    property string message: "Are you Sure?"

    signal cancelled;
    signal accepted;


    Text{
        id: messagetxt
        text:root.message
        horizontalAlignment: Text.AlignHCenter
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter

    }

    Rectangle {
        id: cancelButton
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        width: 50
        height: 40

        Text {
            anchors.centerIn: parent
            text: "Cancel"
        }
        color: "red"

        MouseArea {
            anchors.fill: parent

            onClicked: {
                root.visible = false
                cancelled()
            }
        }
    }

    Rectangle {
        id: okButton
        anchors.bottom: parent.bottom
        anchors.right: parent.right
        width: 50
        height: 40

        Text {
            anchors.centerIn: parent
            text: "Ok"
        }
        color: "blue"

        MouseArea {
            anchors.fill: parent

            onClicked: {
                root.visible = false
                accepted()

            }
        }
    }
}

在传统编程中,会弹出一个单独的对话框,它会准确地响应该问题,而不是我们响应它的 cancelled()accepted() 信号。在 QML 中我们真的不能那样做,对吧?知道按下哪个按钮的最佳方法是什么?具有讽刺意味的是,即使发出了正确的信号,我们似乎也无法对其采取行动。

嗯,首先你真的应该看看 Dialogs 模块,因为它为你提供了现成的解决方案,即 MessageDialog

也就是说,您可以通过不同的方式实现自定义,包括重新定义处理程序或传递 id。如果要执行的操作很简单(例如函数调用),您甚至可以动态创建对话框并将信号与所需的行为绑定。自定义显然可以走得更远,改变 titletext.

这是一个简单的例子,它遵循最后一种方法,并根据按下的按钮打印不同的文本。一旦对话框设置为不可见,它就会通过 destroy 函数销毁。

import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.0

ApplicationWindow {
    id: win
    title: qsTr("MultiDialog")
    visible: true

    RowLayout {
        anchors.fill: parent

        Button {
            text: "Button 1"
            onClicked: {
                var d1 = compDialog.createObject(win)
                // change "title" & "text"?
                d1.accepted.connect(function(){
                    console.info("accepted: " + text)
                })
                d1.rejected.connect(function(){
                    console.info("rejected: " + text)
                })
                d1.visible = true
            }
        }

        Button {
            text: "Button 2"
            onClicked: {
                var d2 = compDialog.createObject(win)
                // change "title" & "text"?
                d2.accepted.connect(function(){
                    console.info("accepted: " + text)
                })
                d2.rejected.connect(function(){
                    console.info("rejected: " + text)
                })
                d2.visible = true
            }
        }
    }

    Component {
        id: compDialog

        MessageDialog {
            title: "May I have your attention please"
            text: "It's so cool that you are using Qt Quick."
            onVisibleChanged: if(!visible) destroy(1)
            standardButtons: StandardButton.Cancel | StandardButton.Ok
        }
    }
}

如果你想使用Rectangle或者被迫使用它,那么你仍然可以使用这种方法。对象的动态创建与 MessageDialog 的使用无关,可以用于(并且应该用于)减少在整个应用程序生命周期中保持实例化的对象数量。查看 here 了解更多详情。

以下示例使用您定义的完全相同的对话框组件(有一些小的修改。如您所见,代码几乎相同。我只是在信号处理程序的末尾移动了对象的销毁。在这种情况下,我还更改了组件中定义的唯一 属性 的值,即 message,以向您展示完整的自定义。

import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4

Window {
    id: win
    visible: true

    RowLayout {
        anchors.fill: parent

        Button {
            text: "Button 1"
            Layout.alignment: Qt.AlignCenter
            onClicked: {
                var d1 = prompt.createObject(win)
                d1.message = text + " - Are you Sure?"
                d1.accepted.connect(function(){
                    console.info("accepted: " + text)
                    d1.destroy()
                })
                d1.rejected.connect(function(){
                    console.info("rejected: " + text)
                    d1.destroy()
                })
            }
        }

        Button {
            text: "Button 2"
            Layout.alignment: Qt.AlignCenter
            onClicked: {
                var d2 = prompt.createObject(win)
                d2.message = text + " - Are you Sure?"
                d2.accepted.connect(function(){
                    console.info("accepted: " + text)
                    d2.destroy()
                })
                d2.rejected.connect(function(){
                    console.info("rejected: " + text)
                    d2.destroy()
                })
            }
        }
    }

    Component {
        id: prompt

        Rectangle {
            id: root
            anchors.fill: parent

            property string message: "Are you Sure?"

            signal rejected()
            signal accepted()

            Text{
                id: messagetxt
                text:root.message
                horizontalAlignment: Text.AlignHCenter
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.verticalCenter: parent.verticalCenter
            }

            Rectangle {
                id: cancelButton
                anchors.bottom: parent.bottom
                anchors.left: parent.left
                width: 50
                height: 40

                Text {
                    anchors.centerIn: parent
                    text: "Cancel"
                }
                color: "red"

                MouseArea {
                    anchors.fill: parent
                    onClicked: rejected()
                }
            }

            Rectangle {
                id: okButton
                anchors.bottom: parent.bottom
                anchors.right: parent.right
                width: 50
                height: 40

                Text {
                    anchors.centerIn: parent
                    text: "Ok"
                }
                color: "blue"

                MouseArea {
                    anchors.fill: parent
                    onClicked: accepted()
                }
            }
        }
    }
}

如果您的组件没有像我对 Component 那样进行内联,而是保存在另一个文件中,您可以使用 createComponent,如上面提供的 link 中所述。您的主要 window 代码如下所示:

import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4

Window {
    id: win
    visible: true
    property var prompt

    RowLayout {
        anchors.fill: parent

        Button {
            text: "Button 1"
            Layout.alignment: Qt.AlignCenter
            onClicked: {
                var d1 = prompt.createObject(win)
                d1.message = text + " - Are you Sure?"
                d1.accepted.connect(function(){
                    console.info("accepted: " + text)
                    d1.destroy()
                })
                d1.rejected.connect(function(){
                    console.info("rejected: " + text)
                    d1.destroy()
                })
            }
        }

        Button {
            text: "Button 2"
            Layout.alignment: Qt.AlignCenter
            onClicked: {
                var d2 = prompt.createObject(win)
                d2.message = text + " - Are you Sure?"
                d2.accepted.connect(function(){
                    console.info("accepted: " + text)
                    d2.destroy()
                })
                d2.rejected.connect(function(){
                    console.info("rejected: " + text)
                    d2.destroy()
                })
            }
        }
    }

    Component.onCompleted: prompt = Qt.createComponent("Prompt.qml");
}

您应该始终检查组件创建是否正确执行(为了简洁起见,我没有这样做)。也就是说,代码与前一个代码相同。

最后但同样重要的是,我注意到您的代码中有一个错误:信号必须 始终 声明时带有括号,即使没有发出任何参数。它应该是 signal accepted(),而不是 signal accepted,其他信号和任何其他信号声明也是如此。