Novedad Velneo 23: Gráficos interactivos

Con la salida de la nueva versión Velneo 23 podremos disfrutar de novedades importantes en el objeto Gráfico que mejorarán las posibilidades, la interacción y la experiencia del usuario. Gráficos interactivos.

Ventajas principales:

  • Leyendas interactivas
  • Variables para porcentajes
  • Señales para navegación

En el seminario online de presentación de las novedades de la versión, Mario Conde nos contó las nuevas posibilidades de los gráficos:

Podéis conocer la lista de novedades más destacadas de esta versión en la página de novedades.

Este artículo Novedad Velneo 23: Gráficos interactivos es original de Velneo.

Postal Navideña 2017. El “making of”

Por tercer año he publicado en el foro una postal navideña realizada con Velneo en la que, además de felicitar las fiestas, pongo en práctica mis conocimientos en aquellos aspectos que pueden dar cierto dinamismo gráfico a nuestras aplicaciones.

Aprovecho para hacer un llamamiento a todos los que lean este artículo para que no dejen morir el foro de Velneo. Creo que es la mejor herramienta que tenemos para dejar nuestro conocimiento grabado y con acceso inmediato para los nuevos discípulos de Velneo.

Este año tocaba desear felices fiestas empleando uno de los objetos largamente esperado por los programadores, el objeto nativo Gráfico.

Los señores de Velneo nos vienen negando desde hace años la disponibilidad de un objeto nativo Canvas que permita aportar a nuestras aplicaciones un punto de libertad a la hora de mostrar elementos gráficos. En su defecto debemos usar el Visor html o QML tal como vimos en la primera y segunda postal navideña respectivamente. Estas soluciones alternativas están muy bien, pero nos encontramos con problemas de rendimiento y multiplataforma, sin contar que debemos aprender lenguajes y técnicas de programación muy poco LifeIsSoft.

Como ya he dicho, este año había que hacer algo con el objeto Gráfico. Este nuevo objeto es el resultado de incorporar a Velneo el módulo QtCharts de Qt, lo que nos permite crear gráficos empresariales tanto en su modalidad de Widget nativo o en su modalidad de Tipo QML (con import QtCharts 2.x).

Teniendo en cuenta que el objeto Gráfico es en realidad un objeto Canvas al que se ha dado una funcionalidad específica, veamos como podemos usarlo para otros menesteres que espero te sorprendan.

Objetivo de este año: Postal navideña con gráficos nativos.

Este año había que mostrar algo dinámico usando el Gráfico en su forma nativa.

El elemento dinámico debía ser una frase de felicitación que contuviera el nombre del Usuario conectado y además hubiera un desplazamiento horizontal a través de la pantalla. Lo más parecido es un Rótulo de leds que vemos habitualmente en las estaciones de autobús o tren para informar de diferentes eventos.

El Rótulo led debe tener los siguientes elementos dinámicos:

  • La frase o mensaje de felicitación es configurable y debe contener el nombre del usuario conectado
  • El Rótulo desplaza horizontalmente la frase de derecha a izquierda y de forma cíclica
  • Los leds luminosos podrán variar de color o de brillo

Diseño del Rótulo LED para mostrar Mensajes

¿Qué tiene que ver un Rótulo led con un objeto Gráfico de Velneo? En realidad mucho.

Un Rótulo led no es más que una matrix de puntos de luz que podemos programar accediendo a ellos mediante sus coordenadas x,y. Por lo tanto, si disponemos de la posibilidad de crear gráficos XY, ya lo tenemos casi hecho. Nos faltará añadir el desplazamiento horizontal y el cambio de color.

Un Gráfico nativo de Velneo al igual que una Rejilla necesita una Lista como entrada para poder representar los datos correspondientes. El Gráfico, por lo tanto, se insertará siempre en los formularios como un control Vista de datos con el correspondiente Proceso que alimenta la Lista de datos a mostrar en el gráfico. Esta característica de Velneo es muy potente y lifeifsoft, ahorrándonos muchas líneas de código, pero también es su talón de aquiles en entornos de redes lentas como Internet.

En Velneo el tipo de gráfico se determina con la propiedad Tipo serie del subobjeto Serie. Para representar un gráfico XY debemos insertar en el Gráfico un subobjeto Serie de tipo Puntos. Los puntos del gráfico se muestran mediante las 2 coordenadas X,Y de los ejes horizontal y vertical respectivamente.

En realidad lo que vamos a hacer es usar el Gráfico nativo como un control Canvas mediante la gestión de coordenadas XY.

Diseño de los caracteres del MENSAJE PARA EL RÓTULO

Para poder construir una frase de forma dinámica debemos disponer del diseño de todos los caracteres posibles en formato de matrix X,Y.

Establecemos una matrix de 5×7 puntos para representar los caracteres.

Construimos una tabla LETRAS_LED con 2 campos, uno con el carácter a representar y otro con una cadena de longitud 7 caracteres con valores posibles de espacio y 1. El espacio indica led apagado y 1 indica led encendido.

En la imagen siguiente se muestran las matrices de las letras A, B, C y D. Cada línea es un registro de la tabla y hay 6 registros por letra, 5 registros de la matrix más 1 de separación entre letras.

La carga de la tabla LETRAS_LED se realiza una sola vez en el ON_INIT_SERVER.

Lista de puntos X,Y de la Vista de datos del Gráfico

