连接QML和Python,其中文本通过按钮单击发送到python函数,结果以QML格式打印



我对QML很陌生,对如何在python和QML上正确连接多个元素有些困惑(我使用的是python 2.7!我有python脚本来打印天气数据,还有一个QML应用程序,它应该将输入作为"cityname"。

理论上,用户在文本字段中输入一个城市,点击按钮,python接受输入,找到天气数据,并将其打印到QML窗口。我正在努力解决与文本字段+按钮+python函数的连接将如何工作!没有QML的python函数工作,QML生成一个带有文本输入和按钮的窗口。

这是我的代码:

QML (weather5.qml(

import QtQuick 2.0
import QtQuick.Controls 2.1
import QtQuick.Window 2.2
ApplicationWindow {
title: qsTr("Test")
width: 300
height: 450
visible: true
Column {
spacing: 20
TextField {
placeholderText: qsTr("City")
echoMode: TextInput.City
id: city
selectByMouse: true
}
ListView{
model: cityy
id: hi
delegate: Text { text: city.display }
}
Button {
signal messageRequired
objectName: "myButton"
text: "Search"
onClicked: {
print(hi)
}
}
}
Connections {
target: 
}
}

这是蟒蛇!!(pyweather.py(

import requests, json, os
from PyQt5.QtQml import QQmlApplicationEngine, QQmlEngine, QQmlComponent, qmlRegisterType
from PyQt5.QtCore import QUrl, QObject, QCoreApplication, pyqtProperty, QStringListModel, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QGuiApplication
import sys
class City(QObject):
def __init__(self):
QObject.__init__(self)
enterCity = pyqtSignal(str, arguments=["weat"])
@pyqtSlot(str)
def weat(self, city_name):
api_key = "key" #I've excluded my key for this post
base_url = "http://api.openweathermap.org/data/2.5/weather?"
complete_url = "http://api.openweathermap.org/data/2.5/weather?q=" + city_name + api_key
response = requests.get(complete_url)
x = response.json()

if x["cod"] != "404":
res = requests.get(complete_url)
data = res.json()
temp = data['main']['temp']
description = data['weather'][0]['description']
print('Temperature : {} degree Kelvin'.format(temp))
rett = ['Temperature : ' + str(temp) + " degree Kelvin"]
return rett
self.enterCity.emit(rett)
else:
print(" City Not Found ")
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
city = City()
engine.rootContext().setContextProperty("cityy", city)
engine.load(QUrl.fromLocalFile('weather5.qml'))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())

逻辑是通过信号或属性返回信息,在本例中,我将展示如何通过属性返回信息。

由于它必须更新到 QML 的某些元素,因此它必须通知,然后它必须与信号相关联。另一方面,您不应该使用请求,因为它会阻塞事件循环(并冻结 GUI(。

考虑到上述情况,解决方案是:

main.py

from functools import cached_property
import json
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QUrlQuery
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkRequest
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlApplicationEngine
import logging
logging.basicConfig(level=logging.DEBUG)

class WeatherWrapper(QObject):
BASE_URL = "http://api.openweathermap.org/data/2.5/weather"
dataChanged = pyqtSignal()
def __init__(self, api_key; str ="", parent: QObject = None) -> None:
super().__init__(parent)
self._data = dict()
self._has_error = False
self._api_key = api_key
@cached_property
def manager(self) -> QNetworkAccessManager:
return QNetworkAccessManager(self)
@property
def api_key(self):
return self._api_key
@api_key.setter
def api_key(self, key):
self._api_key = key
@pyqtProperty("QVariantMap", notify=dataChanged)
def data(self) -> dict:
return self._data
@pyqtSlot(result=bool)
def hasError(self):
return self._has_error
@pyqtSlot(str)
def update_by_city(self, city: str) -> None:
url = QUrl(WeatherWrapper.BASE_URL)
query = QUrlQuery()
query.addQueryItem("q", city)
query.addQueryItem("appid", self.api_key)
url.setQuery(query)
request = QNetworkRequest(url)
reply: QNetworkReply = self.manager.get(request)
reply.finished.connect(self._handle_reply)
def _handle_reply(self) -> None:
has_error = False
reply: QNetworkReply = self.sender()
if reply.error() == QNetworkReply.NoError:
data = reply.readAll().data()
logging.debug(f"data: {data}")
d = json.loads(data)
code = d["cod"]
if code != 404:
del d["cod"]
self._data = d
else:
self._data = dict()
has_error = True
logging.debug(f"error: {code}")
else:
self._data = dict()
has_error = True
logging.debug(f"error: {reply.errorString()}")
self._has_error = has_error
self.dataChanged.emit()
reply.deleteLater()

def main():
import os
import sys
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
app = QGuiApplication(sys.argv)
API_KEY = "API_HERE"
weather = WeatherWrapper()
weather.api_key = API_KEY
engine = QQmlApplicationEngine()
engine.rootContext().setContextProperty("weather", weather)
filename = os.path.join(CURRENT_DIR, "main.qml")
engine.load(QUrl.fromLocalFile(filename))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())

if __name__ == "__main__":
main()

主.qml

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
ApplicationWindow {
title: qsTr("Weather App")
width: 300
height: 450
visible: true
ColumnLayout {
anchors.fill: parent
spacing: 20
TextField {
id: city_tf
placeholderText: qsTr("City")
Layout.alignment: Qt.AlignHCenter
font.pointSize:14
selectByMouse: true
}
Button {
text: "Search"
Layout.alignment: Qt.AlignHCenter
onClicked: {
weather.update_by_city(city_tf.text)
}
}
Label{
Layout.alignment: Qt.AlignHCenter
id: result_lbl
}
Item {
Layout.fillHeight: true
}
}
Connections {
target: weather
function onDataChanged(){
if(!weather.hasError()){
var temperature = weather.data['main']['temp']
result_lbl.text = "Temperature : " + temperature + " degree Kelvin"
}
}
}
}

Python2 语法:

注意:安装 cached_property(python2.7 -m pip install cached_property(

from cached_property import cached_property
import json
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QUrlQuery
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkRequest
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlApplicationEngine
import logging
logging.basicConfig(level=logging.DEBUG)

class WeatherWrapper(QObject):
BASE_URL = "http://api.openweathermap.org/data/2.5/weather"
dataChanged = pyqtSignal()
def __init__(self, api_key="", parent=None):
super(WeatherWrapper, self).__init__(parent)
self._data = {}
self._has_error = False
self._api_key = api_key
@cached_property
def manager(self):
return QNetworkAccessManager(self)
@property
def api_key(self):
return self._api_key
@api_key.setter
def api_key(self, key):
self._api_key = key
@pyqtProperty("QVariantMap", notify=dataChanged)
def data(self):
return self._data
@pyqtSlot(result=bool)
def hasError(self):
print(self._has_error)
return self._has_error
@pyqtSlot(str)
def update_by_city(self, city):
url = QUrl(WeatherWrapper.BASE_URL)
query = QUrlQuery()
query.addQueryItem("q", city)
query.addQueryItem("appid", self.api_key)
url.setQuery(query)
request = QNetworkRequest(url)
reply = self.manager.get(request)
reply.finished.connect(self._handle_reply)
def _handle_reply(self):
has_error = False
reply = self.sender()
if reply.error() == QNetworkReply.NoError:
data = reply.readAll().data()
logging.debug("data: {}".format(data))
d = json.loads(data)
code = d["cod"]
if code != 404:
del d["cod"]
self._data = d
else:
self._data = {}
has_error = True
logging.debug("error: {}".format(code))
else:
self._data = {}
has_error = True
logging.debug("error: {}".format(reply.errorString()))
self._has_error = has_error
self.dataChanged.emit()
reply.deleteLater()

def main():
import os
import sys
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
app = QGuiApplication(sys.argv)
API_KEY = "API_HERE"
weather = WeatherWrapper()
weather.api_key = API_KEY
engine = QQmlApplicationEngine()
engine.rootContext().setContextProperty("weather", weather)
filename = os.path.join(CURRENT_DIR, "main.qml")
engine.load(QUrl.fromLocalFile(filename))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())

if __name__ == "__main__":
main()

最新更新