17. QML与Python混合开发
一、QML与Python混合开发
为了实现用户界面与应用程序逻辑分离的目的,QML 支持使用 Python 进行扩展,允许将 QML、JavaScript 和 Python 三者进行混合开发。由于 QML 引擎与 Qt 元对象系统的集成,实现了在 QML 中可以直接调用 Python 的功能,而 QML 模块提供的 Python 类能够帮助开发人员从 Python 加载、维护 QML 对象。
通过 QM L与 Python 的集成,可以提供下面这些优势。
- 将用户界面代码与应用程序逻辑代码分离。用户界面可以基于 QML 和 JavaScript 实现,程序逻辑则可以使用 Python 实现。
- 在 QML 中调用 Python 功能。例如,调用程序逻辑、使用由 Python 实现的数据模型或者调用第三方 Python 库中的一些函数等。
- 使用 Qt QML 或 Qt Quick 模块中现成的 Python 接口。例如,使用 QQuickImageProvider 来动态生成图像。
- 使用 Python 实现自定义的 QML 对象类型。既可以在自己指定的应用程序中使用,也可以分配给其他程序使用。
我们可以在终端中使用 pip 安装 PySide6 模块。默认是从国外的主站上下载,因此,我们可能会遇到网络不好的情况导致下载失败。我们可以在 pip 指令后通过 -i 指定国内镜像源下载。
pip install pyside6 -i https://mirrors.aliyun.com/pypi/simple
国内常用的 pip 下载源列表:
- 阿里云 https://mirrors.aliyun.com/pypi/simple
- 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple
- 中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple
二、QML中调用Python类
我们可以将 QObject 的子类可以注册到 QML 类型系统,这样就可以在 QML 代码中将该 Python 类作为一个数据类型使用,从而访问它的 属性、槽函数 和 信号。
2.1、QML调用Python类的槽函数
我们新建一个 my_object.py 文件,该文件定义一个类继承于 QObject 。在该类中,我们定义了一个槽函数。
from PySide6.QtCore import QObject
from PySide6.QtCore import Slot
# 创建一个类继承自QObject
class MyObject(QObject):
# 定义一个槽函数
@Slot(str, int)
def my_slot(self, name: str, age: int) -> None:
print(f"你好,我是{name},我今年{age}岁")
我们新建一个 template.py 文件,该文件在加载 QML 引擎之后加载 QML 文件之前使用 content = engine.rootContext() 方法 获取引擎的上下文。然后,通过 content.setContextProperty("上下文属性名", 类对象) 方法 设置上下文属性。
import sys
from PySide6.QtWidgets import QApplication
from PySide6.QtQml import QQmlApplicationEngine
from my_object import MyObject
if __name__ == "__main__":
my_object = MyObject()
app = QApplication(sys.argv) # 1.创建一个QApplication类的实例
engine = QQmlApplicationEngine() # 2.创建QML引擎对象
content = engine.rootContext() # 3.获取QML引擎的根上下文
content.setContextProperty("myObject", my_object) # 4.注册QML引擎的上下文属性,使QML文件可以访问该对象
engine.load("template.qml") # 5.加载QML文件
sys.exit(app.exec()) # 6.进入程序的主循环并通过exit()函数确保主循环安全结束
我们新建一个 template.qml 文件,在该文件中,我们创建一个按钮控件。当我们点击按钮的时候,会调用对应上下文属性的槽函数。
import QtQuick.Window
import QtQuick.Controls
// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {
width: 800 // 窗口的宽度
height: 600 // 窗口的高度
visible: true // 显示窗口
color: "lightgray" // 窗口的背景颜色
Button {
width: 90
height: 40
anchors.centerIn: parent
font.family: "楷体"
font.pointSize: 32
text: "按钮"
onClicked: {
myObject.my_slot("小樱", 10) // 调用mySlot槽函数
}
}
}
2.2、QML响应Python类的信号
修改 my_object.py 文件的内容,在该文件中,我们自定义信号,然后在槽函数中触发这个信号。
from PySide6.QtCore import QObject
from PySide6.QtCore import Signal, Slot
# 创建一个类继承自QObject
class MyObject(QObject):
mySignal = Signal(str, int) # 自定义一个信号
# 定义一个槽函数,在该槽函数触发信号
@Slot(str, int)
def my_slot(self, name: str, age: int) -> None:
self.mySignal.emit(name, age) # 触发信号
在 QML 文件中,我们使用 Connections 对象 创建一个到 QML 的信号的连接。Connections 对象有一个 target 属性,用来 指向发出信号的对象。它的一般用法如下:
Connections {
target: 发出信号的对象ID名
// 定义信号处理函数,函数名为 on<SingnalName>,SingnalName首字母大写
function on信号名(参数名) {
// 处理信号的逻辑
}
}
修改 template.qml 文件的内容。
import QtQuick.Window
import QtQuick.Controls
// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {
width: 800 // 窗口的宽度
height: 600 // 窗口的高度
visible: true // 显示窗口
color: "lightgray" // 窗口的背景颜色
Button {
id: takePhotoButtonId
width: 90
height: 40
anchors.centerIn: parent
font.family: "楷体"
font.pointSize: 32
text: "按钮"
onClicked: {
myObject.my_slot("小樱", 10) // 调用mySlot槽函数
}
}
Connections {
target: myObject // 连接到myObject对象
// 当mySignal信号触发时,执行对应的信号处理器
function onMySignal(name, age) {
console.log("你好,我是" + name + ",我今年" + age + "岁")
}
}
}
2.3、QML访问Python类的属性
如果想要在 QML 文件中访问 Python 类的属性,我们可以在 Python 类中 PySide6 提供的 Property 类定义属性。它的定义如下:
Property(self,
type: type, # 属性类型
fget: Optional[Callable] = None, # 获取属性值的方法
fset: Optional[Callable] = None, # 设置属性值的方法
freset: Optional[Callable] = None, # 重置属性值的方法
fdel: Optional[Callable] = None, # 删除属性值的方法
doc: str = '', # 属性的文档字符串
notify: Optional[PySide6.QtCore.Signal] = None, # 属性值改变时触发的信号
designable: bool = True, # 是否可在GUI设计工具(Qt Designer)中编辑
scriptable: bool = True, # 是否可在脚本引擎中访问
stored: bool = True, user: bool = False, # 是单独存在还是依赖于其他值
constant: bool = False, # 是否为常量属性,即不可改变
final: bool = False # 是否为最终属性,即不可被子类覆盖
) -> PySide6.QtCore.Property
修改 my_object.py 文件的内容。我们在在 Python 类中使用 Property 类定义属性。这里,我们在修改属性之后,一定要触发对应的信号,否则 QML 中不会感应到属性值被修改。
from PySide6.QtCore import QObject
from PySide6.QtCore import Signal
from PySide6.QtCore import Property
# 创建一个类继承自QObject
class MyObject(QObject):
modifyNamePropertySignal = Signal() # 自定义一个信号
modifyAgePropertySignal = Signal() # 自定义一个信号
def __init__(self) -> None:
super().__init__()
self.__name = ""
self.__age = 0
def get_name(self) -> str:
return self.__name
def set_name(self, name: str) -> None:
self.__name = name
self.modifyNamePropertySignal.emit() # 触发信号
def get_age(self) -> int:
return self.__age
def set_age(self, age: int) -> None:
self.__age = age
self.modifyAgePropertySignal.emit() # 触发信号
# 定义一个属性
name = Property(str, get_name, set_name, notify=modifyNamePropertySignal)
age = Property(int, get_age, set_age, notify=modifyAgePropertySignal)
修改 template.py 文件的内容。
import sys
from PySide6.QtWidgets import QApplication
from PySide6.QtQml import QQmlApplicationEngine
from my_object import MyObject
if __name__ == "__main__":
my_object = MyObject()
my_object.set_name("小樱")
my_object.set_age(10)
app = QApplication(sys.argv) # 1.创建一个QApplication类的实例
engine = QQmlApplicationEngine() # 2.创建QML引擎对象
content = engine.rootContext() # 3.获取QML引擎的根上下文
content.setContextProperty("myObject", my_object) # 4.注册QML引擎的上下文属性,使QML文件可以访问该对象
engine.load("template.qml") # 5.加载QML文件
sys.exit(app.exec()) # 6.进入程序的主循环并通过exit()函数确保主循环安全结束
修改 template.qml 文件的内容。
import QtQuick.Window
import QtQuick.Controls
// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {
width: 800 // 窗口的宽度
height: 600 // 窗口的高度
visible: true // 显示窗口
color: "lightgray" // 窗口的背景颜色
Text {
id: textId
anchors.top: parent.top // 顶部对齐父元素的顶部
anchors.topMargin: 10 // 顶部边距
anchors.horizontalCenter: parent.horizontalCenter // 水平居中对齐父元素的水平中心
font.family: "楷体 " // 字体族
font.pointSize: 24 // 字体大小
text: "你好,我是" + myObject.name + ",我今年" + myObject.age + "岁"
}
Grid {
anchors.centerIn: parent // 居中对齐父元素的中心
columns: 2 // 列数
spacing: 10 // 单元格间距
Text {
font: textId.font // 字体
text: "姓名:" // 文本
}
// 创建一个文本输入框
TextField {
width: 600 // 宽度
font: textId.font // 字体
placeholderText: "请输入姓名" // 占位符文本
text: myObject.name
// 编辑完成时触发信号
onEditingFinished: {
myObject.name = text
}
}
Text {
font: textId.font // 字体
text: "年龄:" // 文本
}
// 创建一个数字选择控件
SpinBox {
id: spinBoxId
width: 600 // 宽度
font: textId.font // 字体
from: 0 // 最小值
to: 300 // 最大值
stepSize: 1 // 步长
editable: true // 可编辑
value: myObject.age // 数值
validator: IntValidator { // 整数验证器
bottom: spinBoxId.from // 最小值
top: spinBoxId.to // 最大值
}
// 数值改变时触发信号
onValueModified: {
myObject.age = value
}
}
}
}
2.4、上下文对象
在之前的代码中,我们通过 setContextProperty("属性名", 类对象) 方法将 Python 的类对象通过 属性名 的方式暴露给 QML,这样在 QML 端,我们可以通过 属性名.属性 和 属性名.槽函数() 的方式访问 Python 类的成员。如果我们想要在 QML 中直接注入一个属性,那么可以使用 上下文对象。
修改 my_object.py 文件的内容。这里,我们使用 @Property 装饰器简化写法。
from PySide6.QtCore import QObject
from PySide6.QtCore import Signal
from PySide6.QtCore import Property
# 创建一个类继承自QObject
class MyObject(QObject):
modifyNamePropertySignal = Signal() # 自定义一个信号
modifyAgePropertySignal = Signal() # 自定义一个信号
def __init__(self) -> None:
super().__init__()
self.__name = ""
self.__age = 0
@Property(str, notify=modifyNamePropertySignal)
def name(self) -> str:
return self.__name
@name.setter
def name(self, name: str) -> None:
self.__name = name
self.modifyNamePropertySignal.emit() # 触发信号
@Property(int, notify=modifyAgePropertySignal)
def age(self) -> int:
return self.__age
@age.setter
def age(self, age: int) -> None:
self.__age = age
self.modifyAgePropertySignal.emit() # 触发信号
修改 template.py 文件的内容。在该文件中我们使用 setContextObject(类对象) 方法 注册到 QML 引擎的上下文对象中。
import sys
from PySide6.QtWidgets import QApplication
from PySide6.QtQml import QQmlApplicationEngine
from my_object import MyObject
if __name__ == "__main__":
my_object = MyObject()
my_object.name = "小樱"
my_object.age = 10
app = QApplication(sys.argv) # 1.创建一个QApplication类的实例
engine = QQmlApplicationEngine() # 2.创建QML引擎对象
content = engine.rootContext() # 3.获取QML引擎的根上下文
content.setContextObject(my_object) # 4.注册到QML引擎的上下文对象中
engine.load("template.qml") # 5.加载QML文件
sys.exit(app.exec()) # 6.进入程序的主循环并通过exit()函数确保主循环安全结束
修改 template.qml 文件的内容,在 QML 文件中,我们可以直接访问注册到 QML 引擎的上下文对象中类对象的属性和槽函数。
import QtQuick.Window
import QtQuick.Controls
// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {
width: 800 // 窗口的宽度
height: 600 // 窗口的高度
visible: true // 显示窗口
color: "lightgray" // 窗口的背景颜色
Text {
id: textId
anchors.top: parent.top // 顶部对齐父元素的顶部
anchors.topMargin: 10 // 顶部边距
anchors.horizontalCenter: parent.horizontalCenter // 水平居中对齐父元素的水平中心
font.family: "楷体 " // 字体族
font.pointSize: 24 // 字体大小
text: "你好,我是" + name + ",我今年" + age + "岁"
}
Grid {
anchors.centerIn: parent // 居中对齐父元素的中心
columns: 2 // 列数
spacing: 10 // 单元格间距
Text {
font: textId.font // 字体
text: "姓名:" // 文本
}
// 创建一个文本输入框
TextField {
width: 600 // 宽度
font: textId.font // 字体
placeholderText: "请输入姓名" // 占位符文本
text: name
// 编辑完成时触发信号
onEditingFinished: {
name = text
}
}
Text {
font: textId.font // 字体
text: "年龄:" // 文本
}
// 创建一个数字选择控件
SpinBox {
id: spinBoxId
width: 600 // 宽度
font: textId.font // 字体
from: 0 // 最小值
to: 300 // 最大值
stepSize: 1 // 步长
editable: true // 可编辑
value: age // 数值
validator: IntValidator { // 整数验证器
bottom: spinBoxId.from // 最小值
top: spinBoxId.to // 最大值
}
// 数值改变时触发信号
onValueModified: {
age = value
}
}
}
}
使用
QQmlContext.setContextProperty()显式设置的属性会优先于上下文对象的属性。
三、Python中访问QML
Qt 最核心的一个基础特性,就是元对象系统,通过元对象系统,你可以查询 QObject 的某个派生类的类名、有哪些信号、槽、属性、可调用方法等信息。
3.1、Python访问QML中的控件
修改 template.qml 文件的内容。这里我们在按钮的 onClicked() 处理器中调用 Python 类的一个槽方法,在这个槽方法中,我们访问 QML 中的控件。
import QtQuick.Window
import QtQuick.Controls
// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {
width: 800 // 窗口的宽度
height: 600 // 窗口的高度
visible: true // 显示窗口
color: "lightgray" // 窗口的背景颜色
Text {
id: textId
objectName: "textObjectName"
anchors.top: parent.top
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter
font.family: "楷体"
font.pointSize: 22
text: "你好,我是小樱。"
}
Button {
width: 120
height: 60
anchors.centerIn: parent
font.family: "楷体"
font.pointSize: 32
text: "按钮"
onClicked: {
myObject.my_slot(textId.objectName) // 调用mySlot槽函数
}
}
}
修改 template.py 文件的内容。在 QML 引擎加载完 QML 文件之后,我们可以使用 engine.rootObjects() 方法 获取 QML 中根对象列表,列表中的第一个元素就是我们需要的 QML 根对象。
import sys
from PySide6.QtWidgets import QApplication
from PySide6.QtQml import QQmlApplicationEngine
from my_object import MyObject
if __name__ == "__main__":
my_object = MyObject()
app = QApplication(sys.argv) # 1.创建一个QApplication类的实例
engine = QQmlApplicationEngine() # 2.创建QML引擎对象
content = engine.rootContext() # 3.获取QML引擎的根上下文
content.setContextProperty("myObject", my_object) # 4.注册QML引擎的上下文属性,使QML文件可以访问该对象
engine.load("template.qml") # 5.加载QML文件
root_objects = engine.rootObjects() # 6.获取QML文件中根对象列表
if not root_objects:
sys.exit(-1)
root_object = root_objects[0] # 7.获取QML文件中根对象列表的第一个元素
my_object.set_qml_root_object(root_object)
sys.exit(app.exec()) # 8.进入程序的主循环并通过exit()函数确保主循环安全结束
修改 my_object.py 文件的内容。在 Python 代码中获取 QML 的根对象之后,我们可以使用 findChild("objectName属性名") 方法 查找特定的子控件。找到子控件之后,我们可以使用 property("属性名") 方法 访问属性,使用 setProperty("属性名", 属性值) 的方法 设置属性。
import random
from PySide6.QtCore import QObject
from PySide6.QtCore import Slot
class MyObject(QObject):
def __init__(self) -> None:
super().__init__()
self.qml_root_object = None
self.messages = [
"没问题!无论发生什么事,我都会用笑容去面对的!",
"虽然我现在的力量还不够强,可是我会努力变强的。",
"我不会放弃的,不管多少次失败,我都一定会成功给你看!",
"不论前方有多少难关,我都会一一克服,然后到达你的身边!"
]
def set_qml_root_object(self, root: QObject) -> None:
self.qml_root_object = root
# 定义一个槽函数
@Slot(str)
def my_slot(self, object_name: str) -> None:
if self.qml_root_object:
obj = self.qml_root_object.findChild(QObject, object_name)
if obj:
message = random.choice(self.messages)
obj.setProperty("text", message)
print(obj.property("text"))
3.2、Python调用QML的函数
在 Python 代码中获取 QML 的根对象之后,我们可以使用 QMetaObject.invokeMethod() 调用 QML 中的函数。该函数的说明如下:
返回值 = QMetaObject.invokeMethod(
QML根对象,
要调用的QML函数名,
调用方式,
Q_RETURN_ARG("返回值类型"),
Q_ARG("参数类型", 参数值),
Q_ARG("参数类型", 参数值),
Q_ARG("参数类型", 参数值),
)
参数数量限制为 10。
修改 template.qml 文件,在该文件中定义一个函数。
import QtQuick.Window
import QtQuick.Controls
// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {
width: 800 // 窗口的宽度
height: 600 // 窗口的高度
visible: true // 显示窗口
color: "lightgray" // 窗口的背景颜色
Button {
width: 120
height: 60
anchors.centerIn: parent
font.family: "楷体"
font.pointSize: 32
text: "按钮"
onClicked: {
myObject.my_slot("getMessage", "你好,我是小樱。") // 调用mySlot槽函数
}
}
function getMessage(message) {
console.log(message)
return message
}
}
修改 my_object.py 文件,在获取 QML 根对象之后,使用 QMetaObject.invokeMethod() 调用 QML 中的函数。
from PySide6.QtCore import QObject, Qt
from PySide6.QtCore import Slot
from PySide6.QtCore import QMetaObject
from PySide6.QtCore import Q_ARG, Q_RETURN_ARG
class MyObject(QObject):
def __init__(self) -> None:
super().__init__()
self.qml_root_object = None
def set_qml_root_object(self, root) -> None:
self.qml_root_object = root
# 定义一个槽函数
@Slot(str, str)
def my_slot(self, func_name: str, func_arg1: str) -> None:
if self.qml_root_object:
# 使用invokeMethod调用QML函数
result = QMetaObject.invokeMethod(
self.qml_root_object, # QML根对象
func_name, # 要调用的QML函数名
Qt.ConnectionType.DirectConnection, # 调用方式(直接在当前线程执行)
Q_RETURN_ARG("QVariant"), # 声明返回值类型
Q_ARG("QVariant", func_arg1), # 第一个参数
)
if result:
print(result)
四、Python类注册为QML类型
我们可以在 Python 文件中自定义一个类,接着使用 @QmlElement 装饰器或 qmlRegisterType() 函数注册 Python 类为 QML 类型,然后再 QML 文件中使用该类型。
修改 my_object.py 文件的内容。我们可以使用 @QmlElement 装饰器装饰自定义的类。在使用 @QmlElement 装饰器前,我们还需要类外定义 QML_IMPORT_NAME 和 QML_IMPORT_MAJOR_VERSION 两个常量指明在 QML 文件中导入的 模块标识 和 主版本号。
from PySide6.QtCore import QObject
from PySide6.QtCore import Signal
from PySide6.QtCore import Property
from PySide6.QtQml import QmlElement
# 定义QML模块和主版本号
QML_IMPORT_NAME = "star.light.components"
QML_IMPORT_MAJOR_VERSION = 1
# 创建一个类继承自QObject
@QmlElement
class MyObject(QObject):
modifyNamePropertySignal = Signal() # 自定义一个信号
modifyAgePropertySignal = Signal() # 自定义一个信号
def __init__(self) -> None:
super().__init__()
self.__name = ""
self.__age = 0
def get_name(self) -> str:
return self.__name
def set_name(self, name: str) -> None:
self.__name = name
self.modifyNamePropertySignal.emit() # 触发信号
def get_age(self) -> int:
return self.__age
def set_age(self, age: int) -> None:
self.__age = age
self.modifyAgePropertySignal.emit() # 触发信号
# 定义一个属性
name = Property(str, get_name, set_name, notify=modifyNamePropertySignal)
age = Property(int, get_age, set_age, notify=modifyAgePropertySignal)
修改 templaet.py 文件的内容。这里,我们需要将被 @QmlElement 装饰器装饰的 Python 类所在的模块(Python 文件)在 创建和加载 QML 引擎之前 被导入。
import sys
from PySide6.QtWidgets import QApplication
from PySide6.QtQml import QQmlApplicationEngine
# 导入自定义的Python类
import my_object
if __name__ == "__main__":
app = QApplication(sys.argv) # 1.创建一个QApplication类的实例
engine = QQmlApplicationEngine() # 2.创建QML引擎对象
engine.load("template.qml") # 3.加载QML文件
sys.exit(app.exec()) # 4.进入程序的主循环并通过exit()函数确保主循环安全结束
修改 templae.qml 文件的内容。在这个文件中,我们使用用 Python 类注册的 QML 类型。
import QtQuick.Window
import QtQuick.Controls
// 导入自定义的QML模块
import star.light.components
// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {
width: 800 // 窗口的宽度
height: 600 // 窗口的高度
visible: true // 显示窗口
color: "lightgray" // 窗口的背景颜色
MyObject {
id: myObjectId
name: "小樱"
age: 10
}
Text {
anchors.centerIn: parent
font.family: "楷体"
font.pointSize: 32
text: "姓名:" + myObjectId.name + ",年龄:" + myObjectId.age
}
}
我们还可以使用 qmlRegisterType() 函数注册 Python 类为 QML 类型,它的定义如下:
qmlRegisterType(Python类, "模块名", 主版本, 次版本, "QML中的类型名")
我们修改 my_object.py 文件的内容。
from PySide6.QtCore import QObject
from PySide6.QtCore import Signal
from PySide6.QtCore import Property
class MyObject(QObject):
modifyNamePropertySignal = Signal() # 自定义一个信号
modifyAgePropertySignal = Signal() # 自定义一个信号
def __init__(self) -> None:
super().__init__()
self.__name = ""
self.__age = 0
def get_name(self) -> str:
return self.__name
def set_name(self, name: str) -> None:
self.__name = name
self.modifyNamePropertySignal.emit() # 触发信号
def get_age(self) -> int:
return self.__age
def set_age(self, age: int) -> None:
self.__age = age
self.modifyAgePropertySignal.emit() # 触发信号
# 定义一个属性
name = Property(str, get_name, set_name, notify=modifyNamePropertySignal)
age = Property(int, get_age, set_age, notify=modifyAgePropertySignal)
我们修改 template.py 文件的内容。在该文件中,我们使用 qmlRegisterType() 函数注册 Python 类为 QML 类型。
import sys
from PySide6.QtWidgets import QApplication
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtQml import qmlRegisterType
from my_object import MyObject
if __name__ == "__main__":
# 使用 qmlRegisterType 手动注册
qmlRegisterType(MyObject, "star.light.components", 1, 0, "MyObject")
app = QApplication(sys.argv) # 1.创建一个QApplication类的实例
engine = QQmlApplicationEngine() # 2.创建QML引擎对象
engine.load("template.qml") # 3.加载QML文件
sys.exit(app.exec()) # 4.进入程序的主循环并通过exit()函数确保主循环安全结束
五、单例对象
单例是一种在整个应用程序中只存在一个对象的类型。在 PySide6 中,我们可以使用 @QmlSingleton 装饰器来实现单例模式。我们修改 my_object.py 文件的内容。
from PySide6.QtCore import QObject
from PySide6.QtCore import Slot
from PySide6.QtQml import QmlElement, QmlSingleton
QML_IMPORT_NAME = "star.light.components"
QML_IMPORT_MAJOR_VERSION = 1
# 创建一个类继承自QObject
# @QmlSingleton装饰器必须放在@QmlElement装饰器之后,否则单例不起作用
@QmlElement
@QmlSingleton
class MyObject(QObject):
# 定义一个槽函数
@Slot(str, int)
def my_slot(self, name: str, age: int) -> None:
print(f"你好,我是{name},我今年{age}岁")
我们修改 template.py 文件的内容。
import sys
from PySide6.QtWidgets import QApplication
from PySide6.QtQml import QQmlApplicationEngine
# 导入自定义的Python类
import my_object
if __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
// 导入自定义的QML模块
import star.light.components
// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {
width: 800 // 窗口的宽度
height: 600 // 窗口的高度
visible: true // 显示窗口
color: "lightgray" // 窗口的背景颜色
Button {
width: 120
height: 60
anchors.centerIn: parent
font.family: "楷体"
font.pointSize: 32
text: "按钮"
onClicked: {
MyObject.my_slot("小樱", 10) // 调用mySlot槽函数
}
}
}
使用 PySide6 的单例模式后,我们可以不创建对象,直接使用类型名的方式调用它的成员。如果我们创建了实例,会报
Element is not creatable.。
我们还可以使用 qmlRegisterSingletonType() 函数 或 qmlRegisterSingletonInstance()函数 使用实现的单例模式。
修改 my_object.py 文件的内容。
from PySide6.QtCore import QObject
from PySide6.QtCore import Slot
class MyObject(QObject):
# 定义一个槽函数
@Slot(str, int)
def my_slot(self, name: str, age: int) -> None:
print(f"你好,我是{name},我今年{age}岁")
修改 template.py 文件的内容,使用 qmlRegisterSingletonType() 函数实现单例模式。
import sys
from PySide6.QtWidgets import QApplication
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtQml import qmlRegisterSingletonType
from my_object import MyObject
import my_object
if __name__ == "__main__":
# 注册MyObject类为单例实例
qmlRegisterSingletonType(MyObject, "star.light.components", 1, 0, "MyObject")
app = QApplication(sys.argv) # 1.创建一个QApplication类的实例
engine = QQmlApplicationEngine() # 2.创建QML引擎对象
engine.load("template.qml") # 3.加载QML文件
sys.exit(app.exec()) # 4.进入程序的主循环并通过exit()函数确保主循环安全结束
修改 template.py 文件的内容,使用 qmlRegisterSingletonInstance() 函数实现单例模式。
import sys
from PySide6.QtWidgets import QApplication
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtQml import qmlRegisterSingletonInstance
from my_object import MyObject
import my_object
if __name__ == "__main__":
# 注册MyObject类为单例实例
my_object = MyObject()
qmlRegisterSingletonInstance(MyObject, "star.light.components", 1, 0, "MyObject", my_object)
app = QApplication(sys.argv) # 1.创建一个QApplication类的实例
engine = QQmlApplicationEngine() # 2.创建QML引擎对象
engine.load("template.qml") # 3.加载QML文件
sys.exit(app.exec()) # 4.进入程序的主循环并通过exit()函数确保主循环安全结束

浙公网安备 33010602011771号