Seeed reTerminal 用の WeatherTerminal アプリを作成する (Qt 6 &QML を使用)

このガイドでは、Qt と QML を使用して Seeed reTerminal 用の天気アプリを作成する方法を紹介します。玄関の廊下にある reTerminal を想像してみてください。画面をちらりと見るだけで、今後数時間の天気がどうなるか、傘が必要かどうか、自転車に乗っているときに向かい風が吹くかどうか、または天気が悪いかどうかを知ることができます。晴れて晴れるでしょう。このチュートリアルは、前回の記事で構築した reTerminal Yocto boot2qt ディストリビューションに基づいて構築されており、Qt 6 を使用しています。Qt は C++ フレームワークですが、この Weather アプリはほとんど QML のみを使用します。私は QML だけを使用してガイドをよりアクセスしやすくしています。また、すべてを C++ で行うことに慣れているため、QML を回避するのも楽しいものです。

これは、基本を設定するガイドのパート 1 です。これには、QML を介したネットワーキング、QML での Open Meteo JSON 天気 API の解析、および QML での天気コードの表示が含まれます。 Qt や C++ を初めて使用する場合でも、心配する必要はありません。QML は GUI を定義するための宣言型言語ですが、これには JavaScript が含まれています。つまり、インターフェースを簡単にレイアウトし、JavaScript の一部をいくつかの面倒な作業に使用することができます。この場合、これはネットワーク アクティビティと JSON 解析になります。このガイドの最後には、JSON API 天気コードをテキスト表現に変換し、reTerminal で実行されている現在の気温を表示する基本画面が表示されます。

これは、私のデスクトップで実行されているパート 1 の最終結果の写真です:

パート 2 では、ユーザー インターフェースのスケーリング (PC と reTerminal の両方で実行するため)、永続的な設定、ロケーション ピッカー、更新タイマー、数時間先を含むより多くの気象要素を使用して WeatherTerminal を拡張し、さまざまなレイアウトなどのより高度な QML の概念をカバーします。 、アンカー要素、条件、モデル、およびプロパティ。 reTerminal には物理的なキーボードがないため、パート 2 には QtVirtual キーボードも含まれていますが、場所を入力したくありません。

パート 2 はまだ終わっていません。終わったら、ここにリンクします。

完全な開示:Seeed から連絡があり、いくつかの記事と引き換えにこの reTerminal が送られてきました。金銭的な支払いは関係なく、Seeed は公開前にこの記事をレビューしていません。公式サポートについては、Seeed wiki にアクセスしてください。

パート 1 の完全なソース コードは、私の github にあります

reTerminal とは

reTerminal は、将来に備えたヒューマン マシン インターフェース (HMI) として販売されています。 ThereTerminal は、1.5GHz で動作するクアッドコア ARM Cortex-A72 CPU である Raspberry Pi Compute Module 4 (cm4) と、解像度 1280x720 の 5 インチ IPS 容量性マルチタッチ スクリーンを搭載しています。 4 GB の RAM と 32 GB の eMMC ストレージが組み込まれています (拡張不可)。デュアルバンド 2.4GHz/5GHz Wi-Fi および Bluetooth 5.0 BLE によるワイヤレス接続を備えています。

reTerminal はこちらで購入できます。現在の価格は 195 米ドルです。 これには Compute Module 4 が含まれます。

ハードウェアと機能のより包括的な概要については、別の記事を参照してください。

開始前に必要なこと

Yocto boot2qt のセットアップに関する以前の記事に従ってください。

この Qt アプリは、reTerminal で提供されている Raspbian OS では動作しません。これを書いている時点では、使用している Qt バージョンは、その Debian バージョンで出荷されたものよりも新しいためです。 Qt 6.2 を自分でコンパイルすることもできますが、それはこのガイドの範囲外です。

次に、Qt Creator と Qt バージョン 6.2 がインストールされていることを確認します。 Yoctoboot2qt の記事には、reTerminal 用にクロスコンパイルする必要がある SDK の手順が記載されています。

