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

问题描述 投票:0回答:1

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

用户理论上将城市键入文本字段,单击按钮,然后python获取输入,找到天气数据,并将其打印到QML窗口。我正在努力与textfield + button + pythonfunction的连接如何工作!没有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_())
python pyqt pyqt5 qml
1个回答
0
投票

逻辑是通过信号或属性返回信息,在这种情况下,我将展示如何通过属性返回信息。

因为它必须更新到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()

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_propertypython2.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()
© www.soinside.com 2019 - 2024. All rights reserved.