Cree una aplicación WeatherTerminal para Seeed reTerminal (con Qt 6 y QML)

 C Programming >> Programación C >  >> Tags >> Qt
Cree una aplicación WeatherTerminal para Seeed reTerminal (con Qt 6 y QML)

En esta guía, le mostraré cómo crear una aplicación meteorológica para Seeed reTerminal usando Qt y QML. Imagina el reTerminal en tu vestíbulo de entrada y con un rápido vistazo a la pantalla sabrás qué tiempo hará en las próximas horas, si necesitas paraguas, si vas a tener viento en contra en tu paseo en bicicleta o si es sólo va a ser claro y soleado. Este tutorial se basa en la distribución reTerminal Yocto boot2qt que creamos en el artículo anterior y usa Qt 6. Qt es un marco C++, pero esta aplicación Weather usará QML casi exclusivamente. Estoy usando solo QML para que la guía sea más accesible y también porque estoy acostumbrado a hacer todo en C++, por lo que también me resulta divertido dar un paso al lado de QML.

Esta es la parte 1 de la guía donde configuraremos los conceptos básicos. Eso incluye la conexión en red a través de QML, el análisis de la API meteorológica Open Meteo JSON en QML y la visualización del código meteorológico en QML. Si es nuevo en Qt o C ++, no se preocupe. QML es un lenguaje declarativo para definir GUI, pero incluye JavaScript. Esto significa que es fácil diseñar su interfaz y tener fragmentos de JavaScript haciendo parte del trabajo pesado. , que en nuestro caso será la actividad de la red y el análisis JSON. Al final de esta guía, tendrá una pantalla básica que convierte un código meteorológico API JSON en una representación textual y muestra la temperatura actual, ejecutándose en el reTerminal.

Aquí hay una imagen del resultado final de la parte 1 ejecutándose en mi escritorio:

La Parte 2 ampliará WeatherTerminal con escalado de interfaz de usuario (para ejecutar tanto en su PC como en reTerminal), configuraciones persistentes, un selector de ubicación, un temporizador de actualización, más elementos meteorológicos que incluyen algunas horas en el futuro y cubrir conceptos QML más avanzados como diferentes diseños , elementos de anclaje, condicionales, modelos y propiedades. La Parte 2 también incluye el QtVirtual Keyboard, ya que el reTerminal no tiene teclado físico, pero queremos ingresar nuestra ubicación.

La parte 2 aún no está terminada, una vez que esté lista, la vincularé aquí.

Divulgación completa:Fui contactado por Seeed, me enviaron este reTerminal a cambio de algunos artículos. No se trata de ningún pago monetario y Seeed no ha revisado este artículo antes de publicarlo. Para soporte oficial, visite el wiki de Seeed.

El código fuente completo de la parte 1 está en mi github

¿Qué es el reTerminal

El reTerminal se comercializa como una interfaz hombre-máquina (HMI) preparada para el futuro. ThereTerminal funciona con un Raspberry Pi Compute Module 4 (cm4) que es una CPU Quad-Core ARM Cortex-A72 que funciona a 1,5 GHz y una pantalla multitáctil capacitiva IPS de 5 pulgadas con una resolución de 1280x720. 4 GB de RAM y 32 GB de almacenamiento eMMC integrados (no ampliables). Dispone de conectividad inalámbrica con Wi-Fi de doble banda de 2,4 GHz/5 GHz y Bluetooth 5.0 BLE.

Puedes comprar el reTerminal aquí, el precio actual es de USD 195. Eso incluye un módulo de cómputo 4.

Consulte el otro artículo para obtener una descripción general más completa del hardware y las funciones.

Qué debes hacer antes de empezar

Siga el artículo anterior sobre cómo configurar Yocto boot2qt.

Esta aplicación de Qt no se ejecutará en el sistema operativo Raspbian provisto en el reTerminal, ya que al momento de escribir este artículo, la versión de Qt que estamos usando es más nueva que la que se envió en esa versión de Debian. Podría continuar y compilar Qt 6.2 usted mismo, pero eso está fuera del alcance de esta guía.

