In dieser Anleitung zeige ich Ihnen, wie Sie mit Qt und QML eine Wetter-App für das Seeed reTerminal erstellen. Stellen Sie sich das reTerminal in Ihrem Eingangsbereich vor und mit einem kurzen Blick auf den Bildschirm wissen Sie, wie das Wetter in den nächsten Stunden wird, ob Sie einen Regenschirm brauchen, ob Sie auf Ihrer Fahrradtour Gegenwind haben oder ob nur klar und sonnig sein. Dieses Tutorial baut auf der reTerminal Yocto boot2qt-Distribution auf, die wir im vorherigen Artikel erstellt haben, und verwendet Qt 6. Qt ist ein C++-Framework, aber diese Wetter-App verwendet fast ausschließlich QML. Ich verwende nur QML, um den Leitfaden zugänglicher zu machen, und auch, weil ich daran gewöhnt bin, alles in C++ zu machen, also macht mir ein Abstecher zu QML auch Spaß.
Dies ist Teil 1 des Leitfadens, in dem wir die Grundlagen einrichten. Dazu gehören die Vernetzung über QML, das Parsen der Open Meteo JSON-Wetter-API in QML und die Anzeige des Wettercodes in QML. Wenn Qt oder C++ neu für Sie sind, machen Sie sich keine Sorgen. QML ist eine deklarative Sprache zum Definieren von GUIs, enthält jedoch JavaScript. Dies bedeutet, dass es einfach ist, Ihre Benutzeroberfläche zu gestalten und JavaScript-Teile einen Teil der Schwerarbeit erledigen zu lassen , was in unserem Fall die Netzwerkaktivität und das JSON-Parsing sein wird. Am Ende dieser Anleitung finden Sie einen grundlegenden Bildschirm, der einen JSON-API-Wettercode in eine Textdarstellung umwandelt und die aktuelle Temperatur anzeigt, die auf dem reTerminal läuft.
Hier ist ein Bild des Endergebnisses von Teil 1, das auf meinem Desktop läuft:
Teil 2 erweitert das WeatherTerminal um die Skalierung der Benutzeroberfläche (zur Ausführung sowohl auf Ihrem PC als auch auf dem reTerminal), dauerhafte Einstellungen, eine Standortauswahl, einen Aktualisierungstimer, mehr Wetterelemente, einschließlich einiger Stunden in die Zukunft, und behandeln fortgeschrittenere QML-Konzepte wie verschiedene Layouts , Verankerungselemente, Bedingungen, Modelle und Eigenschaften. Teil 2 enthält auch die QtVirtual-Tastatur, da das reTerminal keine physische Tastatur hat, wir aber unseren Standort eingeben möchten.
Teil 2 ist noch nicht fertig, sobald das fertig ist, werde ich ihn hier verlinken.
Vollständige Offenlegung:Ich wurde von Seeed kontaktiert, sie schickten mir dieses reTerminal im Austausch für ein paar Artikel. Es ist keine Geldzahlung erforderlich und Seeed hat diesen Artikel vor der Veröffentlichung nicht überprüft. Für offiziellen Support besuchen Sie bitte das Seeed-Wiki.
Der vollständige Quellcode für Teil 1 befindet sich auf meinem Github
Was ist das reTerminal
Das reTerminal wird als zukunftsfähiges Human-Machine Interface (HMI) vermarktet. ThereTerminal wird von einem Raspberry Pi Compute Module 4 (cm4) angetrieben, bei dem es sich um eine Quad-Core ARM Cortex-A72-CPU mit 1,5 GHz und einem kapazitiven 5-Zoll-IPS-Multitouch-Bildschirm mit einer Auflösung von 1280 x 720 handelt. 4 GB RAM und 32 GB eMMC-Speicher sind integriert (nicht erweiterbar). Es verfügt über drahtlose Konnektivität mit 2,4-GHz-/5-GHz-Dualband-WLAN und Bluetooth 5.0 BLE.
Sie können das reTerminal hier kaufen, der aktuelle Preis beträgt USD 195. Dazu gehört ein Rechenmodul 4.
Siehe den anderen Artikel für einen umfassenderen Überblick über die Hardware und Funktionen.
Was Sie vor dem Start tun müssen
Bitte folgen Sie dem vorherigen Artikel zum Einrichten von Yocto boot2qt.
Diese Qt-App läuft nicht auf dem bereitgestellten Raspbian-Betriebssystem auf dem reTerminal, da die von uns verwendete Qt-Version zum Zeitpunkt des Schreibens neuer ist als die in dieser Debian-Version ausgelieferte. Sie könnten fortfahren und Qt 6.2 selbst kompilieren, aber das würde den Rahmen dieses Handbuchs sprengen.
Stellen Sie als Nächstes sicher, dass Sie Qt Creator und Qt Version 6.2 installiert haben. Der Yoctoboot2qt-Artikel enthält Anweisungen für das SDK, das Sie für das reTerminal crosskompilieren müssen.
Konfigurieren Sie in Qt Creator das Kit wie in meinem anderen Handbuch beschrieben und konfigurieren Sie Ihr reTerminal als Gerät für die Bereitstellung. Wenn das alles erledigt ist, komm zurück und fahre fort.
Wenn Sie nur die WeatherTerminal-App auf Ihrem Desktop ausführen möchten, müssen Sie yocto boot2qt für das reTerminal nicht einrichten, müssen nicht über Kreuz kompilieren, aber Sie müssen Qt Creator und Qt 6.2 installieren.
Sie können ohne reTerminal folgen, es ist ein guter QML- und Qt-Leitfaden, aber das Ziel dieses Leitfadens ist es, eine App für reTerminal zu erstellen, also denken Sie daran.
Datei -> Neues Projekt
Eines der schönsten Dinge als Entwickler ist der Moment, in dem Sie File -> New
Project
machen . Unbeschriebenes Blatt, bereit, Ihre Welt zu malen. Keine Cruft, Vermächtnis oder was auch immer. Also genieße diesen Moment. Starten Sie Qt Creator (ich verwende Version 7) und führen Sie den magischen Schritt aus.
Stellen Sie sicher, dass Sie eine Qt Quick (QML)-Anwendung auswählen, wählen Sie qmake
als Buildsystem und stellen Sie sicher, dass die Qt-Mindestversion auf 6.2 eingestellt ist. Wählen Sie sowohl das reguläre Qt6-Kit als auch das von Yocto SDK bereitgestellte Kit aus, das Sie im vorherigen Artikel erstellt haben.
Tab-Layout durch Wischen
Wir beginnen mit der Einrichtung eines Layouts mit zwei Registerkarten. Sie können entweder auf die Registerkartenleiste klicken oder nach links/rechts wischen, um zu einer anderen Registerkarte zu navigieren.
Eine Registerkarte ist die Hauptwetterinformationsseite und eine Registerkarte ist für die Einstellungen. Nicht, dass wir viele Einstellungen hätten, aber das grundlegende Layout zu erstellen ist jetzt einfacher, als es später zu ändern.
Navigieren Sie im Datei-Explorer auf der linken Seite zu Resources
, qml.qrc
, /
und öffnen Sie die Datei main.qml
Es sollte einen einfachen ApplicationWindow
geben sowie ein oder mehrere import
Aussagen. Die Struktur der QML-Datei ist einfach, eine QML-Datei hat ein einzelnes Top-Level-Element, das das Verhalten und die Eigenschaften dieser Komponente definiert.
Wenn Sie eine neue QML-Datei mit dem Namen beispielsweise WeatherButton.qml
, könnten Sie diesen Gegenstand in Ihrem ApplicationWindow
platzieren indem Sie WeatherButton
{}
schreiben .
In unserem Fall werden wir einige Komponenten hinzufügen, um das Tablayout aufzubauen. Beginnen Sie, indem Sie oben die folgende Zeile hinzufügen, um die Qt QuickControls zu verwenden:
import QtQuick.Controls
In Qt 5 musste man beim Import eine Versionsnummer angeben, in Qt6 ist das nicht mehr erforderlich.
Ändern Sie den width:
und height:
Eigenschaftswerte auf 1280 und 720, thereTerminals Bildschirmabmessungen. Setzen Sie etwas Nettes in den Titel und entfernen Sie alle weiteren Inhalte innerhalb des ApplicationWindow
Komponente.
Fügen Sie die folgenden Zeilen hinzu:
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
}
footer: TabBar {
id: tabBar
currentIndex: swipeView.currentIndex
TabButton {
text: "Weather"
font.pixelSize: 30
}
TabButton {
text: "Settings"
font.pixelSize: 30
}
}
Fahren Sie fort und drücken Sie STRG+R (oder das grüne Dreieck, das wie eine Wiedergabetaste aussieht) und sehen Sie sich das Wunder an, das Sie vollbracht haben:
Versuchen Sie auch, es auf dem reTerminal auszuführen. Wenn Sie das Wayland + Weston-Setup verwenden, um QML-Apps zu rotieren, fügen Sie Folgendes zur Umgebung in QtCreator hinzu:
Wählen Sie das Yocto-Gerätekit und das Remote-Gerät aus und drücken Sie dann Play
um es auf dem reTerminal zu kompilieren und auszuführen:
Hier ist ein Bild des reTerminals, auf dem unsere einfache leere Tafel mit Registerkarten ausgeführt wird:
Beachten Sie, dass das Wischen nach links oder rechts noch nicht funktioniert, da der SwipeView
hat noch keinen eigentlichen Inhalt.
Ich habe Ihnen gesagt, dass QML einfach ist, kein C++-Code erforderlich ist und Sie bereits eine App mit Registerkarten haben.
Erklären, was wir bisher getan haben, beginnend mit SwipeView
:
-
id: swipeView
:die Text-ID, mit der dieses spezifische Objekt identifiziert und von anderen Objekten referenziert werden kann. Diese ID muss mit einem Kleinbuchstaben oder einem Unterstrich beginnen und darf keine anderen Zeichen als Buchstaben, Ziffern und Unterstriche enthalten. -
anchors.fill: parent
:macht die Swipeview-Anker zu ihrem übergeordneten Element (dem Fenster), ändert effektiv die Größe, um das gesamte Fenster auszufüllen. -
currentIndex: tabBar.currentIndex
:Eine Eigenschaftsbindung. Immer wenn der EigenschaftswertcurrentIndex
destabBar
aktualisiert, aktualisiert die QML-Engine automatisch auch den Wert dieser Eigenschaft. Effektive Kopplung von Wischen und Klicken auf einen Tab.
Eigenschaftsbindungen sind eine der Stärken von QML. Ohne eine Eigenschaftsbindung müssten Sie in diesem Fall eine Funktion schreiben, die immer dann, wenn Sie auf eine Tab-Schaltfläche klicken, den SwipeView-Index ändert (um die SwipeView tatsächlich zu aktualisieren) und umgekehrt.
Anker werden im zweiten Teil näher erläutert. Im Moment können Sie sie sich als eine Art Magneten vorstellen. Eine Seite eines Gegenstands ist an einer Seite eines anderen Gegenstands verankert. Aus Leistungsgründen jedoch nur übergeordnete oder gleichgeordnete Elemente.
Als nächstes kommt die footer: TabBar {}
. Die footer
ist eigentlich eine Eigenschaft von ApplicationWindow
Die Eigenschaft nimmt einen Item
an als seinen Wert, weshalb Sie einen ganzen TabBar
eingeben können im Inneren.
Items
sind visuelle Dinge aus dem QtQuick
Modul. Quick steht fürQt User Interface Creation Kit
.
Die tabBar hat ihren eigenen id:
-Eigenschaft und enthält zwei Items
in sich selbst zwei TabButtons
, die auch ihre eigenen Eigenschaften haben:
TabButton {
text: "Weather"
font.pixelSize: 30
}
text:
enthält den Text, den Sie auf der Schaltfläche sehen, und font.pixelSize
ist, wie zu erwarten, die Größe der Schrift in Pixel.
Wegen TabBar
Wenn es sein eigenes Layout (Platzieren von untergeordneten Elementen) auf dem Bildschirm durchführt, ist es nicht erforderlich, x:
anzugeben , y:
oder anchors:
in den Knöpfen. Der TabBar
stellt sicher, dass sie nebeneinander liegen.
Wenn Sie auf eine Schaltfläche klicken, wird TabBar
, der currentIndex
Eigentumsänderungen. Wenn Sie auf Settings
klicken es wird zu 1
. Da die Eigenschaft currentIndex
ist an currentIndex
gebunden Eigenschaft von swipeView
, diese SwipeView ist currentIndex
wird auch zu 1
. Tatsächlich ergibt dies den SwipeView
ändert sein aktuelles Element in das zweite untergeordnete Element darin (denken Sie daran, dass Arrays bei 0 beginnen).
Wenn Sie neu bei Qt sind, finden Sie hier eine Menge Informationen, die auf ein einfaches Beispiel reduziert wurden. Versuchen Sie, herumzuspielen, schauen Sie sich an, was die automatische Vervollständigung für Eigenschaften bietet, und spielen Sie damit herum. Versuchen Sie, die Textfarbe red
zu machen zum Beispiel.
Tabs mit Seiten füllen
Jetzt, da wir die Registerkarten haben, können wir sie mit etwas Nützlichem füllen. Klicken Sie mit der rechten Maustaste auf /
Ordner in qml.qrc
und erstellen Sie eine neue QML-Datei mit dem Namen SettingsPage.qml
:
Fügen Sie den folgenden Inhalt ein:
import QtQuick
import QtQuick.Controls
Page {
id: root
width: 1240
height: 640
header: Label {
text: "Settings"
font.pixelSize: 50
}
}
Dies ist eine leere Platzhalterseite mit nur einer Kopfzeile. Dasselbe wie footer:
Eigenschaft von ApplicationWindow
, der header:
Eigenschaft nimmt einen Item
an als Wert, der in diesem Fall ein Label
ist . Könnte auch ein Button
sein oder worauf du Lust hast. Der Page
control übernimmt das Layout und stellt sicher, dass header:
Item
befindet sich oben auf der Seite.
In main.qml
, innerhalb von SwipeView
, fügen Sie diese neue Komponente hinzu:
SwipeView {
[...]
SettingsPage {}
}
Drücken Sie Play, um es auszuprobieren, und Sie sollten jetzt einen Header-Text Settings
sehen , auf Ihrem Wetter-Tab. Wieso den? Denn die SwipeView
hat nur ein untergeordnetes Element, das automatisch index
erhält Zahl 0.
Wiederholen Sie die Erstellung der neuen QML-Datei für eine andere Datei, nennen Sie diese WeatherPage.qml
Fügen Sie denselben Inhalt wie in SettingsPage.qml
hinzu Datei, aber ändern Sie die Label
um Weather
zu sagen und fügen Sie es dem SwipeView
hinzu in main.qml
, rechts über dem SettingsPage
:
SwipeView {
[...]
WeatherPage {}
SettingsPage {}
}
Drücken Sie Play und versuchen Sie es erneut, jetzt sollten Sie Weather
sehen als Eröffnungslasche. Seit SwipeView
können Sie jetzt auch nach rechts oder links wischen hat jetzt untergeordnete Elemente. Wenn Sie wischen, sollte sich auch die derzeit aktive Registerkarte in der Registerkartenleiste ändern.
Parsing der Open Meteo API
Ich habe mich für die Open-Meteo-API entschieden, weil sie keinen API-Schlüssel oder eine Benutzerregistrierung erfordert und für Open Source oder nicht-kommerzielle Nutzung kostenlos ist. Es bietet eine ordentliche JSON-API, gibt LAT und LON ein und bamm, Sie erhalten die Vorhersage.
Ich werde die folgende URL in der App verwenden, aber wenn diese aus irgendeinem Grund nicht verfügbar ist, können Sie auch hier den (statischen) Spiegel auf meiner Website verwenden. Letzteres enthält natürlich nicht die aktuelle Prognose, liefert Ihnen aber das korrekte JSON-Format.
Beginnen wir damit, unsere eigenen Eigenschaften in WeatherPage.qml
zu definieren , direkt unter dem width
und height
:
property var parameters: undefined
property double latitude: 52.3738
property double longitude: 4.8910
Die letzten beiden sind selbsterklärend, die erste (parameters
) enthält die dekodierte JSON. Der var
Typ ist der anything
Geben Sie QML ein. Wenn Sie den Typ kennen, den eine Eigenschaft enthalten wird, ist es schneller, ihn anzugeben (string
statt var
zum Beispiel). Der var
type entspricht einer regulären JavaScript-Variable. Beispielsweise können var-Eigenschaften Zahlen, Zeichenfolgen, Objekte, Arrays und Funktionen speichern. Da unser geparstes JSON vom Typ QJSValue
sein wird und es gibt keinen spezifischeren QML-Typ, der dem entspricht, var
ist unsere beste Wahl.
Fügen Sie nach dem Hinzufügen der benutzerdefinierten Eigenschaften eine Funktion hinzu. Dies ist eine reguläre JavaScript-Funktion, aber sie kann auf QML-Eigenschaften zugreifen, wie Sie sehen werden:
function getJson(latitude, longitude) {
var xmlhttp = new XMLHttpRequest()
var url = "https://api.open-meteo.com/v1/forecast?latitude=" + latitude
+ "&longitude=" + longitude + "&hourly=temperature_2m,relativehumidity_2m,apparent_temperature,weathercode,windspeed_10m,winddirection_10m&daily=weathercode,temperature_2m_max,temperature_2m_min,sunrise,sunset¤t_weather=true&timezone=Europe%2FAmsterdam"
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === XMLHttpRequest.DONE
&& xmlhttp.status == 200) {
root.parameters = JSON.parse(xmlhttp.responseText)
}
}
xmlhttp.open("GET", url, true)
xmlhttp.send()
}
Wenn Sie schon einmal mit JavaScript gearbeitet haben, fällt vielleicht nur Folgendes auf:
root.parameters = JSON.parse(xmlhttp.responseText)
Wenn Sie mit JavaScript nicht vertraut sind, sendet diese Funktion eine GET-Anforderung mit einer Callback-Methode an die API-URL. Die Callback-Methode prüft, ob der GETrequest korrekt beendet wurde, parst die JSON-Antwort und weist das Ergebnis dem QML root.parameters
zu Eigentum. root
ist der id:
unseresPage
, hat die QML-Engine komplexe Scoping-Regeln, aber jetzt reicht es zu wissen, dass sie weiß, dass sie die var der Eigenschaft parameters
zuweisen muss in dieser Datei, nicht in SettingsPage
Datei, obwohl diese Seite auch den id:
hat von root
. Andere Datei, anderer Kontext.
Beachten Sie, dass diese JavaScript-Methode das Gleichheitszeichen verwendet (=
) und nicht der Doppelpunkt (:
), um der Eigenschaft einen Wert zuzuweisen. Der QML-Doppelpunkt (:
) macht eine Eigenschaftsbindung, das Gleichheitszeichen (=
) nicht. Also, wenn Sie width =
height
machen würden Innerhalb einer JavaScript-Methode wäre das keine Eigenschaftsbindung, sondern nur eine Zuweisung. Wenn height
spätere Änderungen, width
wird nicht. Wichtiger Unterschied, aber im Moment nicht so relevant.
Lassen Sie uns eine Schaltfläche hinzufügen, die diese Methode aufruft. Fügen Sie unter den Eigenschaften Folgendes hinzu:
Button {
id: refreshButton
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: 5
text: "Update Weather"
font.pixelSize: 30
onClicked: getJson(root.latitude, root.longitude)
}
Die beiden anchors.
Lassen Sie die Schaltfläche unten links mit einem kleinen Rand um sie herum erscheinen (auf allen Seiten). Der onClicked
Die Eigenschaft ruft unsere JavaScript-Methode mit den beiden Parametern Breitengrad und Längengrad auf, die wir als Eigenschaften von Page
definiert haben .
Wenn Sie Play zum Kompilieren und Ausführen drücken, funktioniert die Schaltfläche, aber Sie können das Ergebnis nicht sehen. Die Eigenschaft parameters
hat das entschlüsselte JSON, aber wir tun noch nichts damit. Um sicherzustellen, dass wir es richtig gemacht haben, melden wir uns bei der Konsole an. Unter dem Button
, fügen Sie Folgendes hinzu:
onParametersChanged: console.log(root.parameters['current_weather']['weathercode'])
Kompilieren und ausführen, die Update-Schaltfläche drücken und das Konsolenprotokoll sollte in etwa so aussehen:
qrc:/WeatherPage.qml:30: TypeError: Cannot read property 'current_weather' of undefined
qml: 3
Der erste Fehler ist in Ordnung, den können wir vorerst ignorieren. Als die Eigenschaft deklariert wurde, wurde sie leer initialisiert und löste ein geändertes Signal aus, aber die von uns geschriebene onChanged-Funktion überprüft nicht, ob die Parameter leer sind.
Die zweite Zeile (qml: 3
) ist der eigentliche weathercode
aus der JSON-API.
Nehmen Sie sich einen Moment Zeit, um sich zu amüsieren. Ohne C++-Code zu schreiben, haben Sie eine plattformübergreifende App mit Registerkartenleisten und einer Schaltfläche erstellt, die eine JSON-API von einem Netzwerk-Webdienst erhält. Auch hier verwende ich nur QML für diesen Leitfaden, weil es supereinfach ist.
Hinter den Kulissen, der onParametersChanged:
line ist ein Slot (Signalhandler), der aufgerufen wird, wenn changed
Signal wird von unserem parameters
abgefeuert Variable. Qt hat ein weiteres sehr leistungsfähiges Konzept namens Signals and Slots, das ein bisschen wie ein Observer-Design-Pattern oder Pub-Sub ist, aber auf Steroiden und C++ typsicher ist. Ich werde es nicht weiter erklären, ich könnte ein Buch nur über Signale und Slots schreiben, wenn Sie interessiert sind, sehen Sie sich die Qt-Dokumentation darüber an.
Jede Eigenschaft, auch unsere benutzerdefinierten, hat einen changed
Signal, das erstellt die QML-Engine für uns. Dieses Signal wird automatisch ausgegeben, wenn sich der Wert einer QML-Eigenschaft ändert. Dieser Signaltyp ist ein property change signal
und Signalhandler für diese Signale werden in Form von onPropertyChanged
geschrieben , wobei Property
ist der Name der Eigenschaft, wobei der erste Buchstabe groß geschrieben wird.
Die console.log()
Funktion, die wir dem onParametersChanged
zugewiesen haben slot (Signalhandler) gibt den Inhalt des JSON-Objekts
['current_weather']['weathercode']
aus .
Wettercode parsen
Jetzt, da wir mit einem Klick auf eine Schaltfläche mit der JSON-API kommunizieren können, ist es an der Zeit, diese API zu parsen. Wir beginnen mit dem aktuellen WeatherCode, der ein numerisches Standardformat für Wetterbedingungen ist, wie Clear Sky
oder Thunderstorm
.
Die Codes sind auf der Open-Meteo-API-Seite ausgeschrieben, und eine umfassendere Beschreibung befindet sich auf der noaa.gov-Site.
Neben einer reinen Textausgabe fügen wir ein nettes Symbol hinzu, das sich ändert, wenn sich der Wettercode ändert.
Erstellen Sie wie zuvor eine neue QML-Datei mit dem Namen WeatherCode.qml
und fügen Sie Folgendes ein:
import QtQuick
Item {
id: root
property var parameters: undefined
}
Im WeatherPage.qml
, fügen Sie diese neue Komponente oberhalb von Button
hinzu wir haben früher hinzugefügt:
WeatherCode {
id: weatherCode
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
parameters: root.parameters
}
Die anchors
Platzieren Sie dieses Steuerelement oben links auf der Seite und strecken Sie es nach rechts. Die Höhe definieren wir später im Steuerelement selbst. Wenn ein Steuerelement keine Breite/Höhe oder Anker hat, ist es nicht sichtbar. Wir geben die parameters
weiter des WeatherPage
bis zu WeatherCode
. Dies ist eine Eigenschaftsbindung, wenn Sie also auf Update
klicken Schaltfläche, die WeatherCode
control erhält auch den neuen aktualisierten parameters
.
Erstellen Sie in Ihrem Qt-Projektordner einen neuen Ordner mit dem Namen icons
und laden Sie den folgenden svg
herunter Dateien von FontAwesome.com
:
- circle-question-solid.svg
- clock-solid.svg
- cloud-rain.svg
- cloud-showers-heavy-solid.svg
- cloud-showers-water-solid.svg
- cloud-sun-solid.svg
- poo-storm-solid.svg
- rainbow-solid.svg
- smog-solid.svg
- snowflake-solid.svg
- sun-solid.svg
- temperatur-half-solid.svg
- temperatur-high-solid.svg
- temperatur-low-solid.svg
- wind-solid.svg
Diese sind alle Teil von Font Awesome Free und CC-BY-4.0 lizenziert.
Klicken Sie in Qt Creator mit der rechten Maustaste auf qml.qrc
Datei in der Seitenleiste und klicken Sie auf Add
existing files
. Wählen Sie alle Symbole aus, die Sie im icons
heruntergeladen haben Ordner.
Fügen Sie einen neuen Image
hinzu Steuerung an WeatherCode.qml
Datei, unterhalb der Eigenschaften:
Image {
id: weatherCodeIcon
source: root.parameters ? weathercodeToIcon(
root.parameters['current_weather']['weathercode']) : "qrc:icons/circle-question-solid.svg"
asynchronous: true
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: 5
width: 90
height: width
}
Sie sollten sich inzwischen mit der QML-Syntax vertraut gemacht haben. Die Höhe ist eine an die Breite gebundene Eigenschaft, der anchors
platzieren Sie dies oben links mit einem kleinen Rand darum herum. Der asynchronous
-Eigenschaft weist die QML-Engine an, beim Laden dieses Bildes nicht zu blockieren. Mit einem Bild ist es kein Engpass, aber mit mehr Bildern wird schnell klar, warum alle Bilder asynchron geladen werden sollen (weil die Benutzeroberfläche blockiert, unbrauchbar ist, einfriert).
Der source:
-Eigenschaft ist komplexer und führt Sie in ein weit verbreitetes QML-Konzept ein, das ternary if
Aussage. Wenn root.parameters
gefüllt ist (not undefined
), dann tun Sie, was nach dem Fragezeichen steht (?
). Wenn nicht, tun Sie, was nach dem Doppelpunkt steht (:
). Dies könnte auch (in Pseudocode) geschrieben werden als:
if(root.parameters !== undefined); then
source = weathercodeToIcon(root.parameters['current_weather']['weathercode'])
else
source = "qrc:icons/circle-question-solid.svg"
Wir haben parameters
definiert als undefined
, solange wir nicht auf Update
geklickt haben klicken, wird ein Fragezeichen-Symbol angezeigt. Wenn wir die update
anrufen Funktion, ein parametersChanged
Das Signal wird ausgelöst und diese Eigenschaftsbindung wird neu bewertet.
Der weathercodeToIcon()
Funktion enthält den folgenden Code. Fügen Sie es direkt unter den Eigenschaften in dieser Datei ein:
function weathercodeToIcon(weathercode) {
switch (weathercode) {
case 0:
return "qrc:icons/sun-solid.svg"
case 1:
case 2:
case 3:
return "qrc:icons/cloud-sun-solid.svg"
case 45:
case 48:
return "qrc:icons/smog-solid.svg"
case 51:
case 53:
case 55:
case 56:
case 57:
case 61:
case 80:
return "qrc:icons/cloud-rain.svg"
case 63:
case 66:
return "qrc:icons/cloud-showers-solid.svg"
case 65:
case 67:
return "qrc:icons/cloud-showers-water-solid.svg"
case 71:
case 73:
case 75:
case 77:
case 85:
case 86:
return "qrc:icons/snowflake-solid.svg"
case 81:
case 82:
return "qrc:icons/cloud-showers-heavy-solid.svg"
case 95:
case 96:
case 99:
return "qrc:icons/poo-storm-solid.svg"
default:
return "qrc:icons/rainbow-solid.svg"
}
}
Wie Sie sehen können, nichts Besonderes, nur eine große Switch-Anweisung. Geben Sie für jede Reihe von Wettercodewerten ein anderes Symbol zurück.
Neben dem Bild und über dem geparsten Wettercodetext möchte ich eine kleine Kopfzeile. Lassen Sie uns das hinzufügen, fügen Sie dies über dem Image
ein :
Text {
id: weatherHeaderText
text: "Current Weather"
anchors.top: parent.top
anchors.left: weatherCodeIcon.right
anchors.leftMargin: 20
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignTop
font.pixelSize: 18
}
Hier ist etwas Neues, anchors.left: weatherCodeIcon.right
. Das bedeutet, dass die linke Seite des Textobjekts mit der rechten Seite des Symbols verankert werden sollte. Fügen Sie ein bisschen leftMargin
hinzu um es schön zu machen und du bist fertig. Nun, wo auch immer Sie das Symbol platzieren, direkt daneben steht immer dieser Text. Wenn Sie das Symbol verschieben, müssen Sie den x:
nicht manuell aktualisieren oder y:
des Text
, es wird alles automatisch für Sie erledigt.
Oben in der Datei, direkt unter dem id:
, fügen Sie eine neue Eigenschaft für height
hinzu dieser Kontrolle:
Item {
id: root
height: weatherCodeIcon.height
[...]
Eine weitere Eigenschaftsbindung, die dieses gesamte Steuerelement so hoch macht wie das Bildsymbol. Wir haben WeatherCode
verankert in WeatherPage
bei top
,left
und right
, aber nicht bottom
. Wenn wir keine Höhe festlegen würden, wäre das Element unsichtbar.
Gehen Sie auf Play und führen Sie den Code aus. Klicken Sie auf Update
Schaltfläche und das Symbol sollte sich von einem Fragezeichen in den aktuellen Wettercode ändern, den wir in weathercodeToIcon
abgebildet haben switch
Aussage:
Um die Wettercode-Steuerung abzuschließen, fügen wir auch den aktuellen Wettertext hinzu. Fast identisch mit weathercodeToIcon
Funktion machen wir jetzt eine weathercodeToText
Funktion, mit einem weiteren großen switch
. Fügen Sie es unter der anderen Funktion hinzu:
function weathercodeToText(weathercode) {
switch (weathercode) {
case 0:
return "Clear sky"
case 1:
return "Mainly clear"
case 2:
return "Partly cloudy"
case 3:
return "Overcast"
case 45:
return "Fog"
case 48:
return "Fog (Depositing rime)"
case 51:
return "Light Drizzle"
case 53:
return "Moderate Drizzle"
case 55:
return "Dense Drizzle"
case 56:
return "Light Freezing Drizzle"
case 57:
return "Dense Freezing Drizzle"
case 61:
return "Slight Rain"
case 63:
return "Moderate Rain"
case 65:
return "Heavy Rain"
case 66:
return "Light Freezing Rain"
case 67:
return "Heavy Freezing Rain"
case 71:
return "Slight Snowfall"
case 73:
return "Moderate Snowfall"
case 75:
return "Heavy Snowfall"
case 77:
return "Snow grains"
case 80:
return "Slight Rainshower"
case 81:
return "Moderate Rainshower"
case 82:
return "Violent Rainshower"
case 85:
return "Slight Snowshowers"
case 86:
return "Heavy Snowshowers"
case 95:
return "Thunderstorm"
case 96:
return "Thunderstorm with slight hail"
case 99:
return "Thunderstorm with heavy hail"
default:
return "Rainbows!"
}
}
Unter Ihrem Image
, fügen Sie einen neuen Text
hinzu Steuerung:
Text {
id: weatherCodeText
text: root.parameters ? weathercodeToText(
root.parameters['current_weather']['weathercode']) : "Loading weather, please press update"
anchors.bottom: weatherCodeIcon.bottom
anchors.left: weatherCodeIcon.right
anchors.leftMargin: 20
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignBottom
font.pixelSize: 50
wrapMode: Text.WordWrap
}
Was diese Steuerung tut, sollte keine Überraschung mehr sein. Wir anchor
es rechts neben dem Symbolbild und, wenn der parameters
definiert sind, übergeben Sie sie an unserenweathercodeToText
Funktion, die das aktuelle Wetter zurückgibt. Wenn noch keine Parameter vorhanden sind, wird Loading Weather, please press update
angezeigt .
Denken Sie daran, dass der vollständige Code auf meinem GitHub zu finden ist, sodass Sie überprüfen können, ob Sie richtig gefolgt sind, indem Sie Ihre QML-Datei mit meiner vergleichen.
Nachdem wir den Wettercode geparst haben, können wir mit der Temperatur fortfahren. Sieht diesem Teil sehr ähnlich, ohne die großen JavaScript-Parsingmethoden, da es nur eine Zahl ist.
Temperatur
Erstellen Sie wie zuvor eine neue QML-Datei mit dem Namen Temperature.qml
. Fügen Sie den leeren Item
ein Schablone. Ich schließe den height
ein und dieparameters
, weil wir das bereits im vorherigen Teil behandelt haben:
import QtQuick
Item {
id: root
height: temperatureIcon.height
property var parameters: undefined
}
Da ich möchte, dass dieses Steuerelement wie der WeatherCode aussieht, hat dieses das gleiche Layout, ein Symbol und einen kleinen Kopfzeilentext. Diesmal gibt es keinen Unterschied im Symbol, also kein JSON-Parsing. Fügen Sie es unter parameters
ein :
Image {
id: temperatureIcon
source: "qrc:icons/temperature-half-solid.svg"
asynchronous: true
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: 5
width: 90
height: width
}
Text {
id: apparentTemperatureText
text: "Apparent Temperature"
anchors.top: parent.top
anchors.left: temperatureIcon.right
anchors.leftMargin: 20
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignTop
font.pixelSize: 18
}
Der obige QML-Code sollte Ihnen vertraut sein, da es in diesem Handbuch nichts gibt, was wir noch nicht getan haben.
Wenn Sie möchten, können Sie die aktuelle scheinbare Temperatur analysieren und, wenn sie höher oder niedriger als ein festgelegter Wert ist, ein anderes Temperatursymbol anzeigen. Zeigen Sie für alles unter 10 Grad Celsius das Symbol temperature-low-solid.svg, für alles über 20 das temperature-high-solid.svg und alles dazwischen das temperature-half-solid.svg. Wie das geht, bleibt dem Leser als Übung überlassen, aber mit den Beispielen im vorherigen Wettercode-Abschnitt sollte das nicht schwierig sein.
Ich habe die scheinbare Temperatur im Gegensatz zur regulären Temperatur gewählt, hauptsächlich weil die JSON-API diese Variable nicht in current_weather
verfügbar macht JSON-Struktur, also müssen wir hourly
parsen Teil des JSON. Andernfalls wäre dieses Beispiel sehr ähnlich wie der Wettercode, was langweilig wäre. Und natürlich ist die scheinbare Temperatur nützlicher, wenn Sie das reTerminal in Ihrem Flur aufhängen, um zu wissen, welchen Mantel Sie anziehen müssen. Es könnte 10 Grad, aber sonnig und windstill sein, was sich wärmer anfühlt, oder 15 Grad mit eisigen Winden, was sich viel kälter anfühlt. Für den Zweck des erneuten Anschlusses dort ist also die scheinbare Temperatur zutreffender.
Die API-Dokumentation sagt Folgendes bezüglich des Formats und der stündlichen Daten:
Wenn wir die aktuelle Stunde des Tages abrufen können, können wir dieses Feld aus dem JSON-Objekt auswählen und die Temperatur für die aktuelle Stunde abrufen. Hier ist eine komprimierte JSON-Ausgabe:
{
[...]
"hourly_units": {
"apparent_temperature": "degC",
},
"hourly": {
"apparent_temperature": [-1.9, -2.4, -3.2, -3.3, -3.3, [...] ],
}
}
Das Feld [hourly][apparant_temperature]
ist eine Liste. Stunde 0 des aktuellen Tages hat scheinbare Temperatur -1.9
Grad Celsius. Stunde 1 hat -2.4
und so weiter.
In unserer QML-Datei, wenn der parameters
JSON enthalten, lautet die Syntax für den Zugriff auf Stunde1 wie folgt:
root.parameters['hourly']['apparent_temperature'][1]
Eine schnelle JavaScript-Funktion, um die aktuelle Stunde zu erhalten, ist unten:
function currentHour() {
const date = new Date()
return date.getHours()
}
Durch die Kombination der beiden ergibt der folgende Code einen property
das die aktuelle Stundentemperatur hat:
property double currentTemperature: root.parameters['hourly']['apparent_temperature'][currentHour()]
In diesem Fall suche ich nicht nach parameters
undefiniert sein, weil ich das später in Text
überprüfen werde Kontrolle. Sonst hättest du dort eine magische Zahl, wie 999 oder was auch immer. Nicht die ausdrucksstärkste Art.
Die API legt auch die Einheiten offen, in denen sich die Daten befinden, wie das obige Beispiel ebenfalls zeigt. Sie können darauf zugreifen, wie Sie auf die anderen Elemente zugreifen können:
property string currentTemperatureUnit: root.parameters ? root.parameters['hourly_units']['apparent_temperature'] : ""
Kombinieren der obigen Eigenschaften zu einem Text
Steuerung:
Text {
id: currentTemperatureText
text: root.parameters ? currentTemperature + "<small> "
+ currentTemperatureUnit + "</small>" : "..."
anchors.bottom: temperatureIcon.bottom
anchors.left: temperatureIcon.right
anchors.right: parent.right
anchors.leftMargin: 20
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignBottom
font.pixelSize: 54
minimumPixelSize: 45
textFormat: Text.RichText
}
Eine neue Eigenschaft ist textFormat
. Wenn Sie dies auf Text.RichText
setzen Sie können HTML verwenden. Sie können auch Text.StyledText
verwenden für etwas grundlegendes HTML, aber das enthält nicht den <small>
Schild. Mir gefällt, wie es aussieht, wenn die Einheit kleiner als die Zahl ist.
So sieht das fertige Steuerelement aus, wenn Sie noch nicht auf Aktualisieren geklickt haben:
So sieht es aus, wenn Sie JSON aktualisiert haben:
Fügen Sie das Steuerelement zu WeatherPage.qml
hinzu Datei, direkt unter WeatherCode {}
:
Temperature {
id: temperature
anchors.top: weatherCode.bottom
anchors.topMargin: 30
anchors.left: parent.left
anchors.right: parent.right
parameters: root.parameters
}
Dasselbe wie zuvor, aber jetzt ist dieses Steuerelement in weatherCode
verankert unten mit etwas Rand.
Teil 1 fertigstellen
Die Grundlagen sind alle vorhanden, Sie parsen JSON und zeigen die Daten in Ihren eigenen benutzerdefinierten Steuerelementen an. Gut erledigt! Um Teil 1 abzuschließen, fügen wir zwei weitere Schaltflächen hinzu. Eine zum Beenden der App und eine zum Laden von Beispiel-JSON. Die Schaltfläche Beenden lässt die App über systemd
neu starten auf dem reTerminal, kann praktisch sein.
Die Beispielschaltfläche finde ich nützlich. Ich habe den gesamten JSON-Datenstring in eine String-Eigenschaft mit dem Namen exampleJson
eingefügt :
property string exampleJson: '{"generationtime_ms":2.30...
Die Schaltfläche hat diese Methode als onClicked
Eigenschaft:
root.parameters = JSON.parse(exampleJson)
Das erspart Ihnen einen Netzanruf beim Testen und liefert Ihnen immer die gleichen Daten. Außerdem erspart es das Überladen der API.
Hier sind die beiden Schaltflächen:
Button {
id: exampleButton
anchors.bottom: parent.bottom
anchors.left: refreshButton.right
anchors.margins: 5
text: "Example JSON"
font.pixelSize: 30
onClicked: root.parameters = JSON.parse(exampleJson)
}
Button {
id: quitButtom
anchors.bottom: parent.bottom
anchors.left: exampleButton.right
anchors.margins: 5
text: "Quit"
font.pixelSize: 30
onClicked: Qt.callLater(Qt.quit)
}
Das fertige Ergebnis sieht so aus:
Loben Sie sich selbst, denn Sie haben großartige Arbeit geleistet. Im nächsten Teil fügen wir die Windgeschwindigkeit und -richtung (nützlich auf dem Fahrrad), persistente Einstellungen, das Wetter für die nächsten Stunden und die Qt VirtualKeyboard hinzu:
Die Tabelle beinhaltet eine fortgeschrittenere Verankerung und einen Layout
enthält das Qt VirtualKeyboard auch die Yocto-Konfiguration, um sicherzustellen, dass das reTerminal das Modul erstellt.