Para alimentar la Vista de datos del Gráfico XY necesitamos una tabla con las coordenadas XY ordenadas por el eje X. Estas coordenadas XY serán las que se corresponden con los leds iluminados del Rótulo.

Cada registro de la tabla LETRAS_LED nos proporciona un valor X y hasta 7 valores Y correspondientes a las posiciones de la cadena cuyos valores sean 1.

Por ejemplo la letra C tiene los siguientes valores X,Y que forman la matriz de leds en el rótulo:

eje Y
|               2,7  3,7  4,7
|        1,6                       5,6
|        1,5
|        1,4
|        1,3
|        1,2                       5,2
|               2,1  3,1  4,1
|_____________________ eje X

Necesitamos un proceso PRO_ROT_FRASE_3P que nos devuelva desde la tabla LETRAS_LED las coordenadas de cada una de las letras de la frase que vamos a mostrar en el Rótulo.

Al principio se pensó en rellenar con las coordenadas una tabla en memoria en primer plano, pero lamentablemente en cloud eso es inviable.

Velneo debería revisar el funcionamiento de las tablas en memoria para que en local fuera optativo el crear transacción en el servidor. Esto abriría una multitud de posibilidades en aplicaciones en cloud que necesitan generar listas personalizadas de datos temporales para alimentar objetos de Interfaz como por ejemplo Informes o Gráficos.

De momento se ha optado por una solución más óptima. Ejecutamos en tercer plano el proceso PRO_ROT_FRASE_3P para rellenar la tabla en disco  GRAFICO_XY_3P con las coordenadas de la frase que se mostrará en el Rótulo. Rellenar la tabla en tercer plano y cargar la Lista es mucho más rápido que hacerlo con una tabla local en memoria.

