一、对话框
对话框窗口是一个用来完成简单任务或者和用户进行临时交互的顶层窗口,通常用于输入信息、确认信息或者提示信息。Qt Quick 提供了一系列的标准对话框,如 FileDialog
、ColorDialog
、MessageDialog
、FontDialog
等,它们继承与 Qt.Dialogs.Dialog
。Qt Quick 会首先使用平台相关的标准对话框,如果不可用,就使用自己实现的版本。
Qt.Dialogs.Dialog
的 visible
属性默认是 false
,即对话框是不可见的。如果我们要显示对话框,则需要调用 open()
方法或者手动将 visable
属性设置为 true
。我们可以通过 title
属性 设置对话框的标题。如果我们要关闭对话框,可以使用 close()
方法。
我们可以通过 modality
属性 设置对话框的模态性,它是一个枚举值,可以取值如下:
// 该对话框并非模态框,也不会阻止对其他窗口的输入操作
Qt.NonModal// 默认值,该对话框仅适用于单一窗口层次结构,并会阻止其父窗口、所有祖级窗口以及其父窗口和祖级窗口的所有兄弟窗口的输入操作
Qt.WindowModal// 该对话框与应用程序相关联,并会阻止所有窗口的输入操作
Qt.ApplicationModal
当用户 点击确认按钮 后,会触发 accepted()
信号。当用户 点击取消按钮 时,会触发 rejected()
信号。
Qt.Dialogs.Dialog
为类型,无法使用它创建控件,会报Dialog is not a type
错误。
我们可以在终端中使用 pip
安装 PySide6 模块。默认是从国外的主站上下载,因此,我们可能会遇到网络不好的情况导致下载失败。我们可以在 pip
指令后通过 -i
指定国内镜像源下载。
pip install pyside6 -i https://mirrors.aliyun.com/pypi/simple
国内常用的 pip
下载源列表:
- 阿里云 https://mirrors.aliyun.com/pypi/simple
- 中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple
- 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple
- 中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple
二、文件对话框
FileDialog
是 Qt Quick 中的文件对话框,它继承于 Qt.Dialogs.Dialog
,它可以用来选择已有的文件、文件夹,支持单选、多选,也可以用来在保存文件或创建文件夹时让用户提供一个名字。
我们可以通过 currentFolder
属性 设置打开文件对话框的初始目录。我们还可以通过 fileMode
属性 设置文件对话框的模式,它是一个枚举值,可以取值如下:
FileDialog.OpenFile // 该对话框用于选择一个现有的文件(默认选择的是当前文件)
FileDialog.OpenFiles // 该对话框用于选择多个已存在的文件
FileDialog.SaveFile // 该对话框用于选择任意文件,该文件无需存在
我们可以通过 nameFilters
属性 设置文件过滤器。nameFilters
是一个 字符串类型的列表,其中的每个字符串代表一种过滤器,它的语法格式如下:
nameFilters: ["过滤器名 (*.文件类型1 *.文件类型2 ...)"]
在我们设置过滤器之后,我们可以使用 selectedNameFilter
属性 设置默认的过滤器。它是一个属性组,我们可以通过如下属性设置:
selectedNameFilter.index // 通过索引选择过滤器
selectedNameFilter.name // 通过过滤器名选择过滤器
selectedNameFilter.extensions // 通过文件扩展名选择过滤器
selectedNameFilter.globs // 通过通配符选择过滤器
在我们选择单个文件后,选择的文件会保存在 selectedFile
属性中,如果我们选择了多个文件该属性指代第一个文件,此时我们可以使用 selectedFiles
属性,该属性是选择的文件的列表。
我们新建一个 template.py 文件。
import sysfrom PySide6.QtWidgets import QApplication
from PySide6.QtQml import QQmlApplicationEngineif __name__ == "__main__":app = QApplication(sys.argv) # 1.创建一个QApplication类的实例engine = QQmlApplicationEngine() # 2.创建QML引擎对象engine.load("template.qml") # 3.加载QML文件sys.exit(app.exec()) # 4.进入程序的主循环并通过exit()函数确保主循环安全结束
我们新建一个 template.qml 文件。
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Dialogs// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {width: 800 // 窗口的宽度height: 600 // 窗口的高度visible: true // 显示窗口color: "lightgray" // 窗口的背景颜色Text {id: textIdanchors.top: parent.topanchors.topMargin: 20anchors.horizontalCenter: parent.horizontalCenterfont.family: "楷体"font.pointSize: 14color: "#FF6666"}Button {id: buttonIdanchors.centerIn: parenttext: "文件对话框"font.pointSize: 24onClicked: {fileDialogId.open() // 打开文件对话框}}// 文件对话框FileDialog {id: fileDialogIdtitle: "选择文件" // 文件对话框的标题nameFilters: ["文本文件 (*.txt)", "图像文件 (*.jpg *.png)", "所有文件 (*)"] // 文件对话框的文件类型过滤器fileMode: FileDialog.OpenFiles // 设置文件对话框的模式onAccepted: {// FileDialog的selectedFile保存了用户选择的文件路径,如果是多个文件,则显示第一个textId.text = selectedFile}}
}
三、文件夹对话框
FolderDialog
对话框用来 选择一个文件夹,它继承于 Qt.Dialogs.Dialog
。我们可以通过 currentFolder
属性 设置打开文件夹对话框的初始目录,selectedFolder
属性 获取选中的文件夹路径。
修改 template.qml 文件的内容。
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Dialogs// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {width: 800 // 窗口的宽度height: 600 // 窗口的高度visible: true // 显示窗口color: "lightgray" // 窗口的背景颜色Text {id: textIdanchors.top: parent.topanchors.topMargin: 20anchors.horizontalCenter: parent.horizontalCenterfont.family: "楷体"font.pointSize: 14color: "#FF6666"}Button {id: buttonIdanchors.centerIn: parenttext: "文件夹对话框"font.pointSize: 24onClicked: {folderDialogId.open() // 打开文件对话框// 如果用户选择了文件夹,则将其设置为当前文件夹if (folderDialogId.selectedFolder) {folderDialogId.currentFolder = folderDialogId.selectedFolder}}}// 文件夹对话框FolderDialog {id: folderDialogIdtitle: "选择文件夹" // 文件对话框的标题onAccepted: {// FolderDialog的selectedFolder保存了用户选择的文件路径,如果是多个文件,则显示第一个textId.text = selectedFolder}}
}
四、颜色对话框
ColorDialog
对话框用来 选择一个颜色,它继承于 Qt.Dialogs.Dialog
。我们可以通过 selectedColor
属性 获取选择的颜色,通过 options
属性 设置颜色对话框的样式,它可以是以下值的集合:
ColorDialog.ShowAlphaChannel // 展示一个滑块以及用于输入alpha值的其他输入字段。
ColorDialog.NoButtons // 不要显示“打开”和“取消”按钮(适用于“实时对话框”)
ColorDialog.NoEyeDropperButton // 不要显示“吸管”按钮。此选项是在Qt6.6版本中添加的
ColorDialog.DontUseNativeDialog // 强制该对话使用非原生的快速实现方式
默认状态下,
options
属性未设置任何选项。
修改 template.qml 文件的内容。
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Dialogs// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {width: 800 // 窗口的宽度height: 600 // 窗口的高度visible: true // 显示窗口color: "lightgray" // 窗口的背景颜色Text {id: textIdanchors.top: parent.topanchors.topMargin: 20anchors.horizontalCenter: parent.horizontalCenterfont.family: "楷体"font.pointSize: 32color: "#EE5599"text: "多情自古伤离别,此恨绵绵无绝期。"}Button {id: buttonIdanchors.centerIn: parenttext: "颜色对话框"font.pointSize: 24onClicked: {colorDialogId.open() // 打开颜色对话框}}// 颜色对话框ColorDialog {id: colorDialogIdtitle: "选择颜色" // 颜色对话框的标题selectedColor: textId.color // 初始化颜色对话框的颜色为文本框的颜色options: ColorDialog.ShowAlphaChannel // 显示颜色对话框的Alpha通道onAccepted: {// ColorDialog的selectedColor保存了用户选择的颜色textId.color = selectedColor}}
}
options
属性应在显示对话框之前设置选项。在对话框可见的情况下设置选项,并不能保证能立即对对话框产生影响。
五、字体对话框
FontDialog
对话框用来 选择一个字体,它继承于 Qt.Dialogs.Dialog
。我们可以通过 selectedFont
属性 获取选择的字体,通过 options
属性 设置字体对话框的样式,.它是一个枚举值,它可以是以下值的集合:
// 默认情况下,未设置任何选项,这意味着所有字体类型都会显示出来,并且对话框中会显示标准的“选择”和“取消”按钮。
FontDialog.ScalableFonts // 显示可缩放字体
FontDialog.NonScalableFonts // 显示不可缩放字体
FontDialog.MonospacedFonts // 显示等宽字体
FontDialog.ProportionalFonts // 显示比例字体
FontDialog.NoButtons // 不要显示“打开”和“取消”按钮(适用于“实时对话框)
FontDialog.DontUseNativeDialog // 强制该对话使用非原生的快速实现方式
默认情况下,
option
属性未设置任何选项,这意味着所有字体类型都会显示出来,并且对话框中会显示标准的“选择”和“取消”按钮。
修改 template.qml 文件的内容。
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Dialogs// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {width: 800 // 窗口的宽度height: 600 // 窗口的高度visible: true // 显示窗口color: "lightgray" // 窗口的背景颜色Text {id: textIdanchors.top: parent.topanchors.topMargin: 20anchors.horizontalCenter: parent.horizontalCenterfont.family: "楷体"font.pointSize: 32color: "#FF66CC"text: "科学技术的价值,在于使用者的意图。"}Button {id: buttonIdanchors.centerIn: parenttext: "字体对话框"font.pointSize: 24onClicked: {fontDialogId.open() // 打开字体对话框}}// 字体对话框FontDialog {id: fontDialogIdtitle: "选择字体" // 字体对话框的标题selectedFont: textId.font // 初始化字体对话框的字体为文本框的字体onAccepted: {// FontDialog的selectedFont保存了用户选择的字体textId.font = selectedFont}}
}
当我们运行程序点击按钮弹出字体对话框时,终端会输入如下内容,虽然不影响使用,但也不美观。
qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/qml/FontDialogContent.qml:223:16: QML Label: The current style does not support customization of this control (property: "label" item: Label_QMLTYPE_24(0x1c0ab7fdb90, parent=0x0, geometry=0,0 0x0 ?)). Please customize a non-native style (such as Basic, Fusion, Material, etc). For more information, see: https://doc.qt.io/qt-6/qtquickcontrols2-customize.html#customization-reference
这个问题大致是说当前样式不支持对此控件的自定义设置,请自定义非原生样式(例如基本样式 Basic
、融合样式 Fusion
、材料样式 Material
等)。此时我们需要在加载 QML 文件之前,使用 QQuickStyle.setStyle()
方法设置样式。如果在加载 QML 之后设置,会报如下错误:
ERROR: QQuickStyle::setStyle() must be called before loading QML that imports Qt Quick Controls 2.
修改 template.py 文件的内容。
import sysfrom PySide6.QtWidgets import QApplication
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtQuickControls2 import QQuickStyleif __name__ == "__main__":app = QApplication(sys.argv) # 1.创建一个QApplication类的实例engine = QQmlApplicationEngine() # 2.创建QML引擎对象QQuickStyle.setStyle("Material") # 3.设置QML引擎的样式engine.load("template.qml") # 4.加载QML文件sys.exit(app.exec()) # 5.进入程序的主循环并通过exit()函数确保主循环安全结束
在显示对话框之前应先设置选项。在对话框可见的情况下设置选项,并不能保证能立即对对话框产生影响(这取决于选项以及所使用的平台)。
六、消息对话框
MessageDialog
用来显示一个弹出消息框。我们可以使用 text
属性 设置要在消息对话框中显示的文本内容,使用 detailedText
属性 设置要在详细信息区域显示的文本内容, informativeText
属性 包含了一段具有解释性的文字,该文字对消息进行了更详细的描述。
我们可以使用 buttons
属性 设置使用的按钮集合,它可以是以下值的集合:
MessageDialog.NoButton // 默认值,该对话框中没有按钮MessageDialog.Ok // 一个通过AcceptRole定义的“确定”按钮
MessageDialog.Open // 一个通过AcceptRole定义的“打开”按钮
MessageDialog.Save // 一个通过AcceptRole定义的“保存”按钮
MessageDialog.SaveAll // 一个通过AcceptRole定义的“保存所有”按钮
MessageDialog.Retry // 一个通过AcceptRole定义的“重试”按钮
MessageDialog.Ignore // 一个通过AcceptRole.定义的“忽略”按钮MessageDialog.Cancel // 一个通过RejectRole定义的“取消”按钮
MessageDialog.Close // 一个通过RejectRole定义的“关闭”按钮
MessageDialog.Abort // 一个通过RejectRole定义的“中止”按钮MessageDialog.Discard // 根据不同的平台,会有一个“丢弃”或“不保存”按钮,其功能由DestructiveRole来定义MessageDialog.Apply // 一个通过ApplyRole定义的“应用”按钮MessageDialog.Reset // 一个通过ResetRole定义的“重置”按钮
MessageDialog.RestoreDefaults // 一个通过ResetRole定义的“恢复默认值”按钮MessageDialog.Help // 一个通过HelpRole定义的“帮助”按钮MessageDialog.Yes // 一个通过YesRole定义的“是”按钮
MessageDialog.YesToAll // 一个通过YesRole定义的“全部同意”按钮MessageDialog.No // 一个通过NoRole定义的“否”按钮
MessageDialog.NoToAll // 一个通过NoRole定义的“全部拒绝”按钮
修改 template.qml 文件的内容。
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Dialogs// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {width: 800 // 窗口的宽度height: 600 // 窗口的高度visible: true // 显示窗口color: "lightgray" // 窗口的背景颜色Text {id: textIdanchors.top: parent.topanchors.topMargin: 20anchors.horizontalCenter: parent.horizontalCenterfont.family: "楷体"font.pointSize: 32color: "#FF66CC"}Button {id: buttonIdanchors.centerIn: parenttext: "消息对话框"font.pointSize: 24onClicked: {messageDialogId.open() // 打开消息对话框}}// 消息对话框MessageDialog {id: messageDialogId// 消息对话框的按钮buttons: MessageDialog.Ok | MessageDialog.Canceltitle: "消息对话框" // 消息对话框的标题text: "确定要执行此操作吗?" // 消息对话框的文本内容onAccepted: {textId.text = "用户点击了确定按钮"}onRejected: {// 用户点击了取消按钮textId.text = "用户点击了取消按钮"}}
}
七、自定义对话框
要显示一个原生对话框,我们需要创建一个实现 QtQuick.Controls.Dialog
类的实例,然后再设置所需要的属性。在 QtQuick.Dialogs
模块中同样有一个 Dialog
控件,但是我们不能直接使用,会报 Dialog is not a type
错误。
QtQuick.Controls.Dialog
的 visible
属性默认是 false
,即对话框是不可见的。如果我们要显示对话框,则需要调用 open()
方法或者手动将 visable
属性设置为 true
。我们可以通过 title
属性 设置对话框的标题, head
属性 设置页眉,footer
属性 设置页脚。如果我们要关闭对话框,可以使用 close()
方法。
我们可以使用 standardButtons
属性 设置使用的按钮集合,它可以取值如下:
Dialog.NoButton // 默认值,该对话框中没有按钮Dialog.Ok // 一个通过AcceptRole定义的“确定”按钮
Dialog.Open // 一个通过AcceptRole定义的“打开”按钮
Dialog.Save // 一个通过AcceptRole定义的“保存”按钮
Dialog.SaveAll // 一个通过AcceptRole定义的“保存所有”按钮
Dialog.Retry // 一个通过AcceptRole定义的“重试”按钮
Dialog.Ignore // 一个通过AcceptRole.定义的“忽略”按钮Dialog.Cancel // 一个通过RejectRole定义的“取消”按钮
Dialog.Close // 一个通过RejectRole定义的“关闭”按钮
Dialog.Abort // 一个通过RejectRole定义的“中止”按钮Dialog.Discard // 根据不同的平台,会有一个“丢弃”或“不保存”按钮,其功能由DestructiveRole来定义Dialog.Apply // 一个通过ApplyRole定义的“应用”按钮Dialog.Reset // 一个通过ResetRole定义的“重置”按钮
Dialog.RestoreDefaults // 一个通过ResetRole定义的“恢复默认值”按钮Dialog.Help // 一个通过HelpRole定义的“帮助”按钮Dialog.Yes // 一个通过YesRole定义的“是”按钮
Dialog.YesToAll // 一个通过YesRole定义的“全部同意”按钮Dialog.No // 一个通过NoRole定义的“否”按钮
Dialog.NoToAll // 一个通过NoRole定义的“全部拒绝”按钮
当用户 点击确认按钮 后,会触发 accepted()
信号。当用户 点击取消按钮 时,会触发 rejected()
信号。当用户点击对话框的按钮之后,我们可以通过 result
属性 获取用户点击的结果,如果值为 Dialog.Accepted
则表示 该对话框已被接受,如果值为 Dialog.Rejected
表示 该对话框已被拒绝。
当我们点击对话框外边,会发现对话框消失了,这相当于我们点击取消按钮关闭对话框。此时,我们可以通过 closePolicy
属性 设置对话框的关闭策略,它可以是以下值的集合:
Popup.NoAutoClose // 只有在手动发出关闭指令的情况下,该弹出窗口才会关闭
Popup.CloseOnPressOutside // 当鼠标在弹出窗口外按下时,该弹出窗口将会关闭
Popup.CloseOnPressOutsideParent // 当鼠标在其父元素外按下时,弹出窗口将会关闭
Popup.CloseOnReleaseOutside // 当鼠标在弹出外释放时,该弹出窗口将会关闭
Popup.CloseOnReleaseOutsideParent // 当鼠标在其父元素外释放时,弹出窗口会关闭
Popup.CloseOnEscape // 当按下“退出”键且弹出窗口具有活动焦点时,该弹出窗口将会关闭
closePolicy
属性的默认值是Popup.CloseOnEscape | Popup.CloseOnPressOutside
。
CloseOnPress
和CloseOnRelease
策略仅适用于弹出窗口之外的事件。也就是说,如果有两个弹出窗口打开,而第一个弹出窗口的策略为Popup.CloseOnPressOutside
,那么点击第二个弹出窗 口并不会导致第一个弹出窗口关闭。
修改 template.qml 文件的内容。
import QtQuick.Window
import QtQuick.Controls// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {id: windowIdwidth: 800 // 窗口的宽度height: 600 // 窗口的高度visible: true // 显示窗口color: "lightgray" // 窗口的背景颜色Page {anchors.fill: parentheader: ToolBar {ToolButton {icon.name: "document-new"text: "添加信息"display: AbstractButton.TextUnderIcononClicked: {dialogId.open() // 打开对话框}}}TextArea {id: textAreaIdanchors.fill: parentreadOnly: truefont.family: "楷体"font.pointSize: 32color: "#FF66CC"}}// 对话框Dialog {id: dialogIdwidth: 200anchors.centerIn: parent// 对话框是模态的,即用户必须先关闭对话框,才能继续与应用程序交互modal: true // 对话框的关闭策略closePolicy: Popup.NoAutoClosetitle: "添加信息" // 消息对话框的标题Column {id: columnIdanchors.fill: parentspacing: 10Text {id: nameTextIdfont.family: "楷体"font.pointSize: 16color: "#FF66CC"text: "请输入姓名:"}TextField {id: nameTextFieldIdwidth: columnId.widthfont: nameTextId.fontcolor: nameTextId.colorplaceholderText: "请输入姓名"}Text {font: nameTextId.fontcolor: nameTextId.colortext: "请选择性别:"}ComboBox {id: sexComboBoxIdwidth: columnId.widthmodel: ["保密", "男", "女"]font: nameTextId.font}Text {font: nameTextId.fontcolor: nameTextId.colortext: "请输入年龄:"}SpinBox {id: ageSpinBoxIdwidth: columnId.widtheditable: truefrom: 0to: 300stepSize: 1font: nameTextId.font}}footer: ToolBar {Row {anchors.right: parent.rightToolButton {id: resetToolButtonIdwidth: 60text: "重置"onClicked: {nameTextFieldId.text = ""sexComboBoxId.currentIndex = 0ageSpinBoxId.value = 0}}ToolButton {width: resetToolButtonId.widthtext: "取消"onClicked: {dialogId.reject() // 拒绝对话框}}ToolButton {width: resetToolButtonId.widthtext: "确认"onClicked: {if (nameTextFieldId.text.trim() != "") {dialogId.accept()} else {nameTextFieldId.text = ""errorDialogId.open()}}}}}onAccepted: {textAreaId.text += (nameTextFieldId.text + " " + sexComboBoxId.currentText + " " + ageSpinBoxId.value + "\n")resetToolButtonId.clicked()}onRejected: {resetToolButtonId.clicked()}}Dialog {id: errorDialogIdanchors.centerIn: parentmodal: true standardButtons: Dialog.Ok // 对话框的按钮title: "错误" // 对话框的标题Text {anchors.centerIn: parentfont.family: "楷体"font.pointSize: 16color: "#FF66CC"text: "姓名不能为空"}}
}