A continuación, asegúrese de haber instalado Qt Creator y Qt versión 6.2. El artículo de Yoctoboot2qt tiene instrucciones para el SDK, que deberá realizar una compilación cruzada para el reTerminal.

En Qt Creator, configure el kit como se explica en mi otra guía y configure su reTerminal como un dispositivo para implementar. Una vez que haya terminado, regrese y continúe.

Si solo desea ejecutar la aplicación WeatherTerminal en su escritorio, no necesita configurar yocto boot2qt para el reTerminal, no necesita realizar una compilación cruzada, pero sí necesita instalar Qt Creator y Qt 6.2.

Puede seguir sin un reTerminal, es una buena guía de QML y Qt, pero el objetivo de esta guía es crear una aplicación para el reTerminal, así que tenga eso en cuenta.

Archivo -> Nuevo Proyecto

Una de las mejores cosas como desarrollador es el momento en que haces File -> New Project . Pizarra en blanco, lista para pintar tu mundo. Sin cruft, legado o lo que sea. Así que disfruta este momento. Inicie Qt Creator (estoy usando la versión 7) y ejecute el paso mágico.

Asegúrese de seleccionar una aplicación Qt Quick (QML), seleccione qmake como sistema de compilación y asegúrese de establecer la versión mínima de Qt en 6.2. Seleccione tanto el kit Qt6 normal como el kit proporcionado por Yocto SDK que ha creado en el artículo anterior.

Diseño de pestaña deslizante

Comenzaremos configurando un diseño que tiene dos pestañas. Puede hacer clic en la barra de pestañas o deslizar el dedo hacia la izquierda o hacia la derecha para navegar a otra pestaña.

Una pestaña será la página principal de información meteorológica y la otra pestaña será la Configuración. No es que tengamos muchas configuraciones, pero crear andamios en el diseño básico es más fácil ahora que cambiarlo más adelante.

En el explorador de archivos del lado izquierdo, navegue hasta Resources , qml.qrc , / y abre el archivo main.qml

Debería haber un ApplicationWindow básico así como uno o más import declaraciones. La estructura del archivo QML es simple, un archivo QML tiene un único elemento de nivel superior que define el comportamiento y las propiedades de ese componente.

Si crea un nuevo archivo QML llamado, por ejemplo, WeatherButton.qml , podría colocar ese elemento dentro de su ApplicationWindow escribiendo WeatherButton {} .

En nuestro caso, vamos a incluir algunos componentes para construir el tablayout. Comience agregando la siguiente línea en la parte superior, para usar Qt QuickControls:

import QtQuick.Controls

En Qt 5 tenía que especificar un número de versión para importar, en Qt6 eso ya no es necesario.

Cambia el width: y height: valores de propiedad a 1280 y 720, las dimensiones de la pantalla del terminal. Ponga algo agradable en el título y elimine todo el contenido adicional dentro del ApplicationWindow componente.

Agregue las siguientes líneas:

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

Continúe y presione CTRL+R (o el triángulo verde que parece un botón de reproducción) y observe la maravilla que ha creado:

También intente ejecutarlo en el reTerminal. Si está utilizando la configuración de Wayland + Weston para rotar aplicaciones QML, agregue lo siguiente al entorno en QtCreator:

Seleccione el kit de dispositivo Yocto y el dispositivo remoto, luego presione Play para compilarlo y ejecutarlo en el reTerminal:

Aquí hay una imagen del reTerminal ejecutando nuestra pizarra en blanco básica con pestañas:

Tenga en cuenta que deslizar hacia la izquierda o hacia la derecha aún no funciona, porque el SwipeView no tiene contenido real todavía.

Le dije que QML era fácil, que no se requería código C++ y que ya tiene una aplicación con pestañas.

