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

7 de noviembre de 2016

Obtener la dirección MAC (Mac Address)

Como obtener la dirección MAC (dirección de la tarjeta Ethernet) ...

Uso:

direccion = MacAddress()
FUNCTION MacAddress
Local pGUID,rGUID,lnSize
Declare integer CoCreateGuid in 'OLE32.dll' ;
  string @pguid
Declare integer StringFromGUID2 in 'OLE32.dll' ;
  string rguid, string @lpsz, integer cchMax
pGUID=replicate(chr(0),16)
rGUID=replicate(chr(0),80)

If "5." $ OS() && 2000/XP
  Declare integer UuidCreateSequential in 'RPCRT4.dll'  string @ Uuid
  Return substr( iif( UuidCreateSequential(@pGUID) = 0 ;
    and StringFromGUID2(pGUID,@rGUID,40) # 0, ;
    StrConv(left(rGUID,76),6), "" ), 26,12)
Else
  Return substr( iif( CoCreateGuid(@pGUID) = 0 ;
    and StringFromGUID2(pGUID,@rGUID,40) # 0, ;
    StrConv(left(rGUID,76),6), "" ), 26,12)
Endif
Cetin Basoz