Qt Creator で、別のガイドで説明されているようにキットを構成し、デプロイ先のデバイスとして reTerminal を構成します。すべて完了したら、戻って続行してください。

デスクトップで WeatherTerminal アプリを実行するだけの場合は、reTerminal 用に yocto boot2qt をセットアップする必要はなく、クロスコンパイルも必要ありませんが、Qt Creator と Qt 6.2 をインストールする必要があります。

これは優れた QML と Qt のガイドですが、このガイドの目的は reTerminal 用のアプリを構築することなので、そのことを心に留めておいてください。

ファイル -> 新しいプロジェクト

開発者として最も素晴らしいことの 1 つは、File -> New Project を実行した瞬間です。 .白紙の状態で、あなたの世界を描く準備ができています。クラフト、レガシーなどはありません。ですから、この瞬間を楽しんでください。 Qt Creator (私はバージョン 7 を使用しています) を起動し、魔法のステップを実行します。

必ず Qt Quick (QML) アプリケーションを選択し、qmake を選択してください。 ビルドシステムとして、Qt の最小バージョンを 6.2 に設定してください。通常の Qt6 キットと、前の記事で作成した Yocto SDK 提供のキットの両方を選択します。

スワイプ タブ レイアウト

まず、2 つのタブを持つレイアウトを設定します。タブバーをクリックするか、左右にスワイプして別のタブに移動できます。

1 つのタブはメインの天気情報ページで、もう 1 つのタブは設定用です。多くの設定があるわけではありませんが、基本的なレイアウトの足場は後で変更するよりも簡単です。

左側のファイル エクスプローラーで、Resources に移動します。 、 qml.qrc/ ファイル main.qml を開きます

基本的な ApplicationWindow があるはずです 1 つまたは複数の import ステートメント。 QML ファイルの構造は単純です。QML ファイルには、そのコンポーネントの動作とプロパティを定義する単一の最上位アイテムがあります。

たとえば、WeatherButton.qml という名前の新しい QML ファイルを作成すると、 、そのアイテムを ApplicationWindow 内に配置できます WeatherButton {} と書くことで .

この例では、タブレイアウトを構築するためにいくつかのコンポーネントを含めます。 Qt QuickControls を使用するには、最初に次の行を先頭に追加します。

import QtQuick.Controls

Qt 5 ではインポートするバージョン番号を指定する必要がありましたが、Qt6 では不要になりました。

width: を変更します と height: プロパティ値を 1280 および 720 に変更し、reTerminal の画面サイズを調整します。タイトルに何か良いものを入れて、ApplicationWindow 内のそれ以上のコンテンツをすべて削除します 成分。

次の行を追加します:

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
    }
}

CTRL+R (または再生ボタンのように見える緑色の三角形) を押して、作成した驚異を見てください:

また、reTerminalで実行してみてください。 QML アプリをローテーションするために Wayland + Weston セットアップを使用している場合は、QtCreator の環境に以下を追加します。

Yocto デバイス キットとリモート デバイスを選択し、Play を押します。 再ターミナルでコンパイルして実行するには:

タブ付きの基本的な空白の状態を実行している reTerminal の写真を次に示します。

SwipeView にはまだ実際のコンテンツはありません。

QML は簡単で、C++ コードは必要なく、タブ付きのアプリが既にあると言っていました。

SwipeView から始めて、これまでに行ったことを説明します :

  • id: swipeView :特定のオブジェクトを識別し、他のオブジェクトから参照できるようにするテキスト ID。この ID は小文字またはアンダースコアで始まる必要があり、文字、数字、およびアンダースコア以外の文字を含めることはできません。

  • anchors.fill: parent :swipeview をその親 (ウィンドウ) に固定し、効果的にサイズを変更してウィンドウ全体を埋めます。

  • currentIndex: tabBar.currentIndex :プロパティ バインディング。プロパティ値 currentIndex の場合はいつでも tabBar の 更新すると、QML エンジンはこのプロパティの値も自動的に更新します。タブのスワイプとクリックを効果的に結合します。

