PySide6 简介
PySide6 是 Qt 官方为 Python 提供的跨平台图形界面(GUI)开发库,是 Qt C++ 框架的官方 Python 绑定,让你能用 Python 的简洁语法调用 Qt 的强大功能,开发出专业、美观的桌面应用。
本文介绍了 PySide6 的基础知识、环境搭建及应用开发流程。内容涵盖 PySide6 的特点与优势,如何在 Ubuntu 环境下配置 Python 虚拟环境并安装依赖。详细讲解了使用 Qt Widgets 和 Qt Quick 两种技术创建桌面应用的代码示例,包括解决常见运行报错的方法。此外,文章还介绍了 Qt Creator 编辑器的使用、UI 设计工具(Designer 和 Design Studio)的配置,以及 QML 语法的深入解析,包括对象声明、属性绑定、信号槽机制和常用控件样式。适合希望使用 Python 进行跨平台 GUI 开发的初学者参考。

PySide6 是 Qt 官方为 Python 提供的跨平台图形界面(GUI)开发库,是 Qt C++ 框架的官方 Python 绑定,让你能用 Python 的简洁语法调用 Qt 的强大功能,开发出专业、美观的桌面应用。
核心价值与特点
| 特点 | 说明 |
|---|---|
| 官方绑定,生态统一 | 由 Qt 公司官方维护,与 Qt 6 框架保持同步更新,可以无缝使用 Qt 的成熟工具、文档和模块。 |
| 跨平台原生体验 | 一次编写代码,即可在 Windows、macOS、Linux 三大主流桌面系统上编译运行,提供接近原生的性能和外观。 |
| 双重许可,使用灵活 | 采用 LGPLv3/GPLv3 开源许可及商业许可。对于大部分个人或商业项目,这意味着你可以在遵守协议的前提下免费使用它。 |
| 信号与槽机制 | 采用独特的'信号与槽'机制处理界面交互,比传统的'回调'方式更直观、安全,能高效地连接用户操作与程序功能。 |
如何选择与学习
这里使用 Ubuntu 24.04 + Miniconda3
创建 Python 环境:
conda create -n pyside6_env python=3.8
安装依赖:
pip install pyside6
验证安装:
import PySide6.QtCore # Prints PySide6 version
print(PySide6.__version__)
Qt 提供了两种用于构建用户界面的技术:
这两种技术都可以使用拖放工具来创建界面,Qt Widgets 可使用 pyside6-designer(安装 pyside6 时包含),Qt Quick 可使用 Qt Design Studio。
# hello_world.py
import sys
import random
from PySide6 import QtCore, QtWidgets, QtGui
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.hello = ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"]
self.button = QtWidgets.QPushButton("Click me!")
self.text = QtWidgets.QLabel("Hello World", alignment=QtCore.Qt.AlignCenter)
self.layout = QtWidgets.QVBoxLayout(self)
self.layout.addWidget(self.text)
self.layout.addWidget(self.button)
self.button.clicked.connect(self.magic)
@QtCore.Slot()
def magic(self):
self.text.setText(random.choice(self.hello))
if __name__ == "__main__":
app = QtWidgets.QApplication([])
widget = MyWidget()
widget.resize(800, 600)
widget.show()
sys.exit(app.exec())
运行报错示例:
qt.qpa.plugin: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin...
解决:
sudo apt install libxcb-cursor0 libxcb-xinerama0 libxcb-randr0 libxcb-render0 libxcb-shape0 libxcb-xfixes0 libxcb-xkb-dev libxkbcommon-x11-0
# hello_world_quick.py
import sys
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.addImportPath(sys.path[0])
engine.loadFromModule("Example", "Main")
if not engine.rootObjects():
sys.exit(-1)
exit_code = app.exec()
del engine
sys.exit(exit_code)
UI 定义(Main.qml):
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Window {
width: 300
height: 200
visible: true
title: "Hello World"
readonly property list<string> texts: ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"]
function setText() {
var i = Math.round(Math.random()*3)
text.text = texts[i]
}
ColumnLayout {
anchors.fill: parent
Text {
id: text
text: "Hello World"
Layout.alignment: Qt.AlignHCenter
}
Button {
text: "Click me"
Layout.alignment: Qt.AlignHCenter
onClicked: setText()
}
}
}
创建文件 qmldir:
module Example Main 254.0 Main.qml
创建文件夹 Example,把 qmldir 文件和 Main.qml 放入该目录。
运行效果:
python hello_world_quick.py
下载离线安装包(也可以在线安装)。
赋权,执行:
chmod +x qt-creator-opensource-linux-x86_64-18.0.1.run
./qt-creator-opensource-linux-x86_64-18.0.1.run
创建项目。
此时,点击 构建 -> QML Preview 编译 pyside6 工具:
pyside6-android-deploy pyside6-designer pyside6-metaobjectdump pyside6-qmlimportscanner pyside6-qtpy2cpp pyside6-assistant pyside6-genpyi pyside6-project pyside6-qmllint pyside6-rcc pyside6-balsam pyside6-linguist pyside6-qml pyside6-qmlls pyside6-svgtoqml pyside6-balsamui pyside6-lrelease pyside6-qmlcachegen pyside6-qmltyperegistrar pyside6-uic pyside6-deploy pyside6-lupdate pyside6-qmlformat pyside6-qsb
各类工具的使用方式参考官方文档:https://doc.qt.io/qtforpython-6/tools/index.html
例如使用 pyside6-qml。
用于设计
.ui文件
直接启动即可:
pyside6-designer
用于设计
.qml文件
使用在线安装:
# 赋权
chmod +x qt-online-installer-linux-x64-4.10.0.run
# 执行
./qt-online-installer-linux-x64-4.10.0.run
安装路径通常在 /home/用户名/Qt/Tools/QtDesignStudio/bin。
QML 是一种多范式语言,允许通过对象的属性及其与其他对象变化的关联与响应方式来定义对象。与纯命令式代码不同,QML 的声明式语法将属性和行为变化直接整合到单个对象的定义中,不需要通过一系列按步骤处理的语句来表达属性和行为的变化。在需要复杂自定义应用行为的情况下,这些属性定义还可以包含命令式代码。
QML 源代码通常通过 QML 文档由引擎加载,这些文档是独立的 QML 代码文件。它们可用于定义 QML 对象类型,以便在应用程序中重复使用。注意,类型名称必须以大写字母开头,才能在 QML 文件中声明为 QML 对象类型。
QML 文档在文件顶部可包含一个或多个导入声明。导入项可以是以下任意一种:
语法:
import <ModuleIdentifier>[<Version.Number>][as<Qualifier>]
import "<Directory>"
import "<JavaScriptFile>"[as<ScriptIdentifier>]
示例:
import QtQuick 2.0
import QtQuick.LocalStorage 2.0 as Database
import "../privateComponents"
import "somefile.js" as Script
注意,导入 JavaScript 文件时必须使用限定名称,以便访问它们提供的属性和方法。
对象声明由其对象类型的名称后接一对大括号构成,所有属性和子对象随后在这些大括号内进行声明。
这是一个简单的对象声明:
Rectangle {
width: 100
height: 100
color: "red"
}
这声明了一个类型为 Rectangle 的对象,后面跟着一对大括号,包含了为该对象定义的属性。Rectangle 类型是由 QtQuick 模块提供的类型,而此处定义的属性是矩形的宽度、高度和颜色属性的值。(这些属性是 Rectangle 类型提供的,如 Rectangle 文档中所述。)如果上述对象是 QML 文档的一部分,引擎可以加载它。也就是说,如果在源代码中添加导入 QtQuick 模块的 import 语句(以使 Rectangle 类型可用),如下所示:
import QtQuick 2.0
Rectangle {
width: 100
height: 100
color: "red"
}
当将上述代码放入 .qml 文件中并被 QML 引擎加载时,它会使用 QtQuick 模块提供的 Rectangle 类型创建一个 Rectangle 对象。
显然,本例中声明的 Rectangle 对象非常简单,因为它定义的仅仅是一些属性值。要创建更有用的对象,对象声明可以定义许多其他类型的属性:这些在 QML 对象属性文档中有讨论。
任何对象声明都可以通过嵌套对象声明来定义子对象。通过这种方式,任何对象声明都会隐式声明一个可能包含任意数量子对象的对象树。
例如,下面的 Rectangle 对象声明包括一个 Gradient 对象声明,而该 Gradient 对象声明又包含两个 GradientStop 声明:
import QtQuick 2.0
Rectangle {
width: 100
height: 100
gradient: Gradient {
GradientStop { position: 0.0; color: "yellow" }
GradientStop { position: 1.0; color: "green" }
}
}
当引擎加载这段代码时,它会创建一个以矩形对象为根的对象树;该对象有一个渐变子对象,渐变子对象又有两个渐变停点子对象。
但是请注意,这里所说的是 QML 对象树中的父子关系,而不是视觉场景中的父子关系。在视觉场景中父子关系的概念由 QtQuick 模块中的 Item 类型提供,Item 类型是大多数 QML 类型的基类,因为大多数 QML 对象都旨在进行视觉渲染。例如,Rectangle 和 Text 都是基于 Item 的类型,下面的例子中,一个 Text 对象被声明为 Rectangle 对象的视觉子对象:
import QtQuick 2.0
Rectangle {
width: 200
height: 200
color: "red"
Text {
anchors.centerIn: parent
text: "Hello, QML!"
}
}
在上述代码中,当 Text 对象引用其父值时,它指的是其视觉父对象,而不是对象树中的父对象。在这种情况下,它们是相同的:在 QML 对象树的上下文以及视觉场景的上下文中,Text 对象的父对象都是 Rectangle 对象。然而,虽然可以修改 parent 属性来改变视觉父对象,但无法在对象树的上下文中使用 QML 修改对象的父对象。
另外,请注意,Text 对象在声明时并未将其分配给 Rectangle 的某个属性,这与前面的示例不同,前面的示例将 Gradient 对象分配给了矩形的 gradient 属性。这是因为 Item 的 children 属性已被设置为类型的默认属性,从而启用了这种更方便的语法。
在 QML 中,注释的语法与 JavaScript 类似:
// 开始,并在行末结束。/* 开始,并以 */ 结束Text {
text: "Hello world!"
// a basic greeting
/* We want this text to stand out from the rest so we give it a large size and different font. */
font.family: "Helvetica"
font.pointSize: 24
}
Text {
text: "Hello world!"
// opacity: 0.5
}
在处理 QML 代码时,引擎会忽略注释。注释对于解释代码某一部分的作用非常有用,无论是供日后参考还是向他人说明实现方式。
每种 QML 对象类型都有一组已定义的属性,集合如下:
| QML 属性类型 | OOP 类比 | 说明 |
|---|---|---|
| id 属性 | 对象实例引用(指针/引用名) | 不是数据成员,而是用于在 QML 作用域内标识对象的特殊名称,类似实例变量名(但由运行时管理)。 |
| property 属性 | 类的数据成员(字段) | 可以包含基础类型(int, string)、对象类型、列表等,支持绑定表达式(这是 QML 的核心特性,区别于普通的赋值)。 |
| signal 属性 | 事件/信号声明(类似 Qt C++ 中的 signals:) | 定义对象可以发出的信号,是接口的一部分。 |
| signal handler 属性 | 事件监听器/回调方法 | 名称格式 on<SignalName>,对应信号的槽,可包含 JavaScript 代码。 |
| method 属性 | 类的成员方法 | 可以在 JavaScript 中定义函数,可以被调用。 |
| attached 属性 | 静态成员或扩展属性(来自另一个类) | 允许访问与类型关联的附加元信息或服务(例如 Keys、Component 等)。 |
| enumeration 属性 | 枚举类型 | 在 QML 中通常通过 <Type>.<EnumValue> 访问。 |
一个 QML 元素最多只能有一个 id 属性。这个属性是语言本身提供的,不能被任何 QML 对象类型重新定义或覆盖。
可以为对象实例的 id 属性分配一个值,以允许该对象被其他对象识别和引用。这个 id 必须以小写字母或下划线开头,且不能包含除字母、数字和下划线之外的字符。它也不能是 JavaScript 关键字。
如果在 QML 中使用了不适合作为 JavaScript 标识符的名称,例如 as,你将无法在 JavaScript 中引用该对象的 id,从而使该 id 基本上没有用。不过,你仍然可以使用 C++ 的 QQmlContext 来与这样的 id 进行交互。
下面是一个 TextInput 对象和一个 Text 对象。TextInput 对象的 id 值设置为 'myTextInput'。Text 对象将其 text 属性设置为与 TextInput 的 text 属性相同的值,通过引用 myTextInput.text。现在,这两个对象都会显示相同的文本:
import QtQuick
Column {
width: 200; height: 200
TextInput {
id: myTextInput
text: "Hello World"
}
Text {
text: myTextInput.text
}
}
一个对象可以在其创建的 QML 上下文中的任何位置通过其 id 引用。因此,一个 id 值在其上下文中必须始终是唯一的。
该上下文也通过 QQmlContext 层次结构向 C++ 暴露。例如可以通过 qmlContext 函数获取特定对象的上下文,并请求获取同一上下文中的其他对象:
QObject *textInput = qmlContext(theColumn)->objectForName("myTextInput");
一旦创建了对象实例,其 id 属性的值就不能更改。虽然它看起来像一个普通的属性,但 id 属性并不是普通的属性,其有特殊语义;例如,在上面的例子中无法访问 myTextInput.id。
property 属性是对象的一个特性,可以被赋予静态值或绑定到动态表达式。其他对象可以读取 property 的值。通常,它也可以被另一个对象修改,除非某个特定的 QML 类型明确禁止对某个特定 property 进行修改。
可以理解为:Property 是特殊的 Attribute,当作数据容器使用,具有值和变化通知,而 Attributes 是所有对象特性的总称。
在 C++ 中,可以通过注册一个类的 Q_PROPERTY 来为类型定义 property,该类随后会在 QML 类型系统中注册。或者,也可以在 QML 文档中的对象声明中使用以下语法定义对象类型的自定义 property:
[default] [final] [required] [readonly] property <propertyType> <propertyName>
通过这种方式,对象声明可以向外部对象公开特定的值,或者更容易地维护一些内部状态。
property 名称必须以小写字母开头,并且只能包含字母、数字和下划线。JavaScript 保留字不能用作有效的属性名称。default、final、required 和 readonly 关键字是可选的,并会修改所声明属性的语义。
声明自定义 property 会隐式创建一个该 property 的值变化信号,以及一个相关的信号处理程序,称为 on<PropertyName>Changed,其中 <PropertyName> 是 property 名称,首字母大写。
例如,以下对象声明定义了一个从 Rectangle 基类型派生的新类型。它有两个 property,并为其中一个 property 实现了信号处理逻辑:
Rectangle {
property color previousColor
property color nextColor
onNextColorChanged: console.log("The next color will be: "+ nextColor.toString())
}
任何 QML 值类型都可以用作自定义 property 类型。例如,以下都是有效的声明:
Item {
property int someNumber
property string someString
property url someUrl
}
枚举值只是整数值,也可以使用 int 类型来引用它们。
一些值类型是由 QtQuick 模块提供的,因此除非导入该模块,否则不能将其用作 property 类型。
请注意,var 值类型是一种通用占位符类型,可以包含任何类型的值,包括列表和对象:
property var someNumber: 1.5
property var someString: "abc"
property var someBool: true
property var someList: [1, 2, "three", "four"]
property var someObject: Rectangle { width: 100; height: 100; color: "red" }
此外,任何 QML 对象类型都可以用作 property 类型。例如:
property Item someItem
property Rectangle someRectangle
这同样适用于自定义 QML 类型。如果一个 QML 类型在名为 ColorfulButton.qml 的文件中定义(该文件所在的目录随后被导入),那么类型为 ColorfulButton 的 property 也是有效的。
对象实例的 property 值可以通过两种方式来指定:
在任何一种情况下,值可以是静态值,也可以是绑定表达式的值。
在初始化时为 property 赋值的语法是:
<propertyName> : <value>
如果需要,初始化值赋值可以与对象声明中的 property 定义结合使用。在这种情况下,property 定义的语法变为:
[default] property <propertyType> <propertyName> : <value>
下面是一个 property 值初始化的示例:
import QtQuick
Rectangle {
color: "red"
property color nextColor: "blue"
// combined property declaration and initialization
}
而命令式值赋值是指从命令式 JavaScript 代码向 property 分配属性值,直接使用 JavaScript 语法的赋值运算符,如下所示:
[<objectId>.]<propertyName> = value
示例:
import QtQuick
Rectangle {
id: rect
Component.onCompleted: {
rect.color = "red"
}
}
信号是对象发出的通知,用于说明某个事件已经发生:
MouseArea 类型有一个 clicked 信号,当用户在鼠标区域内点击时会发出该信号。当特定信号发出时,对象可以通过信号处理器接收到通知。
在 C++ 中,可以通过注册一个类的 Q_SIGNAL 并将其注册到 QML 类型系统中,为某个类型定义一个信号。或者,也可以使用以下语法在 QML 文档中的对象声明中为对象类型定义自定义信号:
signal <signalName>[([<parameterName>: <parameterType>[, ...]])]
以下是三个信号声明的示例:
import QtQuick
Item {
signal clicked
signal hovered()
signal actionPerformed(action: string, actionResult: int)
}
如果信号没有参数,'()' 括号是可选的。如果使用了参数,则必须声明参数类型,例如上面 actionPerformed 信号的 string 和 int 参数。
要发出信号,请将其作为方法调用。当信号被发出时,任何相关的信号处理器都会被调用,处理器可以使用定义的信号参数名称来访问相应的参数。
信号处理器的声明语法为 on<Signal>,其中 <Signal> 是信号的名称,首字母大写。注意,信号处理器必须在发出该信号的对象定义中声明,并包含要执行的 JavaScript 代码块。
例如,下面的 onClicked 信号处理器在 MouseArea 对象定义中声明,当 MouseArea 被点击时调用,从而在控制台打印消息:
import QtQuick
Item {
width: 100; height: 100
MouseArea {
anchors.fill: parent
onClicked: {
console.log("Click!")
}
}
}
信号处理器是一种特殊的方法属性,当相关信号被发射时,QML 引擎会调用该方法的实现。在 QML 中向对象定义添加信号会自动向对象定义添加一个关联的信号处理器,默认情况下,该处理器实现为空,客户端可以提供实现来实现程序逻辑。
例如以下 SquareButton 类型,其定义在 SquareButton.qml 文件中,如下所示,并带有激活(activated)和停用(deactivated)的信号:
// SquareButton.qml
Rectangle {
id: root
signal activated(xPosition: real, yPosition: real)
signal deactivated
property int side: 100
width: side; height: side
MouseArea {
anchors.fill: parent
onReleased: root.deactivated()
onPressed: mouse => root.activated(mouse.x, mouse.y)
}
}
这些信号可以被同一目录下另一个 QML 文件中的任何 SquareButton 对象接收,在该文件中信号处理器的实现由客户端提供:
// myapplication.qml
SquareButton {
onDeactivated: console.log("Deactivated!")
onActivated: (xPosition, yPosition) => {
console.log(`Activated at ${xPosition}, ${yPosition}`)
}
}
信号处理器不需要声明它们的参数类型,因为信号已经指定了类型。上面显示的箭头函数语法不支持类型注解。
属性信号 Property Change Signal 的信号处理器使用 on<Property>Changed 的语法形式,其中 <Property> 是属性的名称,并且首字母大写。例如,虽然 TextInput 类型的文档中没有记录 textChanged 信号,但由于 TextInput 有一个 text 属性,隐式提供了此信号,因此可以编写 onTextChanged 信号处理程序,在此属性更改时调用:
import QtQuick
TextInput {
text: "Change this!"
onTextChanged: console.log(`Text has changed to: ${text}`)
}
对象类型的方法是一个函数,可以被调用以执行某些处理或触发进一步的事件。方法可以连接到信号,以便在信号发出时自动调用。
在 C++ 中,可以通过给类的函数加上 Q_INVOKABLE 标签,从而将其注册到 QML 类型系统中,或者将其注册为类的 Q_SLOT,为某个类型定义方法。或者,也可以在 QML 文档中的对象声明中使用以下语法添加自定义方法:
function <functionName>([<parameterName>[:<parameterType>][,...]])[:<returnType>] {<body>}
可以向 QML 类型添加方法,以定义独立的、可重用的 JavaScript 代码块。这些方法可以在内部调用,也可以被外部对象调用。
与信号不同,方法的参数类型不必声明,因为默认类型为 var。然而,你应当声明它们,以帮助 qmlcachegen 生成更高性能的代码,并提高可维护性。
下面是一个带有 calculateHeight() 方法的矩形,当分配高度值时会调用该方法:
import QtQuick
Rectangle {
id: rect
function calculateHeight(): real {
return rect.width / 2;
}
width: 100
height: calculateHeight()
}
如果方法有参数,则可以在方法内部通过名称访问它们。如下所示,当点击 MouseArea 时,它会调用 moveTo() 方法,该方法可以引用接收到的 newX 和 newY 参数来重新定位文本:
import QtQuick
Item {
width: 200; height: 200
MouseArea {
anchors.fill: parent
onClicked: mouse => label.moveTo(mouse.x, mouse.y)
}
Text {
id: label
function moveTo(newX: real, newY: real) {
label.x = newX; label.y = newY;
}
text: "Move me!"
}
}
附加属性和附加信号处理器是使对象能够被标注额外属性或信号处理程序的机制。QML 类型的实现可以选择在 C++ 中创建一个附加类型,该类型具有特定的属性和信号,然后可以在运行时创建该类型的实例并附加到特定对象上,从而允许这些对象访问附加类型的属性和信号。
对附加属性和处理器的引用采用以下语法形式:
<AttachingType>.<propertyName>
<AttachingType>.on<SignalName>
例如,ListView 类型有一个附加属性 ListView.isCurrentItem,该属性可用于 ListView 中的每个委托对象。每个单独的委托对象可以使用它来判断自己是否是视图中当前选中的项目:
import QtQuick
ListView {
width: 240; height: 320
model: 3
delegate: Rectangle {
width: 100; height: 30
color: ListView.isCurrentItem ? "red" : "yellow"
}
}
在这种情况下,附加类型的名称是 ListView,相关的属性是 isCurrentItem,因此附加属性被称为 ListView.isCurrentItem。
附加的信号处理器也以相同的方式引用。例如,Component.onCompleted 附加信号处理器通常用于在组件的创建过程完成后执行一些 JavaScript 代码。在下面的示例中,一旦 ListModel 完全创建,其 Component.onCompleted 信号处理程序将自动被调用以填充模型:
import QtQuick
ListView {
width: 240; height: 320
model: ListModel {
id: listModel
Component.onCompleted: {
for(let i = 0; i < 10; i++) {
append({Name: `Item ${i}`})
}
}
}
delegate: Text {
text: index
}
}
由于附加类型的名称是 Component 并且该类型有一个 completed 信号,因此附加的信号处理程序被称为 Component.onCompleted。
一个常见的错误是:附加属性和信号处理器可以直接从附加了这些属性的对象的子对象访问,实际上并非如此,附加类型的实例只附加到特定的对象,而不会附加到该对象及其所有子对象。
例如,下面是前面涉及附加属性的示例的修改版本。这次,delegate 是一个 Item,而 Rectangle 是该 Item 的子元素:
import QtQuick
ListView {
width: 240; height: 320
model: 3
delegate: Item {
width: 100; height: 30
Rectangle {
width: 100; height: 30
color: ListView.isCurrentItem ? "red" : "yellow"
// WRONG! This won't work.
}
}
}
注意,ListView.isCurrentItem 只附加在 root delegate 对象上(本身),而不是其子对象。然而,Rectangle 是 delegate 的子对象,而不是 delegate 本身,因此,它无法像 ListView.isCurrentItem 那样访问附加属性 isCurrentItem。
Rectangle 应该通过 root delegate 来访问 isCurrentItem:
ListView {
delegate: Item {
id: delegateItem
width: 100; height: 30
Rectangle {
width: 100; height: 30
color: delegateItem.ListView.isCurrentItem ? "red" : "yellow"
// correct
}
}
}
枚举提供了一组固定的命名选项。它们可以使用 enum 关键字在 QML 中声明:
// MyText.qml
Text {
enum TextType { Normal, Heading }
}
如上所示,枚举类型(例如 TextType)和值(例如 Normal)必须以大写字母开头。值通过 <Type>.<EnumerationType>.<Value> 或 <Type>.<Value> 来引用。
// MyText.qml
Text {
enum TextType { Normal, Heading }
property int textType: MyText.TextType.Normal
font.bold: textType === MyText.TextType.Heading
font.pixelSize: textType === MyText.TextType.Heading ? 24 : 12
}
一个对象的属性可以被赋予一个静态值,该值会保持不变,直到显式赋予一个新值。然而,为了充分利用 QML 及其对动态对象行为的内置支持,大多数 QML 对象使用属性绑定。
属性绑定是 QML 的核心功能,它允许开发者指定不同对象属性之间的关系,当属性的依赖项的值发生变化时,该属性会根据指定的关系自动更新。
QML 引擎会监视属性的依赖项(即绑定表达式中的变量),当检测到变化时,QML 引擎会重新计算绑定表达式,并将新的结果应用到该属性上。
要创建属性绑定,需要将一个属性分配给一个 JavaScript 表达式,该表达式会计算出所需的值。在最简单的情况下,绑定可以是对另一个属性的引用。请看以下示例,其中蓝色矩形的高度绑定到了其父元素的高度:
Rectangle {
width: 200; height: 200
Rectangle {
width: 100
height: parent.height
color: "blue"
}
}
每当父矩形的高度发生变化时,蓝色矩形的高度会自动更新为相同的值。
绑定可以包含任何有效的 JavaScript 表达式或语句,因为 QML 使用符合标准的 JavaScript 引擎。绑定可以访问对象属性、调用方法并使用内置的 JavaScript 对象,如 Date 和 Math。以下是前一个示例的其他可能绑定:
height: parent.height / 2
height: Math.min(parent.width, parent.height)
height: parent.height > 100 ? parent.height : parent.height / 2
height: {
if(parent.height > 100) return parent.height
else return parent.height / 2
}
height: someMethodThatReturnsHeight()
下面是一个涉及更多对象和类型的更复杂示例:
Column {
id: column
width: 200
height: 200
Rectangle {
id: topRect
width: Math.max(bottomRect.width, parent.width / 2)
height: (parent.height / 3) + 10
color: "yellow"
TextInput {
id: myTextInput
text: "Hello QML!"
}
}
Rectangle {
id: bottomRect
width: 100
height: 50
color: myTextInput.text.length <= 10 ? "red" : "blue"
}
}
在前面的例子中:
此外,任何在作为绑定使用的 JavaScript 函数中引用的属性都将被重新计算。例如,在下面的代码片段中,每当 Rectangle 的 enabled 属性发生变化时,x 和 y 属性的绑定将被重新计算:
Rectangle {
x: rectPosition()
y: rectPosition()
width: 200
height: 200
color: "lightblue"
function rectPosition() {
return enabled ? 0 : 100
}
}
从语法上讲,绑定可以具有任意复杂度。然而,如果绑定过于复杂,例如涉及多行代码或命令式循环,这可能表明绑定被用来做超出描述属性关系的事情。复杂的绑定可能会降低代码的性能、可读性和可维护性。对于具有复杂绑定的组件,重新设计可能是一个好主意,或者至少将绑定提取到一个单独的函数中。一般来说,用户不应依赖绑定的求值顺序。
具有绑定的属性会根据需要自动更新。然而,如果该属性随后在 JavaScript 语句中被赋予一个静态值,绑定将被移除。
例如,下面的矩形最初确保其高度始终是宽度的两倍。但是,当按下空格键时,宽度乘以 3 的当前值将作为静态值分配给高度。之后,即使宽度变化,高度也将保持固定在该值。
import QtQuick 2.0
Rectangle {
width: 100
height: width * 2
focus: true
Keys.onSpacePressed: {
height = width * 3
}
}
如果目的是给矩形一个固定的高度并停止自动更新,那么这不是问题。但是,如果目的是要在宽度和高度之间建立新的关系,那么新的绑定表达式必须使用 Qt.binding() 函数包装起来,如下所示:
import QtQuick 2.0
Rectangle {
width: 100
height: width * 2
focus: true
Keys.onSpacePressed: {
height = Qt.binding(function() {
return width * 3
})
}
}
现在,在按下空格键后,矩形的高度将继续自动更新,始终为其宽度的三倍。
在 QML 应用程序中,常见的错误原因是无意中用 JavaScript 语句的静态值覆盖绑定。为了帮助开发者追踪这类问题,每当绑定因命令式赋值而丢失时,QML 引擎能够发出消息。
为了生成此类消息,你需要启用 qt.qml.binding.removal 日志类别的信息输出,例如通过调用:
QLoggingCategory::setFilterRules(QStringLiteral("qt.qml.binding.removal.info=true"));
当从 JavaScript 创建属性绑定时,this 关键字可以引用接收绑定的对象。这对于解决属性名称的歧义非常有帮助。
例如,下面的 Component.onCompleted 处理程序是在 Item 的作用域内定义的。在此作用域中,width 指的是 Item 的宽度,而不是 Rectangle 的宽度。要将 Rectangle 的高度绑定到它自己的宽度,绑定表达式必须明确引用 this.width(或者可以使用 rect.width):
Item {
width: 500
height: 500
Rectangle {
id: rect
width: 100
color: "yellow"
}
Component.onCompleted: {
rect.height = Qt.binding(function() {
return this.width * 2
})
console.log("rect.height = "+ rect.height)// prints 200, not 1000
}
}
Qt Quick Controls 提供了一组控件,可用于在 Qt Quick 中构建完整的界面,该模块在 Qt 5.7 中引入。
通过 QtQuick.Controls 导入使用,在 .qml 文件中添加以下导入语句:
import QtQuick.Controls
使用示例:
import QtQuick
import QtQuick.Controls
ApplicationWindow {
title: "My Application"
width: 640
height: 480
visible: true
Button {
text: "Push Me"
anchors.centerIn: parent
}
}
Qt Quick Controls 有多种样式可供选择。
基本风格是一种简单轻便的全能风格,可为 Qt Quick Controls 提供最大性能。
融合风格是一种与平台无关的风格,可为 Qt Quick Controls 提供面向桌面的外观和感觉。
想象风格基于图像资产,该风格自带一组默认图片,只需提供一个使用预定义命名规则的图片目录,即可轻松更改。
注意:此样式仅适用于在 macOS 上运行的应用程序。
macOS 风格是 macOS 的原生外观风格。
注意:此样式仅适用于在 iOS 上运行的应用程序。
iOS 风格是基于图像资产的 iOS 原生外观风格。
材质风格根据 Google Material Design Guidelines 提供了吸引人的设计,但比 Basic 风格需要更多的系统资源。
通用风格根据《微软通用设计指南》提供了吸引人的设计,但比基本风格需要更多的系统资源。
注意:该样式仅适用于在 Windows 上运行的应用程序。
Windows 风格是 Windows 的本机外观风格。
FluentWinUI3 风格是一种现代的本地外观风格,专为运行 Windows 11 及以上版本的平台设计,遵循 Fluent UI 和 WinUI 3 设计指南。FluentWinUI3 可在所有支持的平台上运行。
如果未明确设置样式,则将使用默认样式。使用的样式取决于操作系统:
对于所有其他操作系统,则使用 Basic Style。
在 QML 中明确导入样式来指定要使用的样式,例如,导入 Material 样式:
// The style must be imported before any other QtQuick.Controls imports
// in order for run-time style selection API like QQuickStyle::name() to
// work.
import QtQuick.Controls.Material
ApplicationWindow {
// ...
}
通过导入 QtQuick.Controls 来指定要使用的样式:
import QtQuick.Controls
QtQuick.Controls 插件将通过以下方法之一导入运行时设置的样式:
QQuickStyle::setStyle()-styleQT_QUICK_CONTROLS_STYLEqtquickcontrols2.conf这些方法的优先级按照从高到低的顺序排列,例如,使用 QQuickStyle 设置样式总是优先于使用命令行参数。
同样,也可以通过以下方法之一设置后备样式:
QQuickStyle::setFallbackStyle()QT_QUICK_CONTROLS_FALLBACK_STYLEqtquickcontrols2.conf注意:只有在主样式的 qmldir 文件中未静态选择回退样式时,才能动态选择回退样式。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online