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
。如果要执行的操作很简单(例如函数调用),您甚至可以动态创建对话框并将信号与所需的行为绑定。自定义显然可以走得更远,改变 title
和 text
.
这是一个简单的例子,它遵循最后一种方法,并根据按下的按钮打印不同的文本。一旦对话框设置为不可见,它就会通过 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
,其他信号和任何其他信号声明也是如此。
当用户按下按钮时,我需要一个确认或警告对话框。根据他们选择 '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
。如果要执行的操作很简单(例如函数调用),您甚至可以动态创建对话框并将信号与所需的行为绑定。自定义显然可以走得更远,改变 title
和 text
.
这是一个简单的例子,它遵循最后一种方法,并根据按下的按钮打印不同的文本。一旦对话框设置为不可见,它就会通过 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
,其他信号和任何其他信号声明也是如此。