Rem PROCESO PRO_ROT_FRASE_3P
Rem ( Recibe la Frase y el Nombre para identificar las coordenadas en la Tabla )
Rem ( Insertamos NOMBRE, convertimos a mayúsculas, quitamos acentos y sustituimos los espacios por _ )
Set ( _CFRASE, replaceString(removeAccents(toUpper(replaceString(_CFRASE, "[[NOMBRE]]", _CNOMBRE))), " ", "_") )
Libre
Rem ( Rellenamos la Tabla del Gráfico XY desde la tabla LETRAS_LEDS )
For ( NLETRA, 0, NLETRA < len(_CFRASE), 1 )
        Set ( CLETRA, mid(_CFRASE, NLETRA, 1) )
        Rem ( Carga los registros que componen la Letra )
        Cargar lista ( LETRAS_LED@0PS_Navidad_dat, NAME, CLETRA, , , )
                Recorrer lista solo lectura
                        Set ( CLINEA, #LEDS )
                        Set ( NCON_X, NCON_X + 1 )
                        If ( ! isEmpty(CLINEA) )
                                For ( N, 0, N < len(CLINEA), 1 )
                                        If ( mid(CLINEA, N, 1) = "1" )
                                                Crear nueva ficha en memoria ( hGraf, GRAFICO_XY_3P@0PS_Navidad_dat )
                                                        Modificar campo ( NAME, _CNOMBRE )
                                                        Modificar campo ( VALOR_X, NCON_X )
                                                        Modificar campo ( VALOR_Y, N+1 )
                                                        Modificar campo ( TIPO, "M" )
                                                Alta de ficha ( hGraf )
                                                        Libre

Configuración del objeto Gráfico XY (tipo Puntos)

Una vez que ya tenemos diseñado el procedimiento de obtención de las coordenadas de la frase a mostrar en el Rótulo, ya podemos configurar el objeto Gráfico nativo de Velneo GRF_ROTULO_LEDS.

No seleccionamos ningún Tema. Desactivamos la Animación y Menú de contexto y ajustamos los márgenes para aprovechar el espacio de los ejes ya que no los vamos a mostrar. Se puede ocultar el Título si ponemos a cero el canal Alfa del Color.

Añadimos la Serie ROTULO de Tipo Puntos. El Eje X (Categoría) se rellena con el campo #VALOR_X y el Eje Y (Valor) con el campo #VALOR_Y. El Tipo de categoría (coordenadas X) es Numérico y ocultamos las Etiquetas de los valores X,Y. El color de la serie lo determina la variable local CCOLOR que usaremos para atenuar y aumentar el brillo de los leds.

Añadimos un subobjeto Eje de Tipo Eje categorías EJE_X. El Tipo de categoría Numérico, sin Título, sin Etiquetas y sin Línea. Los valores Mínimo y Máximo se determinan mediante las variables locales NEJEX_INI y NEJEX_FIN que como veremos luego son las que desencadenan el refresco del gráfico.

Añadimos un subobjeto Eje de Tipo Eje valores EJE_Y. Sin Título, sin Etiquetas y sin Línea. Los valores Mínimo y Máximo se ajustan manualmente para conseguir una altura correcta del Rótulo.

El diseñador nos muestra el siguiente gráfico. Los círculos representan las coordenadas X,Y de los leds.

Echamos en falta poder cambiar el tamaño de los Puntos para ajustar el tamaño del Rótulo (un despiste tonto del equipo de desarrollo). En la versión QML existe la propiedad markerSize y también se puede cambiar la forma con la propiedad markerShape.

Formulario con el Rótulo del mensaje

Ahora diseñemos un Formulario en el que podamos integrar toda la funcionalidad de este ejercicio.



    • En la parte superior introducimos la frase que deseamos mostrar en el Rótulo.
      El botón <Actualizar> ejecuta el proceso PRO_ROT_FRASE_3P y Recalcula la Vista de datos para obtener las coordenadas XY de la nueva Frase.
    • La Vista de datos GRF_ROTULO con el Gráfico XY (o de Puntos) debe tener una altura fija de 210px.
      Esto es debido a que los caracteres tienen una altura mínima limitada a 7 leds de tamaño fijo (ya que como hemos visto no disponemos de la propiedad markerSize).
      La anchura de los caracteres también debe ser fija y se ha establecido en 110px.
    • El control RUEDA desplaza en los dos sentidos la Frase dentro del Rótulo.
      En ambos lados de la Rueda se muestran las coordenadas del Eje X de la parte de la Frase NEJEX_VENTANA que aparece en el Rótulo .
      Estos valores son NEJEX_INI y NEJEX_INI + NEJEX_VENTANA que se corresponden con las propiedades NEJEX_INI y NEJEX_FIN del Gráfico.
      La Rueda cambia el valor de NEJEX_INI y mediante el manejador RESIZE_JS se calcula NEJEX_VENTANA.
      El siguiente proceso javascript es el más importante desde el punto de vista gráfico, porque permite mantener el ancho fijo de los caracteres cuando redimensionamos la Ventana en los equipos de escritorio o giramos la pantalla en los dispositivos móviles.
// Manejador RESIZE_JS 
// Tenemos que mantener la anchura de los caracteres fija aunque cambie el ancho del Rótulo
// Ancho del Rótulo
var nAnchoRotulo = theRoot.dataView().control("GRF_ROTULO").width
// Los caracteres deben tener un ancho en pixeles determinado para que sean legibles (110px)
// Calculamos entonces el Nº de caracteres que entran en el Rótulo
var nNumLetras = nAnchoRotulo / 110
// Calculamos también el valor X máximo a mostrar (el que está a la derecha de la rueda)
var nEjeX_Fin = nNumLetras * 6  // Cada letra son 6 puntos X
theRoot.setVar("NEJEX_VENTANA", Math.round(nEjeX_Fin))
theMainWindow.showMessageStatusBar("Ancho: " + theMainWindow.width() + "px", 0)
    • El manejador del evento RESIZE de la Vista de datos también recalcula los valores NEJEX_INI y NEJEX_VENTANA para reposicionar la Frase en el Rótulo.
    • El botón CMD_PLAY  pone en marcha el Timer del Formulario (otra ausencia imperdonable es el evento Timer en el objeto Gráfico nativo).
      El manejador TIMER incrementa el valor de NEJEX_INI para desplazar la Frase de derecha a izquierda dentro del Rótulo.
      También cambia el color de los Leds actualizando la propiedad CCOLOR del Gráfico.
    • Tanto manualmente, como a través del Timer o redimensionando el formulario, tan solo necesitamos actualizar las propiedades NEJEX_INI y NEJEX_FIN del Gráfico XY para desplazar la Frase.

Hagamos un Reloj con nuestro Gráfico XY

En un Reloj Digital tenemos siempre un texto de ancho fijo (8 caracteres) y solo necesitamos un Timer de 1 segundo para refrescar la Hora actual.



    • Para el reloj digital lo que hacemos es crear en la tabla de disco GRAFICO_XY_3P las coordenadas de todas las posibles horas del día, desde las 00:00:00 hasta las 23:59:59. Este proceso PRO_RELOJ_CARGAR solo debe ejecutarse una vez y por eso lo ejecutamos en el ON_INIT_SERVER.
    • En el arranque de la aplicación rellenamos mediante un Tubo de Lista la tabla en memoria RELOJ_XY_MEM con todas las coordenadas posibles del Reloj digital.
    • En cada evento TIMER la Frase del Rótulo será currentTime() y esta vez construimos hh:mm:ss de forma dinámica desde la tabla en memoria. La variable global APP_RELOJ_SEGUNDOS determina si se muestran los segundos o no. En el caso de que no se muestren los segundos hacemos que el separador de minutos se encienda y apague cada segundo.
Rem ( Proceso PRO_RELOJ para la Vista de datos del Reloj digital)

Rem ( Obtenemos las coordenadas XY de la Hora actual desde la Tabla en memoria )
Set ( CHORA, timeToString(currentTime(), "hhmmss") )

Rem ( DECENAS DE HORA )
Cargar lista ( RELOJ_XY_MEM@0PS_Rotulo_LED_dat, NAME, "DECENA_HORA_" + mid(CHORA, 0, 1), , ,)
        Añadir lista a la salida
Rem ( UNIDADES DE HORA )
Cargar lista ( RELOJ_XY_MEM@0PS_Rotulo_LED_dat, NAME, "UNIDAD_HORA_" + mid(CHORA, 1, 1), , ,)
        Añadir lista a la salida
If ( $APP_RELOJ_SEGUNDOS@0PS_Rotulo_LED_dat.dat | ((second(currentTime()) % 2) = 0) )
        Rem ( SEPARADOR DE MINUTOS )
        Cargar lista ( RELOJ_XY_MEM@0PS_Rotulo_LED_dat, NAME, "SEP_MINUTOS", , , )
                Añadir lista a la salida
Rem ( DECENAS DE MINUTOS )
Cargar lista ( RELOJ_XY_MEM@0PS_Rotulo_LED_dat, NAME, "DECENA_MINUTO_" + mid(CHORA, 2, 1), , , )
        Añadir lista a la salida
Rem ( UNIDADES DE MINUTOS )
Cargar lista ( RELOJ_XY_MEM@0PS_Rotulo_LED_dat, NAME, "UNIDAD_MINUTO_" + mid(CHORA, 3, 1), , , )
        Añadir lista a la salida
If ( $APP_RELOJ_SEGUNDOS@0PS_Rotulo_LED_dat.dat )
        Rem ( SEPARADOR DE SEGUNDOS )
        Cargar lista ( RELOJ_XY_MEM@0PS_Rotulo_LED_dat, NAME, "SEP_SEGUNDOS", , , )
                Añadir lista a la salida
        Rem ( DECENAS DE SEGUNDOS )
        Cargar lista ( RELOJ_XY_MEM@0PS_Rotulo_LED_dat, NAME, "DECENA_SEGUNDO_" + mid(CHORA, 4, 1), , , )
                Añadir lista a la salida
        Rem ( UNIDADES DE SEGUNDOS )
        Cargar lista ( RELOJ_XY_MEM@0PS_Rotulo_LED_dat, NAME, "UNIDAD_SEGUNDO_" + mid(CHORA, 5, 1), , , )
                Añadir lista a la salida
        Libre
    • El manejador del evento RESIZE ocultará los segundos cuando el ancho del Rótulo muestre menos de 28 coordenadas (4 caracteres x 6 + 4 del separador).

Conclusiones

Con estos 2 ejercicios hemos comprobado que, sin quererlo, Velneo nos ha proporcionado un objeto Canvas nativo para realizar pequeños retos que necesitan algo de dinamismo y potencia gráfica.

Que el objeto Gráfico nativo sea una Vista de datos es por un lado una gran ventaja LifeIsSoft, pero por otro es un handicap en redes lentas debido a la dependencia de Velneo con el hilo de conexión con del vServer, sobre todo en tablas temporales o en memoria.

El uso de Timers en el hilo principal es otro problema con Velneo debido a su bajo rendimiento gráfico. Estos ejercicios en equipos de escritorio se ejecutan con cierta agilidad pero en dispositivos móviles la experiencia gráfica deja mucho que desear. En el evento ON_HIDE de los formularios de ambos ejercicios ejecuto un stopTimer para que no perjudique un formulario en el otro.

En cualquier caso que estos ejercicios os sirvan para aprender sobre las posibilidades de los objetos de Velneo y al mismo tiempo divertirnos con ello. También una forma de hacer ver a los desarrolladores de Velneo las posibilidades que daría un objeto nativo Canvas, junto con tablas en memoria locales y no dependientes del gestor de transacciones.

Podéis probar el ejercicio en el Cloud en la url habitual vatp://pruebas:pruebas@c6.velneo.com:16400/0PS_Rotulo_LED_iapp. En principio es funcional en todas las plataformas.

Finalmente he preparado la versión QML para comparar. En principio podemos pensar que va a ser mucho más ágil el funcionamiento porque tendremos los Timers aislados y los modelos de datos pueden estar totalmente desconectados del servidor. He usado un modelo de datos procedente de un string json.

En los ejercicios del Cloud vatp://pruebas:pruebas@c6.velneo.com:16400/0PS_Varios_iapp tenéis también el código QML (rama Tipos QML de QtChart/Rótulo LEDS – TabView).
Es un buen ejercicio para probar algunos objetos QML interesantes para construir interfaces en dispositivos móviles.

 

Si te ha gustado el post, no dudes en compartirlo por tus redes sociales.

Además podéis dejar un comentario contándome vuestra experiencia usando los Gráficos nativos y vuestros avances con QML.

 

La entrada Postal Navideña 2017. El “making of” aparece primero en AyudaVelneo.

Sabías que … (5)

Nuestro “Papá Noel” Particular nos ha regalado una nueva entrega  “Sabías que“. En este post veremos curiosidades sobre funciones, contenidos iniciales, rejillas y personalización de gráficos nativos mediante CSS.

Por si te perdiste las cuatro primeras entregas del “juego”, no está de mas recordarlas:

¿Sabías que..?

Si no te fijas bien, la función getStringRegExp() te puede jugar una mala pasada

    • La función de cadena getStringRegExp(cadena, expresionRegular, posIni, caseSensitive, numSubExpresion) recibe como expresión regular una cadena.
    • En las expresiones regulares se usan metacaracteres, algunos de los cuales deben escaparse y ¿cuál es el caracter de escape?, pues precisamente el caracter \.
    • Por lo tanto, en Velneo debemos escapar los metacaracteres con doble caracter \ para que funcione correctamente la función getStringRegExp

Un ejemplo para obtener el código a la derecha de la palabra Informe:
 getStringRegExp(“Informe  233421122”, “Informe (\\d{1,}$)”, 0, 0, 1)  = 233421122

    • Cuando copiéis expresiones regulares de Internet, prestad atención a estos detalles, o padeceréis horas de desconcierto

Podemos recorrer los controles de una Caja de grupo (QGroupBox) para aplicar una determinada acción

    • El API de Velneo es una herramienta muy útil y este es un ejemplo de ello.
    • La clase QGroupBox (control caja de Grupo) dispone de 2 funciones, childWidgetAt(nIndex) y childWidget(nCount), que nos permiten recorrer todos los controles contenidos en la caja de grupo.
    • La función childWidgetAt devuelve un objeto Widget al que podremos aplicar una determinada acción.

Se puede establecer el tiempo que se muestran los caracteres en las cajas de edición que tienen la propiedad Contraseña activada

    • Debemos usar un par de propiedades CSS, lineedit-password-character y lineedit-password-mask-delay
    • La propiedad CSS lineedit-password-character determina código UNICODE elegido para ocultar la contraseña.
      Ejem:  ● 9679  * 42
    • La propiedad CSS lineedit-password-mask-delay establece el retardo en milisegundos
QLineEdit { 
   lineedit-password-character: 9679;
   lineedit-password-mask-delay: 300;
}

La columna de la rejilla puede mostrar directamente un icono

    • Si solo deseamos mostrar un icono en la columna de un rejilla no es necesario hacerlo en la parte reservada para ello.
    • Podemos hacerlo en la zona reservada para el contenido de la columna.
    • El editor de fórmulas no nos dejará insertar, por ejemplo, el campo ICONO de la tabla estática. Lo tecleamos manualmente.
      #BLOQUEADO_ICO.ICON

¿En qué plano se determinan los valores iniciales de los campos de la tabla?

    • El plano donde se ejecuta el comando Crear nueva ficha en memoria es en el que se determinan los valores iniciales.
    • Si queremos que siempre se determinen en 3P tendremos que calcular el valor inicial en el Trigger anterior al Alta.
    • En el API con el VRegister, los valores iniciales se calculan en la función setTable(), ¡¡ cuidado con esto !!.

¿Para qué sirve la función de campo getID()?

    • ¿Qué sentido tiene la función GetID() si devuelve el mismo ID que uso para llamarla?
    • Pues nos permite refactorizar los nombres de campo de una tabla cuando éstos son renombrados
      Por ejemplo:
      Hay que pasar a una función un string con los nombres de campo separados por comas.
      Si renombramos los campos AUTOR, CSS_FINAL o NOTAS, la aplicación seguirá funcionando correctamente.
Set (CLISTA_CAMPOS, "ID,NAME," + #AUTOR:getID() + "," + #CSS_FINAL:getID()  + "," + #NOTAS:getID()

Podemos usar el control Combobox en formularios sin Origen

    • El control combobox del formulario es muy cómodo de usar para el usuario si va a seleccionar de una lista corta, entre otras cosas, porque la lista se despliega directamente al pulsar con el ratón.
    • A veces tenemos un formulario sin origen y necesitamos desplegar los valores de una tabla maestra.
    • Una técnica habitual es crear una tabla temporal con todos los punteros a maestro que vayamos a usar en controles de selección
    • En el formulario sin origen añadimos una Ficha de Extensión FIEXT que se alimente por proceso, Alta, Baja y Modificación estarán a cero.
Crear nueva ficha en memoria (hMaestros, MAESTROS_TEMP@MiApp_app)
        Añadir ficha a la salida
    • Añadimos el control combobox y lo asociamos al campo puntero a maestro de la Ficha de Extensión FIEXT.
    • Ya sabemos que el valor seleccionado en el Combobox se lee mediante ##FIEXT.EMPRESA.ID.

Podemos personalizar los Gráficos nativos con CSS

    • El nuevo gráfico de Velneo es un control nativo LifeIsSoft y por lo tanto desesperadamente poco personalizable.
    • Sin embargo, teniendo en cuenta que los Títulos y Etiquetas son objetos Label de Qt, podemos usar tags HTML y código CSS.

Veamos 2 ejemplos que puedes probar en la aplicación del cloud vatp://pruebas:pruebas@c6.velneo.com:16400/0PS__MisCSS_iapp:

/* Gráfico nativo */
/* No existe selector de clase
Aunque podemos usar los tags HTML y código CSS en los Títulos y etiquetas del Gráfico
Pero limitados al subconjunto de HTML4 - http://doc.qt.io/qt-5/richtext-html-subset.html */

/* Cada etiqueta QLbel personaliza un elemento del gráfico.
Lo que hacemos es parsear el código CSS y aplicar un Atributo HTML style="font: bold 18px verdana; ...." mediante variables locales 
definidas en el Gráfico nativo que se añaden como prefijo a los Títulos y Etiquetas
*/

QLabel#GRAF_TITULO {
   font: bold 18px verdana;
   text-align: center;
   background-color: WhiteSmoke;
   color: DarkSlateGray;
   padding: 0 80 0 80;
}
QLabel#GRAF_TITULO_EJEY {
   font: bold 12px arial;
   text-align: center;
   color: DarkSlateGray;
}
QLabel#GRAF_TITULO_EJEX {
   font: bold 11px arial;
   text-align: center;
   color: DarkSlateGray;
}
QLabel#GRAF_ETIQUETAS_EJEY {
   font: bold 12px arial;
   text-align: center;
   color: DimGray;
}
QLabel#GRAF_ETIQUETAS_EJEX {
   font: bold 10px arial;
   background-color: IndianRed;
   color: Cornsilk;
   padding: 3 10 3 10;
}
QLabel#GRAF_ETIQUETAS_SERIE1 {
   font: bold 12px verdana;
   background-color: DarkGreen;
   color: HoneyDew;
   padding: 3 10 3 10;
}
QLabel#GRAF_ETIQUETAS_SERIE2 {
   font: bold 12px arial;
   color: white;
}

Las funciones CurrentDateTime() y CurrentUTCDateTime() tienen truco

    • Un día quise obtener el huso horario de vClient y como no me acordaba de la función getSysTimeZone(), hice lo siguiente:
set ( NDIF_HOR, secondsTo(currentDateTime(), currentUTCDateTime()))

Sin embargo, el resultado es cero.

    • Consultado a soporte, resulta que currentDateTime() y currentUTCDateTime() representan internamente la misma fecha, lo que ocurre es que las variables de tipo Tiempo guardan el huso horario en el que han sido creadas y Velneo no nos proporciona ese dato. Estas funciones devuelven fechas distintas en algunas operaciones y en otras actúan como valores iguales, lo que crea una gran confusión. Como muchas veces ocurre, no está documentado.
    • Así que tenerlo en cuenta:
dateTimeToTime(currentUTCDateTime()) es distinto de dateTimeToTime(currentDateTime())
dateTimeToString(currentUTCDateTime(), "hh:mm") es distinto de dateTimeToTime(currentDateTime(), "hh:mm")
pero
currentUTCDateTime() es igual a currentDateTime()
    • Si no existiera getSysTimeZone() la forma correcta de obtener el huso horario sería:
// Creamos 2 variables Tiempo nuevas a partir de información devuelta por las funciones currentDateTime y currentUTCDateTime
Set (THORA_LOCAL, setDateTime(dateTimeToDate(currentDateTime()), dateTimeToTime(currentDateTime()))
Set (THORA_UTC, setDateTime(dateTimeToDate(currentUTCDateTime()), dateTimeToTime(currentUTCDateTime()))
// Ya podemos operar con las variables Tiempo
Set (NDIF_HORARIA , secondsTo(THORA_UTC, THORA_LOCAL) / 3600)
Mensaje ("Hora local: " + dateTimeToString(currentDateTime(), "dd/MM/yyyy hh:mm") + "<br>" + 
"Hora UTC: " + dateTimeToString(currentUTCDateTime(), "dd/MM/yyyy hh:mm") + "<br>" +
"Diferencia horaria: " + numberToString(NDIF_HORARIA, "L", 0)

Los formularios del Bloc de Formularios no ejecutan el evento PRE_INI

    • Los formularios pueden ser los contenedores de los registros en algunos controles de Vista de datos de tipo Lista como el Bloc de formularios.
    • En este caso hay que tener en cuenta lo siguiente:
      • El evento Pre-inizialización del formulario solo se ejecutará cuando se muestra el Bloc de formularios, es decir, solo se ejecuta para el formulario mostrado en ese momento. Cuando navegamos por la Lista de registros no se dispara este evento.
      • Aparece un evento Item: pre cambio de seleccionado que funciona al contrario que el evento Pre-inizialización. En este caso se ejecuta solo cuando navegamos por la LIsta de registros.
      • En el Bloc de formularios no podemos usar en el manejador PRE_INI código que dependa del registro de la tabla, sobre todo el código que determina el nivel de acceso del Usuario a los registros. 

Y ahora confiesa… ¿cuántas de estas curiosidades sobre gráficos, rejillas y funciones sabías? 

Déjame un comentario mas abajo y comenzamos el debate.

La entrada Sabías que … (5) aparece primero en AyudaVelneo.

Sabías que … (5)

Nuestro “Papá Noel” Particular nos ha regalado una nueva entrega  “Sabías que“. En este post veremos curiosidades sobre funciones, contenidos iniciales, rejillas y personalización de gráficos nativos mediante CSS.

Por si te perdiste las cuatro primeras entregas del “juego”, no está de mas recordarlas:

¿Sabías que..?

Si no te fijas bien, la función getStringRegExp() te puede jugar una mala pasada

    • La función de cadena getStringRegExp(cadena, expresionRegular, posIni, caseSensitive, numSubExpresion) recibe como expresión regular una cadena.
    • En las expresiones regulares se usan metacaracteres, algunos de los cuales deben escaparse y ¿cuál es el caracter de escape?, pues precisamente el caracter \.
    • Por lo tanto, en Velneo debemos escapar los metacaracteres con doble caracter \ para que funcione correctamente la función getStringRegExp

Un ejemplo para obtener el código a la derecha de la palabra Informe:
 getStringRegExp(“Informe  233421122”, “Informe (\\d{1,}$)”, 0, 0, 1)  = 233421122

    • Cuando copiéis expresiones regulares de Internet, prestad atención a estos detalles, o padeceréis horas de desconcierto

Podemos recorrer los controles de una Caja de grupo (QGroupBox) para aplicar una determinada acción

    • El API de Velneo es una herramienta muy útil y este es un ejemplo de ello.
    • La clase QGroupBox (control caja de Grupo) dispone de 2 funciones, childWidgetAt(nIndex) y childWidget(nCount), que nos permiten recorrer todos los controles contenidos en la caja de grupo.
    • La función childWidgetAt devuelve un objeto Widget al que podremos aplicar una determinada acción.

Se puede establecer el tiempo que se muestran los caracteres en las cajas de edición que tienen la propiedad Contraseña activada

    • Debemos usar un par de propiedades CSS, lineedit-password-character y lineedit-password-mask-delay
    • La propiedad CSS lineedit-password-character determina código UNICODE elegido para ocultar la contraseña.
      Ejem:  ● 9679  * 42
    • La propiedad CSS lineedit-password-mask-delay establece el retardo en milisegundos
QLineEdit { 
   lineedit-password-character: 9679;
   lineedit-password-mask-delay: 300;
}

La columna de la rejilla puede mostrar directamente un icono

    • Si solo deseamos mostrar un icono en la columna de un rejilla no es necesario hacerlo en la parte reservada para ello.
    • Podemos hacerlo en la zona reservada para el contenido de la columna.
    • El editor de fórmulas no nos dejará insertar, por ejemplo, el campo ICONO de la tabla estática. Lo tecleamos manualmente.
      #BLOQUEADO_ICO.ICON

¿En qué plano se determinan los valores iniciales de los campos de la tabla?

    • El plano donde se ejecuta el comando Crear nueva ficha en memoria es en el que se determinan los valores iniciales.
    • Si queremos que siempre se determinen en 3P tendremos que calcular el valor inicial en el Trigger anterior al Alta.
    • En el API con el VRegister, los valores iniciales se calculan en la función setTable(), ¡¡ cuidado con esto !!.

¿Para qué sirve la función de campo getID()?

    • ¿Qué sentido tiene la función GetID() si devuelve el mismo ID que uso para llamarla?
    • Pues nos permite refactorizar los nombres de campo de una tabla cuando éstos son renombrados
      Por ejemplo:
      Hay que pasar a una función un string con los nombres de campo separados por comas.
      Si renombramos los campos AUTOR, CSS_FINAL o NOTAS, la aplicación seguirá funcionando correctamente.
Set (CLISTA_CAMPOS, "ID,NAME," + #AUTOR:getID() + "," + #CSS_FINAL:getID()  + "," + #NOTAS:getID()

Podemos usar el control Combobox en formularios sin Origen

    • El control combobox del formulario es muy cómodo de usar para el usuario si va a seleccionar de una lista corta, entre otras cosas, porque la lista se despliega directamente al pulsar con el ratón.
    • A veces tenemos un formulario sin origen y necesitamos desplegar los valores de una tabla maestra.
    • Una técnica habitual es crear una tabla temporal con todos los punteros a maestro que vayamos a usar en controles de selección
    • En el formulario sin origen añadimos una Ficha de Extensión FIEXT que se alimente por proceso, Alta, Baja y Modificación estarán a cero.
Crear nueva ficha en memoria (hMaestros, MAESTROS_TEMP@MiApp_app)
        Añadir ficha a la salida
    • Añadimos el control combobox y lo asociamos al campo puntero a maestro de la Ficha de Extensión FIEXT.
    • Ya sabemos que el valor seleccionado en el Combobox se lee mediante ##FIEXT.EMPRESA.ID.

Podemos personalizar los Gráficos nativos con CSS

    • El nuevo gráfico de Velneo es un control nativo LifeIsSoft y por lo tanto desesperadamente poco personalizable.
    • Sin embargo, teniendo en cuenta que los Títulos y Etiquetas son objetos Label de Qt, podemos usar tags HTML y código CSS.

Veamos 2 ejemplos que puedes probar en la aplicación del cloud vatp://pruebas:pruebas@c6.velneo.com:16400/0PS__MisCSS_iapp:

/* Gráfico nativo */
/* No existe selector de clase
Aunque podemos usar los tags HTML y código CSS en los Títulos y etiquetas del Gráfico
Pero limitados al subconjunto de HTML4 - http://doc.qt.io/qt-5/richtext-html-subset.html */

/* Cada etiqueta QLbel personaliza un elemento del gráfico.
Lo que hacemos es parsear el código CSS y aplicar un Atributo HTML style="font: bold 18px verdana; ...." mediante variables locales 
definidas en el Gráfico nativo que se añaden como prefijo a los Títulos y Etiquetas
*/

QLabel#GRAF_TITULO {
   font: bold 18px verdana;
   text-align: center;
   background-color: WhiteSmoke;
   color: DarkSlateGray;
   padding: 0 80 0 80;
}
QLabel#GRAF_TITULO_EJEY {
   font: bold 12px arial;
   text-align: center;
   color: DarkSlateGray;
}
QLabel#GRAF_TITULO_EJEX {
   font: bold 11px arial;
   text-align: center;
   color: DarkSlateGray;
}
QLabel#GRAF_ETIQUETAS_EJEY {
   font: bold 12px arial;
   text-align: center;
   color: DimGray;
}
QLabel#GRAF_ETIQUETAS_EJEX {
   font: bold 10px arial;
   background-color: IndianRed;
   color: Cornsilk;
   padding: 3 10 3 10;
}
QLabel#GRAF_ETIQUETAS_SERIE1 {
   font: bold 12px verdana;
   background-color: DarkGreen;
   color: HoneyDew;
   padding: 3 10 3 10;
}
QLabel#GRAF_ETIQUETAS_SERIE2 {
   font: bold 12px arial;
   color: white;
}