プロパティ バインディングは、QML の強みの 1 つです。この場合、プロパティ バインディングがないと、タブ ボタンをクリックするたびにスワイプ ビュー インデックスを変更し (実際にはスワイプ ビューを更新する)、その逆も同様にする関数を作成する必要があります。

アンカーについては、パート 2 で詳しく説明します。今のところ、それらを一種の磁石と考えることができます。アイテムの片側が別のアイテムの片側に固定されています。ただし、パフォーマンス上の理由から、親アイテムまたは兄弟アイテムのみ。

次は footer: TabBar {} です . footer 実際には ApplicationWindow のプロパティです プロパティは Item を取ります その値として、TabBar 全体を配置できるのはそのためです。 その中。

Items QtQuick からの視覚的なものです モジュール。クイックはQt User Interface Creation Kitの略です .

tabBar には独自の id: があります プロパティであり、2 つの Items が含まれています 内部、2 つの TabButtons 、独自のプロパティもあります:

TabButton {
    text: "Weather"
    font.pixelSize: 30
}

text: ボタンに表示されるテキストと font.pixelSize が含まれています ご想像のとおり、フォントのピクセル単位のサイズです。

TabBar のせいで 画面上で独自のレイアウト (子要素の配置) を行う場合、x: を指定する必要はありません。 、 y: または anchors: ボタンの内側。 TabBar 隣り合っていることを確認してください。

TabBar のボタンをクリックすると 、currentIndex プロパティの変更。 Settingsをクリックすると 1 になります .プロパティ currentIndex のため currentIndex にバインドされています swipeView のプロパティ 、そのスワイプビューのcurrentIndex 1 にもなります .実際、これは SwipeView になります 現在のアイテムを、その中の 2 番目の子アイテムに変更します (配列は 0 から始まることに注意してください)。

Qt を初めて使用する場合、これは単純な例に凝縮された多くの情報です。いろいろ試してみて、オートコンプリートがプロパティに提供するものを見て、それをいじってみてください。テキストの色を red にしてみてください 例えば。

タブをページで埋める

タブができたので、便利なものを入力してみましょう。 / を右クリック qml.qrc 内のフォルダ SettingsPage.qml という名前の新しい QML ファイルを作成します。 :

次の内容を貼り付けます:

import QtQuick
import QtQuick.Controls

Page {
    id: root
    width: 1240
    height: 640

    header: Label {
        text: "Settings"
        font.pixelSize: 50
    }
}

これは、ヘッダーのみの空のプレースホルダー ページです。 footer: と同じ ApplicationWindow のプロパティ 、header: プロパティは Item を取ります 値として、この場合は Label です . Button の可能性もあります またはあなたが好きなもの。 Page コントロールはレイアウトを処理し、header: を確認します Item ページの上部にあります。

main.qml で 、 SwipeView 内 、この新しいコンポーネントを追加します:

  SwipeView {
    [...]
    SettingsPage {}
}

Play を押してテストすると、ヘッダー テキスト Settings が表示されます。 、天気タブ。なんで? SwipeView 子項目が 1 つだけあり、自動的に index を取得します 番号 0。

別のファイルに対して新しい QML ファイルの作成を繰り返し、これに WeatherPage.qml という名前を付けます

SettingsPage.qml と同じ内容を追加 ファイルですが、Label を変更してください Weather と言う SwipeView に追加します main.qml で 、 SettingsPage のすぐ上 :

  SwipeView {
    [...]
    WeatherPage {}
    SettingsPage {}
}

[再生] を押してもう一度試してください。Weather が表示されます。 開くタブとして。 SwipeView 以来、右または左にスワイプすることもできます に子アイテムが追加されました。スワイプすると、タブ バーの現在アクティブなタブも変更されます。

Open Meteo API の解析

私が Open-Meteo API を選択したのは、API キーやユーザー登録が不要で、オープン ソースまたは非営利目的での使用が無料であるためです。きちんとした JSON API を提供し、LAT と LON を渡すと、予測が得られます。

