Sabías que … (3)

Vamos con una nueva entrega de nuestro mítico juego… “Sabías que” en este post veremos curiosidades sobre búsquedas, cestas y listas.

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

¿Sabías que..?

En los objetos Búsqueda no sabemos a priori el campo por el que va a estar ordenada la lista de salida

    • Aunque esto ya está documentado en la ayuda, no está de más recordarlo. Para ordenar la salida de un objeto Búsqueda tendremos que usar siempre un comando Ordenar lista.
    • En procesos, funciones o manejadores de evento que disparen búsquedas, usaremos el comando Ordenar lista, en acciones que disparen búsquedas, incluiremos entre la Búsqueda y el objeto de lista de la salida un proceso, con origen y destino lista de la tabla de la búsqueda, que ordene y añada la lista a la salida, .

Las Cestas pierden la ordenación que tuviera establecida la Lista Origen

    • Si ordenamos la Lista de salida de una Búsqueda y la copiamos a una Cesta para poder volcarla a una Rejilla, entonces perderemos el orden establecido.
    • Por lo tanto, el comando Ordenar lista deberá ejecutarse después de copiar la Cesta a la Rejilla. Veamos un ejemplo:
Rem ( Ejecución de una Búsqueda para alimentar la Rejilla del formulario )
Rem ( Utilizamos la Cesta como bufer intermedio para rellenar la Rejilla con el resultado de la Búsqueda )
Cesta: Crear cesta local ( MI_BUSQUEDA@MiApp_app, oCesta )
Crear manejador de objeto ( oBuscar, Proceso PRO_BUSCAR@MiApp_app )
Libre
Set variable local de objeto ( oBuscar, DFECHA_INI, DFECHA_DES_INI )
Set variable local de objeto ( oBuscar, DFECHA_FIN, DFECHA_DES_FIN )
Libre
Disparar objeto ( oBuscar, 3º plano: Servidor (síncrono), )
   Set ( NNUM_REGISTROS, sysListSize )
   Rem ( ¡OJO! La Cesta pierde la ordenación que tuviera establecida la Lista Origen )
   Cesta: Agregar lista a la cesta ( oCesta )
