HP Prime - Consideraciones del Lenguaje PPL - 3° Parte

Consideraciones importantes para programadores en PPL, se presenta un resumen de puntos relevantes al lenguaje, tercera parte donde se describe el funcionamiento de gráficos y también una introducción a interfaces gráficas propias.



Consideraciones del Lenguaje PPL - III
GRÁFICOS

Como punto principal de este artículo se debe aclarar que aprender a manipular o editar los gráficos que podamos crear en la calculadora, no significa que ya podamos crear Interfaces gráficas propias, por ese motivo en la parte final de este artículo se adjunta una pequeña introducción a ese tema. La manipulación de gráficos representa un ~25% de los conocimientos necesarios para crear Interfaces.

La calculadora posee variables gráficas integradas, son en total 10 y no es posible crear más de su tipo, existe una forma de crear más objetos gráficos pero no es recomendado por muchas razones (ICON).


Variables gráficas

Existen 10 variables de gráfico en HP Prime, los cuales son G0 a G9. G0 es siempre el gráfico actual de la pantalla, y es G0 a quien debemos modificar para visualizar los cambios deseados en pantalla, por lo ya descrito no es posible redefinir su dimensión o alguna acción similar.

Por lo tanto nos queda G1 a G9 para almacenar nuestros gráficos (objetos gráficos temporales), en inglés se denomina GROB (Graphic Object). Las variables gráficas pueden llegar a ser términos o elementos confusos para programadores principiantes, es por ello que podemos compararlos como lienzos invisibles que se encuentran en la memoria.


En estos lienzos podemos dibujar rectángulos, textos, líneas, arcos y triángulos, al igual como se hace en G0 (pantalla de la calculadora); estos lienzos se encuentran en memoria y necesitan ser creados antes de ser usados (inicializados y dimensionados). También no son visibles, si fueran siempre visibles no aportarían muchos beneficios, es por ello que debemos recordar que son variables, similar a una variable con un valor numérico que no es visible hasta mostrarla en Inicio, MSGBOX o PRINT; en el caso de las variables gráficas las debemos enviar a G0 para poder visualizarlas (pantalla de la calculadora).

Las variables gráficas son elementos estáticos (basado en píxeles y no en vectores), en ningún sentido se puede asignar una animación a una variable como si fuera una propiedad admitida, esto incluso va para G0, aunque sabemos que la pantalla tiene un comportamiento dinámico, debemos identificar que G0 esta siendo modificado cada vez que eso pasa (existe una estructura de fondo que manipula su variación).

Uso incorrecto

La manipulación de las variables gráficas debe ser comprendida de forma adecuada, por ese motivo se muestra las formas incorrectas al imaginar usarlas. No es correcto sumar, restar, multiplicar, asignar o usar cualquier otra operación que no esta enfocada a la manipulación de gráfico; es posible digitar sumas o alguna otra expresión, pero eso se debe a que las variables G0-9 siempre son interpretadas como datos tipo función (ver Consideraciones - 2° Parte); en este sentido se afirma que su manipulación esta controlada y solo accesible mediante ciertos comandos.



Aunque en Inicio es posible ver a los gráficos insertados en las expresiones creadas, eso no significa que el gráfico sea parte de ella, solo es una característica de visualización que adiciona Inicio para los identificadores gráficos G0-9.



Uso correcto

Los objetos gráficos deben manipularse con comandos de dibujo, es el único medio que permite acceder a la fuente gráfica a las que hacen referencia sus identificadores.

Los comandos de dibujo tienen tareas como Inicializar-Dimensionar, Editar píxeles, Interactuar el contenido entre gráficos y Consultar datos; el formato común de estos comandos requieren parámetros como la variable gráfica a editar y las características de la acción.




Comandos de dibujo

Dentro de los comandos de dibujo encontramos algunos que no influyen en la edición de los gráficos, pero se relacionan al tema como RGB, PX→C, C→PX, ICON, DRAWMENU y FREEZE; algunos de estos comandos serán discutidos más adelante.

Respecto a los comandos que sí manipulan directamente nuestros gráficos, podemos clasificarlos con el fin de comprender todo lo que es posible hacer con las variables gráficas (comprender capacidades y límites). Debemos prestar atención al comando BLIT, aunque sea el único en su clase, nos permite realizar la tarea más esencial al usar gráficos: insertar, mezclar y copiar; los gráficos pueden ser copiados parcial o totalmente.


Ya que el fin de este artículo no es colocar la misma información del manual de usuario y ayuda de la calculadora, sino dar pautas para su uso, me he limitado solo a listar los comandos en la imagen anterior, ya existe información suficiente para estos comandos;  posterior a esto se indican algunos usos prácticos.

Para hacer nuestras pruebas en Inicio, usaremos el comando FREEZE, para salir de la pausa de pantalla presionamos Esc u otra tecla, no podemos presionar Enter por la razón ya mencionada en la última parte de Consideraciones - 1° Parte. El comando FREEZE solo congela la pantalla al finalizar el programa, no es útil para Interfaces gráficas propias.


Si bien es cierto, se puede trabajar directamente sobre G0, no es incoveniente si no hay necesidad de almacenar los gráficos creados, los gráficos se deben entender como objetos de almacenamiento al igual que las variables comunes, solo usarlas si es necesario. Se recomienda eliminar su contenido antes de finalizar el programa, ya que este no se eliminará hasta apagar la calculadora, por ejemplo con la siguiente instrucción: DIMGROB(G1,0,0).

Usar gráficos resulta un asunto sencillo, solo es poner sentencias una detrás de otra, cosa que no representa un reto para el programador; el verdadero reto será al momento de interactuar con ellos (crear juegos por ejemplo). Algunos comandos gráficos tienen usos prácticos que resultan muy útiles en ocasiones.


La mayoría de estos usos mostrados estan orientados a un manejo complejo de gráficos, por lo que es probable que no sean usados (es más solicitado para juegos).

Coordenadas y dimensiones expresadas en listas

La mayoría de comandos de dibujo acepta pares de datos en formato de listas, tanto para coordenadas y dimensiones; usarlo al momento de programar resulta muy práctico. Esta característica no esta documentada claramente en el manual.




Sistema de coordenadas: píxel y cartesiano

Hasta el momento se han mostrado ejemplos rápidos, con la finalidad de hacer una breve demostración de los comandos existentes, pero esto no esta completo si no entendemos los sistemas de coordenadas que maneja la calculadora.

Los comandos encargados de manipular los objetos gráficos, se encuentran duplicados debido a que existen 2 sistemas de coordenadas; Las coordenadas cartesianas (para aplicaciones) y las coordenadas de píxel basadas en la pantalla física (para uso general, incluso aplicaciones), los comandos que funcionan con píxeles tienen un sufijo _P incluído en su nombre.


En este artículo se utilizan los comandos que funcionan con píxeles, debido a que el otro sistema esta orientado a Aplicaciones (tema que se verá en el siguiente artículo). Las referencias a píxeles siempre son truncadas a un valor entero, no es posible referenciar un píxel como [0.5,0.5].


Colores

A menos que se especifique de otra forma, los colores se definen en el formato #A8R8G8B8 (8 bits para R, G, B y A). El manual recomienda usar la función RGB al definir los colores con el fin de brindar compatibilidad en dispositivos futuros (la definición de color puede variar en firmwares futuros, aunque es poco probable).





Variando la intensidad de cada canal de color se logra obtener un gran espectro de colores; puedes leer más sobre el Modelo de color RGB que se basa en la síntesis aditiva.

En términos de números enteros hexadecimales, cada 2 cifras se refiere a un canal, eso gracias a que 0 - 255 es equivalente a #00h - #FFh; la definición de colores esta popularizado en base hexadecimal (HTML, JavaScript y otros).



Mezclando colores: canal alfa