アプリでは次の URL を使用しますが、何らかの理由で使用できない場合は、こちらのサイトの (静的) ミラーも使用できます。後者には明らかに現在の予測が含まれていませんが、正しい JSON 形式が提供されます。

WeatherPage.qml 内で独自のプロパティを定義することから始めましょう 、 width のすぐ下 と height :

property var parameters: undefined
property double latitude: 52.3738
property double longitude: 4.8910

最後の 2 つは自明ですが、最初の 1 つ (parameters ) は、デコードされた JSON を保持します。 var タイプは anything です QML を入力します。プロパティが保持する型がわかっている場合は、それを指定する方が高速です (string var の代わりに 例えば)。 var type は、通常の JavaScript 変数と同等です。たとえば、var プロパティには、数値、文字列、オブジェクト、配列、および関数を格納できます。解析された JSON は QJSValue 型になるため、 var と一致するより具体的な QML タイプはありません。

カスタム プロパティを追加したら、関数を追加します。これは通常の JavaScript 関数ですが、次のように QML プロパティにアクセスできます:

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&current_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()
}

以前に JavaScript を使用したことがある場合、突出している唯一の点は次のとおりです。

root.parameters = JSON.parse(xmlhttp.responseText)

JavaScript に慣れていない場合、この関数はコールバック メソッドを使用して API URL に GET 要求を送信します。コールバック メソッドは、GET リクエストが正しく終了したかどうかを確認し、終了した場合は JSON 応答を解析し、結果を QML root.parameters に割り当てます。 財産。 root id: です Page の 、QMLエンジンには複雑なスコープルールがありますが、今のところ、変数をプロパティ parameters に割り当てる必要があることを知っているだけで十分です SettingsPage ではなく、このファイルで そのページにも id: がありますが、ファイル root の .別のファイル、別のコンテキスト。

この JavaScript メソッドは等号 (=) を使用していることに注意してください。 ) ではなく、コロン (: ) を使用して、プロパティに値を割り当てます。 QML コロン (: ) プロパティ バインディング、等号 (=) を作成します。 ) ではない。 width = height を実行する場合 JavaScript メソッド内。これはプロパティ バインディングではなく、単なる代入です。 height の場合 後で変更、width しない。重要な違いですが、今のところ関係ありません。

このメソッドを呼び出すボタンを追加しましょう。プロパティの下に、以下を追加します:

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)
}

2 つの anchors. ボタンを左下に表示し、周囲に少し余白を付けます (すべての側面に)。 onClicked プロパティは、Page のプロパティとして定義した緯度と経度の 2 つのパラメータを使用して JavaScript メソッドを呼び出します。 .

Play を押してコンパイルして実行すると、ボタンは機能しますが、結果を見ることができません。プロパティ parameters デコードされた JSON がありますが、まだ何もしていません。正しく実行したことを確認するために、コンソールにログインしてみましょう。 Button の下 、以下を追加:

onParametersChanged: console.log(root.parameters['current_weather']['weathercode'])

コンパイルして実行し、更新ボタンを押すと、コンソール ログに次のように表示されます:

qrc:/WeatherPage.qml:30: TypeError: Cannot read property 'current_weather' of undefined
qml: 3

最初のエラーは問題ありません。今のところ無視できます。プロパティが宣言されると、空に初期化され、変更されたシグナルが発生しますが、記述した onChanged 関数はパラメーターが空かどうかをチェックしません。

2 行目 (qml: 3 ) は実際の weathercode です JSON API から。

ひとときを楽しんでください。 C++ コードをまったく記述せずに、ネットワーク Web サービスから JSON API を取得するタブ バーとボタンを備えたクロス プラットフォーム アプリを作成しました。繰り返しますが、このガイドで QML だけを使用している理由は、非常に簡単だからです。