Explicando lo que hemos hecho hasta ahora, comenzando con el SwipeView :

  • id: swipeView :la identificación textual que permite que ese objeto específico sea identificado y referido por otros objetos. Esta identificación debe comenzar con una letra minúscula o un guión bajo, y no puede contener caracteres que no sean letras, números y guiones bajos.

  • anchors.fill: parent :hace que la vista de deslizamiento se ancle a su elemento principal (la ventana), lo redimensiona efectivamente para llenar toda la ventana.

  • currentIndex: tabBar.currentIndex :un enlace de propiedad. Siempre que el valor de propiedad currentIndex del tabBar actualizaciones, el motor QML también actualiza automáticamente el valor de esta propiedad. Acoplamiento efectivo entre deslizar y hacer clic en una pestaña.

Los enlaces de propiedad son uno de los puntos fuertes de QML. Sin un enlace de propiedad, en este caso tendría que escribir una función que, cada vez que haga clic en un botón, cambie el índice de vista de deslizamiento (para actualizar realmente la vista de deslizamiento) y viceversa.

Las anclas se explicarán con más detalle en la segunda parte. Por ahora puedes pensar en ellos como una especie de imanes. Un lado de un artículo está anclado a un lado de otro artículo. Sin embargo, solo elementos principales o hermanos, por motivos de rendimiento.

El siguiente es el footer: TabBar {} . El footer es en realidad una propiedad de ApplicationWindow La propiedad toma un Item como su valor, por lo que puede poner un TabBar completo dentro de eso.

Items son cosas visuales del QtQuick módulo. Soportes rápidos para Qt User Interface Creation Kit .

El tabBar tiene su propio id: propiedad y contiene dos Items dentro de sí mismo, dos TabButtons , que también tienen sus propias propiedades:

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

text: contiene el texto que ve en el botón y font.pixelSize es, como era de esperar, el tamaño en píxeles de la fuente.

Debido al TabBar haciendo su propio diseño (colocando elementos secundarios) en la pantalla, no hay necesidad de especificar x: , y: o anchors: dentro de los botones. El TabBar se asegura de que estén uno al lado del otro.

Si hace clic en un botón en el TabBar , el currentIndex cambios de propiedad. Si hace clic en Settings se convertirá en 1 . Porque la propiedad currentIndex está vinculado al currentIndex propiedad del swipeView , ese swipeview'scurrentIndex también se convierte en 1 . En efecto, esto hace que el SwipeView cambie su elemento actual a cualquiera que sea el segundo elemento secundario dentro de él (recuerde, las matrices comienzan en 0).

Si es nuevo en Qt, esta es una gran cantidad de información condensada en un ejemplo simple. Trate de jugar, mire lo que ofrece la función de autocompletar para las propiedades y juegue con eso. Intenta hacer que el color del texto sea red por ejemplo.

Rellenar las pestañas con páginas

Ahora que tenemos las pestañas, llenémoslas con algo útil. Haga clic derecho en el / carpeta dentro de qml.qrc y crea un nuevo archivo QML, llamado SettingsPage.qml :

Pegue los siguientes contenidos:

import QtQuick
import QtQuick.Controls

