// === view.qml ===
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Window 2.12
Window {
width: 600; height: 400
visible: true
Button {
text: "hello"
onClicked: speaker.say(text)
}
}
# === main.py ===
import sys
from PySide2.QtCore import *
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtWidgets import QApplication
class Speaker(QObject):
# noinspection PyCallingNonCallable
@Slot(str)
def say(self, s):
print(s)
if __name__ == '__main__':
app = QApplication()
engine = QQmlApplicationEngine()
x = Speaker()
engine.rootContext().setContextProperty('speaker', x)
engine.load(QUrl.fromLocalFile('./view.qml'))
sys.exit(app.exec_())
运行截图:
注意事项
主要是 main.py 有很多坑, 这里一一道来:
# === main.py ===
import sys
from PySide2.QtCore import *
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtWidgets import QApplication
class Speaker(QObject): # 1) 必须继承 QObject.
@Slot(str) # 2) 这里 Pycharm 会标记黄色警示, 忽略即可.
def say(self, s): # 3) 第一个参数 self 不要丢了.
print(s)
if __name__ == '__main__':
app = QApplication()
engine = QQmlApplicationEngine()
x = Speaker() # 4) 经测试, 必须先实例化 Speaker 赋给一个变量, 然后把变量传
# 进去.
engine.rootContext().setContextProperty('speaker', x)
# 5) 如果我们没有赋给变量传进去, 而是直接实例化给第二个参数:
# engine.rootContext().setContextProperty('speaker', Speaker())
# 在 QML 中就会报错. 确实让人无法理解.
# 6) 当把 Speaker 传入上下文后, 我们才可以 load 布局文件.
engine.load(QUrl.fromLocalFile('./view.qml'))
sys.exit(app.exec_())
个人比较喜欢的做法, 唯一的难点是 Python 要如何找到目标 QML 对象 (特别是找某个 parent 的 child items 比较头疼).
下面会介绍两种情况, 一种是根布局下的信号, 一种是其他 qml 文件的信号.
// === view.qml ===
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Window 2.12
Window {
visible: true
width: 600; height: 400
signal say(string s)
Button {
text: "hello"
onClicked: say(text)
}
}
# === main.py ===
import sys
from PySide2.QtCore import *
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtWidgets import QApplication
def say(s):
print(s)
if __name__ == '__main__':
app = QApplication()
engine = QQmlApplicationEngine()
engine.load(QUrl.fromLocalFile('./view.qml'))
# 获取 root 对象.
root = engine.rootObjects()[0] # type: QObject
# 找到目标对象. 由于我们的目标对象是 Window, 也就是 root 对象. 所以直接用.
target_view = root
# 绑定信号.
target_view.say.connect(say) # 前一个 say 是 qml 的信号, 后一个是 Python 的
# say() 方法.
sys.exit(app.exec_())
// === view.qml ===
import QtQuick 2.14
import QtQuick.Window 2.12
Window {
visible: true
width: 600; height: 400
MyButton {}
}
// === MyButton.qml ===
import QtQuick 2.14
import QtQuick.Controls 2.14
Item {
objectName: "my_item"
signal say(string s)
Button {
text: "hello"
onClicked: say(text)
}
}
# === main.py ===
import sys
from PySide2.QtCore import *
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtWidgets import QApplication
def say(s):
print(s)
if __name__ == '__main__':
app = QApplication()
engine = QQmlApplicationEngine()
engine.load(QUrl.fromLocalFile('./view.qml'))
# 获取 root 对象.
root = engine.rootObjects()[0] # type: QObject
# 找到目标对象.
target_view = root.findChild(QObject, 'my_item')
# 绑定信号.
target_view.say.connect(say) # 前一个 say 是 qml 的信号, 后一个是 Python 的
# say() 方法.
sys.exit(app.exec_())
这个需求比较少见, 一般不使用这种方案.
请阅读这篇文章实现: TODO
参考: