Una barra de progreso en VFP "puro"
15/03/2020
Contenido
- Introducción
- La implementación del layout
- Coloquemos el código
- Haciéndolo funcionar
- Conclusión
Introducción
Muchas veces necesitamos indicar el avance de un proceso en Visual FoxPro. Para ello podemos utilizar una barra de progreso ActiveX, como las provistas en la instalación de VFP. Sin embargo, a veces es necesario optar por la simplicidad de tener solamente código puro VFP.
Las barras de progreso (progressbars) son elementos útiles a la hora de mostrar avances en procesos que demandan, de algunos segundos a varios minutos, para su terminación. Proveen un feedback importante al operador del programa, para indicar a éste dos parámetros importantes que hacen a la “sensación de usuario”: (a) que el proceso se sigue ejecutando, y (b) cuánto tiempo podría consumir éste. Todos los sistemas de desarrollo importantes tienen al menos un control progressbar.
VFP no es la excepción, pero los controles progressbar que se proveen con el producto son del tipo ActiveX, para ser utilizados en prácticamente cualquier entorno de desarrollo de Windows. La barra de progreso es un control OLE, cuyo “servidor OLE” tiene el identificador MSComCtlLib.ProgCtrl.2, donde el último dígito, de existir, indica la versión del mismo.
Sin embargo, al incluir este tipo de controles en nuestra aplicación a ser distribuida, tengamos en cuenta que el control ActiveX es, en definitiva, una especialización de COM; por lo tanto, como aquéllos, debe registrarse en las máquinas donde nuestra aplicación vaya a ser ejecutada. Los controles de este tipo generan entradas en la registry de Windows, donde se indica al resto de las aplicaciones dónde está alojado físicamente, cómo invocarlo, y alguna otra información para su correcta utilización. Esto debe hacerse conjuntamente con la instalación de nuestra aplicación VFP; de lo contrario la misma fallará al intentar enlazar en tiempo de ejecución al objeto OLE.
Para evitar estos potenciales problemas, si necesitamos un entorno de instalación directo y sencillo, y deseamos disminuir al mínimo las posibilidades de que la instalación falle, podemos proponer una solución sencilla: hacer una barra de progreso totalmente en VFP, que no sea muy distinta de aquella OLE.
La implementación del layout
Utilizaremos clases puras de VFP para esta tarea. Crearemos una clase llamada Therm (por termómetro, nombre dado en algunos entornos de desarrollo a este tipo de controles progressbar). La clase de base de la barra de progreso será un formulario. Este no será un control incrustable en un form, sino que el usuario percibirá una ventana que se abre al momento de lanzar el proceso, que mostrará la barra de progreso. Si queremos que sea incrustable en un formulario, deberemos cambiar la clase de base de “form” a “container”.
Por lo tanto, abrimos nuestro querido VFP, y lanzamos la creación de la clase Therm, en una biblioteca de clases a la que podemos llamar custom.vcx:
CREATE CLASS therm OF custom.vcx AS form
Luego coloquemos estas propiedades en el diseñador visual:
Top = 0 Left = 0 Height = 95 Width = 394 Caption = "AVANCE" Icon = milogo.ico && podemos colocar cualquier icono aquí Name = "therm" Caption=”Avance”
Generemos dos propiedades para la clase:
Hagamos clic en la superficie del form de la clase;
Busquemos en la barra de menú la opción Clase;
Seleccionemos la opción Nueva Propiedad;
Agreguemos MaxValue =0;
Agreguemos SelfUpdate = .F.
La propiedad MaxValue alojará el valor máximo a representarse en el control. En tiempo de diseño la dejamos en cero para hacer notar su naturaleza numérica. Por ejemplo, si deseamos contar los registros procesados en un lazo SCAN..ENDSCAN, y la cantidad de registros es 12000, entonces este valor debería ir en la propiedad MaxValue.
La propiedad SelfUpdate indicará si queremos que actualice la cantidad de porcentaje terminado de manera automática.
En este momento, nuestra clase tendrá más o menos el aspecto que se muestra en la Figura 1:
Figura 1. Volver al texto.
Coloquemos ahora los componentes que hacen a la barra en sí. Para ello seleccionaremos el control shape, colocaremos 20 cuadraditos de estos controles para simular la existencia de 20 “luces” que muestran el avance del proceso. El control shape es un control gráfico, muy sencillo, que nos permitirá mostrar el avance del proceso al cambiar el color de relleno del mismo.
Arrastremos un control shape y démosle un tamaño de unos 15x15 píxeles; podemos hacer esto con las propiedades height y width del control. Démosle el nombre de led1.
Una vez colocado uno, copiémoslo y peguémoslo al lado, luego copiemos estos dos y peguémoslos al lado de los dos anteriores, y así sucesivamente hasta tener 20 cuadraditos de controles shape, nombrados de led1 a led20 respectivamente. Cada cuadradito indicará un avance del 5%. Ver Figura 2:
Figura 2. Volver al texto.
Ahora coloquemos alrededor otro control shape, para darle un aspecto estético solamente. Démos a este control el efecto de 3D en la propiedad SpecialEffect. Ver Figura 3:
Figura 3. Volver al texto.
Luego coloquemos dos o tres controles label, a saber:
lblMensaje1, para indicar el proceso que se está corriendo. Coloquemos la propiedad AutoSize en .T.;
lblMensaje2, para indicar opcionalmente otro mensaje relativo a la cantidad de objetos procesados, con cualquier formato. También con la propiedad AutoSize en .T. Démosle de tamaño unos dos puntos menos que a la fuente del label anterior para indicar cierto tipo de precedencia (el mensaje 1 es mas importante que el 2). Ver Figura 4:
Figura 4. Volver al texto.
Ahora coloquemos un control Textbox para reflejar la cantidad porcentual procesada, siempre que la propiedad SelfUpdate esté en .T. Para evitar tener la necesidad de manipular la propiedad Caption de un control label, elegimos el control textbox. A fin de lograr el efecto deseado, llamémoslo txtPorcentaje y coloquemos sus propiedades de la siguente forma:
BackStyle = 0 && Transparent BorderStyle = 0 && None FontBold = .T. FontName = “Arial” FontSize = 11 ForeColor = 0,0,255 && azul Style = 1 && 2.x &SAY compatibility
Al colocar Style en 1, el textbox no será editable y se comportará más bien como un label que refrescará automáticamente lo exhibido al invocarse un evento Form.Refresh. La clase debe quedar como se muestra en la Figura 5:
Figura 5. Volver al texto.
Coloquemos el código
Creamos dos métodos para la clase, uno llamado SetCaption, para poder cambiar los rótulos del progressbar, y otro llamado UpdateBar, que se utiliza para efectivamente hacer avanzar o retroceder la barra con tan sólo pasarle la cantidad numérica de objetos procesados.
Los códigos son más bien reducidos, y son los que siguen:
Método SetCaption
* METODO SETCAPTION LPARAMETER cTitulo,ntitulo * cTitulo = string a ser exhibido * ntitulo =1, mensaje principal * ntitulo =2, mensaje secundario bajo la barra del termómetro DO CASE CASE ntitulo=1 THIS .lblMensaje1. Caption =cTitulo CASE ntitulo=2 THIS .lblMensaje2. Caption =cTitulo OTHERWISE THIS .lblMensaje1. Caption ="" THIS .lblMensaje2. Caption ="" ENDCASE
Método UpdateBar
* UPDATEBAR * nValor = cantidad numérica que puede oscilar entre cero y MaxValue LPARAMETER nValor AS Number * averigua cuántos leds deben prenderse IF THIS .SelfUpdate=.T. THIS .txtPorcentaje. Value = ROUND (nValor/ THIS .MaxValue*100,0) ENDIF nLeds= ROUND (nValor/ THIS .MaxValue*20,0) FOR led=1 TO 20 && cantidad de leds IF led<=nLeds STORE RGB (0,0,255) TO ; ("THIS.Led"+ ALLTRIM ( STR (led))+".BackColor") ELSE STORE RGB (192,192,192) TO ; ("THIS.Led"+ ALLTRIM ( STR (led))+".BackColor") ENDIF ENDFOR
Nótese que el funcionamiento es relativamente simple; cuando el método UpdateBar recibe una cantidad numérica, la divide por el valor almacenado en la propiedad MaxValue, obteniendo un valor porcentual que se exhibe en el control txtPorcentaje si el flag SelfUpdate lo permite. Luego se calcula cuantos “leds” deben prenderse en función de ese valor porcentual. En función de esa cantidad de leds, se cambia la propiedad BackColor a azul de aquellos que deban “encenderse”, y se dejan en color gris por defecto a aquellos que deban “apagarse”.
Se puede tener un poco mas de performance desactivando SelfUpdate a .F. Por ultimo, en el evento Init de la clase, forzamos a que ésta se muestre visualmente:
Método Init
THIS.Show
Importante: la progressbar puede ralentizar lazos que tengan poca carga de procesamiento efectivo. Donde la operación iterativa consuma muy poco tiempo, el llamar por cada ciclo al método UpdateBar puede introducir un retardo mayor que el proceso en si. Por ello, sólo indicamos esta solución para escenarios donde el proceso dentro del lazo sea más bien complejo, y se justifique la pequeña demora que introduce el método UpdateBar.
Haciéndolo funcionar
Como ejemplo, hagamos un conteo de un proceso en el lazo SCAN..ENDSCAN. Supongamos que es necesario procesar una cantidad determinada de registros en un cursor, y que deseamos actualizar en nuestra progressbar doméstica este proceso. Damos a continuación un modelo de código, simplificado al máximo al sólo efecto de mostrar cómo podemos aplicarla:
*primero creamos el objeto, este se instanciará y se mostrará por el THIS.Show de su evento Init oThermo = NEWOBJECT(“Therm”,”custom.vcx”) * colocamos los titulos oThermo.SetCaption(“CONTEO DE REGISTROS”,1) oThermo.SetCaption(“Titulo secundario”,2) * queremos actualización automática de cantidad porcentual oThermo.SelfUpdate = .t. *luego contamos los registros a procesar SELECT micursor lnRecs = RECCOUNT() * transferimos esta cantidad a la propiedad MaxValue oThermo.MaxValue = lnRecs *entramos en el lazo SCAN inicializando antes una variable de conteo lnContador lnContador = 0 SCAN lnContador = lnContador + 1 * codifico mi proceso aquí oThermo.UpdateBar(lnContador) *opcionalmente cambiamos el titulo secundario mostrando “XX de NNN registros procesados”: oThermo.SetCaption(TRANSFORM(lnContador)+ “ de ” + TRANSFORM(oThermo.MaxValue) + “ registros procesados.”,2) ENDSCAN * al finalizar conviene liberar el objeto RELEASE oThermo
A continuación se muestra una corrida de este código. En la Figura 6 se puede ver un 40% de avance sobre el total:
Figura 6. Volver al texto.
Conclusión
A pesar de la aparente limitación en el manejo de controles visuales intrínsecos (es decir, controles propios o nativos) cuando se lo compara con otras ofertas de la competencia, VFP puede generar interesantes controles combinando las clases visuales propias del producto. Sin duda, con un poco más de imaginación, esta clase se puede mejorar significativamente, como por ejemplo, hacer uso de las rutinas gráficas para mostrar una barra de avance continuo, o bien utilizar un rectángulo shape y modificar su propiedad width de acuerdo a cómo avanza el proceso, etc. Como vemos, VFP exhibe siempre una notable flexibilidad al momento de ofrecer soluciones como la que brevemente comentamos aquí.
Vea también
Clase Barra de progreso con Visual FoxPro
Mostrar el porcentaje de ejecución de un comando SELECT o USE VIEW
Referencias
Artículo original: Una barra de progreso en VFP "puro" - Microsoft
Autor: Carlos Alejandro Pérez
Barra de progreso al Indexar
En las Fox Foundation Classes (FFC) hay un termometro hecho completamente con código de VFP (con tecnicas OOP), lo encuentras aqui:
Tools -> Component Gallery -> Fox Foundation Classes ->
Dialog -> Thermometer
Tambien puedes llamar al Component Gallery con el siguiente comando desde el Command Window:
DO (_gallery)
Una vez que estes el FFC de tu preferencia puedes hacer click con el boton secundario del mouse y escoger View Sample.
Enlaces de foros externos
Preguntas y respuestas sobre barras de progreso
[GUFA] Barra de progreso mientras indexo una tabla
FoxPro/Visual FoxPro - Barra de porcentajes
Hola a todos, alguien me puede orientar sobre una barra de proceso