Page {
    id: root
    width: 1240
    height: 640

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

Esta es una página de marcador de posición vacía con solo un encabezado. Igual que el footer: propiedad del ApplicationWindow , el header: propiedad toma un Item como valor, que en este caso es un Label . También podría ser un Button o lo que te apetezca. El Page control maneja el diseño y se asegura de que el header: Item está en la parte superior de la página.

En main.qml , dentro del SwipeView , agregue este nuevo componente:

  SwipeView {
    [...]
    SettingsPage {}
}

Presione Reproducir para probarlo y ahora debería ver un texto de encabezado Settings , en la pestaña Clima. ¿Por qué? Porque el SwipeView tiene solo un elemento secundario, que obtiene automáticamente index número 0.

Repita la creación del nuevo archivo QML para otro archivo, asígnele el nombre WeatherPage.qml

Agregue los mismos contenidos que el SettingsPage.qml archivo, pero cambie el Label decir Weather y añádelo al SwipeView en main.qml , justo encima del SettingsPage :

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

Presiona Reproducir e inténtalo de nuevo, ahora deberías ver Weather como pestaña de apertura. Ahora también puedes deslizar el dedo hacia la derecha o hacia la izquierda, ya que el SwipeView ahora tiene elementos secundarios. Si desliza, la pestaña activa actual en la barra de pestañas también debería cambiar.

Análisis de la API de Open Meteo

Elegí la API de Open-Meteo porque no requiere una clave de API ni registro de usuario y es gratuita para uso de código abierto o no comercial. Proporciona una API JSON ordenada, pasa un LAT y LON y bamm, obtienes el pronóstico.

Usaré la siguiente URL en la aplicación, pero si por alguna razón no está disponible, también puede usar el espejo (estático) en mi sitio aquí. Este último obviamente no contendrá el pronóstico actual, pero le dará el formato JSON correcto.

Comencemos definiendo nuestras propias propiedades dentro de WeatherPage.qml , justo debajo del width y height :

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

Los dos últimos se explican por sí mismos, el primero (parameters ) contendrá el JSON decodificado. El var el tipo es el anything escriba en QML. Si conoce el tipo que contendrá una propiedad, es más rápido especificarlo (string en lugar de var por ejemplo). El var type es equivalente a una variable JavaScript normal. Por ejemplo, las propiedades var pueden almacenar números, cadenas, objetos, matrices y funciones. Dado que nuestro JSON analizado será del tipo QJSValue y no hay un tipo de QML más específico que coincida con eso, var es nuestra mejor opción.

Después de agregar las propiedades personalizadas, agregue una función. Esta es una función normal de JavaScript, pero puede acceder a las propiedades de QML como verá:

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

Si ha hecho JavaScript antes, lo único que podría sobresalir es:

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

Si no está familiarizado con JavaScript, esta función envía una solicitud GET a la URL de la API con un método de devolución de llamada. El método de devolución de llamada verifica si la solicitud GET finalizó correctamente y, de ser así, analiza la respuesta JSON y asigna el resultado al QML root.parameters propiedad. root es el id: de nuestro Page , el motor QML tiene reglas de alcance complejas, pero por ahora es suficiente saber que tiene que asignar la var a la propiedad parameters en este archivo, no en el SettingsPage archivo a pesar de que esa página también tiene el id: de root . Diferente archivo, diferente contexto.

Tenga en cuenta que este método de JavaScript utiliza el signo igual (= ) y no los dos puntos (: ) para asignar un valor a la propiedad. Los dos puntos QML (: ) vincula una propiedad, el signo igual (= ) no es. Entonces, si hicieras width = height dentro de un método de JavaScript, eso no sería un enlace de propiedad, solo una asignación. Si height cambios posteriores, width no lo haré Diferencia importante, pero no tan relevante por ahora.

Agreguemos un botón que llame a este método. Debajo de las propiedades, agregue lo siguiente:

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

Los dos anchors. haga que el botón aparezca en la parte inferior izquierda con un poco de margen alrededor (en todos los lados). El onClicked property llama a nuestro método JavaScript con los dos parámetros, latitud y longitud, que definimos como propiedades del Page .

Si presiona Reproducir para compilar y ejecutar, el botón funcionará pero no podrá ver el resultado. La propiedad parameters tiene el JSON decodificado, pero aún no hacemos nada con él. Para asegurarnos de que lo hemos hecho correctamente, iniciemos sesión en la consola. Debajo del Button , agregue lo siguiente:

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

Compile y ejecute, presione el botón de actualización y el registro de la consola debería mostrar algo como lo siguiente:

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

El primer error está bien, podemos ignorarlo por ahora. Cuando se declaró la propiedad, se inicializó vacía, disparando una señal modificada, pero la función onChanged que escribimos no comprueba si los parámetros están vacíos.

La segunda línea (qml: 3 ) es el weathercode real de la API de JSON.

Tómese un momento para disfrutar. Sin escribir ningún código C++, creó una aplicación en todas las plataformas con barras de pestañas y un botón que obtiene una API JSON de un servicio web de red. Nuevamente, la razón por la que estoy usando solo QML para esta guía es porque es muy fácil.

Detrás de escena, el onParametersChanged: línea es una ranura (controlador de señal) que se llama cuando changed la señal se dispara desde nuestro parameters variable. Qt tiene otro concepto muy poderoso llamado señales y ranuras, que es como un patrón de diseño de observador, o pub-sub, pero seguro con esteroides y tipo C++. No voy a explicarlo más, podría escribir un libro solo sobre señales y tragamonedas, si está interesado, consulte el documento Qt sobre él.

Cada propiedad, incluso las personalizadas, tiene un changed señal, el motor QML crea eso para nosotros. Esa señal se emite automáticamente cuando cambia el valor de una propiedad QML. Este tipo de señal es un property change signal y los manejadores de señales para estas señales están escritos en la forma de onPropertyChanged , donde Property es el nombre de la propiedad, con la primera letra en mayúscula.

El console.log() función que hemos asignado al onParametersChanged slot (controlador de señal) imprime el contenido del objeto JSON ['current_weather']['weathercode'] .

Análisis del código meteorológico

Ahora que podemos hablar con la API JSON con solo hacer clic en un botón, es hora de analizar esa API. Comenzaremos con el WeatherCode actual, que es un formato numérico estándar para las condiciones climáticas, como Clear Sky o Thunderstorm .

Los códigos están escritos en la página API de Open-Meteo y hay una redacción más completa en el sitio noaa.gov.

Junto a solo una salida de texto, agregaremos un bonito icono que cambia a medida que cambia el código meteorológico.

Cree un nuevo archivo QML tal como lo hizo antes, asígnele el nombre WeatherCode.qml y pega lo siguiente:

import QtQuick

Item {
    id: root
    property var parameters: undefined
}

En el WeatherPage.qml , agregue este nuevo componente arriba del Button agregamos anteriormente:

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

El anchors coloque este control en la parte superior izquierda de la página y estírelo hacia la derecha. Definiremos la altura más adelante en el propio control. Si un control no tiene ancho/alto o anclas, no será visible. Pasamos el parameters del WeatherPage hasta el WeatherCode . Este es un enlace de propiedad, por lo que si hace clic en Update botón, el WeatherCode el control también obtiene el nuevo parameters actualizado .

Dentro de la carpeta de su proyecto Qt, cree una nueva carpeta llamada icons y descarga el siguiente svg archivos de FontAwesome.com :

  • círculo-pregunta-sólido.svg
  • reloj-solido.svg
  • nube-lluvia.svg
  • duchas-de-nubes-pesadas-solidas.svg
  • duchas-de-nubes-agua-solida.svg
  • nube-sol-solido.svg
  • poo-storm-solid.svg
  • arcoiris-solido.svg
  • smog-solid.svg
  • copo-de-nieve-solido.svg
  • sol-solido.svg
  • temperatura-medio-sólido.svg
  • temperatura-alta-sólido.svg
  • temperatura-baja-sólido.svg
  • viento-solido.svg

Todos estos son parte de fuente impresionante gratis y tienen licencia CC-BY-4.0.

En Qt Creator, haga clic derecho en qml.qrc archivo en la barra lateral y haga clic en Add existing files . Seleccione todos los iconos que ha descargado en el icons carpeta.

Agregar un nuevo Image control al WeatherCode.qml archivo, debajo de las propiedades:

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
}