Interfaz: Procesar ( GRD_DOCUMENTOS, Todas )
   Cortar lista ( 0, )
   Cesta: Agregar a la lista en curso ( oCesta )
   Rem ( Tenemos que ordenar la lista obtenida después de pasar por la Cesta porque ésta deshace la ordenación )
   Ordenar lista ( #F_REGISTRO, #ID, , , , )
   Invertir lista
   Seleccionar ficha por posición ( 1 )
Interfaz: Establecer foco ( GRD_DOCUMENTOS )

El comando “Modificar ficha de maestro” FUERZA EL refresco DE los campos DEL MAESTRO

    • En los controles del formulario, después de haber modificado la ficha del Maestro desde un proceso independiente, los campos de dicho maestro no refrescan los cambios porque el puntero a maestro no ha cambiado.
    • Para forzar la lectura de la ficha del maestro desde el servidor y actualizar la caché es suficiente con ejecutar el comando Modificar ficha de maestro desde un manejador de evento.
    • Este comando produce un refresco en el formulario de todos los campos del maestro.

El ancho y alto mínimos de los controles Caja de texto vienen determinados por la clase VMainWindow y las propiedades CSS min-weight y min-height

    • Si has fijado a un valor determinado las propiedades CSS min-weight y min-height de la clase VMainWindow comprobarás que los controles Cajas de texto de los formularios en modo Vista también se verán afectados.

El comando Set dato de retorno no funciona en los procesos, aunque hay una excepción

    • En los procesos que van a ser usados desde la web (con VModApache) la forma de devolver el dato al servicio de Apache es mediante el comando Set dato de retorno.
    • Esto ocurre cuando el Estilo del proceso lo fijamos a “Accesible Web“.

La propiedad Valor del control Botón de radio debemos expresarla como una Constante

    • El control Botón de radio tiene la propiedad Contenido (expresión de fórmula) y la propiedad Valor (valor constante).
    • Por lo tanto, si el contenido es numérico pondremos Valor = 9 y si es de tipo carácter pondremos Valor = A, sin las comillas.

La función HomePath() en Cloud nos permite crear directorios en tercer plano

    • Crear directorio” es un comando de Velneo que actúa a nivel de sistema operativo. Por esa razón en Cloud tenemos que acceder con al ruta completa homePath() + “/Velneo/datos/<<archivo.ext>>” que es la ruta del sistema Linux en el vServer.
    • SDV: Subir fichero al servidor” es un comando de Velneo de tipo Cliente/Servidor, por lo tanto la ruta donde guardamos el fichero es una ruta Virtual. Esa ruta Virtual se define en vAdmin en la opción de menú Datos. Por defecto disponemos de la ruta Virtual “datos/” que equivale a la ruta física homePath() + “/Velneo/datos/ en el vServer.

Si cruzamos 2 listas del mismo tamaño el resultado se ordena igual que la lista cargada en primer lugar

    • Ya sabemos que cuando cruzamos 2 listas, la lista resultante tendrá el mismo orden que la lista de mayor tamaño
    • Cuando cruzamos 2 listas, si éstas tienen el mismo tamaño, el orden de la lista resultante es el mismo que la lista que hayamos cargado primero

Por ejemplo:  Cargo la Lista1 y se añade a la Cesta1. Cargo nuevamente la Lista1 con el mismo número de registros pero en distinto orden y cruzo la Cesta1 con la Lista1.
El orden que prevalece es el de la Cesta1.

Podemos usar el selector CSS CLASE[objectName|=valor] para filtrar los controles de nuestra aplicación

    • Cuando queremos aplicar un determinado CSS a un objeto concreto de nuestra aplicación usamos el selector CLASE#IDENTIFICADOR
    • Sin embargo, podemos usar también un Selector que nos aporta una gran flexibilidad, es el selector CLASE[objectName|=<valor>] que funciona seleccionando los controles cuya propiedad Identificador comienza con <valor>.
      Veamos un ejemplo:

Queremos que el usuario identifique visualmente los campos obligatorios dentro de los formularios. Para ello usaremos un fondo amarillo brillante.
La selección en el CSS será QDoubleSpinBox[objectName|=NUM_O_], QDateEdit[objectName|=DAT_O_], QDateTimeEdit[objectName|=DAT_O_], QTimeEdit[objectName|=TIM_O_], QLineEdit[objectName|=TXT_O_] {background: yellow;}

De esta forma es suficiente con cambiar el Identificador de un control para que tenga fondo amarillo, sin tocar el CSS de la aplicación.

Otros ejemplos:

QPushButton:flat[objectName|=CMD_IMP_] {background-color: orange;}    /* Botones naranjas */
QLabel[objectName|=LBL_AZUL_] {color: SteelBlue;}                     /* Etiquetas azules */

Y ahora confiesa… ¿cuántas sabías? 

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

 

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

Sabías que (2) …

Vamos con una nueva entrega de nuestro mítico juego… “Sabías que“.

Por si te perdiste la primera parte del juego y quieres ganar el primer “quesito” del trivial, aquí te dejo el enlace ¿Sabías que?… Parte 1

¿Sabías que..?

¿Sabías que se puede ocultar la Barra de Estado con un comando nativo de Velneo?

    • Usar el comando Interfaz: Ocultar (STATUS_BAR).
    • Podemos usarlo al inicio de la Aplicación en el evento POS_INI del marco Autoexec.
    • También disponemos de la función del API theMainWindow.hideStatusBar().

¿Sabías que a veces los comandos Else y Else if no funcionan correctamente porque …?

    • Los comandos de instrucción Else y Else if siempre deben estar precedidos de un comando If o Else if. Los comandos If y Else if deben estar situados al mismo nivel y no puede haber ninguna otra línea al mismo nivel entre ambos, ni siquiera un Rem.

¿Sabías que se puede convertir un control de puntero a maestro en un control de edición alfabética?

    • El control de edición del puntero a maestro tiene un comportamiento especial para permitir el Autocompletado o la selección de un registro desde la Vista de datos.
    • Si solo nos interesa mostrar el Nombre del maestro como un control de edición alfabética añadimos el string vacío “” al contenido del campo.
      Por ejemplo el contenido   “” + #PUNTERO_MAESTRO.NAME  hará que las propiedades Autocompletar y Tipo de botones desaparezcan.
      Fijando a 1 el valor de la propiedad Solo lectura conseguimos un control no editable.
    • Recordad que la clase VBoundFieldEdit representa al control de edición de puntero a maestro y la clase VLineEdit al control de edición alfabética.

¿Sabías que se puede determinar el tamaño de los iconos del objeto de lista ListView?

    • El tamaño de los Iconos en un objeto de lista ListView se determina con el tamaño del Dibujo del proyecto que asignamos a la propiedad Icono nulo.
    • Añade al proyecto un objeto Dibujo y ponle un tamaño con el editor (por ejemplo 150x100px) y lo asignas a la propiedad Icono nulo del ListView.

¿Sabes cuándo se calculan por primera vez los valores iniciales de los campos de una Tabla?

    • Desde un formulario se calculan siempre en primer plano.
    • En el plano que corresponda cuando se ejecuta el comando Crear nueva ficha en memoria.
    • La función del API VRegister.setTable() es la que ejecuta los valores iniciales. Tenedlo en cuenta en los bucles que añaden registros a una tabla.
    • Crear Copia de ficha en memoria no ejecuta valores iniciales. Podemos usar el comando Calcula campos dependientes, pero solo funciona con fórmulas que contienen campos de la tabla.
    • Los Tubos de ficha o lista no ejecutan los valores iniciales. Solamente se ejecutan los valores iniciales que dependan de otros campos de la tabla de destino

¿Sabes el orden de las señales cuando se han definido mas de una vez sobre el mismo control?

    • Un objeto de lista del proyecto con una conexión de evento Cambio de seleccionado se incrusta en un control Vista de datos del formulario.
    • En el control Vista de datos definimos la misma conexión de evento Cambio de seleccionado.
    • Se ejecutarán los 2 manejadores de evento, pero siempre primero el manejador de evento del objeto del proyecto.
    • De esta forma podemos tener funcionalidad común en el objeto del proyecto (por ejemplo guardar el ID seleccionado) y en los distintos formularios disparar una conexión de evento con un manejador de evento personalizado cuya primera línea podrá ser

Interfaz: Get variable local de vista de datos (CTR_LISTA, NID_FICHA, NID_FICHA_LISTA)

La variable local de la Vista de datos NID_FICHA tendrá un valor fijado previamente por la conexión de evento del objeto de lista.

¿Sabías que VReport SOLO tiene acceso al proyecto de datos?

    • Desde el editor de fórmulas de VReport solo tenemos acceso a los controles del proyecto de datos de la tabla de entrada (funciones, constantes, variables globales, …)

¿Sabías que un proceso ejecutado desde un formulario recibe como entrada la ficha en memoria que está en Alta o Modificación?

    • El comando Ejecutar proceso ejecutado desde un manejador de evento o desde un botón del formulario pasa como entrada la Ficha que tenemos en memoria en Alta o Modificación.
    • Por lo tanto podemos modificar los campos de la Ficha en memoria sin generar nueva transacción (equivale al comando Pedir formulario).
    • ¡¡Ojo!! no es lo mismo que ejecutar los comandos Crear manejador de objeto, Añadir ficha al objeto y Disparar objeto. En este caso lo que pasamos al proceso es la ficha en disco.

¿Sabías que puedes aplicar formato HTML/CSS a los controles de Texto estático en los formularios?

    • La propiedad Nombre de los controles de tipo Texto estático aceptan código HTML/CSS que se interpreta en tiempo de diseño y en ejecución.
      Un ejemplo de lo que se puede conseguir se muestra en la captura siguiente. Encima de cada Texto estático aparece el contenido de la propiedad Nombre.

VAF_S2_HTML

    • Como se puede ver podemos usar directamente Titulares H1, H2, …, colorear texto, crear líneas horizontales con un simple <hr>, insertar imágenes desde la carpeta caché o directorio por defecto, subíndices y superíndices.
    • No es una funcionalidad documentada, por lo tanto puede desaparecer en futuras versiones.

Y ahora confiesa… ¿cuántas sabías? 

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

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

Comandos de Configuración del sistema

Velneo proporciona un conjunto de comandos que permiten escribir y leer entradas en el fichero de configuración del sistema operativo de una máquina. 

Aprovecharemos esta funcionalidad, al igual que lo hace Velneo, para guardar y recuperar información útil de nuestras aplicaciones.

configuración del sistema

En el sistema operativo Windows los valores de configuración del sistema se guardan en el registro accesible mediante el comando regedit.exe. En Linux, OS X, iOS y Android se guardan en ficheros de texto. En este artículo vamos a centrarnos en Windows, aunque en el resto de plataformas el funcionamiento es el mismo.

Para acceder al registro de Windows tenemos que ejecutar el comando regedit desde la ventana Ejecutar (Tecla Windows+R).

Desde nuestras aplicaciones solo tenemos acceso a la información del registro que cuelga de la rama HKEY_CURRENT_USER y concretamente Velneo guardará siempre todos los valores de configuración en la rama HKEY_CURRENT_USER\SOFTWARE\Velneo.

Rama HKEY_CURRENT_USER (HKCU)
En esta rama se encuentra la configuración del usuario que está actualmente usando el equipo. Los cambios que hagamos en esta rama afectarán solo al usuario actual. Las carpetas del usuario, los colores de la pantalla y la configuración del Panel de control se almacenan aquí. Esta información está asociada al perfil del usuario. Esta clave a veces aparece abreviada como “HKCU”.

En la imagen siguiente se muestra un ejemplo de los valores de configuración en el Editor del Registro.

VAF_04_REGEDIT

Cuando usamos los comandos de Configuración del sistema, debemos tener en cuenta lo siguiente:

    • El ámbito de estos comandos de instrucción es la rama HKEY_CURRENT_USER\Software por lo tanto esta parte de la senda de una entrada del registro debemos omitirla en los parámetros.
    • El parámetro Fórmula de clave raíz es una subrama de Software. Normalmente valdrá Velneo.
    • En el parámetro Fórmula de senda de sección se especifican una o múltiples subcarpetas de la clave raíz, separadas por doble slash \\.
    • El parámetro Fórmula de entrada contiene el nombre de la clave que vamos a leer o escribir
    • La Fórmula de dato contiene la expresión de tipo fórmula que devuelve el valor de cadena o número que vamos a excribir.

Ejemplos:

  • Configuración del sistema: Escribir cadena de texto ( “Velneo”, “Mis_APPS”, “Path_Librerías”, $APP_INFO_CARPETA_LIBRERIAS@MiAplicacion.dat )
  • Configuración del sistema: Leer cadena de texto ( “Velneo”, “Mis_APPS”, “Parametro1”, CPARAMETRO_1 )
  • Configuración del sistema: Escribir cadena de texto ( “Velneo”, “ExtReport”, “zintdir”, sysCacheClientPath)
  • Configuración del sistema: Escribir cadena de texto ( “Velneo”, “WebBrowser\\websettings”, “enableJavascript”, “true” )

¿Qué secciones de la clave Velneo nos pueden interesar para nuestras aplicaciones?

Si revisamos por orden alfabético algunas de las secciones que utiliza Velneo, podemos destacar las siguientes entradas:

HKEY_CURRENT_USER\Software\Velneo\ExtReport\zintdir

Esta configuración es utilizada por VReport para localizar la utilidad de creación de códigos de barras Zint (zint.exe, zint.dll y zlib1.dll).
Podemos adjuntar en nuestro proyecto los 3 ficheros de la utilidad Zint y en el Pre_Init del marco Autoexec ejecutar el comando

Configuración del sistema: Escribir cadena de texto ( “Velneo”, “ExtReport“, “zintdir“, sysCacheClientPath)

HKEY_CURRENT_USER\Software\Velneo\SerialPorts\

En esta sección se guarda la configuración de los Dispositivos series que el usuario utiliza desde la aplicación mediante el comando Puerto serie: Configurar dispositivo.
Por ejemplo, la configuración del objeto Dispositivo serie ARDUINO_PLACA del proyecto de aplicación se guardará en la clave siguiente:

HKEY_CURRENT_USER\SOFTWARE\Velneo\SerialPorts\41mcn2yd.vca\ARDUINO_PLACA

La clave se creará únicamente cuando el usuario cambia la configuración del Dispositivo serie que viene por defecto con la aplicación.

HKEY_CURRENT_USER\SOFTWARE\Velneo\vAdmin\

El componente vAdmin.exe lee las claves LastServer, Servers y LastUser de la sección vAdmin para usarlas como valores iniciales en la ventana de Login.

HKEY_CURRENT_USER\SOFTWARE\Velneo\vClient

El componente vClient.exe también dispone de las claves LastServer, Servers y LastUser para inicializar la ventana de Login.

Las secciones DebuggerV7 y DebuggerJS guardan algunos aspectos del interface de ambos depuradores de código. Si eliminamos ambas secciones al inicio de nuestra aplicación conseguiremos tener siempre el depurador con el mismo aspecto.

La sección EditorFormulasDlg determinará el aspecto del Editor de fórmulas en tiempo de ejecución.

La sección MainWindow guarda para cada usuario/servidor/instancia/ el aspecto de la ventana principal de la aplicación (posición, tamaño, maximizada, …).

Si nuestra aplicación accede a Internet a través de un Servidor proxy con o sin seguridad, la sección proxy contendrá los parámetros de acceso.

HKEY_CURRENT_USER\SOFTWARE\Velneo\vDataClient

El componente vDataClient.exe dispone también de claves para configurar la ventana de login, tamaño de la ventana principal y el aspecto visual del interface.

HKEY_CURRENT_USER\SOFTWARE\Velneo\WebBrowser

Esta sección se corresponde con la configuración del Visor HTML de Velneo accesible a través del botón de la Barra de Herramientas.

Hay que tener en cuenta que esta configuración es común a todos los componentes de Velneo que usen el Visor HTML en la misma máquina y con el mismo usuario conectado. Esto quiere decir que si desde vDevelop se cambia la configuración del Visor HTML, ésta afectará a los Visores HTML de todas las aplicaciones que ejecutemos con el componente vClient. Si desde una aplicación, por ejemplo, cambiamos la clave enableJava a false, el resto de aplicaciones que ejecutamos con el mismo vClient tendrán también el java desactivado en el Visor HTML.

VAF_04_WEBBROWSER

Así que esto es lo mismo que si configuramos el explorador web de la máquina que usarán todas las aplicaciones del usuario conectado.

HKEY_CURRENT_USER\SOFTWARE\Velneo\Mis_APPS

Por supuesto, nosotros también podemos tener nuestra propia sección dentro de la raíz Velneo, por ejemplo Mis_APPS.

Algunos ejemplos de uso:

  • Path de la carpeta con nuestras librerías javascript que usaremos desde el Visor HTML: Mis_APPS\Path_Librerias = D:/MisLibrerias
  • Path del ejecutable del visor para ficheros PDF: Mis_APPS\Path_VisorPDF = C:\Program Files (x86)\Adobe\Acrobat 11.0\Acrobat\Acrobat.exe
  • Valor devuelto por una utilidad externa: Mis_APPS\Resultado_AutoIt3 = resultado
  • Valor del parámetro pasado a la Aplicación: Mis_APPS\Parametro1 = valor del parámetro

Para pasar un parámetro a nuestra aplicación Velneo creamos el siguiente archivo de comandos miAPP_parametros.cmd

@ECHO OFF
 REM Comprueba si hay parámetros
 IF %1.==. GOTO INICIAR
REM Escribe el parámetro en el registro para que lo lea vClient
 REG ADD HKCU\Software\Velneo\Mis_APPS /v Parametro1 /t REG_SZ /d %1 /f
REM Inicia la Aplicación Velneo y cierra el batch
 :INICIAR
 START "VELNEO" "C:\Program Files\Velneo\vClient.exe" vatp://Mi_Usuario:@127.0.0.1/Mi_Aplicacion
 EXIT

Conclusiones

    • A través de las opciones de Configuración del sistema podemos establecer y reiniciar el aspecto visual de los componentes de Velneo.
    • Es también una forma de comunicarnos con aplicaciones de terceros recibiendo parámetros o resultados de otros cálculos.
    • Todas las opciones deben colgar siempre de la rama HKEY_CURRENT_USER\Software\Velneo a la que el usuario tiene siempre acceso total.
    • Hay que tener en cuenta que solo podemos escribir y leer valores de texto o números enteros (ver los comandos de Configuración).
    • Aunque hemos visto el detalle en el sistema operativo Windows, los comandos de Configuración del sistema son multiplataforma.

¿Preparado para utilizar los comandos de configuración del sistema? 

Me gustaría escuchar tu opinión. Déjame un comentario más abajo.

La entrada Comandos de Configuración del sistema aparece primero en AyudaVelneo.

Introducción a los objetos de lista

En el proyecto de aplicación de nuestra solución disponemos de un conjunto de objetos con los que podemos gestionar listas de registros de una determinada tabla. El objeto más conocido es la Rejilla porque su funcionalidad es la más extensa, aunque existen otros objetos de lista visualmente más atractivos para mostrar los datos al usuario.

Los objetos de lista

objetos de lista

Hagamos un repaso de todos los objetos de lista, analizando a fondo aquellos aspectos más interesantes para el programador.

Enumeramos los objetos de lista disponibles:

    1. Rejilla: Conjunto de celdas distribuidas en filas y columnas. Una fila se corresponde con un registro de la tabla y una columna con un campo de dicha tabla.
    2. Árbol visor de tablas: Presenta, en distintos niveles, la información de una tabla con clave arbolada.
    3. Bloc de formularios: Permite editar en un formulario cada uno de los registros de una tabla.
    4. Casillero: Está compuesto por una serie de celdas (o casillas) distribuidas en filas y columnas. Cada celda permitirá mostrar un formulario de un registro de la tabla. Cada celda a su vez es un botón que permite disparar un comando al hacer clic sobre él.
    5. ComboView: Presenta una lista desplegable de registros de la tabla.
    6. Informe: Se utilizarán para enviar información de los registros de una lista de la tabla a una impresora o a un documento PDF en disco.
    7. ListView: Presenta una lista de registros de la tabla en una zona con scroll.
    8. Lista QML: Aprovecha toda la potencia gráfica de QML para mostrar listas de registros de una o varias tablas.
    9. ViewFlow: Usa un modo gráfico a modo de presentación de diapositivas para mostrar un formulario o campo dibujo por cada registro de la tabla.
    10. Rejilla avanzada: Partiendo de una Rejilla normal podemos crear otro Rejilla con opciones avanzadas, disponibles por el usuario en tiempo de ejecución.
    11. Alternador de lista: En este objeto podemos declarar múltiples objeto de salida (rejilla, informe, casillero, …) entre los que el usuario final podrá alternar en tiempo de ejecución.
    12. MultiVista: Permite presentar varios objetos sincronizados en función de su entrada y salida.

Características comunes a los objetos de lista

Algunas características comunes a todos ellos son:

    • Los objetos de lista implementan el esquema Modelo/Vista (Model/View) a la hora de mostrar los datos de una tabla. El Modelo se corresponde con la Lista de entrada y la Vista es el propio objeto que proporciona el interface para que el usuario pueda interactuar con los datos de entrada. Por lo tanto, el objeto de lista necesita siempre una Lista de entrada para que pueda mostrar registros de la tabla. La propiedad Tabla asociada determina a qué tabla del proyecto pertenece la Lista de entrada.
    • En la mayoría de objetos de lista podemos añadir, modificar o eliminar fichas o registros de la lista de entrada. Para ello especificaremos el Formulario de alta, modificación y baja, lo cual activa automáticamente las teclas Ins para mostrar el formulario de Inserción y Supr para el formulario de eliminación de la ficha seleccionada. El formulario de modificación se muestra haciendo doble click sobre la ficha seleccionada o con las teclas Enter/Return/Barra espacio. Existe la opción de mostrar un menú contextual por defecto que mostrará las opciones de Alta, Modificación y Baja de ficha. En este caso existe un problema, el menú por defecto seguirá mostrando todas las opciones aunque no hayamos especificado algún formulario de alta, modificación o baja. En ese caso usaremos un Menú personalizado.
    • En todos los objetos de lista se pueden definir los subobjetos Conexión de evento, Manejador de evento, Variable y en la mayoría el Drop. Estos subobjetos permiten encapsular código en los objetos de lista y de esta forma darles funcionalidad que podremos aprovechar en múltiples lugares de nuestra aplicación.

El evento de drag&drop permite seleccionar registros en un objeto de lista para soltarlos en otro objeto de lista. Consultar los siguientes enlaces: qué es drag&drop, ejemplo drag&drop de ayudavelneo y tutor de drag&drop.

    • Los objetos de lista se pueden mostrar en el interface de 3 formas:
      • Como un control “Vista de datos incrustado en el formulario.
      • Como un objeto incrustado directamente en una ventana independiente (en modo Vista o Modal) mediante un objeto “Acción“.
      • Disparar el objeto desde un proceso con el comando “Crear manejador de objeto(), en este último caso la ventana siempre es Modal, es decir, la ejecución del proceso se interrumpe hasta que se cierra la ventana contenedora del objeto de lista.
    • Además desde el API podemos disparar una Acción con theMainWindow.runAction() o instanciar el objeto mediante la clase VDataViewDialog.

Veamos un ejemplo:

Queremos mostrar un Casillero CAS_ARTICULOS con datos de la tabla ARTICULOS. Se ha preparado un formulario FRM_ARTICULOS_CAS con cuatro campos de la tabla y lo asignaremos a la propiedad Formulario del objeto CAS_ARTICULOS.

vaf_03_cas_frm

Necesitamos un proceso que proporcione la Lista de entrada para el objeto CAS_ARTICULOS.

Rem ( Proceso PRO_LISTA_ARTICULOS - Entrada ninguna y Salida la Lista de Artículos)
Rem ( Obtenemos la Lista de Artículos que alimentará nuestro objeto de lista)
Cargar lista ( ARTICULOS@facturas_dat, ID, , , , )
        Añadir lista a la salida

Para insertar el control Vista de datos en un formulario que muestre el objeto CAS_ARTICULOS nevesitamos 2 objetos:
       Objeto 1: PRO_LISTA_ARTICULOS@facturas_app
       Objeto 2: CAS_ARTICULOS@facturas_app

Para mostrar el casillero en una ventana independiente en modo Vista necesitamos un objeto Acción ACC_ARTICULOS_CAS que nos permita encadenar el proceso y el objeto de lista tal como hemos visto en el control Vista de datos. En el objeto ACC_ARTICULOS_CAS seleccionamos el comando Disparar objetos y elegimos el proceso PRO_LISTA_ARTICULOS y el Casillero CAS_ARTICULOS.

Una Acción es el objeto del proyecto de aplicación que permite disparar un comando. Éste puede ser un comando en stock (preprogramado) o un comando programado que, en general, disparará objetos.

Rem ( Mostrar el Casillero en modo Vista )
Interfaz: Ejecutar acción ( ACC_ARTICULOS_CAS@facturas_app )

El comando Crear manejador de objeto muestra el objeto de lista en una ventana Modal o en cuadro de diálogo.

Rem ( Mostrar el Casillero en una Ventana Modal )
Crear manejador de objeto ( hCasillero, Casillero CAS_ARTICULOS@facturas_app )
Rem ( Obtenemos la lista de entrada y la añadimos al objeto de lista)
Cargar lista ( ARTICULOS@facturas_dat, NAME, , , , )
        Añadir lista al objeto ( hCasillero )
Rem ( Mostramos la ventana Modal. El proceso se detiene hasta que se cierra la ventana )
Disparar objeto ( hCasillero, No aplicable, LOK )

Con el API y la clase VDataViewDialog también podemos hacer algo equivalente:

// Instanciamos objeto Lista en modo Vista ejecutando una Acción del proyecto
theMainWindow.runAction("facturas_app/ACC_ARTICULOS_CAS")

// Usando la clase VDataViewDialog para mostrar el Casillero en Ventana Modal o cuadro de Diálogo
// Obtenemos la lista de entrada
var oLista = new VRegisterList(theRoot)
if (oLista.setTable("facturas_dat/ARTICULOS")) {
        if (oLista.load("ID", [])) {
                // Instanciamos el Casillero
                var oCasillero = new VDataViewDialog(theRoot)
                oCasillero.setDataView(17, "facturas_app/CAS_ARTICULOS")
                // Alimentamos la entrada
                if (oCasillero.setRegisterList(oLista)) {
                        // Ejecutamos el objeto. El script se detiene hasta que cerramos la ventana modal.
                        oCasillero.exec()
                }
        }
}

vaf_03_cas_final

    • A todos los objetos de lista se les puede asociar una Barra de herramientas (Toolbar) que irá situada en la parte superior o inferior.

Una Toolbar o Barra de herramientas es un objeto de interfaz gráfica que contiene botones que al ser presionados activan ciertas funciones de una aplicación. Cada botón de la toolbar disparará un objeto (Acción o Menú) declarado en el proyecto en curso o en los proyectos heredados o un menú en stock.

Los botones de la Barra de herramientas solo puede disparar Acciones o menús.
El uso más común es mostrar al Usuario de forma gráfica las opciones de Añadir, Modificar y Eliminar.

Las 3 Acciones son:

      • Ficha: Formulario de alta – Muestra el formulario indicado en la Acción o en su defecto el indicado en el objeto de lista.
      • Ficha: Formulario de modificación – Muestra el formulario indicado en el objeto de lista.
      • Ficha: Formulario de baja – Muestra el formulario indicado en el objeto de lista.

vaf_03_toolbar

Estas tres Acciones refrescan automáticamente la Ficha afectada del objeto de lista.
Si queremos refrescar la lista del objeto de lista no funcionará el comando Interfaz: Recalcular(), éste solo se puede aplicar a un control Vista de datos ya que necesitamos ejecutar de nuevo el proceso que alimenta la lista de entrada del objeto de lista.

¿Cómo podemos refrescar la lista del objeto de lista desde su propio manejador de evento?
Creamos un botón en la Toolbar cuya Acción ejecuta el comando Disparar señal. En el objeto de lista una conexión de evento captura la señal Acción disparada y ejecuta el siguiente manejador:

Rem ( El comando Recalcular() no funciona en el contexto del objeto de lista )
// Interfaz: Recalcular ( )
Libre
Rem ( Usamos una Cesta local para recargar la lista en el objeto de lista )
Cesta: Crear cesta local ( ARTICULOS@facturas_dat, cesArt )
Cargar lista ( ARTICULOS@facturas_dat, NAME, , , , )
Cesta: Agregar lista a la cesta ( cesArt )
Cortar lista ( 0, )
Cesta: Agregar a la lista en curso ( cesArt )
Seleccionar ficha por posición ( 1 )
    • La clase del API VAbstractListDataView es la clase base de todos los objetos de lista. Con VAbstractListDataView tendremos acceso a funciones relacionadas con la lista asociada al objeto de lista. Para cada tipo de objeto de lista (rejilla, casillero, …) disponemos de la correspondiente clase que hereda de VAbstractListDataView que proporciona funciones específicas para la interfaz.

Veamos un ejemplo de un manejador de evento javascript que se dispara en el objeto de lista mediante una Acción que ejecuta el comando Disparar señal. Es un código genérico que podemos usar en cualquier objeto de lista independientemente de su tipo y de la tabla asociada.

// Manejador BUSCAR_LISTA_JS genérico que sirve para cualquier objeto de lista
// Obtenemos el objeto de lista
var oObjetoLista = theRoot.dataView()

// Obtenemos la Lista asociada al objeto de lista
var oLista = new VRegisterList(theRoot)
if (oObjetoLista.getList(oLista)) {
        // Tabla de la lista (alias/idTabla)
        var cTabla = oLista.tableInfo().idRef()
        // Instanciamos un registro de la Tabla para seleccionar una Ficha en la Lista
        var oRegistro = new VRegister(theRoot)
        if (oRegistro.setTable(cTabla)) {
                // Buscamos un registro por el índice ID único
                var cIDArt = prompt("ID del elemento de la Lista", "", "Búsqueda")
                if (cIDArt.length > 0) {
                        // Localizamos un registro por el Índice único
                        if (oRegistro.readRegister("ID", [cIDArt], VRegister.SearchThis)) {
                                oObjetoLista.setCurrentSelect(oRegistro)
                                // Edita el registro de la Lista
                                oObjetoLista.execEditForm()
                        }
                        else alert("No existe el elemento en la Lista", "Búsqueda")
                }
        }
}

Estas son algunas de las funcionalidades comunes a los objetos de lista.

En un próximo artículo repasaremos las peculiaridades de cada tipo de los objetos de lista y comprobaremos lo sencillo que resulta con Velneo mostrar una lista de registros de múltiples maneras solo con cambiar el objeto de lista, recuerda el esquema Modelo/Vista, un solo modelo y múltiples vistas.

¿Qué objetos de lista utilizas tú? 

Me gustaría escuchar tu opinión. Déjame un comentario más abajo.

La entrada Introducción a los objetos de lista aparece primero en AyudaVelneo.

Sabías que …

Hoy os propongo un “juego”…

Se sincero…

¿Sabías que..?

El juego se llama “Sabías que?

¿Sabías que se puede añadir un campo nuevo a una tabla que ya contiene datos y rellenarlo con un valor inicial para los registros que ya existen?

    • Crear el nuevo campo con el valor inicial si es necesario.
    • Añadir un subobjeto Traspaso de campo seleccionando el nuevo campo y rellenando la fórmula con el valor que tendrá inicialmente en los registros existentes.

¿Sabías que los campos Alfa 256 se pueden ordenar sin tener en cuenta mayúsculas/minúsculas, acentos y signos de puntuación?

    • Crear un índice por el campo Alfa 256 con el Modo igual a Campo porción. Establecer la longitud del texto que se incluirá en el índice y la conversión a Alfa 40 para no tener en cuenta mayúsculas/minúsculas, acentos y signos de puntuación.

¿Sabías que no es necesario usar vInstallBuilder si solo queremos actualizar la Aplicación y no es necesario copiar nuevas tablas con datos iniciales?

    • Copiar los ficheros actualizados de los proyectos de aplicación (vca) y de datos (vcd) a la carpeta cajas de vServer (en un sistema Windows 64 es C:/Windows/SysWOW64/config/systemprofile/Velneo/cajas). La subcarpeta donde copiamos los ficheros vca y vcd tiene el mismo nombre que la solución a la que pertenecen.
    • Reinicar la solución desde vAdmin teniendo en cuenta que no puede haber enganches activos en el proyecto de aplicación principal ni en todos los proyectos heredados por la solución.

¿Sabías que el servidor vServer desconectará automáticamente aquellos componentes clientes de edición (vDevelop, vTranslator) y de administración (vAdmin) tras 12 horas sin uso?

    • En el caso de clientes de edición (vDevelop y vTranslator), no se deshará la desprotección de los proyectos, con el fin de permitir enviar los cambios realizados en esa sesión de edición que se ha mantenido abierta e inactiva durante más de 12 horas. En ese caso, para guardar los cambios simplemente deberíamos ejecutar la opción de conexión con el servidor y, una vez restablecida, guardar el proyecto que estábamos editando.

¿Sabías que si un enganche de vClient se ha perdido (por ejemplo un fallo de conexión), el enganche quedará activo en el servidor durante 5 minutos? Este tiempo es configurable.

    • El tiempo de desconexión del enganche es configurable mediante una entrada en el registro o archivo de configuración del servidor. La entrada se llama ConnectionExpiredSeconds y el valor por defecto es 300 segundos. El tiempo debemos indicarlo en segundos y la clave se lee en el arranque del servidor.
      Por ejemplo, en Windows la clave se guarda en HKEY_USERS/.DEFAULT/Software/Velneo/vServer/ConnectionExpiredSeconds para el usuario system.

¿Sabías que cuando instanciamos un Objeto mediante un manejador de objeto podemos dispararlo las veces que queramos en el mismo proceso?

    • El manejador de objeto es local al proceso o manejador en el que se haya declarado.
    • Una vez declarado el manejador, podemos asignar valores a las Variables locales y Disparar el Objeto las veces que queramos.

Por ejemplo, queremos obtener todos los pedidos de las zonas geográficas donde el Usuario es responsable:

Rem ( Declaramos el manejador del objeto Búsqueda )
Crear manejador de objeto ( hBuscar, Búsqueda BUS_GBX_EXP@0PS_Gabex_app )

Rem ( Asignamos valor a las variable locales del Objeto)
Set variable local de objeto ( oBuscar, NPERIODO, NPERIODO )
Set variable local de objeto ( oBuscar, NID_TIPO_PED, NID_TIPO_PED )

Rem ( Solo se buscan los pedidos de las Zonas geográficas del Usuario conectado )
Cesta: Crear cesta local ( PEDIDOS@MiApp_dat, cesPedidos )

Rem ( Obtenemos las Zonas geográficas del Usuario )
Cargar lista ( ZONAS_USUARIOS@MiApp_dat, ZONA_USU, NID_USUARIO, , , )
        Rem ( Disparamos la búsqueda tantas veces como Zonas geográficas tenga asignadas el Usuario )
        Rem ( Acumulamos el resultado de las búsquedas en una cesta )
        Recorrer lista sólo lectura
                Set variable local de objeto ( oBuscar, CID_ZONA, #ZONA )
                Disparar objeto ( oBuscar, No aplicable, )
                        Cesta: Agregar lista a la cesta ( cesPedidos )
Rem ( Devolvemos la lista de pedidos )
Cesta: Procesar ( cesPedidos )
Ordenar lista ( #FECHA_ALTA, , , , , )
Invertir lista
Añadir lista a la salida

¿Sabías que las cestas declaradas en un manejador son compartidas con los manejadores que éste llama?

    • Las cestas declaradas en un Manejador del formulario son visibles también desde los Manejadores que se ejecutan desde el Manejador inicial.
    • Hay que ser conscientes de este hecho y en su caso evitar usar el mismo nombre de Cesta en los manejadores de un formulario.

En este ejemplo la cesta cesEmpleados es compartida por el manejador SECUNDARIO llamado por el manejador PRINCIPAL.

Rem ( Manejador PRINCIPAL )
Rem ( ----------------------------------- )
Rem ( Una Cesta creada en un manejador es compartida con los manejadores que éste llama )
Cesta: Crear cesta local ( EMPLEADOS@MiApp_dat, cesEmpleados )
Cargar lista ( EMPLEADOS@MiApp_dat, NAME_TROZOS, "jos", , , )
        Cesta: Agregar lista a la cesta ( cesEmpleados )
Cesta: Procesar ( cesEmpleados )
        Crear manejador de objeto ( oLisEmplados, Rejilla GRD_EMPLEADOS@MiApp_app )
        Añadir lista al objeto ( oLisEmplados )
        Disparar objeto ( oLisEmplados, No aplicable, )

Rem ( Ejecuta un Manejador secundario )
Interfaz: Ejecutar manejador de evento ( SECUNDARIO, )


Rem ( Manejador SECUNDARIO )
Rem ( ----------------------------------- )
Rem ( Si el manejador cesEmpleados ¡¡ya existe!! la cesta en lugar de crearse se reutiliza con los registros que tenga )
Cesta: Crear cesta local ( EMPLEADOS@MiApp_dat, cesEmpleados )
Cargar lista ( EMPLEADOS@MiApp_dat, NAME_TROZOS, "luis", , , )
        Cesta: Agregar lista a la cesta ( cesEmpleados )
Cesta: Procesar ( cesEmpleados )
        Rem ( Aquí obtenemos los Empleados "jos" más los empleados "luis" )
        Crear manejador de objeto ( oLisEmplados, Rejilla GRD_EMPLEADOS@MiApp_app )
        Añadir lista al objeto ( oLisEmplados )
        Disparar objeto ( oLisEmplados, No aplicable, )

Y ahora confiesa… ¿cuántas sabías? 

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

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

Texto enriquecido en comandos de diálogo

La base de datos de Velneo guarda los textos con formato usando el campo Objeto Texto enriquecido. En el interfaz mostraremos el texto con formato mediante el control Texto enriquecido y el formato del texto se gestionará gracias a las etiquetas HTML.

El texto con formato no es una página HTML estándar ni el control Texto enriquecido es un editor de HTML, simplemente las opciones de formato como negrita, cursiva, listas, tipo de letra, … se guardan con etiquetas HTML.

Las etiquetas de formato son un subconjunto de HTML 4, el listado completo lo puedes consultar en http://doc.qt.io/qt-5.7/richtext-html-subset.html.

Añadir formato al texto de los comandos Mensaje y Pregunta

Hay 2 comandos de Interfaz, Mensaje y Pregunta, que muestran una ventana de diálogo con un mensaje de texto.

Una ventana de diálogo es una ventana de nivel superior que la aplicación utiliza para ejecutar tareas inmediatas y enviar breves comunicaciones al usuario.

Quizás no sepas que la fórmula del mensaje de texto de la ventana de diálogo puede contener etiquetas HTML, que se interpretarán como opciones de formato cuando se muestren en la pantalla.

Veamos un ejemplo en el que usaremos algunas etiquetas HTML para dar formato al mensaje. La imagen de la izquierda es un Mensaje y la de la derecha una Pregunta.

vaf_02_mensajevaf_02_pregunta

Tal como muestran las imágenes las posibilidades de dar formato a los mensajes son bastante interesantes. Vamos a examinar la fórmula del mensaje para entender cómo hemos conseguido dar formato al texto con las etiquetas HTML:

<html>
 <!-- Usamos el tag html, aunque no es necesario -->
 <!-- El estilo definido en el tag body afecta a todo el documento -->
 <body style=" font-family:'Consolas'; font-size:11pt; font-weight:normal; font-style:normal;">
 <!-- 
 margin                         establece los márgenes (sup der inf izq)
 color                         acepta los colores por nombre
 -qt-block-indent         es 1 para establecer un párrafo blockquote
 text-indent                 indenta la primera línea del párrafo
 -->
 <h1 style="margin:0 0 0 5; color:DarkRed; text-indent:5px;">
         HTML en comandos de Diálogo
 </h1>
 <hr width="100%">
 <!-- Los hiperenlaces de las etiquetas <a> funcionan desde las ventanas de diálogo -->
 <p style="margin:5 5 0 0;">
    Los comandos de Velneo
    <a href="https://velneo.es/info_v7_719_es/velneo_vdevelop/proyectos_objetos_y_editores/proceso/comandos/interfaz/dialogos/mensaje/">Mensaje</a>
    y
    <a href="https://velneo.es/info_v7_719_es/velneo_vdevelop/proyectos_objetos_y_editores/proceso/comandos/interfaz/dialogos/pregunta/">
    Pregunta</a> 
    pueden contener código HTML para mostrar texto enriquecido.
 </p>
 <p style="margin:10 0 5 0; text-indent:5px;">Podemos: </p>
 <p style="margin:0 0 0 30;">
    Cambiar <span style=" font-size:18px;">el tamaño del texto</span><br>
    Colorear <span style=" color:#ff0000;">el texto</span><br>
    Aplicar <span style=" font-weight:600;">negrita</span> y <span style=" font-style:italic;">cursiva</span><br>
    <font face="Segoe script" size="5" color="green">Texto manuscrito</font>
 </p>
 <p style=" margin:20 0 0 30;">
    Lista de opciones:
 </p>
 <!-- -qt-list-indent indenta las líneas de la Lista -->
 <ul type="square" style="margin:0; -qt-list-indent:2;">
    <li>Opción 1</li>
    <li>Opción 2</li>
    <li>Opción 3</li>
 </ul>
 <!-- La imagen se carga desde el directorio por defecto. Lo fijamos en Velneo. 
 El pie de imagen puede tener estilo personalizado con el tag span
 -->
 <p style="margin:20 0 10 30;">
    <img src="BannerRueda.png" width="200" /><br>
    <span style="font-family:'Helvetiva'; font-size:12px; font-style:oblique;">Imagen desde la caché</span>
 </p>
 <p align="center" style="margin:10px; font-family:'helvetica'; font-size:x-large; font-weight:bold;">
    TABLA
 </p>
 <!-- La mayor parte del aspecto de la tabla se determina en el tag table
 border                 es 1 para mostrar los bordes de la tabla
 border-color        color de los bordes
 border-style        estilo none,solid,dotted, ...
 cellspacing        espacio entre celdas
 cellpadding        espacio entre texto y bordes
 -->
 <table border="1" style="border-color:#2f4f4f; border-style:solid; margin:0px;" align="center" width="90%" cellspacing="1" cellpadding="4" bgcolor="#f0fff0">
 <!-- Las celdas pueden ajustarse individualmente
 th                        celdas de cabecera
 bgcolor                color de fondo de la celda
 padding                espacio entre texto y bordes
 -->
 <tr>
    <th bgcolor="#adff2f" style="padding:10 2 4 2;">Cabecera 1</th>
    <th bgcolor="#adff2f" style="padding:10 2 4 2;">Cabecera 2</th>
 </tr>
 <tr>
    <td>Fila 1</td>
    <td align="right">2.562,23</td>
 </tr>
 <tr>
    <td>Fila 2</td>
    <td style="font-weight:600;" align="right">56.215,31</td>
 </tr>
 </table>
 </body>
 </html>
 

Podemos destacar lo siguiente:

    • En el tag <body> se pueden establecer las opciones de formato globales de todo el texto.
    • El formato asociado a un tag se establece con los valores de sus atributos (bgcolor, border, align, style, …).
    • Es conveniente empezar siempre con el tag <html>, aunque no es necesario.
    • Los enlaces de los tag <a> funcionan abriendo la página con el explorador predeterminado.
    • El tag <img> accede a la imagen en el directorio por defecto establecido en Velneo (comando Cambiar directorio por defecto).

Existe otra ventana de diálogo que admite formato de texto enriquecido, es el comando Pedir dato.

vaf_02_pedirdato

En este ejemplo hemos colocado una imagen capcha y un texto debajo. El tag table nos permite colocar los elementos fácilmente.
La imagen se ha introducido en formato base64 porque es el resultado de la generación dinámica de un capcha.

Captcha o CAPTCHA son las siglas de Completely Automated Public Turing test to tell Computers and Humans Apart (prueba de Turing completamente automática y pública para diferenciar ordenadores de humanos).

<html>
<table cellspacing="0" cellpadding="0" bgcolor="DarkRed">
<tr>
<!-- Tenemos que ajustar el padding manualmente para que se centre correctamente la imagen-->
<td align="center" style="padding:3 0 0 3;">
<img src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDABYPERMRDhYTEhMZFxYaITckIR4eIUQwMyg3UEZUU09GTUxYY39sWF54X0xNbpZweIOHjpCOVmqcp5uKpn+Ljon/2wBDARcZGSEdIUEkJEGJW01biYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYn/wAARCAA5ASwDASIAAhEBAxEB/8QAGgABAAIDAQAAAAAAAAAAAAAAAAUGAgMEAf/EAC4QAAICAgECBQMDBAMAAAAAAAABAgMEBRESIQYTIjFBMlFhFHGRFSNCgWKhwf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwC3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADhydpTiXRhlQsqjL2sa5h/PwdOPk0ZNasotjZF+zizi8RpPR5fKT9Bz+Faalpce2NaVj6uZcd33YE0AcuwxbMulQryJ0NPlyj8gdRFZedlXWzo1sIucPqnP2R3yccbF9c/TCPeUmVfQbhwnlQ8iy6ydjlHoXwBYNXXnwhJ59sZyb7KK9juI7O2aw8BX2w6LJdlB/c47ZPIx+uu66WQ1yvL56UwJ0HDqHnPFS2CirF7NfK/J3AAAAAAAAAAAAAAAGNk41wcpSjH8yfCIDC2uye1eNk1Vyqk+0ov2QFhANVmTRXLpsuhF/Zy4A2gxjZCaTjOL5+zMgAB42orltL9wPQeKUZe0k/2Z6AA5AAGvIurx6Z3Wy6YQXLZXbvFVklOzD19ltEPqsk+EBZgRem3ePtYNQTrtj9UH/4bNpt6NbbRC6Mn50uOV8fkCQAT5XKAEb4ifGkyn/wNfhaXVosf8cr/ALN+ZqqMumVU53RjJcPix9/5MMLCyNdjxox7IW1R9lYuH/KAkiF8T/qq8CV1GS64rs4pe/8AsbCe7urdNGPVWpPh2Rs5aRr3eNk5Gtjg42PbKUePW2uHx+eQO7BirtPWsj+4nDvz8kV4ThCORmeXHiHX2JOpZj1kaY0Ku3p6fVLsvyc+p0uRgRl1Zj5m+ZKMQNPi6i2zFptqg5uufLSRngb/AB51Qrjj3eYlw4xh8k4l6eH3/c8jCEfpil+yA1Y1l1vMrK/Lj8J+5vAAAAAAAAAAAAAAANWTjVZVLqvh1wfwVTxNraNbTTk4nVXNT47SZaM7JeJjSujTO5r/ABgu5T9/sMva1wqhr764xfPLi+4Fu11zydfTbL3nBclTy44+HsL/AOp1TyK5y9Nql7I6tNucymunDs19rUfT1KLRyZmBsadhblfpJW4ys6vLk+eQOW7LxMbNx7dZdclGS5hNvgv1U1ZVGaaakuexRNhGzbTqrwtZOhxfqfTxyXPVY9uLrqaLpdU4R4bA6yr+JOuGfXLJlbLB47xr+H+S0FU8TYufbsFLEqssg6+JpfS0BH5MtbVRK3XbO+u191Bt/wAEtRt76vDH6lvrtXpTff8A2QmTfr54TohrrK8vjp9vkkNZqs2Xh/Jrsg4ufqrhL3AwxNJtc+qOXZnSrdndLqfsZYmyztTtFr8y53VSfCk/dflHmu8UTwa44udiy/trjmPZ/wAGmPnbrfRy68ecaINNuS+EBJeINflrX3W/1Cyyv38uSXHBp0uly7dbF/r5V1W93Wo8rgz3+/wb9bdi0ys82Xbhxa4PND4gwKMGnEsdqsXp7x55YEotMsXF8rW2rGsb9VnT1ORVvEeJmY+ZjxyMx5M5/S326e5f0+Vz9yl+MVbRtsfK6eYKKa59uU/YCWw8DeVWVSs2UJ1rjqg4+6+xPFGw97tMzaY7r+nqUXBL0tfkvK9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADzojzz0rn78HoAGE6apy6p1Qk/u4pmSSiuEkl9kegDTPExrJdU6K5N/LimeRwsSMlKONSpR7pqC5RvAAxsrhZHpshGa+0lyZADCumqpcV1xgvtFcGYAH//Z">
</td>
</tr>
<tr>
<td style="padding:4;">
   <span style="color:white; font:italic bold 14px Georgia, serif;">Introduzca el texto de la imagen</span>
</td>
</tr>
</table>
</p>
</html>

Con estos ejemplos, a partir de ahora podrás hacer más vistosos tus cuadros de diálogo, de una forma rápida y sencilla.

Aplicar CSS a las ventanas de diálogo

Además de aplicar formato al texto de los mensajes, podemos estar interesados también en aplicar formato al resto de los elementos de las ventanas de diálogo.

Lamentablemente la implementación de CSS en Velneo no está del todo completada y por si fuera poco escasamente documentada. Por lo tanto debemos probar los distintos estilos CSS y esperar que los resultados sean los esperados. Las pruebas hechas con los comandos de Diálogo y con la versión Velneo 20 han llevado a las siguientes conclusiones:

    • La clase que debemos utilizar con los CSS en las ventanas de diálogo es QMessageBox. La clase QDialog también funciona pero hay que tener en cuenta que los estilos aplicados también afectarán a los formularios modales.
    • De la clase QMessageBox solo podemos fijar las propiedades CSS background, background-clip y background-origin.
    • El comando Mensaje SÍ aplica el CSS. La versión javascript alert() SÍ aplica el CSS.
    • El comando Pregunta SÍ aplica el CSS. La versión javascript confirm() SÍ aplica el CSS.
    • El comando Pedir dato NO aplica el CSS. La versión javascript prompt() NO aplica el CSS (usando QDialog SÍ aplica el CSS).
    • Para que no aparezca el título de la ventana tenemos que pasar como parámetro un espacio en blanco.
    • El icono de la ventana de diálogo no se puede elegir. Si el formulario origen es una vista mostrará el icono del objeto autoexec y si es un formulario modal mostrará el de éste.
    • La imagen de fondo y el resto de las imágenes deberán adjuntarse al proyecto para que se copien a la carpeta caché de vClient. Tendremos que fijar la carpeta por defecto a la caché antes de aplicar los estilos CSS.

Este comportamiento es deseable que mejore con las futuras versiones de Velneo para conseguir una homogeneidad plena en nuestro interface.

A continuación se muestran 2 ventanas de diálogo con CSS aplicado y texto enriquecido.
A la izquierda un mensaje con la función javascript alert() y a la derecha el comando de Velneo Pregunta.

vaf_02_mensaje_css vaf_02_pregunta_css

El código CSS aplicado es el siguiente:

QMessageBox {
   /* background-color: qlineargradient(x1:1, y1:0, x2:0, y2:1, stop:1 #cfe7fa, stop:0 #6393c1); */
   background-image: url(fondo_claro_1.jpg);
   /* background-color: transparent; */
}
QMessageBox QPushButton {
   background: #63B8EE;
   background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #63B8EE, stop:1 #468CCF);
   min-width: 80px;
   font-family: arial;
   font-weight: bold;
   color: #FFFFFF;
   font-size: 16px;
   text-shadow: 1px 1px 0px #7CACDE;
   box-shadow: 1px 1px 1px #BEE2F9;
   padding: 10px 20px;
   border-radius: 8px;
   border: 1px solid #3866A3;
}
QMessageBox QPushButton:hover {
   color: #BAF7F5;
   background: #468CCF;
   background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #468CCF, stop: 1 #63B8EE);
}

El texto enriquecido:

<p style="font: normal bold 30px verdana; color:Navy; margin:0; padding:0;">
   BACKUP
</p>
<hr>
<table border="0" style="margin:10px;" align="center" width="95%">
<tr>
<td style="padding:20 20 20 30; background-color: rgba(25,128,225,0.15);">
   <img src="data_disk.png" width="48">
   <p style="font: normal 18px arial; color:DarkSlateGray;">
      Se han generado los archivos de datos. <br>
      Pulse <b>Aceptar</b> para iniciar la copia de seguridad.
   </p>
</td>
</tr>
</table>
<hr width="90%">

En este artículo hemos partido de la ventana de diálogo nativa de Velneo y una vez que le aplicamos un poco de CSS y usando texto con formato HTML hemos conseguido cambiar totalmente su aspecto visual. Recuerda que el código CSS y el HTML nos permitirán cambiar dinámicamente el resultado final del interface de nuestras aplicaciones.

Coméntanos mas abajo tus progresos cambiando el interface de las aplicaciones Velneo.

 

La entrada Texto enriquecido en comandos de diálogo aparece primero en AyudaVelneo.

Las variables en Velneo

El tema de las variables en Velneo siempre ha suscitado dudas y algún que otro quebradero de cabeza, debido al mal uso de los planos de ejecución y del ámbito de las variables.

Recordemos el artículo ayudavelneo.com/el-peligro-de-las-variables-en-velneo-v7 para comprenderlo mejor.

En Velneo a Fondo hemos recopilado la información que hay dispersa en diferentes artículos y en la propia ayuda de Velneo y queremos preparar un artículo en el que podamos encontrar lo más interesante sobre el uso de las variables en nuestras aplicaciones.

Si crees que debemos añadir un concepto o idea nueva sobre las variables en Velneo no dudes en comentarlo.

Las variables en Velneo V7

Tipos de variables en Velneo

Las variables en Velneo V7 son objetos que permiten almacenar datos individuales. Hay dos tipos: las variables globales y las variables locales.

La variable global es un objeto del proyecto de datos, cuyo contenido es global a la aplicación y común, en el ámbito de red, para todos los usuarios si su persistencia es en disco, y de carácter local si su persistencia es en memoria.

Un objeto es un elemento definible dentro de un proyecto que, en tiempo de ejecución, realiza las tareas de un programa. La Rejilla es un objeto.

La variable local es un subobjeto definible dentro de un objeto. Se trata de una variable local al objeto en el que ha sido declarada, y solamente será accesible desde ese objeto o desde otros subobjetos del mismo. La persistencia de este tipo de variables es en memoria.

Un subobjeto es un objeto que está contenido en otro objeto y que no tiene ninguna entidad por sí mismo, ya que depende totalmente del objeto contenedor. El manejador de evento es un subobjeto.

Las variables contienen valores de tipo alfabético, numérico, fecha, hora, tiempo y booleano. Además pueden ser al mismo tiempo arrays unidimensionales.

Ámbito de las variables globales en disco

El contenido de una variable global en disco es común a todos los usuarios y planos de ejecución (tanto en los clientes como en el servidor).

Los planos de ejecución entran en juego cuando desarrollamos en una arquitectura cliente-servidor como la de Velneo V7.

Existen 4 planos de ejecución:
       El 1º plano es el hilo principal de ejecución en el cliente, con el que interactúa el usuario de nuestras aplicaciones.
       El 2º plano también se ejecuta en el cliente, pero a diferencia del 1º plano, la ejecución es asíncrona, es decir, no interrumpe el resto de procesos.
       El 3º y 4º planos se ejecutan en el servidor, el primero de forma síncrona y el segundo asíncrona.

Por tanto, si desde una sesión de Velneo vClient V7 se modifica una variable global en disco, esta modificación afectará tanto a los clientes que estén ejecutando la misma instancia como a los procesos que hagan uso de ella en el servidor (procesos ejecutados en 3º y 4º  plano).

Una instancia es una aplicación abierta en Velneo vServer que está en ejecución. Es posible abrir en un mismo Velneo vServer diferentes instancias de una misma aplicación. Una aplicación Velneo está compuesta por proyectos, tanto de datos como de aplicación, que serán instanciados de forma independiente.

Debemos tener en cuenta que cada vez que se usa una variable global en disco, vClient solicitará al servidor el valor de dicha variable, por si ha cambiado, lo que supone una conexión (socket) a través del enganche correspondiente, lo que puede ralentizar el proceso sobre todo en cloud.

Un socket es una conexión que se establece cada vez que queremos comunicar dos máquinas. La comunicación se ralentiza sobre todo al establecer el socket, ya que una vez establecida el traspaso de información es rápido. En cloud nuestras aplicaciones deben generar el menor número de sockets posible y para ello hemos de optimizar nuestras aplicaciones.

Debemos evitar el uso de variables globales en disco en la definición de propiedades de campos de las tablas. Por ejemplo, un valor inicial basado en una variable global en disco ralentizará el alta del registro. En la Interfaz de nuestras aplicaciones, un campo fórmula con una variable global en disco se recalcula en cada registro de la Rejilla o en comandos de Recorrer lista, lo que en redes lentas se traduce en una mala experiencia de usuario.

Ámbito de las variables globales en memoria

Las variables globales en memoria existen solo en la sesión de la máquina donde se ejecuta la aplicación. Si ejecutamos varios vClients en una misma máquina, las sesiones se ejecutan en diferentes espacios de memoria y por lo tanto no comparten las variables globales en memoria.

En el servidor, la instancia de la aplicación comparte la memoria con todas las sesiones iniciadas por los usuarios, en consecuencia en 3º y 4º plano, cualquier cambio de una variable global en memoria en una sesión afectará al resto de sesiones.

Cada vez que un usuario se conecta al servidor mediante vClient se crea una nueva sesión de usuario también llamado enganche. La sesión es efímera, solo debe dejar un rastro en forma de Log o histórico.

Las variables globales en memoria de un mismo proyecto de datos no se replican entre el cliente y el servidor. Si el vClient  modifica una variable global en memoria, ésta misma variable en 3º y 4º plano no se verá afectada por la modificación de vClient, son espacios de memoria separados.

Los procesos en 1º y 2º plano comparten la memoria en la misma sesión de vClient. Un proceso en 2º plano que modifica una variable global en memoria afectará al resto de procesos en 1º y 2º plano.

Una sesión de vClient solo comparte con otros vClient de la red (local o remota) los siguientes datos:

    • Las tablas de la instancia de datos del vServer
    • Las tablas en memoria en 3º y 4º plano de la instancia de datos del vServer
    • Las variables globales en disco
    • Las variables globales en memoria en 3º y 4º plano de la instancia de datos del vServer

Cuando una variable global (en memoria o disco) es modificada en 1º plano, todos los elementos del interfaz activos en ese momento se actualizan inmediatamente. Todas las fórmulas de las propiedades de los objetos se vuelven a calcular. Habrá que tenerlo en cuenta en nuestras aplicaciones para evitar comportamientos no controlados.

Variables del sistema

Aunque la ayuda dice que son variables de tipo local predefinida en el sistema, se puede considerar que su ámbito es global a la aplicación. Están siempre disponibles a través del editor de fórmulas y algunas, como sysUserName, sysCacheClientPath y sysIsAdministrator, se fijan al inicio de la aplicación y su valor estará siempre disponible en cualquier plano de ejecución.

Ámbito de las variables locales

Las variables locales sólo son accesibles desde la instancia del objeto en que se han definido.

Si el objeto es un formulario, las variables locales también son accesibles desde los subobjetos de dicho formulario. Cuando instanciamos el mismo formulario 2 veces, las variables locales de ambas instancias son totalmente independientes.

Los subobjetos de un formulario son los controles, conexiones de evento, manejadores de evento, variables locales, inserciones y extensiones de ficha.

Para insertar un control del proyecto en un formulario usamos un control Vista de datos (o un separador, caja o pila de formularios) que hace de contenedor de dicho control. Las variables locales del control insertado no se comparten con el formulario anfitrión porque el subobjeto es el control Vista de datos y no el objeto incrustado.

Por ejemplo, cuando insertamos una Rejilla en una Vista de datos, las variables locales de la Rejilla no se comparten con las variables locales del formulario.

La tabla no es un subobjeto del formulario y por lo tanto la tabla y el formulario no comparten las variables locales.

Hay una excepción con la compartición de variable locales en el objeto Búsqueda. Si tiene un formulario declarado para introducir las variables locales y éstas coinciden en nombre con las variables locales de la Búsqueda entonces se traspasan automáticamente los valores del formulario a la Búsqueda.

Variables locales especiales

Existen 3 variables locales cuyo identificador está reservado. Se pueden crear manualmente o a través de la opción “Crear variables locales eventos” del menú de Objetos.

    • EVENT_SENDER_ID: De tipo alfabética. Contiene el identificador del control que envía la señal. Estará vacía si el que envía la señal es el propio objeto.
      La variable EVENT_SENDER_ID podemos usarla dentro de un manejador para conocer qué Botón ha sido pulsado si existen varios botones que disparan el mismo manejador de evento.
    • EVENT_SENDER_COL: De tipo alfabética. Permite obtener información de la columna en la que estamos posicionados cuando se lanza la señal. Estará vacía en todos los eventos salvo los de columna editable de rejilla.
    • EVENT_PARAMS: De tipo array alfabética. Contiene información que podrá ser leída desde el manejador de evento que ha ejecutado la señal disparada.

Acceso a variables locales de Vistas de datos

Ya hemos dicho que un objeto del proyecto insertado en un formulario no comparte las variables locales, sin embargo Velneo proporciona comandos que nos van a permitir leer y escribir valores desde el formulario en las Vistas de datos que contenga.

Las Vistas de datos son objetos del proyecto que gestionan Listas de registros de una tabla, la excepción es el formulario que gestiona solo una Ficha. Las Vistas de datos se pueden incrustar en un formulario para mostrar, entre otras cosas, las relaciones maestro-detalle de nuestro proyecto de datos.

Existen 5 controles del formulario que permiten incrustar objetos Vista de datos:

    • Vista de datos propiamente dicha que mostrará objetos aparcables (rejilla, multivista, casillero, …) mediante un proceso cargador
    • Separador de formularios que mostrará varios formularios en pestañas
    • Splitter que mostrará varios formularios con un separador ajustable
    • Pila de formularios para crear asistentes mediante formularios sucesivos
    • Caja de formularios que mostrará varios formularios en otro tipo de pestañas

Existen 2 comandos de Velneo que nos dan acceso a las variables locales del objeto incrustado en la Vista de datos:

    • Interfaz: Set variable local de vista de datos
    • Interfaz: Get variable local de vista de datos

Tened en cuenta que estos 2 comandos solo funcionan con objetos incrustados en formularios como Vista de datos. No se puede acceder a variables locales de otro formulario. Por ejemplo, si queremos acceder a las variables locales de un Dock del marco de la Aplicación tendremos que diseñar 2 formularios, uno principal que será el Dock y otro secundario, incrustado como Vista de datos, que contendrá las variables locales que queremos gestionar. El comando que lee el valor de una variable local quedará de la siguiente manera:

Interfaz: Get variable local de vista de datos (.AUTOEXEC.DOCK_CONFIG.CTR_VISTADATOS, VAR_LOCAL, VAR_LOCAL_RETORNO)

Acceso a las variables en Velneo usando el API

En Velneo se ha desarrollado un API que incluye un conjunto de clases que aportan los objetos y funciones para acceder a los elementos de nuestros proyectos. Para programar el API se han incorporado 2 lenguajes, Javascript y QML. En ambos lenguajes podemos usar el conjunto de funciones del estándar EcmaScript-262 (ver http://doc.qt.io/qt-5/qtqml-javascript-functionlist.html).

La interfaz de programación de aplicaciones, abreviada como API (del inglés: Application Programming Interface), es el conjunto de subrutinas, funciones y procedimientos (o métodos, en la programación orientada a objetos) que ofrece cierta biblioteca para ser utilizado por otro software como una capa de abstracción.

Con el API de Velneo podemos acceder a cualquier variable de nuestros proyectos. La herencia permite el acceso y uso de cualquier objeto de la aplicación desde cualquier script.

La herencia es la propiedad que permite que un proyecto pueda hacer uso de los objetos de otro proyecto.

Acceso con el API a las variables globales

Las variables globales son objetos del proyecto de datos. Desde el API usaremos la clase VApp para el acceso a dichas variables.

La clase VApp representa a la aplicación. Siempre estará instanciada en el objeto global theApp.

Las funciones del objeto theApp para el acceso a variables globales son globalVarTo<tipo>() y setGlobalVar(), la primera obtiene el valor de la variable global (tendremos que especificar el tipo de dato que vamos a obtener) y la segunda establece el valor de la variable global.

La variable global se identifica desde el script con el formato “ALIAS_PROYECTO_DATOS/Identificador

El Identificador es la etiqueta alfanumérica que identifica de forma unívoca al objeto dentro del proyecto y para el mismo tipo de objeto. Este identificador será el que usemos para referenciarlo en las propiedades de otros objetos. El identificador constará de mayúsculas y números exclusivamente.

El objeto theApp representa al proyecto de aplicación principal, es decir, la instancia desde la que iniciamos nuestra aplicación. Por lo tanto, mediante el mecanismo de la herencia tendremos acceso a todos los proyectos de datos de la cadena de herencia. Usar la función mainProjectInfo() de la clase VApp para obtener información del proyecto principal y de todos los objetos de la herencia.

En el siguiente ejemplo obtenemos un JSON con todas las variables globales de la Herencia.


// Obtiene un Array con todas las variables globales en la herencia
// Proyecto principal
var mainProjectInfo = theApp.mainProjectInfo()
// Obtenemos el JSON
alert(JSON.stringify(getAllVarGlobal(mainProjectInfo), null, "  ")

function getAllVarGlobal(ProjectInfo, aLisVarGlobal){
        // Inicializa el array
        aLisVarGlobal = aLisVarGlobal || []
        // Añade al array las variables globales del proyecto
        aLisVarGlobal = getVarGlobal(ProjectInfo, aLisVarGlobal)
        // Proyectos heredados (bucle recursivo en toda la herencia)
        for(var i=0, count=ProjectInfo.legacyProjectCount(); i<count; i++){
                var oProyHeredado = ProjectInfo.legacyProjectInfo(i)
                if (oProyHeredado.type() == 0) {
                        // Solo exploramos los proyectos de Datos
                        getAllVarGlobal(oProyHeredado, aLisVarGlobal)
                }
        }
        return aLisVarGlobal
}

// Devuelve todas las variables globales del proyecto
function getVarGlobal(oProyecto, aVarGlobal){
        var count = oProyecto.objectCount(VObjectInfo.TypeVariable)
        var oObjVarGlobal = null
        for(var i=0; i<count; i++){
                oObjVarGlobal = oProyecto.objectInfo( VObjectInfo.TypeVariable, i )
                aVarGlobal.push ( 
                        {
                                idRef: oObjVarGlobal.idRef(),
                                id: oObjVarGlobal.id(),
                                name: oObjVarGlobal.name(),
                                valor: theApp.globalVarToString(oObjVarGlobal.idRef()).substr(0, 100)
                        }
                )
        }
        return aVarGlobal
}

Acceso con el API a las variables LOCALES

Las variables locales pertenecen a objetos que pueden ser ejecutados desde los proyectos de datos y aplicación. Desde el API usaremos la clase VRoot para acceder al objeto principal que está en ejecución.

La clase VRoot representa al objeto en ejecución. Siempre estará instanciada en el objeto theRoot, accesible desde los scripts del objeto actual.

Las funciones del objeto theRoot para el acceso a las variables locales son VarTo<tipo>() y setVar(), la primera obtiene el valor de la variable local (tendremos que especificar el tipo de dato que vamos a obtener) y la segunda establece el valor de la variable local.

La variable local se identifica con su Identificador único en el objeto al que pertenece.

Para acceder a las variables locales del objeto incrustado en un formulario como una Vista de datos tendremos que obtener el objeto theRoot que representa a dicho objeto incrustado. Existe para ello la función root() que aplicada al objeto Vista de datos nos permitirá acceder a sus variables locales.

Veamos un ejemplo. Obtenemos el ID del registro seleccionado en una Rejilla y lo asignamos a la variable local del formulario incrustado como Vista de datos.

// Formulario principal
var oForm = theRoot.dataView()
// Rejilla con la lista de Fichas
var oGRDLista = oForm.control("GRD_LISTA")
var oLista = new VRegisterList(theRoot)
// Reiniciamos la variable local NID
theRoot.setVar("NID",0)
// Ficha seleccionada en la Rejilla
var nSel = oGRDLista.currentSelect()
if (nSel > -1) {
        // Lista de Fichas de la rejilla
        oGRDLista.getList(oLista)
        // Ficha seleccionada en la Rejilla
        var oFicha = oLista.readAt(nSel)
        // Fijamos la variable local NID
        theRoot.setVar("NID", oFicha.fieldToInt("ID"))
        if (oFicha) {
                // Mostramos la Ficha en el formulario incrustado como Vista de datos
                var oVistaDatos = oForm.control("FRM_INCRUSTADO")
                oVistaDatos.setRegister(oFicha)
                // Establecemos el valor de la variable local de la Vista de datos
                oVistaDatos.root().setVar("NID", oFicha.fieldToInt("ID"))
                // Accedemos al objeto en ejecución de la Vista de datos mediante la función root()
                //         y obtenemos el valor de la variable local de la Vista de datos
                alert(oVistaDatos.root().varToInt("NID"))
        }
}

Existe otra clase VObjectInstance que nos permite instanciar objetos de la aplicación de la misma forma que lo hacemos con el comando de Velneo “Crear manejador de objeto”. Para disparar el objeto usaremos la función exec(). Antes de disparar el objeto podemos asignar valores a las variables locales con setVar(). Después de ejecutar exec() y si éste devuelve true, obtenemos el valor de las variables locales con la función VarTo<tipo>().

Variables globales y locales de tipo Array

Las variables array en Velneo no son un objeto independiente del proyecto. Para disponer de un array tendremos que poner a 1 la propiedad Array de una variable global o local.

Los arrays son siempre unidimensionales y el tipo de dato que contiene lo determina el Tipo de la variable. Los elementos del array se acceden mediante la posición que ocupan, siendo 0 la primera posición. En las fórmulas podemos obtener el valor de un elemento concreto del array usando la sintaxis ID_ARRAY[<posición>].

Acceso a variables array globales

Los valores de las posiciones en un array global se pueden leer desde Velneo con la sintaxis ID_ARRAY_GLOBAL[<posición>], pero la asignación de valores solo se puede realizar a través del API. Se podrá trabajar con variables globales de tipo array a través del API de JavaScript mediante las funciones de variables globales de tipo array de la clase VApp.

El uso de un array global facilita la gestión de determinada información al poder agrupar n-variables globales en una sola.

Las funciones del objeto theApp para el acceso a variables array globales son globalVarArrayTo<tipo>() y setGlobalVarArray(), la primera obtiene el valor de una posición de la variable array global (tendremos que especificar el tipo de dato que vamos a obtener) y la segunda establece el valor de una posición de la variable array global. El segundo parámetro de estas funciones es la posición del elemento del array, empezando en 0. Podemos conocer el tamaño de una variable array global mediante la función globalVarArraySize().

Vamos a ver un ejemplo de gestión de variable array global usando el API de Velneo.
Hemos creado en el proyecto de datos una variable global en memoria G_CONEXION de tipo Array. En ella queremos guardar algunos datos de la Sesión actual.
El tamaño del array se irá adaptando al número de elementos creados.

Disponemos de 2 procesos javascript, uno que inicializa los valores del array y otro obtiene un determinado elemento del array.

// Script PRO_CONEXION_INI_JS – Fija los elementos del array global G_CONEXION
// ----------------------------------------------------------------------------
// Usuario conectado
theApp.setGlobalVarArray("MiApp_dat/G_CONEXION", 0, theApp.userName());

// ¿Es administrador?
theApp.setGlobalVarArray("MiApp_dat/G_CONEXION", 1, theApp.isAdministrator()?"Sí":"No");

// Fecha-Hora de la conexión
var f = new Date();
var dias_semana = new Array('domingo','lunes','martes','miércoles','jueves','viernes','sábado');
var fecha = dias_semana[f.getDay()]
+ " " + f.getDate() + "/" + (((f.getMonth()+1)<10?"0":"")) + (f.getMonth() + 1) + "/" + f.getFullYear()
+ " " + ((f.getHours()<10)?"0":"") + f.getHours() + ":" + ((f.getMinutes()<10)?"0":"") + f.getMinutes();
theApp.setGlobalVarArray("MIApp_dat/G_CONEXION",2,fecha);
//theApp.setGlobalVarArray("MiApp_dat/G_CONEXION", 2, f.toLocaleString());

// Código de Pais
theApp.setGlobalVarArray("MiApp_dat/G_CONEXION", 3, theApp.sysCountryIsoCode());

// Nombre de máquina
theApp.setGlobalVarArray("MiApp_dat/G_CONEXION", 4, theApp.sysMachineName());

// Script PRO_CONEXION_GET_JS – Obtiene un elemento del array global G_CONEXION // -------------------------------------------------------------------------------- // Devuelve el valor del elemento del Array G_CONEXION pasando como parámetro un String CVALOR que describe la información solicitada var aValores = new Array("USER","ISADMIN","DATELOGIN","COUNTRY","MAQUINA") var cValor = theRoot.varToString("CVALOR") var pos = aValores.indexOf(cValor) // Devuelve en VRETORNO el valor del elemento del Array theRoot.setVar("VRETORNO",theApp.globalVarArrayToString("MiApp_dat/G_CONEXION", pos)) En el proyecto de Aplicación podremos usar una función que obtenga directamente cualquier valor del array global: Mensaje(        "Usuario - " + fun:FUN_G_CONEXION_GET@MiApp_dat.dat("USER") + "\n" +        "Administrador - " + fun:FUN_G_CONEXION_GET@MiApp_dat.dat("ISADMIN") + "\n" +        "Conectado el - " + fun:FUN_G_CONEXION_GET@MiApp_dat.dat("DATELOGIN") + "\n" +        "Código de país - " + fun:FUN_G_CONEXION_GET@MiApp_dat.dat("COUNTRY") + "\n" +        "Máquina - " + fun:FUN_G_CONEXION_GET@MiApp_dat.dat("MAQUINA"), Informacion)

Acceso a variables array LOCALES

Las variables array locales son el mismo subobjeto variable pero con el valor de la propiedad Array a 1.

Disponemos de un conjunto de comandos Array: que utilizaremos en los procesos para gestionar los valores del array local. Podemos asignar un valor, eliminar uno o todos los elementos del array, obtener el tamaño, ordenar el array e insertar un elemento. Para obtener el valor de un elemento del array no existe un comando, simplemente usamos la sintaxis ID_ARRAY_LOCAL[<posición>].

Existen 2 comandos de Velneo que nos dan acceso a las variables array locales del objeto incrustado en la Vista de datos:

    • Interfaz: Set ítem de variable local array de vista de datos
    • Interfaz: Get ítem de variable local array de vista de datos

Estos comandos funcionan de la misma forma que con las variables locales, indicando el elemento del array empezando en 0.

Desde el API disponemos también de las funciones del objeto theRoot para el acceso a las variables array locales, las cuales son VarTo<tipo>() y setVar(). La primera obtiene el valor de un elemento de la variable array local (tendremos que especificar el tipo de dato que vamos a obtener) y la segunda establece el valor de un elemento de la variable array local. El segundo parámetro de estas funciones es la posición del elemento del array, empezando en 0.

Hagamos un ejercicio interesante sobre el acceso a variables locales array. A veces queremos pasar parámetros a un formulario y nos encontramos que en Velneo solo es posible hacerlo cuando usamos el comando Crear manejador de objeto. Para los formularios que se muestran en modo Vista, o en los comandos Pedir formulario y Modificar ficha con formulario, tenemos que recurrir a variables globales u otras técnicas con el API, json, … En este caso os propongo una solución creada íntegramente con Velneo nativo, que consiste en guardar los parámetros del formulario en una Vista de datos de un dock de la aplicación y usar los comandos de acceso a las variables array locales del objeto incrustado en la Vista de datos.

    • Creamos un formulario FRM_PARAMETROS que solo contenga tantos Arrays como formularios tenga nuestra aplicación que necesiten acceso a parámetros.
    • En el evento PRE_INI de FRM_PARAMETROS opcionalmente podemos inicializar los valores de los parámetros. Los valores del array se almacenan como cadenas de texto. Con los números y booleanos la conversión no presenta problemas, con las fechas y tiempos usaremos el formato ISO.
  Rem (Manejador PRE_INI de FRM_PARAMETROS)
  Rem (Aquí podemos documentar los formularios que van a necesitar acceso a parámetros y los inicializamos si es necesario)
  Rem (Parámetros del formulario FRM_CLIENTE)
  Array: Set ( AFRM_CLIENTE, 1, "parámetro 1", LOK)
  Array: Set ( AFRM_CLIENTE, 2, 200.45, LOK)
  Array: Set ( AFRM_CLIENTE, 3, 1, LOK)
  Array: Set ( AFRM_CLIENTE, 4, dateToString(currentDate(), "yyyy-MM-dd"), LOK)
  Array: Set ( AFRM_CLIENTE, 5, dateTimeToString(currentDateTime(), "yyyy-MM-dd hh:mm"), LOK)
  Rem (Parámetros del formulario FRM_PEDIDO)
  Array: Set ( AFRM_PEDIDO, 1, "", LOK)
  
    • En un dock de la aplicación insertamos un control Vista de datos CTR_PARAMETROS con el formulario FRM_PARAMETROS. La Vista de datos tendrá la propiedad Visible a 0 porque no necesita interfaz, sin embargo será un contenedor de parámetros que funcionalmente actuará igual que las variables globales en memoria, ya que accederemos a él en toda la aplicación a través del objeto Marco Autoexec.
    • Y eso es todo. Ya podemos fijar valores de los parámetros antes de ejecutar un formulario y leer dichos parámetros en el PRE_INI del formulario.

En un proceso determinado ejecutamos lo siguiente:

  Crear nueva ficha en memoria (hNuevaFicha, CLIENTES@apli.dat)
    Rem ( Establecemos los parámetros que deben tenerse en cuenta en el formulario)
    Interfaz: Set ítem de variable local array de vista de datos (.AUTOEXEC.DOCK_APP.CTR_PARAMETROS, AFRM_CLIENTE, 1, "CODIGO_HU33")
    Interfaz: Set ítem de variable local array de vista de datos (.AUTOEXEC.DOCK_APP.CTR_PARAMETROS, AFRM_CLIENTE, 2, 2565.32)
    Pedir formulario (FRM_CLIENTE_EDI@apli.app, LOK)
    If (LOK)
      Alta de ficha (hNuevaFicha)
        Mensaje ("Nueva ficha: " + #NAME)

En el manejador PRE_INI del formulario FRM_CLIENTE_EDI:

  If (! registerExist())
    Rem ( Obtenemos los valores iniciales desde el formulario de FRM_PARAMETROS)
    Interfaz: Get ítem de variable local array de vista de datos (.AUTOEXEC.DOCK_APP.CTR_PARAMETROS, AFRM_CLIENTE, 1, CNOMBRE_INI)
    Interfaz: Get ítem de variable local array de vista de datos (.AUTOEXEC.DOCK_APP.CTR_PARAMETROS, AFRM_CLIENTE, 2, NIMPORTE_INI)
    Modificar campo (NAME, CNOMBRE_INI)
    Modificar campo (IMPORTE, NIMPORTE_INI)

Conclusión

En este artículo hemos hecho un repaso de los aspectos más importantes del objeto variable de Velneo. Para más detalles de los comandos y funciones del API acudir siempre a la documentación de Velneo en el apartado de vDevelop.

La manera más rápida de acceder es buscar en Google la expresión “variable velneo”.

Tened muy claros los conceptos de ámbito de las variables y cómo acceder a las variables globales y locales tanto desde otros objetos como desde el API.

Si crees que falta algo importante sobre las variables en Velneo no dudes en comentarlo.

La entrada Las variables en Velneo aparece primero en AyudaVelneo.

Algunos conceptos que debemos tener claros sobre Velneo V7.

Presentamos esta nueva sección de “Velneo a fondo” con 2 objetivos principales:

  • Explicar en detalle los comandos y características de la plataforma de desarrollo Velneo.
  • Mostrar trucos y ejercicios prácticos que hagan más divertido programar con Velneo.

Conceptos básicos sobre Velneo V7

Los artículos de esta sección serán una fuente de información de aquellos aspectos de Velneo que no encontramos en el manual de ayuda.

Cogeremos un comando o funcionalidad de Velneo y explicaremos a fondo sus características más útiles e interesantes para el programador.

También habrá como no, trucos que hagan más sencillo y divertido programar con Velneo. Finalmente diseñaremos pequeñas aplicaciones, planteadas como ejercicios prácticos que pongan a prueba nuestros conocimientos.

No pretendemos enseñar a programar, sino conocer a fondo Velneo de tal forma que facilite el desarrollo de nuestras aplicaciones.

Queremos que los artículos sean útiles tanto a los programadores que empiezan a conocer la herramienta como a los que ya llevan tiempo con ella.

Antes de meternos en materia y como primer artículo de Velneo a fondo vamos a enumerar una serie de conceptos que consideramos interesantes tener claros. Es una lista que irá creciendo en próximas entregas y conviene revisarla periódicamente para comprobar que tenemos claros los conceptos básicos de Velneo.

Conceptos básicos sobre Velneo V7

vDevelop

Con vDevelop estamos editando los proyectos de Aplicación (código fuente) y los proyectos de datos (estructura de las tablas). En ningún caso con vDevelop generamos contenido en las tablas (las tablas estáticas no cuentan).

En vDevelop, cada vez que pulsamos F5, se producen las mismas acciones que realiza el VIN en Producción, se reinicia la Solución en desarrollo con los cambios realizados (código fuente y estructura de tablas).

En alguna ocasión necesitamos cambiar el identificativo de un campo, pero esto produce la pérdida de los datos ya existentes en ese campo.

El subobjeto Traspaso de campo facilita la migración de información entre campos de una Tabla y el control de versiones. De esta forma ya podremos cambiar el identificativo de los campos y traspasar los datos antes de eliminar el campo original.

Lo que se llama migración masiva de datos no tiene nada que ver con los ficheros .VIN, éstos actualizan la estructura de tablas y las migraciones habrá que programarlas con código fuente en la aplicación.

Los nombres de las Tablas nunca deben cambiarse o habrá problemas en la actualización.

En el editor de informes nativos las reglas están en pulgadas y los valores de las propiedades se deben poner en pixeles:
100px = 1pulgada = 2,54cm.

Esto vale para los tamaños de los controles, pero para los márgenes se sigue otra equivalencia:
Márgenes -> 50px = 1pulgada = 2,54cm

Aunque la calidad del renderizado en pantalla de los Informes nativos es deficiente, el comando de proceso “Imprimir Informe” y generar PDF mejoran considerablemente la calidad de impresión.

Usar siempre Aspecto de dibujo = Proporcionar para que los dibujos salgan al tamaño deseado.

Los objetos por sí mismos no muestran Listas de registros, necesitan que en el flujo de ejecución el objeto anterior haya creado una Lista (o Ficha) de la misma Tabla asociada al objeto destino.

Las Vistas de Datos permiten concentrar varios objetos en un flujo para que el resultado sea la Lista (o Ficha) mostrada en el control visual  (rejilla, formulario, casillero, …).

El comando de instrucción Recalcular (Vista de datos) vuelve a Disparar los objetos que integran el control Vista de datos para obtener de nuevo la Lista. Es decir, se vuelven a comprobar todos los parámetros que conforman la Vista de Datos.

Desde un Manejador de Evento de un Formulario no tenemos Acceso a los Manejadores de Evento de los subobjetos de dicho formulario.

Los procesos siempre ejecutan los formularios en Modal (Ventana de Diálogo). Para mostrar el formulario No Modal necesitaremos ejecutar una Acción que establezca un flujo nuevo.

Todos los objetos de las Bases de datos (Triggers, Actualizaciones,…) se ejecutan siempre en el servidor.

Los planos de ejecución son absolutos: 1º plano en cliente, 2º plano en cliente multitarea y 3º plano en servidor.

El plano donde realmente se ejecutará el objeto dependerá del plano absoluto en el que se encuentre el objeto llamador.

Así un proceso en 3º plano ejecutará todos los objetos subsiguientes en el servidor, independientemente del valor del parámetro Plano de ejecución.

Los objetos (incluso los procesos) no pueden dispararse (Disparar Objeto) en 2º plano, porque necesitan de un handle local al proceso llamador.

Las Listas (y las Fichas) únicamente guardan un puntero interno al registro físico de la Tabla en el vServer.

Los registros físicos de las tablas llevan asociado un número de registro o puntero interno (no es el ID) que sirve como puntero para las Listas y Cestas. Tiene un tamaño de 4 Bytes (4.294.967.295). Es el número que aparece a la izquierda en las rejillas.

Las Cestas son Listas de punteros a registros de una tabla. No almacenan información, solo apuntan a los registros.

La Caché de fichas acelera la carga de los datos en Local. Cuando establecemos una transacción de lectura/escritura se fuerza siempre la lectura de la ficha desde el Servidor para tener siempre la última versión.

Lo que viaja entre cliente y servidor son listas de punteros, no fichas completas de campos. La ficha completa se cargará solo cuando vClient necesita acceder a un campo de la ficha.

El Bloqueo Blando es el que realizan los formularios por defecto. El Bloqueo Duro es el que realizan los Procesos que transaccionan y los formularios con Estilo = Bloqueo Duro.

En Bloqueo Blando las transacciones son independientes para cada tabla. En Bloqueo Duro la transacción es global a todo el Proceso o Formulario.

El Refresco terciario no se produce en formularios Modales.

Las Funciones en 1º Plano siempre generan transacción independiente, aunque ya estén dentro de otra transacción. En 3º Plano las Funciones se integran en la transacción abierta.

Las Variables Globales en disco se comparten por el Cliente y el Servidor. Usarlas para leer datos globales una vez, almacenando su valor en una variable global en memoria.

Las Variables Globales en memoria no se comparten entre el Cliente y el Servidor.

Las Tablas en memoria pueden usarse para dar de Alta registros de manera diferida en 2º plano.

Las Tablas en memoria no se comparten entre Cliente y Servidor.

vServer

vServer de forma nativa tiene los mecanismos necesarios para actualizar una Aplicación en Producción de forma segura y fiable, no necesitamos herramientas externas.

Para actualizar una aplicación podemos prescindir del fichero .VIN y como alternativa, simplemente copiamos los ficheros de los proyectos de aplicación (vca) y de datos (vcd). Una vez copiados es suficiente con reiniciar la Solución.

Una determinada Solución solo puede ser reiniciada si no existe ninguna instancia en ejecución en otras Soluciones que la hereden.

Cada Instancia de datos apunta a una carpeta donde se guardan las Tablas de la Aplicación. Estas tablas son ficheros del sistema de archivos y pueden manipularse como tales.

Por ejemplo: cuando queremos sustituir la tabla auxiliar de Códigos postales por una nueva, simplemente paramos la Instancia de Datos y sustituimos los ficheros de dicha tabla (dat e idx). Por supuesto lo podemos hacer remotamente por FTP, SDV…

vInstallBuilder

Una vez terminado el trabajo con vDevelop y probada la aplicación en desarrollo, se genera un fichero .VIN con vInstallBuilder. El fichero .VIN se compone únicamente de los proyectos de Aplicación y Datos, es decir, código fuente y estructura de las tablas.

Existe la opción, si es necesario, de añadir al VIN algunas tablas para que la Aplicación disponga de ellas. Por ejemplo: queremos incorporar a nuestra aplicación las tablas de Códigos Postales y Poblaciones.
Normalmente el VIN irá sin tablas (lo que se conoce como
VIN vacío), pero siempre irá con el código fuente y la estructura de las tablas (o sea la Solución).

El fichero .VIN es el que llevamos a Producción (Cliente). El fichero .VIN actualizará los proyectos de Aplicación y Datos del vServer.

Ahora viene lo más importante: cuando se reinicia la Solución, las instancias de Aplicación ejecutarán el nuevo código fuente y las instancias de Datos actualizarán la estructura de las tablas a partir de la nueva estructura del proyecto de Datos.
Las tablas que ya existen verán actualizada su estructura y las que no existen se crearán vacías, pero
NUNCA SE SOBRESCRIBEN DATOS.
Entonces no tiene sentido incorporar tablas al VIN, excepto cuando queremos instalar una Aplicación con datos de prueba o incorporar nuevas tablas auxiliares.

vClient

En sistemas Windows, analizando el Setup de vClient y el registro, observamos que Velneo no instala absolutamente nada, simplemente copia la carpeta Velneo/V7 con las librerías de QT, librerías C++, el vUpdater y el ejecutable vClient.exe.

Los paquetes de distribución de Runtimes C++ realmente sobran del Setup ya que pueden ser descargados de Microsoft e instalarse una sola vez por separado (ver https://support.microsoft.com/es-es/kb/2977003).

El cliente vUpdater no es necesario, si vClient no lo encuentra simplemente lo ignora y ejecuta la versión vClient disponible.

El uninstall-vclient.exe simplemente lo que hace es borrar la carpeta Velneo/V7. Se puede ejecutar en modo silencioso con /S.

Así que el despliegue de vClient puede consistir en empaquetar la carpeta de Velneo/V7 en un zip con los ficheros necesarios y simplemente copiarla a la máquina del cliente y descomprimirla en la carpeta que queramos.

Con esto podemos tener varias versiones de vClient ejecutando de forma simultánea.

Optimización

El campo Alfa 256 es el más rápido de gestionar de los campos alfanuméricos. No confundir con el tamaño del campo.

Índices. No abusar innecesariamente de ellos. Lleva su tiempo gestionarlos.

Los Índices Condicionados, aunque penalizan un poco, merecen la pena cuando se transacciona poco y se lee mucho.

Las Rejillas con campos de tipo objeto demoran la carga para evitar lapsus en la visualización de los registros.

¿Cuántos de estos “conceptos básicos sobre Velneo V7” conocías?. Ya sabes que valoro tus opiniones sobre los artículos… puedes dejarla mas abajo en los comentarios.

La entrada Algunos conceptos que debemos tener claros sobre Velneo V7. aparece primero en AyudaVelneo.

Crear una empresa de Software

Hace unas semana la gente de INNO tv estuvo grabando en el vCenter una entrevista. El objetivo es fomentar el espíritu emprendedor entre los Universitarios como fórmula para salir de la crisis. En la entrevista se habla de nuestra experiencia para crear una empresa de Software.
.
Nuestra entrevista empieza en el min 12:00
.
.