25 de enero de 2017

Control Grid de Visual FoxPro - un truco y una clase calendario

Control Grid de Visual FoxPro - un truco y una clase calendario

Artículo original: Visual FoxPro Grid – A Tip and a Calendar Class
http://www.sweetpotatosoftware.com/blog/index.php/2005/08/20/visual-foxpro-grid-a-tip-and-a-calendar-class
Autor: Craig Boyd
Traducido por: Ana María Bisbé York


En mi opinión, el control grid de Visual FoxPro, por momentos ha sido poco valorado. Pienso que es probablemente uno de los controles más poderosos y útiles con los que he tenido el placer de trabajar. Las cosas que son capaces de hacer son sencillamente increíbles. Puede tener varios controles en una columna o incluso colocar un grid dentro de otro grid, de tal forma tiene filas y columnas que se interceptan en celdas representando muchos registros.

En esa entrada, deseo mostrarle una característica conocida del control grid. Si sabe de esta característica, entonces es uno entre un puñado. Cuando se refrescan los grids o si se repintan va a acceder al fondo (backstyle) de cada control que contiene en las columnas, y cualquier cosa que haga en los controles individuales va a ser mostrado en el grid.

Ayuda del FoxTeam de Microsoft

Permítame aclarar... digamos que tenemos un grid con una única columna y que esa columna contiene Textbox1. Si establece el backcolor del Textbox1 a través del código, digamos, el color Rojo, luego cada celda mostrada en la columna1 será roja. Entonces, cómo obtener colores dinámicos (celdas individuales coloreadas de forma diferente dentro de la misma columna)? Bien, el Fox Team de Microsoft nos brinda algunas propiedades dinámicas del objeto columna que va a actuar sobre celdas individuales ((DynamicAlignment, DynamicBackColor, DynamicCurrentControl, DynamicFontBold, DynamicFontItalic, DynamicFontName, DynamicFontOutline, DynamicFontShadow, DynamicFontSize, DynamicFontStrikeThru, DynamicFontUnderline, DynamicForeColor, y DynamicInputMask). Estas son muy utilizadas y en el escenario que brindo DynamicBackColor trabajará muy bien para cambiar el backcolor de una celda individual dentro de una columna del grid. Pero ¿Y si desea hacer algo más complejo? ¿Y si tiene un contenedor en la columna y el contenedor contiene múltiples objetos y desea establecer sus propiedades forecolor y backcolor a colores completamente diferentes dinámicamente o si desea mostrar diferentes imágenes dentro de las celdas del grid?

Soluciones ingeniosas y frecuentes de los desarrolladores

Un enfoque a estos problemas es utilizar múltiples controles dentro de las columnas del grid y luego, utilizar DynamicCurrentControl para decidir cuál mostrar. Algunos controles textbox que tienen fondo rojo y otros que tienen fondo blanco, o diferentes controles image configurados con figuras diferentes.

Otro método ingenioso de algunos desarrolladores Visual FoxPro al solucionar este problema es subclasear el objeto columna y luego enganchar en una propiedad dinámica de la columna que no se utilice para otros propósitos (por ejemplo, DynamicForeColor) Si el ForeColor es igual a 1, entonces hace esto, si es 2 entonces se hace otra cosa y si es 3 .... y etcétera. Aún cuando este enfoque y los dos precedentes son válidos, hay otra vía.

Otra vía

Como se ha visto antes, a la propiedad BackStyle se accede desde el CurrentControl en una columna, y no se accede sólo una vez, se accede por cada celda visible del grid. Entonces, utilizando esto podemos hacer la cercarnos a lo que queremos: un formateo dinámico y mostrar. ¿Desea mostrar imágenes diferentes? sólo hay que crear una subclase del contenedor y colocamos un control image dentro y lo colocamos en una columna. Luego, el método backstyle_access (necesitará añadir este método access al contenedor subclaseado), vea el valor de la propiedad Picture de la imagen en un campo en el RecordSource que guarda todos los caminos diferentes en el método backstyle_access de la imagen.

this.picture = crsImages.Paths

He aquí un ejemplo incluido en el archivo de descarga download. Cuando ejecute el ejemplo se le pedirá una carpeta que contiene las imágenes. Si no tiene archivo disponible, puede seleccionar la carpeta del proyecto ya que he incluido la imagen que se ve en la figura. Esta solución es más eficiente que tener un control image en la columna por cada imagen que desea mostrar en el control grid... 100 imagenes = 100 controles imagen? Lo acabo de solucionar con solo uno..

Clase calendario y un ejemplo del mundo real

Bueno, ¿qué tal si hacemos algo de la vida real?¿Qué tipo de cosas se pueden hacer al alterar dinámicamente elementos dentro de un contenedor utilizando para ello el backstyle_access? Todo tipo de cosas. Para demostrar un ejemplo útil, he colocado juntos algunos ejemplos de calendarios. He creado una clase calendar empleando el control grid. Esto debe demostrar el poder del empleo de backstyle_access en un grid, por no mencionar el poder de Visual FoxPro. Debajo está el enlace para la descarga del código fuente del ejemplo y algunas capturas de pantalla (para que sepa lo que se va a encontrar). Hay también un ejecutable en la carpeta fuente para la ejecución de los ejemplos, o si prefiere, puede ejecutar los formularios individualmente. Los ejemplos fueron creados en VFP 9.0, así que si desea utilizarlo en una versión anterior debe modificar el código, o lo que es aún mejor, actualizar su versión. Hay muy buenas cosas que ver. ¡Me encanta Visual FoxPro!

Descargar ejemplo y código de VFP Calendar (91 KB)
http://www.sweetpotatosoftware.com/files/vfpcalendar.zip

Un calendario sencillo

Demuestra algunos de las características avanzadas de la clase calendar

Controles Date y DateTime mejorados, creados empleando la clase calendar

15 de enero de 2017

Unir archivos PDF (Merge PDFs) mediante Ghostscript

A veces se requiere unir varios archivos PDF en un solo archivo, esto es posible mediante algunos software de manera manual, sin embargo es necesario establecerlo de manera automática para los usuarios. Después de probar varias alternativas quede con las siguientes líneas, lo cual es lo básico, ustedes podrán explotarles más opciones.

La solución de la herramienta Ghostscript es posible. Esta solución está disponible para 32Btis y 64Bits. En este ejemplo utilizaremos la de 64, mediante el archivo gswin64c.exe

folderactual = Sys(5) + Curdir()

* Armanos el BAT que lanzara el Script Merge
TEXT TO cComando TEXTMERGE NOSHOW PRETEXT 15
<<m.folderactual>>bin\gswin64c.exe -dBATCH -dNOPAUSE -dCompatibilityLevel=1.4 -sDEVICE=pdfwrite -sOutputFile="merge.pdf" "pdf1.pdf" "pdf2.pdf" "pdf3.pdf"
ENDTEXT

cArchivo = folderactual + 'Merge.bat'

* Creamos el archivo BAT
Strtofile(cComando, cArchivo)

* Ejecutamos el archivo BAT
oShell = Createobject("WScript.Shell")
oShell.Run(cArchivo,0,.T.)
Messagebox('Proceso de fusión realizado por éxito!',64,"")

* Abrimos el Archivo fusionado
cArchivo = folderactual + 'Merge.pdf'
oShell.Run(cArchivo,0,.T.)

*--- Eliminamos el archivo backup.bat
Delete File folderactual + 'Merge.b

Descarga el proyecto de ejemplo: Merge_PDF.rar

Enlaces:

https://es.wikipedia.org/wiki/Ghostscript
https://ghostscript.com/download/gsdnld.html

Nota: Descargar Ghostscript AGPL Release e instalarlo en la PC usuario

Lic. Allan Raul Acuña
Analista Programador
Managua, Nicaragua

11 de enero de 2017

Clase Barra de progreso con Visual FoxPro

Artículo original: Visual FoxPro Progress Bar Class
http://www.sweetpotatosoftware.com/SPSBlog/PermaLink,guid,87d20512-82d6-4ab2-827f-13a1bb5bbbf4.aspx
Autor: Craig Boyd
Traducido por: Ana María Bisbé York


Las barras de progreso se encuentran por doquier

Ya he creado antes una barra de progreso con Visual FoxPro (¿y quien no?). La última vez que hice una, la subí a la sección de descargas (DownLoads) de Universal Thread http://www.universalthread.com. Esta es una barra de progreso COM que permite indicar el progreso suavemente continúa incluso cuando Visual FoxPro está ocupado en algún tema candente o en una sencilla línea de código. Pero en el proyecto de hoy, voy a crear una sencilla barra de progreso que pudiera ser colocada en un contenedor (formulario o lo que sea).

Otra clase de barra de progreso

Yo quería que esta barra de progreso luciera verdaderamente profesional. Deseaba además, que su control sea redimensionado, por el desarrollador en tiempo de diseño, a cualquier tamaño y ancho . Luego deseo que sea una barra sólida o crear bloques individuales como los que hemos visto en las barras estándar de Windows XP y deseaba tener la posibilidad de mostrar el porcentaje completo con una etiqueta que pudiera cambiar de color como la barra de progreso que tiene un color en una mitad (es decir la mitad, 50% de un color y 50% de otro color.) Finalmente que sea capaz de mostrar colores diferentes (verde como en Windows XP, también rojo y azul.) He aquí algunos aspectos que he visto en otras barras de progreso, y siento que Visual FoxPro podía utilizar también.

Propiedades para una barra de progreso:

barcolor = El color que desea que tenga la barra: 1 = Rojo, 2 = Verde, 3 = Azul (predeterminado es 2)
min = El valor que se considera es 0% (predeterminado es 0)
max = El valor que se considera es 100% (predeterminado es 100)
percentage = El porcentaje completo basado en el valor actual asignado (predeterminado es 0)
showpercentage = Si se debe mostrar el porcentaje al usuario (predeterminado igual a .F.; mejor utilizado cuando solidbar es .T.)
solidbar = Si la barra de progreso debe mostrarse como una barra sólida en lugar de bloques (predeterminado es .F.)
value = El valor actual del progreso (predeterminado en 0; debe entrar en el rango entre el mínimo y el máximo)

Otras notas de desarrollo

El gradiente de la barra de progreso fue creado agregando líneas variando dinámicamente el grado del color. El porcentaje mostrado a través de la barra de progreso fue facilitado estableciendo la propiedad drawmode de las líneas de 14 - Líneas discontinuas.

Bajar el proyecto

Puede ejecutar progressbarex.exe o puede abrir el proyecto y ejecutar example.scx. Todo está incluido en el archivo. He aquí el enlace de descarga y una captura de la pantalla del ejemplo incluido ...

Descargar ejemplo y código de ProgressbarEx (26 KB)
http://www.sweetpotatosoftware.com/files/progressbarex.zip

5 de enero de 2017

Controlar dinámicamente los datos de un Grid

Artículo original: Controlling grid data dynamically
http://www.ml-consult.co.uk/foxst-20.htm
Autor: Mike Lewis
Traducido por: Ana María Bisbé York


¿Cómo le puede dar a sus usuarios mayor control sobre los contenidos de un Grid en Visual FoxPro?

Supongamos que desea crear un formulario como el que se muestra en la figura 1. Como ve, utiliza un Grid para mostrar los datos de una tabla Productos. Los usuarios pueden controlar el contenido del Grid de las siguientes formas:

  • Pueden limitar los registros a mostrar en el Grid según la categoría seleccionada.
  • Pueden escoger cuál de los dos campos  - el nombre en Inglés o el nombre original - es el que va a aparecer en la columna Description.
  • Pueden estipular el orden para el Grid.

Figura1: Tres formas para que el usuario controle el Grid

Luego de hacer estas selecciones, el usuario presiona el botón Refresh. Los datos del Grid cambian para refrescar según la selección del usuario.

Está claro, ¿verdad? Entonces, ¿cómo podemos crear este formulario?

Primeras ideas

La primera idea pudiera ser acceder al dato por medio de una vista local. Esto suena razonable, ya que usted puede modificar el contenido de una vista parametrizada. Se puede hacer en la cláusula WHERE de una vista, de esta forma:

WHERE Products.Category = ?lcCat

Aquí, lcCat es una variable que guarda la categoría escogida por el usuario. Si especifica entonces la vista como RecordSource del Grid, el dato se filtrará por la categoría requerida tantas veces como se invoque la vista. La llamada a la función REQUERY() va en el evento Click del botón Refresh del formulario.

Hasta aquí todo bien, en lo relativo a los filtros. Pero no es posible parametrizar los campos a mostrar en las columnas dadas ni el orden de la vista. Es posible recrear toda la vista programáticamente cada vez que el usuario presione el botón Refresh; pero esto no es una solución particularmente elegante. ¿Existe alguna forma más sencilla?

Intentar SQL SELECT

Utilizar una instrucción SQL SELECT para crear un cursor suena muy prometedor. Sin mucha dificultad, puede escribir un SELECT que represente las opciones de los usuarios, y que genera un cursor, que puede ser utilizado como el RecordSource del Grid.

Vamos a asumir que hemos configurado las siguientes variables:

  • lcCat contiene la categoría requerida.
  • llEnglish es .T. si el usuario escoge English como el lenguaje para la descripción del producto (en cuyo caso vamos a utilizar el campo eng_name como la segunda columna). Es .F. si el usuario desea verlo en el lenguaje original (para lo cual va a utilizar en su lugar el campo prod_name).
  • lcOrder contiene el número de la columna por la que se ordenará el dato (se almacena como cadena de caracteres).

El código en el botón Refresh puede tener este aspecto:

SELECT product_id,; 
  IIF(llEnglish,eng_name,prod_name) AS descript,;
  unit_price, in_stock ;
  FROM Products ;
  WHERE ALLTRIM(Category) = ALLTRIM(lcCat) ; 
  ORDER BY &lcOrder INTO CURSOR csrTemp
THISFORM.refresh

La instrucción SELECT envía el dato requerido al cursor, csrTemp. Este es el RecordSource para el Grid, entonces después que el formulario fue refrescado, el Grid  debe mostrar exactamente el dato que necesita el usuario. Problema solucionado.

No es tan sencillo

Desafortunadamente, no es tan sencillo. Si va a crear este formulario y ejecutarlo, el SELECT debía traer el dato correcto; pero el Grid aparecería como un rectángulo vacío. No se verían los datos.

La razón para este comportamiento no es difícil de ver. Siempre que utilice SELECT para crear un cursor de esta forma, Visual FoxPro destruye primero el cursor existente (si existe), luego, construye completamente uno nuevo. El Grid se desestabiliza con esto, ya que no desea perder el RecordSource, ni siquiera por un pequeño instante. Debido a que los controles dentro del Grid, están enlazados al cursor, destruyendo el cursor se destruyen los controles dentro del Grid, por eso se ve el rectángulo vacío.

Existirá alguna diferencia si utilizamos una tabla física en lugar de un cursor para la salida del SELECT? No, no habrá diferencia alguna.

La solución

Sin embargo, una vez que se entiende lo que ocurre, no es difícil idear una solución. El truco es crear un segundo cursor como el RecordSource, y mover los datos desde el primer cursor (aquel creado por el SELECT) al segundo cursor, el que el usuario desea actualizar en el Grid.

Vamos a colocar el siguiente código en el evento Load del formulario:

CREATE CURSOR csrProducts ; 
  ( product_id C(6), descript C(40), ;
  unit_price N(6,2), in_stock N(6) )

Esto va a crear un cursor, llamado crsProducts, con la misma estructura que el generado por SELECT. Establezca este cursor como RecordSource del Grid.

En el botón Refresh, mantenga el SELECT tal y como lo tenía antes; pero agregue algo de código para copiar el contenido del cursor generado por ese SELECT (csrTemp) en un cursor nuevo (csrProducts). El código entonces sería así:

SELECT product_id, ;
  IIF(llEnglish,eng_name,prod_name) AS descript,;
  unit_price, in_stock ;
  FROM Products ;
  WHERE ALLTRIM(Category) = ALLTRIM(lcCat) ; 
  ORDER BY &lcOrder INTO CURSOR csrTemp
SELECT csrProducts 
ZAP 
APPEND FROM DBF("csrTemp")
THISFORM.refresh

El efecto de esto es copiar los resultados del SELECT en csrProducts. Después que se ha refrescado el formulario, el dato se mostrará correctamente en el Grid.

Vea que no puede utilizar el comando COPY TO para transferir los datos a csrProducts, debido a que ese comando crea un archivo nuevo. En su lugar, necesita limpiar (ZAP) el contenido existente de csrProducts y agregar los datos nuevos. Observe además, el uso de la función DBF(). Esto es necesario debido a que el comando APPEND FROM puede solamente copiar datos desde una tabla física. DBF() devuelve la ruta y el nombre del archivo real que guarda el cursor.

Un detalle final al que debe prestar atención. Probablemente desee que el Grid muestre algún dato inicialmente cuando aparece el formulario por primera vez. Este dato debe basarse en los valores predeterminados para las tres selecciones del usuario. Para lograr esto, simplemente agregue código al Init del formulario para hacer el Select y para abrir los resultados en csrProducts. Por supuesto, va a necesitar además código para configurar las variables utilizadas en el SELECT (lcCat, llEnglish and lcOrder),  pero dejaremos esto como ejercicio para el lector.

Agradecimientos

La técnica que se ha descrito está basada en parte en la información del excelente libro "1001 Things You Always Wanted to Know About Visual FoxPro", por Marcia Akins, Andy Kramek y Rick Schummer (Hentzenwerke, 2000).

Mike Lewis Consultants Ltd. Septiembre 2001