Las funciones CurrentDateTime() y CurrentUTCDateTime() tienen truco

    • Un día quise obtener el huso horario de vClient y como no me acordaba de la función getSysTimeZone(), hice lo siguiente:
set ( NDIF_HOR, secondsTo(currentDateTime(), currentUTCDateTime()))

Sin embargo, el resultado es cero.

    • Consultado a soporte, resulta que currentDateTime() y currentUTCDateTime() representan internamente la misma fecha, lo que ocurre es que las variables de tipo Tiempo guardan el huso horario en el que han sido creadas y Velneo no nos proporciona ese dato. Estas funciones devuelven fechas distintas en algunas operaciones y en otras actúan como valores iguales, lo que crea una gran confusión. Como muchas veces ocurre, no está documentado.
    • Así que tenerlo en cuenta:
dateTimeToTime(currentUTCDateTime()) es distinto de dateTimeToTime(currentDateTime())
dateTimeToString(currentUTCDateTime(), "hh:mm") es distinto de dateTimeToTime(currentDateTime(), "hh:mm")
pero
currentUTCDateTime() es igual a currentDateTime()
    • Si no existiera getSysTimeZone() la forma correcta de obtener el huso horario sería:
// Creamos 2 variables Tiempo nuevas a partir de información devuelta por las funciones currentDateTime y currentUTCDateTime
Set (THORA_LOCAL, setDateTime(dateTimeToDate(currentDateTime()), dateTimeToTime(currentDateTime()))
Set (THORA_UTC, setDateTime(dateTimeToDate(currentUTCDateTime()), dateTimeToTime(currentUTCDateTime()))
// Ya podemos operar con las variables Tiempo
Set (NDIF_HORARIA , secondsTo(THORA_UTC, THORA_LOCAL) / 3600)
Mensaje ("Hora local: " + dateTimeToString(currentDateTime(), "dd/MM/yyyy hh:mm") + "<br>" + 
"Hora UTC: " + dateTimeToString(currentUTCDateTime(), "dd/MM/yyyy hh:mm") + "<br>" +
"Diferencia horaria: " + numberToString(NDIF_HORARIA, "L", 0)

Los formularios del Bloc de Formularios no ejecutan el evento PRE_INI

    • Los formularios pueden ser los contenedores de los registros en algunos controles de Vista de datos de tipo Lista como el Bloc de formularios.
    • En este caso hay que tener en cuenta lo siguiente:
      • El evento Pre-inizialización del formulario solo se ejecutará cuando se muestra el Bloc de formularios, es decir, solo se ejecuta para el formulario mostrado en ese momento. Cuando navegamos por la Lista de registros no se dispara este evento.
      • Aparece un evento Item: pre cambio de seleccionado que funciona al contrario que el evento Pre-inizialización. En este caso se ejecuta solo cuando navegamos por la LIsta de registros.
      • En el Bloc de formularios no podemos usar en el manejador PRE_INI código que dependa del registro de la tabla, sobre todo el código que determina el nivel de acceso del Usuario a los registros. 

Y ahora confiesa… ¿cuántas de estas curiosidades sobre gráficos, rejillas y funciones sabías? 

Déjame un comentario mas abajo y comenzamos el debate.

La entrada Sabías que … (5) aparece primero en AyudaVelneo.

Novedad Velneo 22: Gráficos nativos

Con la nueva versión Velneo 22 nace el nuevo objeto Gráfico, un objeto de lista que nos permite diseñar gráficos para nuestras aplicaciones en cuestión de segundos, no solo para mostrarlos en formularios, sino que podremos utilizarlos como cualquier otro objeto de lista o incluso imprimirlos. Disfruta de tus datos.

Ventajas principales:

  • Nativo
  • Fácil
  • Optimizado

En el seminario online de presentación de las novedades de la versión, Mario Conde nos mostró la increíble potencia de los gráficos nativos:

Podéis conocer la lista de novedades más destacadas de esta versión en la página de novedades.

 

Si quieres conocer ésta y otras novedades en profundidad y aprender como aprovechar todas la ventajas de las versiones 21 y 22 de Velneo, no puedes perderte el Curso de actualización 2018, un curso online impartido por nuestros expertos. ¡Aprovecha la oportunidad y consigue el título de Desarrollador Certificado Velneo!

Este artículo Novedad Velneo 22: Gráficos nativos es original de Velneo.

Disponible la nueva versión Velneo 22

Como viene siendo habitual en estas fechas, estamos muy contentos ante el lanzamiento de la nueva versión de Velneo. El trabajo de meses, en algún caso años, se ve reflejado en esta nueva versión, en la que podréis observar el esfuerzo realizado para reforzar las ventajas que más valoran nuestros clientes y para mejorar algunos de aquellos aspectos en los que nos pedían mayores avances.

Gráficos, funcionalidades nativas y cloud han sido las principales áreas de trabajo para esta versión.

Tanto si ya eres suscriptor de Velneo como si estás pensando en cambiar de plataforma, en esta nueva versión encontrarás importantes novedades que te harán la vida más fácil como programador de aplicaciones empresariales.

Durante los próximos días se publicarán entradas en este blog sobre cada una de las novedades principales, junto con los vídeos en los que se explica cada una de ellas.

Mientras tanto, visita la página de novedades. No esperes más, ¡hazte con la nueva versión Velneo 22!

Este artículo Disponible la nueva versión Velneo 22 es original de Velneo.