onParametersChanged: の舞台裏 行は、changed のときに呼び出されるスロット (シグナル ハンドラー) です。 信号は parameters から発射されます 変数。 Qt には、シグナルとスロットと呼ばれる別の非常に強力な概念があります。これは、オブザーバー デザイン パターンまたは pub-sub のようなものですが、ステロイドと C++ タイプ セーフです。これ以上説明するつもりはありません。シグナルとスロットについてだけ本を書くことができます。興味があれば、Qt ドキュメントをチェックしてください。

すべてのプロパティは、カスタムのものも含めて changed を持っています 信号、QML エンジンがそれを作成します。そのシグナルは、QML プロパティの値が変更されると自動的に発行されます。このタイプの信号は property change signal です これらのシグナルのシグナルハンドラは、onPropertyChanged の形式で記述されます。 、ここで Property 最初の文字を大文字にしたプロパティの名前です。

console.log() onParametersChanged に割り当てた関数 スロット (シグナル ハンドラー) は、JSON オブジェクト ['current_weather']['weathercode'] の内容を出力します。 .

WeatherCode の解析

ボタンをクリックするだけで JSON API と対話できるようになったので、今度はその API を解析します。 Clear Sky のような気象条件の標準数値形式である現在の WeatherCode から始めます。 または Thunderstorm .

コードは Open-Meteo API ページに書かれており、より包括的な記事は noaa.gov サイトにあります。

テキスト出力の横に、天気コードの変更に応じて変化する素敵なアイコンを追加します。

前と同じように新しい QML ファイルを作成し、WeatherCode.qml という名前を付けます。 以下を貼り付けます:

import QtQuick

Item {
    id: root
    property var parameters: undefined
}

WeatherPage.qml で 、この新しいコンポーネントを Button の上に追加します 以前に追加しました:

WeatherCode {
    id: weatherCode
    anchors.top: parent.top
    anchors.left: parent.left
    anchors.right: parent.right
    parameters: root.parameters
}

anchors このコントロールをページの左上に配置し、右に伸ばします。後でコントロール自体で高さを定義します。コントロールに幅/高さまたはアンカーがない場合、表示されません。 parameters を渡します WeatherPageWeatherCode まで .これはプロパティ バインディングなので、Update をクリックすると ボタン、WeatherCode コントロールも新しく更新された parameters を取得します .

Qt プロジェクト フォルダー内に、icons という名前の新しいフォルダーを作成します。 次の svg をダウンロードします 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
  • レインボー ソリッド.svg
  • smog-solid.svg
  • snowflake-solid.svg
  • sun-solid.svg
  • 温度半固体.svg
  • 温度-高-固体.svg
  • 温度-低固体.svg
  • wind-solid.svg

これらはすべて font awesome free の一部であり、CC-BY-4.0 ライセンスです。

Qt Creator で、qml.qrc を右クリックします。 サイドバーのファイルを開き、Add existing files をクリックします。 . icons でダウンロードしたすべてのアイコンを選択します

新しい Image を追加 WeatherCode.qml への制御 ファイル、プロパティの下:

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
}

ここまでで、QML 構文に慣れる必要があります。高さは、幅、anchors へのプロパティ バインドです。 これを左上に配置し、その周りに少し余白を置きます。 asynchronous プロパティは、この画像の読み込み中にブロックしないように QML エンジンに指示します。 1 つの画像ではボトルネックにはなりませんが、より多くの画像を使用すると、すべての画像を非同期でロードする理由がすぐにわかります (UI がブロックされ、使用できなくなり、フリーズするため)。

source: プロパティはより複雑で、広く使用されている QML の概念である ternary if を紹介します。 声明。 root.parameters の場合 満たされています(not undefined )、その後、疑問符 (?) の後にあることを実行します。 )。そうでない場合は、コロン (:) の後にあることを実行します。 )。これは (疑似コードで) 次のように書くこともできます:

if(root.parameters !== undefined); then
    source = weathercodeToIcon(root.parameters['current_weather']['weathercode'])
else
    source = "qrc:icons/circle-question-solid.svg"