Ya debería familiarizarse más con la sintaxis QML. La altura es una propiedad vinculada al ancho, el anchors coloque esto en la parte superior izquierda con un poco de margen alrededor. El asynchronous La propiedad le dice al motor QML que no bloquee mientras carga esta imagen. Con una imagen no es un cuello de botella, pero con más imágenes rápidamente te das cuenta de por qué quieres que todas las imágenes se carguen de forma asíncrona (porque la interfaz de usuario se bloquea, no se puede utilizar, se congela).

El source: La propiedad es más compleja y le presenta un concepto QML ampliamente utilizado, el ternary if declaración. Si root.parameters está lleno (not undefined ), luego haz lo que esté después del signo de interrogación (? ). Si no, haz lo que esté después de los dos puntos (: ). Esto también podría escribirse (en pseudocódigo) como:

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

Hemos definido parameters como undefined , siempre y cuando no hayamos hecho clic en el Update botón, mostrará un icono de signo de interrogación. Si llamamos al update función, un parametersChanged la señal se activará y se volverá a evaluar el enlace de esta propiedad.

El weathercodeToIcon() La función contiene el siguiente código. Péguelo justo debajo de las propiedades en este archivo:

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

Como puede ver, nada especial, solo una gran declaración de cambio. Para cada serie de valores de códigos meteorológicos, devuelve un icono diferente.