Las mezclas se relacionan con el uso del nivel de opacidad/transparencia, valor que también varía de 0 a 255 (8 bits - #FFh); no todos los comandos aceptan el canal alfa al definir colores. Las mezclas realizadas sobre los gráficos se basan en el grado de superposición de un color de mezcla sobre un color base, esta acción no debe confundirse como un sistema de color aditivo (como pasa con la combinación de canales RGB).



Los comandos ARC, TEXTOUT, LINE y TRIANGLE no aceptan el canal alfa (LINE y TRIANGLE sí lo aceptan en sus formas avanzadas). La creación de GROB's sin fondo con el comando DIMGROB presenta problemas pero se espera que sea solucionado en el futuro (reemplazable con un BLIT con exclusión de color).

La pantalla y los colores realmente almacenados

Aunque se haya mostrado la mezcla de colores mediante el canal alfa, el resultado en el gráfico es un color sólido por píxel; el color de píxel final no tiene un valor de transparencia, tampoco es posible definir un color base con un canal alfa porque la calculadora no esta preparado para ello.



Ya que estamos usando el comando GETPIX (consultar el color de un píxel), aprovecharemos para reconocer la limitación de bits que posee la pantalla de la calculadora. Veamos cómo los colores insertados no son los mismos que los consultados.



Esto esencialmente se debe a que la pantalla de la calculadora tiene una profundidad de color de 16 bits (especificaciones técnicas de pantalla). Estos 16 bits de datos estan separados en 1 bit para alfa, 5 bits para rojo, 5 bits para verde y 5 bits para azul, A1R5G5B5 (15bits definen el color), medidas distintas a las que se ha estandarizado en la calculadora con fines prácticos A8R8G8B8 (32 bits donde 24bits definen el color).


Con esto podemos afirmar que la calculadora posee menos combinaciones de colores que las pantallas comerciales a las que estamos acostumbrados; pero esto no se considera una desventaja si no somos quisquillosos. La reducción de tonos no es notable a menos que se usen imágenes como la mostrada, donde existen colores similares con tonos cercanos como en el cielo.

Otra observación a este ajuste de bits para cada píxel en la calculadora, es que solo tenemos 1 bit para definir un dato alfa almacenable (color final en un gráfico), siendo así que muchos comandos no aceptan el canal alfa y simplemente muestran o no muestra un color (similar a un verdadero y falso). Esto representa claramente un obstáculo para una combinación avanzada de gráficos, pero son limitaciones a las que nos debemos ajustar a la hora de programar (el programador se adapta al dispositivo con el que trabaja).



Funciones con paso por referencia?

Se mostró antes que usar variables graficas en expresiones, resultan asumidas como datos tipo función, y que sólo los comandos de dibujo pueden interpretarlos para acceder a los objetos gráficos que hacen referencia; por esa razón la creación de funciones para editar variables gráficas resulta práctica, ya que al pasar los identificadores como datos tipo función permite seleccionar el gráfico deseado.





La función propia Imprimir simula un paso por referencia, esto gracias a cómo son interpretados las variables gráficas en el código, la forma de acceder a los GROB's que tienen los comandos de dibujo y por último que las variables gráficas son globales (accesibles desde cualquier ámbito).


DRAWMENU

Recordando nuevamente que los gráficos son estáticos, debemos sobreentender que este comando solo nos apoya con el dibujado estándar de menús fiel al tema de la calculadora. Por lo tanto siempre se ubica en la parte inferior de la pantalla; el dibujo solo se crea en G0. El comando también permite una lista como único parámetro.



La calculadora tiene esa área definida como la Zona de menú (320x21), área que responde de forma especial frente al resto de la pantalla ante los gestos táctiles.

No es posible personalizar el comando mediante parámetros para mostrar opciones desplegables, DRAWMENU no es una interfaz gráfica integrada.




ICON (No recomendado)

La directiva ICON permite crear más objetos gráficos locales a un programa (conserva cambios), para ello se necesita el programa DIMGROB Generator con el que obtenemos un código hexadecimal de la imagen solicitada; este código suele ser extenso de acuerdo a la cantidad de colores que componen la imagen.

Sintaxis

Se definen con la directiva ICON antepuesta al nombre del objeto gráfico seguido del código hexadecimal. Esta línea de código puede ubicarse en cualquier parte del programa pero fuera de cualquier función (el código hexadecimal no puede ser modificado por espacios o saltos de línea).



Para usar estos objetos gráficos usamos sus identificadores en formato de cadenas, de esta manera es posible usarlos con los comandos de dibujo, excepto con DIMGROB y SUBGROB.





Objetivo

Esta característica nos permite usar imágenes complejas dentro de un programa (la idea es enviar imágenes pequeñas); no debe reemplazar el uso de las variables gráficas integradas (usarlo como destino de los comandos de dibujo), es posible modificarlos pero tienen más un propósito de origen.

Tamaño excesivo en programas

Programar en HP Prime significa adecuarse a sus características, no es óptimo tratar de usar imágenes de computadora sin una verdadera necesidad (aunque muchos crean que usar imágenes de fondo es genial). El uso de imágenes sí es adecuado en Aplicaciones, ya que permiten usar archivos JPG y PNG.

Inconvenientes con la memoria RAM

ICON ha sido el causante de fallas de memoria desde su nacimiento, los desarrolladores de HP explicaron su diseño y desventaja en un foro concurrente. El problema surge debido al uso inadecuado que le han dado los programadores al tomar la desición de enviar imágenes de forma despreocupada; este problema incluso dió la idea de eliminarlo en firmwares futuros (decisión muy dificil).

En resumen, un objeto gráfico creado a partir de ICON, permanece en memoria RAM aunque sea local al programa (así fue diseñado); por este motivo muchos usuarios presentan problemas de memoria insuficiente, más aún si mencionamos la loca idea de leer PDF's en la calculadora, donde hasta hace pocos años estos documentos eran convertidos en imagenes y luego convertidos en gráficos nombrados con ICON (presentándose como beneficio cuando era una desventaja).

Notas sobre ICON

Debe evitarse aún si se toman consideraciones para su uso, ocupa espacio en almacenamiento y memoria RAM de manera no óptima. Si es necesario el uso de gráficos detallados se recomienda usar Aplicaciones.

Debido a esto es que en los tutoriales que he fabricado para la calculadora, no recomiendo su uso y menciono que no envíen programas mayores a 500KB, asumiendo que el programa ha explotado esta característica.


Introducción a Interfaz gráfica propia

Asumiendo que entendemos perfectamente cómo diseñar el cuerpo de un programa, usar variables gráficas, elegir los tipos de datos adecuados, crear códigos eficientes y tener en mente las limitaciones de la calculadora; avanzaremos un paso más hacia la creación de flujos gráficos.

Hasta ahora hemos visto la manipulación de gráficos estáticos, puesto que no lo hemos integrado en una estructura de código que le indique hacer cambios ante los eventos que pueda accionar el usuario; es aquí donde el programador nuevamente debe usar su magia y mejorar sus conocimientos. Para lograr crear una interfaz debemos dibujar la pantalla en momentos exactos y necesarios, darle al usuario el tiempo de responder ante una petición como lo haría el comando INPUT; dicho de otro modo, engañar a los usuarios con la interacción que tendrá la pantalla con las teclas y pantalla (como hacerles creer que en la pantalla existen botones).

Detectando acciones de teclado y pantalla

Para lograr esto usaremos la instrucción WAIT(-1), al ejecutarlo el comando retornará el ID de la tecla presionada o la coordenada de píxel y tipo de gesto en pantalla, es un comando que permite una total consulta sobre las acciones que pueda realizar el usuario.



El programa en espera de una instrucción

El comando WAIT(-1) no avanza a menos que se realice una acción o pase 60 segundos, por lo que solo nos falta ingresarlo en un bucle que permita tener un flujo constante de captura de acciones. En el siguiente ejemplo se observa cómo la instrucción WAIT(-1) puede retornar el tipo de evento de pantalla, para ello se adicionó un WAIT(0.5) para observar todos los eventos identificados.



Debemos evitar usar los comandos ISKEYDOWN o GETKEY dentro del mismo bucle, ya que ocasionan conflictos incluso con los eventos de pantalla, es posible usarlo dentro de subfunciones que regulen un compartamiento independiente al bucle principal.

La inserción de gráficos

Como se aprecia en el código anterior, existen instrucciones gráficas fuera del bucle que crean el gráfico base (gráfico estático que no es necesario redibujar), esto es igual a una buena práctica de ahorro de recursos, los fondos y gráficos base deben insertarse o crearse fuera de los bucles, solo pueden ir instrucciones gráficas que involucren la parte dinámica del programa.

Otro punto importante es que no es recomendable usar directamente G0, ya que es posible que la pantalla muestre un parpadeo aparente (molestia visual), por lo que se puede recurrir a G1 (320x240) para construir todos los gráficos necesarios antes de mostrarse en pantalla.

Función de verificación de toque: zona presionada

A nivel de programación se suele usar zonas rectangulares que limitan la zona de influencia de algún botón o campo dinámico, esto permite simplificar los datos a usar para la verificación del toque realizado; pues solo necesitaremos verificar si la coordenada del toque esta entre las coordenadas de la zona definida para un campo.



La función Si_Toca es relativamente sencilla, recibe como parámetros las coordenadas de la zona a verificar como datos tipo lista, retorna 1 (verdadero) si se ha tocado la zona indicada, de lo contrario retorna 0 (falso); debido a que esta función necesita interactuar con Accion, esta variable deberá tener un ámbito global.

Estructura de captura de la acción: bucle de reconocimiento de acciones

En el caso de una Interfaz Gráfica Propia, la estructura del programa pasa a convertirse en una estructura de reconocimiento de acciones de usuario; mediante una estructura CASE se organiza las diferentes respuestas teniendo en cuenta el orden de los tipos de gestos en pantalla. Sabemos que la variable Acción puede ser tipo lista para pantalla y tipo real para teclado, por lo que debemos separar su lectura.





Descarga el código: PPL-EstructuraCaptura

En el bucle de reconocimiento de acciones, capturaremos en primer lugar el evento, luego dirigimos la acción con la estructura CASE, para ello debemos conocer el comportamiento de WAIT(-1) y adecuarnos; aquel comando posee respuestas muy útiles como el inicio de toque, fin de toque, finalización del toque (permite diferenciar entre un clic y un desplazamiento o zoom).

Ejemplo aplicativo: Intefaz de entrada

En este ejemplo simple, se simula una interfaz de entrada de datos en celdas, como se sabe los fondos pueden editarse como el programador lo desee; en este caso nos enfocamos un poco en la interacción del código para capturar las acciones del usuario, es posible agregarle más características que mejoren la experiencia del usuario, pero este ejemplo trata de ser sencillo para servir como un ejemplo base.

Los campos de edición reconocen solo las operaciones básicas, cuando los datos numéricos van a imprimirse suele usarse STRING para dar el formato deseado puesto que cada usuario puede tener una configuración distinta. Los valores ingresados estan controlados por un intervalo de valores y siempre serán enteros por referirse a coordenadas de píxel. No tiene implementado una característica que limite la escritura de datos dentro de su campo, por lo tanto cuando se salga del campo habrá dibujos residuales.



Descarga el código: PPL-EntradaDatos

Capacidad de una Interfaz gráfica con WAIT(-1)

Entendiendo cada comportamiento, es posible definir cualquier tipo de acción ante los diversos tipos que reconoce la calculadora, ya sea crear una interfaz similar a un INPUT, una tabla de datos y demás interfaces que se nos puedan ocurrir.

En esta pequeña introducción solo se muestra una de las 2 formas que existen para crear una interfaz gráfica, la cual es la más útil, simple y recomendada. Si deseas ver un artículo más completo puedes ingresar a Creando una Interfaz Gráfica.



En resumen y datos finales

Las variables gráficas no se pueden asignar con := o , ya que su sistema de edición o entrada de datos es diferente que los tipos numéricos o cadenas; para manipular estos objetos debemos usar comandos especiales de dibujo como BLIT, DIMGROB, LINE, etc.

Las variables gráficas deben usarse si se necesita almacenar gráficos, y su visualización se logra con BLIT (refiriéndose a su inserción en G0). Es posible usar parte de ellas y tener varios gráficos organizados en una sola variable.

Para visualizar los gráficos en pantalla, luego de usar un BLIT, necesitamos pausar o retener el refrescado en pantalla, eso se logra con FREEZE, WAIT o WAIT(-1). También es posible mediante un bucle debidamente controlado con un WAIT(0.01), el valor de 0.01 se recomienda para estos casos.

La instrucción WAIT(-1) es la mejor opción para la creación de Intefaces Gráficas Propias, debido a que brinda características de pausa y reconocimiento de teclado/pantalla. Recuerda no combinarlos con ISKEYDOWN y GETKEY.


Retorna a Consideraciones en Programas II

Comentarios

  1. tengo un problema con un programa el cual tras la actualización dejo de funcionar por un comando de colores "RECT_P(0,0,320,240,# FF0000,#FFFFE0);" el error es en este color #FF0000 y no consigo arreglarlo, de antemano se agradece la ayuda

    ResponderBorrar
    Respuestas
    1. Al final resultó ser un tema no cerrado, lo usual sería que al no estar definiendo la base del entero, no se compile bien debido a la diferencia con la base predeterminada, para ello es que se recomienda usar la directiva #pragma visto en el artículo anterior.

      Borrar
  2. Ayuda .. como desarrollar la siguiente sintaxis ..
    Local Fy, Y;
    Fy= 5Y+Y-e^Y;
    Condicion Fy=0,0001
    Mostrar Y

    Cual seria la sintaxis correcta .. para este problema iterativo por favor.

    ResponderBorrar
    Respuestas
    1. Lo que deseas es encontrar las raíces de Y para ese punto? a qué te referieres a que es un problema iterativo, lo debes hacer con un método analítico?

      Borrar
  3. Muy útil todo el contenido que bridas, ayudan a gente de desea mucho aprender a programar la calculadora. Muchas gracias!!!

    ResponderBorrar
  4. Muy útil la información que brindas, podrías por favor como convertir PPL-EntradaDatos en una aplicacion, gracias de antemano

    ResponderBorrar
  5. Amigo y para gráficos tipo funciones algebraicas cuadraticas o cubicas...como hago para que se muestren en las variable grafica G0?

    ResponderBorrar
    Respuestas
    1. No entiendo la pregunta, si ya has logrado graficar la función en otra variable gráfica como G1-9, se usa BLIT para enviarlo a G0, es el único comando que hace eso. Por cierto te recomiendo ver un tutorial sobre los fundamentos de gráficos que hice, actualmente lo he recompartido en la página de FB.

      Borrar
  6. como logro que al ejecutar una aplicacion, modifique la unidad de medicion angular, de radianes a sexagesimales o viceversa.

    ResponderBorrar
    Respuestas
    1. Hola, puedo entender 2 cosas, así que explicaré ambas de una vez.

      1. Al iniciar una aplicación que se trabaje solo en una medida angular independiente (ángulo de aplicación).
      Basta que configures la app desde Shift+Symb en el ángulo que deseas, y la app cambiará a esa medida cada vez que actives la app, si vas a otra app el ángulo se restablece. Si aún quisieras que al entrar nuevamente a la app se vuelva a asignar la medida, usarás AAngle:=2 (gradianes) o el que necesites.

      2. Una aplicación que cambia la medida angular de toda la calculadora (ángulo de sistema).
      Solo se requerirá conmutar HAngle con los valores 0,1,2, ejercicio típico de MOD: HAngle:=(HAngle+1) MOD 3. Puedes hacer que la app regrese a la biblioteca ya que no tiene más utilidad como para quedarse sobre ella, usando STARTVIEW(-4)

      En el primero basta con configurar la app una sola vez, mientras que en el segundo es un típico ejercicio con MOD. Espero que ambos ejemplos te sirvan de algo, era eso lo que buscabas?

      Borrar
    2. Me refería a la primera, desarrolle un pequeño programa en el cual los algoritmos que uso consideran los angulos en radianes. Gracias!

      Borrar
  7. hola. primero felicitarte por tu blog, me parece genial la informacion que compartes. Espero me ayudes con la siguiente duda. Wait(-1) como indicas ayuda a visualizar los graficos en la pantalla y espera alguna accion del usuario. En mi opinión ocasiona un efecto de parpadeo y esto se visualiza en las calculadoras físicas mas no en el emulador y es mas evidente cuando realizamos la accion de arrastre, similar es usar wait(0.01) Habra algun otro comando o alguna manera de evitar ese efecto?

    ResponderBorrar
    Respuestas
    1. Intenta comprender porqué pasa lo del parapadeo, no tiene que ver con el comando, como mencionas solo es un comando de pausa de ejecución y que puede devolver el gesto que hiciste al interrumpir la pausa, no tiene que ver con gráficos (sería tener mal el enfoque del comando).
      Todo es cuestión de cómo gestiones los cambios de pantalla, si los haces adecuadamente no generarás el parapadeo, el parpadeo lo genera uno mismo, no es un problema ya que HP PPL tiene la suficiente velocidad de refrescamiento de pantalla para no notarlo.

      Borrar
    2. Olvidé mencionar que uno mismo puede hacer interfaces sin parpadeo, yo los hago así, no creo que deba explicarlo, espero que entiendas el origen y encontrar tu manera de evitarlo.

      Borrar

Publicar un comentario

Lo más visto

Matriz (Tipo) - HP Prime