parameters を定義しました undefined として Update をクリックしない限り、 ボタンをクリックすると、疑問符アイコンが表示されます。 update を呼び出す場合 関数、parametersChanged シグナルが発生し、このプロパティ バインディングが再評価されます。

weathercodeToIcon() 関数には次のコードが含まれています。このファイルのプロパティのすぐ下に貼り付けます:

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"
    }
}

ご覧のとおり、特別なことは何もありません。ただの大きな switch ステートメントです。一連の気象コード値ごとに、異なるアイコンを返します。

画像の横で、解析された天気コード テキストの上に、小さなヘッダーが必要です。それを追加して、これを Image の上に貼り付けます :

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
}

anchors.left: weatherCodeIcon.right という新しいものがあります .つまり、テキスト オブジェクトの左側をアイコンの右側に固定する必要があります。 leftMargin を少し追加 それを美しくするために、あなたは完了です。これで、アイコンをどこに配置しても、そのすぐ隣に常にこのテキストが表示されます。アイコンを移動した場合、x: を手動で更新する必要はありません または y: Text の 、すべて自動的に行われます。

ファイルの先頭、id: のすぐ下 、height の新しいプロパティを追加します このコントロールの:

Item {
    id: root
    height: weatherCodeIcon.height
    [...]

このコントロール全体を imageicon と同じ高さにする別のプロパティ バインディング。 WeatherCode を固定しました WeatherPagetop ,leftright 、ただし bottom ではありません .高さを設定しないと、アイテムが見えなくなります。

[再生] を押して、コードを実行します。 Update をクリックします ボタンとアイコンがクエスチョン マークから、weathercodeToIcon でマップした現在の天気コードに変わるはずです。 switch ステートメント:

天気コード コントロールを終了するために、現在の天気テキストも追加しましょう。 weathercodeToIconとほぼ同じ 関数、weathercodeToText を作成します 関数、別の大きな switch .他の関数の下に追加します:

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!"
    }
}

Image の下 、新しい Text を追加します コントロール:

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
}

このコントロールが行うことは、もはや驚くべきことではありません。私たちは anchor アイコン画像のすぐ隣にあり、 parameters の場合 定義されている場合は、weathercodeToText に渡します 現在の天気を返す関数。パラメータがまだない場合は、Loading Weather, please press update と表示されます .

完全なコードは私の GitHub で見つけることができるので、自分の QML ファイルと私の QML ファイルを比較して、正しく従ったかどうかを確認できます。

天気コードの解析が完了したので、引き続き気温に進みましょう。これは単なる数値であるため、大規模な JavaScript 解析メソッドを除いて、この部分と非常によく似ています。

温度

以前と同じように新しい QML ファイルを作成し、Temperature.qml という名前を付けます。 .空の Item を貼り付けます テンプレート。 height を含めています そしてparameters 、これは前のパートで既に説明したためです:

import QtQuick

Item {
    id: root
    height: temperatureIcon.height
    property var parameters: undefined

}

このコントロールを WeatherCode のように見せたいので、これには同じレイアウト、アイコン、および小さなヘッダー テキストがあります。今回はアイコンに違いがないので、JSON 解析はありません。 parameters の下に貼り付けます :

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
}

このガイドでこれまで行ったことがないことは何もないため、上記の QML コードはおなじみのはずです。

必要に応じて、現在の見かけの温度を解析し、設定された量よりも高いまたは低い場合は、別の温度アイコンを表示できます。摂氏 10 度未満の場合はすべて、温度-低-固体.svg アイコンを表示し、20 度を超えるすべての場合は、温度-高-固体.svg および温度-半固体.svg の間のすべてを表示します。どのようにするかは読者の練習問題として残しておきますが、前のweathercodeのパラグラフの例を使えば、難しいことではありません.

通常の温度ではなく見かけの温度を選択したのは、主に JSON API が current_weather でこの変数を公開していないためです。 JSON 構造なので、hourly を解析する必要があります JSON の一部です。そうでなければ、この例はウェザーコードとほとんど同じになり、退屈になります。もちろん、見かけの温度は、reTerminal を廊下に吊るして、どんなコートを着るかを知るのに役立ちます。気温は 10 度だが晴れていて無風で暖かく感じたり、15 度で氷のように風が吹いて寒く感じたりすることがあります。そのため、そこでの reTerminal の目的では、見かけの温度の方がより適切です。