Junto a la imagen y encima del texto del código meteorológico analizado, quiero un pequeño encabezado. Agreguemos eso, pegue esto arriba del 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
}

Aquí hay algo nuevo, anchors.left: weatherCodeIcon.right . Esto significa que el lado izquierdo del objeto de texto debe estar anclado al lado derecho del icono. Agrega un poco de leftMargin para que quede bonito y listo. Ahora, donde sea que coloques el ícono, justo al lado siempre estará este texto. Si mueve el ícono, no necesita actualizar manualmente el x: o y: del Text , todo se hace automáticamente por ti.

En la parte superior del archivo, justo debajo de id: , agregue una nueva propiedad para el height de este control:

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

Otro enlace de propiedad, que hace que todo este control sea tan alto como el imageicon. Hemos anclado el WeatherCode en WeatherPage en el top ,left y right , pero no el bottom . Si no estableciéramos una altura, el elemento sería invisible.

Ve a presionar Reproducir y ejecuta el código. Haz clic en Update y el ícono debe cambiar de un signo de interrogación a cualquiera que sea el código meteorológico actual, que mapeamos en el weathercodeToIcon switch declaración:

Para finalizar el control del código meteorológico, agreguemos también el texto meteorológico actual. Casi lo mismo que el weathercodeToIcon función, ahora hacemos un weathercodeToText función, con otro gran switch . Agréguelo debajo de la otra función:

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

Debajo de su Image , agrega un nuevo Text controlar:

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
}

Lo que hace este control ya no debería ser una sorpresa. Nosotros anchor justo al lado de la imagen del icono y, si el parameters están definidos, pásalos a nuestro weathercodeToText función, que devuelve el clima actual. Si aún no hay parámetros, dice Loading Weather, please press update .

Recuerde, el código completo se puede encontrar en mi GitHub, por lo que puede verificar si ha seguido correctamente comparando su archivo QML con el mío.

Ahora que hemos analizado el código meteorológico, continuemos con la temperatura. Se parece mucho a esta parte, sin los grandes métodos de análisis de JavaScript, ya que es solo un número.

Temperatura

Cree un nuevo archivo QML como lo ha hecho antes, asígnele el nombre Temperature.qml . Pegar el vacío Item modelo. Incluyo el height y el parameters , porque ya lo hemos cubierto en la parte anterior:

import QtQuick

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

}

Como quiero que este control se parezca al WeatherCode, este tiene el mismo diseño, un ícono y un pequeño texto de encabezado. Esta vez no hay diferencia en el icono, por lo que no hay análisis de JSON. Pégalo debajo del 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
}

El código QML anterior debería resultarte familiar, ya que no hay nada que no hayamos hecho antes en esta guía.

Si lo desea, puede analizar la temperatura aparente actual y, si es superior o inferior a una cantidad establecida, mostrar un icono de temperatura diferente. Para todo lo que esté por debajo de 10 grados centígrados, muestre el icono de temperatura-baja-sólido.svg, para todo lo que esté por encima de 20, el icono de temperatura-alta-sólido.svg y todo lo que esté en el medio, el icono de temperatura-medio-sólido.svg. Cómo hacerlo se deja como un ejercicio para el lector, pero con los ejemplos en el párrafo anterior del código meteorológico, eso no debería ser difícil.

