本期知識點:
- 前端(QML)和后端(Python/C++)如何通過信號或property交互
- 異步處理:JS ajax, Python Thread
- SwipeView組件
代碼已更新:kevinqqnj/qml_weather: PySide6+QML weather App (github.com)
小程序運行演示視頻:Qt QML+PySide天氣小程序
前端(QML)和后端(Python/C++)如何通過信號兄渺、setProperty交互
從QML發(fā)送signal給后端,有四種方式:
- QML調(diào)用Python slot函數(shù)
QML:
import kevinqqnj.signals.qmltopy1
Console {
id: pyConsole
}
onClicked: {
pyConsole.output(mouse.x)
}
PySide:
QML_IMPORT_NAME = "kevinqqnj.signals.qmltopy1"
@QmlElement
class Console(QtCore.QObject):
@QtCore.Slot(str)
def output(self, s):
print(s)
view = QtDeclarative.QDeclarativeView()
context = view.rootContext()
context.setContextProperty("con", con)
# or: engine.rootObjects()[0].setProperty('backend', backend)
2. QML調(diào)用slot函數(shù),并使用返回值
QML:
import kevinqqnj.signals.qmltopy1
Console {
id: pyConsole
}
onClicked: {
helloText.rotation = rotatevalue.val()
}
PySide:
@QtCore.Slot(result=int)
def val(self):
self.r = self.r + 10
return self.r
3. Python通過engine.connect付翁,訪問QML signal
QML:
- 可在QML里隱藏Python class行為
- 但Python需要知道QML component的定義
Rectangle {
id: page
signal textRotationChanged(double rot)
// make some other signal handler emit this signal:
onRotationChanged: textRotationChanged(rotation)
PySide:
view:
root = view.rootObject()
root.textRotationChanged.connect(sayThis)
Engine:
engine.rootObjects()[0].connect(sayThis)
4. Python通過ObjectName來直接訪問某component的事件
QML: 給某個component定義ObjectName
MouseArea {
id: buttonMouseArea
objectName: "buttonMouseArea"
anchors.fill: parent
}
PySide:
button = root.findChild(QtCore.QObject,"buttonMouseArea")
button.clicked.connect(lambda: sayThis("clicked button (signal directly connected)"))
button.setProperty("width", 100)
以上四種各有利弊,一般使用(3)
異步處理:JS ajax, Python Thread
QML可以直接import JavaScript
腳本扣溺,然后使用其函數(shù)和變量睁冬。
- 使用ajax,異步訪問當前城市的天氣預報:
- 如果QML要訪問本地文件旗唁,需要在PySide中聲明:
os.environ["QML_XHR_ALLOW_FILE_READ"] = "1"
function requestWeatherData(cntr) {
var xhr = new XMLHttpRequest;
xhr.open("GET", "weather.json");
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE) {
cntr.weatherData = JSON.parse(xhr.responseText)
}
}
xhr.send();
}
QML也可以發(fā)信號給后端,讓其抓取天氣預報數(shù)據(jù)痹束。后端再通過setProperty()發(fā)送回QML來顯示
main.qml
發(fā)信號:
ApplicationWindow {
signal updateWeather(int cityId)
检疫。。祷嘶。
onLaunched: (cityId) => {
updateWeather(cityId)
root.isUpdating = true
stackView.push("WeatherPage.qml")
}
Python接收信號并處理:
- 使用全局變量engine root
- 通過connect來連接前端signal:root.updateWeather.connect()
- 通過threading.Thread來多線程執(zhí)行屎媳,不阻塞QML運行
- 結(jié)果數(shù)據(jù):通過
root.setProperty('weatherData', weatherdata)
夺溢,發(fā)送給QML - 通過
root.setProperty('isUpdating', False)
,來告知QML烛谊,操作已完成
root = engine.rootObjects()[0]
root.updateWeather.connect(update_weather)
def update_weather(cityId):
print('python: updating...', cityId)
task = threading.Thread(target=getData, args=(cityId,))
task.start()
def getData(cityId):
global root, weatherdata
try:
url = f"http://t.weather.sojson.com/api/weather/city/{cityId}"
print('start downloading... ', url)
response = urllib.request.urlopen(url)
data = json.loads(response.read().decode('utf-8'))
weatherdata[str(cityId)] = data['data']['forecast'][0]
weatherdata[str(cityId)]['cityInfo'] = data['cityInfo']
weatherdata[str(cityId)]['UdateTime'] = data['time']
# {'date': '07', 'high': '高溫 29℃', 'low': '低溫 17℃', 'ymd': '2022-06-07', 'week': '星期二',
# 'sunrise': '04:58', 'sunset': '19:09', 'aqi': 18, 'fx': '東北風', 'fl': '2級', 'type': '多云',
# 'notice': '陰晴之間风响,謹防紫外線侵擾', 'UdateTime': '2022-06-07 19:12:55'}
# must have some delay, either urlopen or time.sleep, otherwize QML cannot update property
time.sleep(1)
print('downloaded, data len:', len(str(data)), weatherdata)
root.setProperty('weatherData', weatherdata)
except Exception as e:
print(e)
root.setProperty('isUpdating', False)
QML:定義property,來接收Python數(shù)據(jù)
- weatherData:字典JSON格式數(shù)據(jù)
- isUpdating:布爾值丹禀,控制顯示BusyIndicator(后臺數(shù)據(jù)未返回前状勤,顯示轉(zhuǎn)圈)
ApplicationWindow {
property var weatherData: ({})
property bool isUpdating: false
。湃崩。荧降。
BusyIndicator {
id: busyIndicator
visible: root.isUpdating
}
- 舊數(shù)據(jù)暫存放在Settings變量里,以供離線訪問:
Component.onCompleted: {
settings.savedWeatherData = root.weatherData
}
SwipeView組件
手機上常見的多頁面展示方法攒读,在下方顯示多個點朵诫,以指示當前有多個頁面,可左右滑動切換薄扁。
下一篇:TBD