API ドキュメントには、形式と時間別データに関して次のように記載されています。

1 日の現在の時間を取得できる場合は、JSON オブジェクトからそのフィールドを選択して、現在の時間の気温を取得できます。以下は、condensedJSON の出力です:

{
    [...]
    "hourly_units": {
        "apparent_temperature": "degC",
    },
    "hourly": {
        "apparent_temperature": [-1.9, -2.4, -3.2, -3.3, -3.3, [...] ],
    }
}

フィールド [hourly][apparant_temperature] リストです。今日の時間 0 の気温は -1.9 です 摂氏。 1 時間目は -2.4 です など。

私たちの QML ファイルでは、parameters JSON を含む場合、hour1 にアクセスするための構文は次のようになります:

 root.parameters['hourly']['apparent_temperature'][1]

現在の時間を取得する簡単な JavaScript 関数は次のとおりです:

function currentHour() {
    const date = new Date()
    return date.getHours()
}

2 つを組み合わせると、以下のコードは property になります。 現在の時間の温度を持っています:

property double currentTemperature:  root.parameters['hourly']['apparent_temperature'][currentHour()]

この場合、私は parameters をチェックしません Text で後で確認するので、未定義です。 コントロール。それ以外の場合は、999 などのマジックナンバーが表示されます。最も表現力豊かな方法ではありません。

上記の例でも示されているように、API はデータの単位も公開します。他のアイテムにアクセスできるようにアクセスできます:

property string currentTemperatureUnit: root.parameters ? root.parameters['hourly_units']['apparent_temperature'] : ""

上記のプロパティを Text に組み合わせる コントロール:

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
}

1 つの新しいプロパティは textFormat です .これを Text.RichText に設定すると HTML を使用できます。 Text.StyledText も使用できます <small> は含まれませんが、いくつかの基本的な HTML には 鬼ごっこ。単位が数字よりも小さいときの見た目が好きです。

更新をクリックしていない場合の完成したコントロールは次のようになります:

JSON を更新すると、次のようになります。

WeatherPage.qml にコントロールを追加 ファイル、WeatherCode {} のすぐ下 :

Temperature {
    id: temperature
    anchors.top: weatherCode.bottom
    anchors.topMargin: 30
    anchors.left: parent.left
    anchors.right: parent.right
    parameters: root.parameters
}

以前と同じですが、このコントロールは weatherCode に固定されています 少し余裕のある底。

パート 1 の仕上げ

基本はすべて揃っています。JSON を解析し、独自のカスタム コントロールでデータを表示しています。素晴らしい!パート 1 を終了するために、さらに 2 つのボタンを追加しましょう。 1 つはアプリを終了するためのもので、もう 1 つは JSON の例をロードするためのものです。終了ボタンは systemd 経由でアプリを再起動します

例のボタンは、私が便利だと思うものです。 JSON データ文字列全体を exampleJson という名前の文字列プロパティに入れました :

property string exampleJson: '{"generationtime_ms":2.30...

ボタンには、このメソッドが onClicked としてあります プロパティ:

root.parameters = JSON.parse(exampleJson)    

これにより、テスト中のネットワーク呼び出しが節約され、毎回同じデータが得られます。さらに、API のオーバーロードを節約できます。

ここに 2 つのボタンがあります:

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)
}

最終的な結果は次のようになります:

あなたは素晴らしい仕事をしたので、背中を軽くたたいてください。次のパートでは、風速と風向 (自転車で便利)、永続的な設定、今後数時間の天気、Qt VirtualKeyboard を追加します。

テーブルには、より高度なアンカーと Layout が含まれます 、Qt VirtualKeyboard には、reTerminal がモジュールをビルドすることを確認するための Yocto 構成も含まれています。