Elegí la temperatura aparente en lugar de la temperatura normal, principalmente porque la API de JSON no expone esta variable en el current_weather estructura JSON, por lo que tenemos que analizar el hourly parte del JSON. De lo contrario, este ejemplo sería muy parecido al código meteorológico, lo que sería aburrido. Y por supuesto, la temperatura aparente es más útil si cuelgas el reTerminal en tu pasillo, para saber qué abrigo ponerte. Podría hacer 10 grados pero soleado y sin viento, lo que se siente más cálido, o 15 grados con vientos helados, que se siente mucho más frío. Entonces, para el propósito de la reTerminal allí, la temperatura aparente es más aplicable.

Los documentos de la API dicen lo siguiente, con respecto al formato y los datos por hora:

Si podemos obtener la hora actual del día, podemos seleccionar ese campo del objeto JSON y obtener la temperatura para la hora actual. Aquí hay una salida JSON condensada:

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

El campo [hourly][apparant_temperature] es una lista La hora 0 del día actual tiene temperatura aparente -1.9 grados Celsius. La hora 1 tiene -2.4 Etcétera.

En nuestro archivo QML, cuando parameters contiene JSON, la sintaxis para acceder a la hora 1 es la siguiente:

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

A continuación se muestra una función rápida de JavaScript para obtener la hora actual:

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

Combinando los dos, el siguiente código da como resultado un property que tiene la temperatura horaria actual:

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

En este caso no busco parameters siendo indefinido, porque lo comprobaré más adelante en el Text control. De lo contrario, tendrías un número mágico allí, como 999 o lo que sea. No es la forma más expresiva.

La API también expone las unidades en las que se encuentran los datos, como también muestra el ejemplo anterior. Puede acceder a eso como puede acceder a los otros elementos:

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

Combinando las propiedades anteriores en un Text controlar:

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
}

Una nueva propiedad es textFormat . Al establecer esto en Text.RichText puede usar HTML. También puedes usar Text.StyledText para algo de HTML básico, pero eso no incluye el <small> etiqueta. Me gusta cómo se ve cuando la unidad es más pequeña que el número.

Así es como se ve el control terminado cuando aún no ha hecho clic en actualizar:

Así es como se ve cuando ha actualizado el JSON:

Agregue el control al WeatherPage.qml archivo, justo debajo del WeatherCode {} :

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

Lo mismo que antes, pero ahora este control está anclado a weatherCode inferior con un poco de margen.

Terminando la parte 1 arriba

Los conceptos básicos están en su lugar, está analizando JSON y mostrando los datos en sus propios controles personalizados. ¡Bien hecho! Para terminar la parte 1, agreguemos dos botones más. Uno para salir de la aplicación y otro para cargar JSON de ejemplo. El botón Salir hace que la aplicación se reinicie a través de systemd en el reTerminal, puede ser útil.

El botón de ejemplo es uno que encuentro útil. Puse toda la cadena de datos JSON en una propiedad de cadena llamada exampleJson :

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

El botón tiene este método como onClicked propiedad:

root.parameters = JSON.parse(exampleJson)    

Esto le ahorra una llamada de red en la prueba y le brinda los mismos datos cada vez. Además, evita sobrecargar la API.

Aquí están los dos botones:

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

El resultado final se ve así:

Date una palmadita en la espalda porque has hecho un gran trabajo. En la siguiente parte agregaremos la velocidad y dirección del viento (útil en la bicicleta), la configuración persistente, el clima para las próximas horas y el Qt VirtualKeyboard:

La tabla implica un anclaje más avanzado y un Layout , Qt VirtualKeyboard también incluye la configuración de Yocto para asegurarse de que el reTerminal construya el módulo.