扩展QML - 创建新类型¶
这是关于使用Python扩展QML的教程系列中的第一个示例,共有6个示例。
Qt QML 模块提供了一组 API,用于通过 Python 扩展来扩展 QML。您可以编写扩展来添加自己的 QML 类型,扩展现有的 Qt 类型,或调用普通 QML 代码无法访问的 Python 函数。
本教程展示了如何使用Python编写包含核心QML功能的QML扩展,包括属性、信号和绑定。它还展示了如何通过插件部署扩展。
扩展QML时的一个常见任务是提供一个新的QML类型,该类型支持一些超出内置Qt Quick类型提供的自定义功能。例如,这可以用于实现特定的数据模型,或提供具有自定义绘制和绘图功能的类型,或访问系统功能,如网络编程,这些功能无法通过内置的QML功能访问。
在本教程中,我们将展示如何使用Qt Quick模块中的C++类来扩展QML。最终结果将是一个简单的饼图显示,通过几个自定义的QML类型实现,这些类型通过QML的特性(如绑定和信号)连接在一起,并通过插件提供给QML运行时。
首先,让我们创建一个名为 PieChart
的新 QML 类型,它有两个属性:名称和颜色。我们将使其在一个名为 Charts
的可导入类型命名空间中可用,版本为 1.0。
我们希望这个PieChart
类型可以在QML中这样使用:
import Charts 1.0
PieChart {
width: 100; height: 100
name: "A simple pie chart"
color: "red"
}
为此,我们需要一个C++类来封装这个PieChart
类型及其两个属性。由于QML广泛使用Qt的元对象系统,这个新类必须:
类实现¶
这是我们的PieChart
类,定义在basics.py
中:
21
22@QmlElement
23class PieChart (QQuickPaintedItem):
24
25 nameChanged = Signal()
26
27 def __init__(self, parent=None):
28 QQuickPaintedItem.__init__(self, parent)
29 self._name = u''
30 self._color = QColor()
31
32 def paint(self, painter):
33 pen = QPen(self.color, 2)
34 painter.setPen(pen)
35 painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
36 painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16)
37
38 @Property(QColor, final=True)
39 def color(self):
40 return self._color
41
42 @color.setter
43 def color(self, value):
44 self._color = value
45
46 @Property(str, notify=nameChanged, final=True)
47 def name(self):
48 return self._name
49
50 @name.setter
51 def name(self, value):
该类继承自QQuickPaintedItem
,因为我们想要重写paint()
以使用QPainter
API执行绘图操作。如果该类仅表示某种数据类型并且不是实际需要显示的项,它可以简单地继承自QObject
。或者,如果我们想要扩展现有的基于QObject
的类的功能,它可以继承自该类。另外,如果我们想要创建一个不需要使用QPainter
API执行绘图操作的可视项,我们可以直接子类化QQuickItem
。
PieChart
类使用 Property
装饰器定义了两个属性,name
和 color
,并重写了 QQuickPaintedItem.paint()
。PieChart
类使用 @QmlElement
装饰器进行注册,以便可以从 QML 中使用。如果不注册该类,app.qml
将无法创建 PieChart
。
QML 使用¶
既然我们已经定义了PieChart
类型,我们将在QML中使用它。app.qml
文件创建了一个PieChart
项目,并使用标准的QMLText
项目显示饼图的详细信息:
7Item {
8 width: 300; height: 200
9
10 PieChart {
11 id: aPieChart
12 anchors.centerIn: parent
13 width: 100; height: 100
14 name: "A simple pie chart"
15 color: "red"
16 }
17
18 Text {
19 anchors {
20 bottom: parent.bottom;
21 horizontalCenter: parent.horizontalCenter;
22 bottomMargin: 20
23 }
24 text: aPieChart.name
25 }
26}
请注意,尽管颜色在QML中指定为字符串,但它会自动转换为PieChart的color
属性的QColor
对象。自动转换提供了各种其他QML值类型。例如,像“640x480”这样的字符串可以自动转换为QSize
值。
我们还将创建一个主函数,该函数使用QQuickView
来运行和显示app.qml
。以下是应用程序basics.py
:
54
55if __name__ == '__main__':
56 app = QGuiApplication(sys.argv)
57
58 view = QQuickView()
59 view.setResizeMode(QQuickView.SizeRootObjectToView)
60 qml_file = os.fspath(Path(__file__).resolve().parent / 'app.qml')
61 view.setSource(QUrl.fromLocalFile(qml_file))
62 if view.status() == QQuickView.Status.Error:
63 sys.exit(-1)
64 view.show()
65 res = app.exec()
66 # Deleting the view before it goes out of scope is required to make sure all child QML instances
67 # are destroyed in the correct order.
68 del view
注意
你可能会看到一个警告 表达式 … 依赖于不可通知的属性:
PieChart.name。这是因为我们向可写的 name
属性添加了绑定,但尚未为其定义通知信号。因此,如果 name
值发生变化,QML 引擎无法更新绑定。这个问题将在接下来的章节中解决。
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
"""PySide6 port of the qml/tutorials/extending-qml/chapter1-basics example from Qt v5.x"""
import os
from pathlib import Path
import sys
from PySide6.QtCore import Property, Signal, QUrl
from PySide6.QtGui import QGuiApplication, QPen, QPainter, QColor
from PySide6.QtQml import QmlElement
from PySide6.QtQuick import QQuickPaintedItem, QQuickView
# To be used on the @QmlElement decorator
# (QML_IMPORT_MINOR_VERSION is optional)
QML_IMPORT_NAME = "Charts"
QML_IMPORT_MAJOR_VERSION = 1
@QmlElement
class PieChart (QQuickPaintedItem):
nameChanged = Signal()
def __init__(self, parent=None):
QQuickPaintedItem.__init__(self, parent)
self._name = u''
self._color = QColor()
def paint(self, painter):
pen = QPen(self.color, 2)
painter.setPen(pen)
painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16)
@Property(QColor, final=True)
def color(self):
return self._color
@color.setter
def color(self, value):
self._color = value
@Property(str, notify=nameChanged, final=True)
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
qml_file = os.fspath(Path(__file__).resolve().parent / 'app.qml')
view.setSource(QUrl.fromLocalFile(qml_file))
if view.status() == QQuickView.Status.Error:
sys.exit(-1)
view.show()
res = app.exec()
# Deleting the view before it goes out of scope is required to make sure all child QML instances
# are destroyed in the correct order.
del view
sys.exit(res)
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import Charts
import QtQuick
Item {
width: 300; height: 200
PieChart {
id: aPieChart
anchors.centerIn: parent
width: 100; height: 100
name: "A simple pie chart"
color: "red"
}
Text {
anchors {
bottom: parent.bottom;
horizontalCenter: parent.horizontalCenter;
bottomMargin: 20
}
text: aPieChart.name
}
}