12 de diciembre de 2006

La forma más fácil y rápida de crear imágenes degradadas

Artículo original: Creating Gradient Images the Fast and Easy Way
http://doughennig.blogspot.com/2007/02/creating-gradient-images-fast-and-easy.html
Autor: Doug Hennig
Traducido por: Ana María Bisbé York

He estado intentando últimamente embellecer algunos formularios. Una vía para hacer un formulario atractivo es utilizar algún elemento gráfico. En particular, me gustan las imágenes degradadas, como se ilustra en esta imagen de la pantalla (el degradado aparece vinculado a la imagen; pero no en el formulario real):



Entonces, ¿cómo crear una imagen con color degradado de los colores y tamaño deseados? Aunque yo podría hacerlo dinámicamente utilizando la biblioteca VFPX GDIPlusX, como ha descrito Cesar Ch. en varias entradas de su excelente Blog, he decidido simplemente crear un archivo imagen y emplearlo como la propiedad Picture de un objeto Image.

Con alguna ayuda del blog de Cesar, he creado un programa sencillo, CreateGradient.PRG, que genera una imagen degradada con el archivo especificado, tamaño y colores. El color se auto-explica. Tenga en cuenta que se espera que la librería de clases GDIPlus estén en la raíz de VFP.

lparameters tcFileName, ;
  tnHeight, ;
  tnWidth, ;
  tnColor1, ;
  tnColor2
local lnColor1, ;
  lnColor2, ;
  lnMode, ;
  lnFormat, ;
  loSystem, ;
  lcExt, ;
  loBitmap, ;
  loRect, ;
  loGfx, ;
  loGradBrush

* Asegúrese de que fueron pasados los parámetros.

if vartype(tcFileName) <> 'C' or empty(tcFileName) or ;
  vartype(tnHeight) <> 'N' or vartype(tnWidth) <> 'N'
  error 11
  return .F.
endif vartype(tcFileName) <> 'C' ...

* Si no se han pasado los colores, pídalos.

if pcount() < 4
  lnColor2 = getcolor(rgb( 0, 128, 255))
  lnColor1 = getcolor(rgb(255, 255, 255))
else
  lnColor1 = tnColor1
  lnColor2 = tnColor2
endif pcount() < 4

* Esto asume un degradado horizontal. 
* Configure lnMode a un valor diferente para los otros tipos.

lnMode = 0

* Crea un objeto GDIPlusX System.

loSystem = newobject('xfcSystem', 'System.vcx')
with loSystem.Drawing

  * Determina el tipo de la imagen de la extensión del archivo.

  lcExt = upper(justext(tcFileName))
  do case
    case lcExt = 'PNG'
      lnFormat = .Imaging.ImageFormat.Png
    case lcExt = 'BMP'
      lnFormat = .Imaging.ImageFormat.Bmp
    case lcExt = 'GIF'
      lnFormat = .Imaging.ImageFormat.Gif
    case inlist(lcExt, 'JPG', 'JPEG')
      lnFormat = .Imaging.ImageFormat.Jpeg
    case lcExt = 'ICO'
      lnFormat = .Imaging.ImageFormat.Icon
    case inlist(lcExt, 'TIF', 'TIFF')
      lnFormat = .Imaging.ImageFormat.Tiff
    case lcExt = 'WMF'
      lnFormat = .Imaging.ImageFormat.Wmf
    otherwise
      error 11
      return .F.
  endcase

  * Crea un objeto bitmap y un objeto rectangle del tamaño deseado.

  loBitmap = .Bitmap.New(tnWidth, tnHeight)
  loRect = .Rectangle.New(0, 0, tnWidth, tnHeight)

  * Crea un objeto graphics.

  loGfx = .Graphics.FromImage(loBitmap)
  loGfx.Clear(.Color.White)

  * Crea una brocha de degradado lineal.

  loGradBrush = .Drawing2D.LinearGradientBrush.New(loRect, ;
    .Color.FromRgb(lnColor1), .Color.FromRgb(lnColor2), lnMode)

  * Llena el rectángulo con la brocha de degradado lineal.

  loGfx.FillRectangle(loGradBrush, loRect)

  * Guarda la imagen con el archivo especificado.

  loBitmap.Save(tcFileName, lnFormat)

endwith
return

Gracias a Craig Boyd, Bo Durban, y Cesar por todo el trabajo que han hecho en esta increíble biblioteca de clases.

11 de diciembre de 2006

Leer y modificar valores de cabecera de tabla para un campo autoincrementado

A partir de VFP 8 podemos usar campos enteros autoincrementados automáticamente, aún en tablas libres. (Siempre está la opción de hacer uso de los triggers en tablas vinculadas a una DB, pero hay que armarlo...)

VFP nos informa en que lugar de la cabecera de la tabla coloca el próximo valor y el del incremento a usar en este tipos de campos. En esta oportunidad acerco una rutina que permite leer / modificar los valores en la cabecera de la tabla para un campo autoincrementado.

El programa de ejemplo, que tomé ex-profeso de una versión que alguien ofreció para indicar como modificar el valor y/o el incremento a través del comando SQL ALTER TABLE, es válido y funciona Ok si la tabla puede abrirse en modo exclusivo.

Si por algún motivo (lo más común en un entorno multiusuario) tenemos la tabla abierta en modo compartido y queremos modificar el próximo valor y/o el incremento, esta rutina lo permite (así como leer los valores actuales).
Nota: Para que el ejemplo funcione correctamente se debe descargar la librería LeeIncPtr.dll (incluida en el archivo comprimido junto al programa autoincrement_altera.prg) haciendo clic en el siguiente enlace: AutoIncrement.zip (8,77 KB).
******************************************
* AutoIncrement_Altera.prg
******************************************
Declare Long LEEINCRE In "LeeIncPtr.DLL" ;
  String @ Nom_tabla, String  @ Paso, ;
  String @ Campo_autoinc
Declare Long PONEINCRE In "LeeIncPtr.DLL" ;
  String @ Nom_tabla, Long @ Valor_prox, ;
  String  @ Paso, String @ Campo_autoinc

* La que sigue es la rutina "casi original",
* modificada para forzar una apertura compartida
#Define CRLF Chr(13)+Chr(10)
Local Proximo As Long, Paso As String
Set Exclusive Off
Local lcStr As String
Local lnSelect As Integer

* Save environment and erase auto increment table.
lnSelect = Select()
Select 0
Erase AI_Table.Dbf

* Create Auto Increment table setting the
* 'iID' column as an auto increment field
* with the starting value set to 1 and the
* increment value set to 1.
Create Table AI_Table Free ;
  ( iID i Autoinc Nextvalue 1 Step 1, ;
  CustName c(30))
Close Database All && Agregado
Use In 0 "AI_Table" Shared  && Agregado
* Insert three records into the table.
* You do not assign any values to
* the auto increment field.
Insert Into AI_Table (CustName) Values ("Jane Smith")
Insert Into AI_Table (CustName) Values ("John Doe")
Insert Into AI_Table (CustName) Values ("Greg Jones")
Go Top

* Browse the table.
Browse Nowait

lcStr = "Auto Increment Table created with the " + ;
  "starting value set to 1 and the increment set to 1"
Messagebox(lcStr)

* Alter the table to set the next auto increment value
* inserted to be 100 and incrementing step to be 10.

* Se agregó rutina de error y uso de la librería
Ok = .T.
Try
  Alter Table AI_Table Alter Column iID i Autoinc Nextvalue 100 Step 10
Catch To oErr
  Messagebox("Catch: " + Transform(oErr.ErrorNo) + ;
    Chr(10) + oErr.Message + Chr(10) + ;
    "No puede usar en este modo la sentencia: " + ;
    Chr(10) + oErr.LineContents + Chr(10) + ;
    "Se intentará modificar con la librería", ;
    48,"Error")
  Ok = .F.
Finally
Endtry
If !Ok
  Tbl = Dbf()
  Proximo = 100
  Paso = Chr(10)
  Campo = "iID"
  If PONEINCRE(@Tbl, @Proximo, @Paso, @Campo) <> 1
    Messagebox("Error procesando con PONEINCRE", 48, "Error")
    Return
  Endif
  * Debemos cerrar y volver a abrir la tabla,
  * para que VFP actualice internamente el Step
  Use In ("AI_Table")
  Use In 0 "AI_Table" Shared
Endif
* Insert three more records into the table.
Insert Into AI_Table (CustName) Values ("Jay Lewis")
Insert Into AI_Table (CustName) Values ("Steve Appleton")
Insert Into AI_Table (CustName) Values ("Ken Garvy")
Go Top

* Browse the table.
Browse Nowait

lcStr = "Auto Increment Table was altered to set " + ;
  "the next auto increment value " + ;
  "to 100 and the increment step to 10"
Messagebox(lcStr)

* Leemos como quedó ahora la tabla
Tbl = Dbf()
Campo = "iID"
Prox = LEEINCRE(@Tbl, @Paso, @Campo)
If Prox > 0
  Messagebox("Próximo valor: " + Transform(Prox) + ;
    Chr(10) + "Step: " + Transform(Asc(Paso)), ;
    64, "Estado autoincremento")
Endif

* Restore environment.
Use
Select (lnSelect)

Return
Espero pueda serles de utilidad.

Gustavo Devincenzi

5 de diciembre de 2006

Buscar direcciones postales en Google Maps desde VFP (II)

Buscar direcciones postales en Google Maps e imprimir el resultado, desde un formulario de Visual FoxPro.

Tras haber publicado el articulo Buscar direcciones postales en Google Maps con VFP que permitía mostrar el plano o imagen de satélite de una dirección postal, y viendo que el resultado permitía incorporarlo fácilmente a una aplicación, me puse a buscar la manera de poder imprimirlo.

De momento, la unica forma que he encontrado ha sido la utilizacion de una libreria llamada dibapi32.dll, que solo necesita ser copiada junto con el '.prg' que generemos con el codigo adjunto.
Para quien no disponga de ella, puede ser descargada desde La Web de Davphantom, donde el registro es gratuito.

Tambien he añadido un segundo parametro opcional, que podemos pasar al '.prg' y que correspondera al nombre o identificacion de la direccion buscada, como puede ser el nombre de un cliente, empresa o lugar conocido. Este segundo parametro no se utiliza en la busqueda, sino que se usa para mostrarlo en el 'globo' de información que se muestra sobre el mapa, una vez localizada la dirección.

Creo que eso es todo, asi que pasemos al codigo:
Do MiPrograma With "Paseo Castellana 142, Madrid", "Santiago Bernabeu"

* Con "do MiPrograma", se muestra la pantalla para introducir la direccion deseada.

*
* MiPrograma.prg
*
Procedure MiPrograma
  Lparameters miaddress, miname_id
  *
  Public oMiForm
  oMiForm = Createobject("MiForm", miaddress, miname_id)
  oMiForm.Show
  Return
Endproc

Define Class MiForm As Form
  Height = 560
  Width = 625
  AutoCenter = .T.
  Name = "MiForm"
  SetPoint = ""
  SetDecimals = 2
  ShowWindow = 1
  WindowType = 1
  TitleBar = 0
  BorderStyle = 2
  miname_id = ""

  Add Object Descrip As TextBox With ;
    HEIGHT = 24, Left = 12, Top = 12, Width = 330, MaxLength = 100, ;
    ForeColor = Rgb(88,99,124), BackColor = Rgb(255,255,255), ;
    SelectedForeColor = Rgb(255,255,255), SelectedBackColor = Rgb(88,99,124), ;
    STYLE = 0, Name = "Descrip", SelectOnEntry = .T., Enabled = .T.

  Add Object cmdMostrar As CommandButton With ;
    TOP = 10, Left = 350, Height = 27, Width = 100, ;
    CAPTION = "Mostrar mapa", Name = "cmdMostrar"

  Add Object cmdPrint As CommandButton With ;
    TOP = 10, Left = 500, Height = 27, Width = 60, ;
    CAPTION = "Imprimir", Name = "cmdPrint"

  Add Object cmdCerrar As CommandButton With ;
    TOP = 10, Left = 573, Height = 27, Width = 40, ;
    CAPTION = "Salir", Name = "cmdCerrar"

  Add Object oleIE As OleControl With ;
    TOP = 48, Left = 12, Height = 500, Width = 600, ;
    NAME = "oleIE", OleClass = "Shell.Explorer.2"

  Procedure Load
    Sys(2333,1)
    This.SetPoint = Set("Point")
    This.SetDecimals = Set("Decimals")
    Set Point To .
    Set Decimals To 8
    Set Safety Off
    Declare Integer ReleaseCapture In WIN32API
    Declare Integer SendMessage In WIN32API Integer, Integer, Integer, Integer
    Declare Integer GetFocus In WIN32API
  Endproc

  Procedure Init
    Lparameters miaddress, miname_id
    If Type('miname_id')<>'C'
      miname_id=''
    Endif
    This.miname_id = miname_id
    If Type('miaddress')<>'C'
      This.Descrip.Value=''
    Else
      This.Descrip.Value=miaddress
      Thisform.cmdMostrar.Click()
    Endif
  Endproc

  Procedure MouseDown
    Lparameters nButton, nShift, nXCoord, nYCoord
    Local lnHandle
    If nButton = 1
      ReleaseCapture()
      SendMessage(This.HWnd, 0x112, 0xF012,0)
    Endif
  Endproc

  Procedure cmdCerrar.Click
    Set Point To (Thisform.SetPoint)
    Set Decimals To (Thisform.SetDecimals)
    Thisform.Release
  Endproc

  Procedure cmdPrint.Click
    Thisform.PrintWindow(GetFocus(), "Imprimiendo ...")
  Endproc

  Procedure PrintWindow
    Lparameters tnHWnd, tcJobName
    *
    Local lcJobName    && Nombre de la tarea de impresion
    Local lnRetVal    && Valor de retorno de las funciones del API

    Declare Integer PrintWindow In DibApi32 ;
      INTEGER HWnd, ;
      INTEGER fPrintArea, ;
      INTEGER fPrintOpt, ;
      INTEGER wxScale, ;
      INTEGER wyScale, ;
      STRING @ szJobName

    *!* PW_WINDOW para imprimir la ventana entera
    *!* PW_CLIENT para imprimir el area cliente
    *!* Como ajustar la imagen
    *!* PW_BESTFIT se ajusta al papel pero se mantienen las proporciones
    *!* PW_STRETCHTOPAGE se ajusta para cubrir totalmente el papel pero distorsiona las proporciones
    *!* PR_SCALE escala el tamaño de impresion

    #Define PW_WINDOW 1
    #Define PW_CLIENT 2
    #Define PW_BESTFIT 1
    #Define PW_STRETCHTOPAGE 2
    #Define PW_SCALE 3

    lcJobName = tcJobName + Chr(0)
    lnRetVal = PrintWindow( tnHWnd, PW_CLIENT, PW_BESTFIT, 0, 0, @lcJobName)
    If lnRetVal != 0
      If lnRetVal != 6  && 6 = El usuario cancelo la impresión
        = Messagebox("Imposible Imprimir la ventana" + CRLF + ;
          "PrintWindow API retorno " + Str(lnRetVal), ;
          MB_ICONEXCLAMATION + MB_OK, ;
          "ERROR")
      Endif
    Endif
  Endproc

  Procedure cmdMostrar.Click
    If Empty(Alltrim(Thisform.Descrip.Value))
      Thisform.Descrip.SetFocus()
      Return
    Else
      *
      lcClave = "http://maps.google.es/maps?file=api&v=2.x&" + ;
        "key=ABQIAAAAtOjLpIVcO8im8KJFR8pcMhQjskl1-YgiA" + ;
        "_BGX2yRrf7htVrbmBTWZt39_v1rJ4xxwZZCEomegYBo1w"
      *
      TEXT TO lcHtml NOSHOW TEXTMERGE
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
    <title>Busqueda en Google Maps</title>
    <script src="<<lcClave>>" type="text/javascript"></script>
    <script type="text/javascript">
    //<![CDATA[
    var map = null
    var geocoder = null
    var address = "<<Strtran(ALLTRIM(ThisForm.Descrip.Value),'ñ','n',1,10,1)>>"

    function load()
    { if (GBrowserIsCompatible())
      { map = new GMap2(document.getElementById("map"),'G_SATELLITE_TYPE');
        map.addControl(new GLargeMapControl());
        map.addControl(new GMapTypeControl());
        map.addControl(new GOverviewMapControl());
        geocoder = new GClientGeocoder();
        if (geocoder) {
          geocoder.getLatLng(address,
        function(point)
        { if (!point)
          { alert("No Encontrado");
            }
          else
          { map.setCenter(point, 17);
            map.setMapType(G_MAP_TYPE);
            var marker = new GMarker(point);
            map.addOverlay(marker);
            marker.openInfoWindowHtml('<<Thisform.miname_id>>' + '<br>' + address);
            }
          }
        );
       }
      }
    }
    //]]>
    </script>
  </head>
  <body  scroll="no" bgcolor="#CCCCCC" topmargin="0" leftmargin="0" onload="load()" onunload="GUnload()">
  <div id="map" style="width: 100%; height: 100%"></div>
  </body>
</html>
      ENDTEXT
      *
      Strtofile(lcHtml,"MiHtml.htm")
      Thisform.oleIE.Navigate2(Fullpath("MiHtml.htm"))
    Endif
  Endproc

Enddefine

Hasta la próxima.

Jose Antonio Blasco, Zaragoza, España

1 de diciembre de 2006

Añadir controles (Widgets) a sus pantallas Visual FoxPro

Artículo original: Add Glass XP Widgets to your Visual Foxpro Screens
http://weblogs.foxite.com/bernardbout/archive/2006/11/04/2798.aspx
Autor: Bernard Bout
Traducido por: Ana María Bisbé York


Google, Yahoo, Vista ya lo tienen. Ahora hagamos que lo tengan Visual FoxPro y usted. Hablo de aplicaciones llamadas Widgets que puede utilizar para combinar con su pantalla.

Utilizar gráficos que sean soportado por VFP es una vía para crear Widgets muy interesantes. He descrito aquí cómo el lograr PNG con máscara alpha, puede ser utilizado para mostrar algunos efectos cristalinos como semi "formularios".

Ahora voy a describir cómo esto se puede extender para crear Widgets cristalinos. Una vez que ya tenemos los Widgets, y los hemos utilizado, la técnica descrita aquí le va a ofrecer un Widget Cristalino que podrá arrastrar, similar a Vista. ¡ Y lo mejor de todo es que se puede lograr en XP !
Para este escrito he creado un widget reloj. Puede descargar el código completo desde el archivo adjunto al final de este escrito.

El reloj es una clase sencilla VFP que va a auto ejecutarse al soltarla sobre un formulario y ejecutar el formulario. El truco detrás del efecto cristalino está en la imagen PNG de fondo de la clase:




Mientras que se ve como un viejo gráfico plano en blanco y negro, contiene una máscara de canal alpha embebido en el. El formato PNG admite guardar esta máscara en una imagen y el objeto imagen de VFP respeta y utiliza esta máscara. Si abre esta imagen en un programa Paint que admite máscara, como Photoshop, (Utilizo Paint Shop Pro) entonces, puede ver la máscara. Cuando esta imagen es cargada en un objeto imagen VFP, la máscara tiene efecto en ciertas áreas transparentes basadas en la información en la máscara.

Todo lo que queda es insertar un objeto imagen en un contenedor transparente y se completa el efecto. La clase reloj solamente agrega código a la fecha, año y movimiento de las manecillas. Está también el código en la clase imagen que permite dibujar el contenedor, de tal forma que el reloj se pueda mover. Verifique el código en los eventos Mouse de la clase objeto imagen.

El reloj se puede instanciar en el _Screen o, si está empleando un formulario de nivel superior como la base para su aplicación, como he descrito aquí y aquí, entonces pueden ser utilizadas esta técnica y clase.

Debajo puede ver el efecto Cristalino (Glass) del área exterior que permite que traspase la imagen de fondo. Esta área se utiliza también para arrastrar y soltar el reloj en una nueva localización. Los colores pueden cambiarse sencillamente ya que yo he incluido todo el código. Ejecute el EXE y arrastre el reloj. Tenga cuidado cuando lo suelte, no va a querer romper el cristal ! :)

Aquí está el reloj corriendo dentro de un formulario de nivel superior.



¡ Aquí estoy logrando sus Widgest propios para VFP !

Adjunto: 2798_clockwidget.zip

30 de noviembre de 2006

Cómo trabaja FoxPro internamente

Artículo original: How FoxPro works internally
http://www.foxpert.com/docs/howfoxproworks.en.htm
Autor: Christof Wollenhaupt
Traducido por: Fernando D. Bozzo

FoxPro desde adentro

Érase una vez, que el xBase no era un lenguaje de programación, era una herramienta para recuperar y para manipular automáticamente datos. Los usuarios de las herramientas xBase no eran principalmente desarrolladores; eran expertos en una variedad enorme de diversas áreas que utilizaban FoxBase, dBase, y herramientas similares para manejar sus datos. El xBase fue mejorado constantemente y finalmente desarrollado como un verdadero lenguaje de programación. FoxPro se convirtió en un entorno profesional de desarrollo que alcanzó sus alturas con FoxPro 2.5/2.6. En 1995 el paradigma de la herramienta cambió otra vez. Un lenguaje de programación procedural se convirtió en una herramienta orientada a objetos que continúa desarrollándose como un diseño basado en componentes.

Visual FoxPro no es sólo un entorno orientado a objetos como Delphi o Visual Basic.NET. Visual FoxPro todavía contiene sus raíces. Sólo intente hacer funcionar un programa de Turbo PASCAL 3.0 en Delphi 7.0. ¿Qué tal sus programas de GW-BASIC en Visual Basic.NET? ¿Pero Foxbase? Hasta hoy puede hacer funcionar código sin cambios en Visual FoxPro que ha escrito en los años ochenta. La salida por pantalla no luce tan agradable, pero todavía puede ejecutar código en VFP 8 casi 20 años después de que lo ha escrito.

Visual FoxPro es casi totalmente compatible hacia atrás. Pensando sobre esto, significa que mucho del código en FoxPro y FoxBase son todavía parte de Visual FoxPro. Esto significa que la orientación a objetos se sienta encima de FoxPro, y no viceversa. Muchos comportamientos extraños de Visual FoxPro llegan a ser solamente explicables si piensas cómo habrías hecho algo en FoxBase, sólo para darse cuenta de que Visual FoxPro no lo hace distinto.

Una advertencia por adelantado: Los siguientes artículos intentan describir cómo trabaja internamente Visual FoxPro. Los interiores reales de FoxPro son propiedad intelectual de Microsoft y no se divulgan públicamente. Cada uno de los que realmente saben como trabaja FoxPro internamente esta impedido para hablar de esto firmando un Acuerdo de No-Divulgación (NDA). He recogido la siguiente información de una variedad de fuentes públicas. Cierta información está en la biblioteca MSDN que Microsoft publica trimestralmente (algunos items existen solamente en versiones más viejas de la biblioteca MSDN). Otra información viene del código de ejemplo que Microsoft envía. La mayoría de las piezas, sin embargo, vienen de pruebas y de observaciones, no sólo de mí, sino de muchos, muchos desarrolladores en varios foros. Especialmente las diferencias en el comportamiento de varias versiones permiten hacer conclusiones de la estructura interna de VFP. Algunas de las estructuras siguientes se han extendido en la versión más reciente de FoxPro.

El índice de la tabla de nombres (NTI)

En Visual FoxPro podemos nombrar varios items. A esos items, Visual FoxPro les asigna algo llamado un nombre. Estos items son variables (no propiedades), nombres de matrices, procedimientos, funciones, alias de tablas, nombres de campo y objetos (no clases). Cada uno de estos elementos tiene una etiqueta y un alcance. La visibilidad (alcance) de una variable, por ejemplo, depende de su tipo, mientras que el alcance de un alias es la sesión de datos. Un nombre de campo debe ser único en una tabla y los procedimientos son limitados en alcance a un archivo de programa.

Siempre que se crea una variable, se abre una tabla, y así sucesivamente, Visual FoxPro crea una nueva entrada en una lista interna, la Tabla de Nombres. La posición dentro de esta lista es el Índice de la Tabla de Nombres - o NTI para abreviar. En esta lista, a cada nombre se el asigna un número único entre 1 y 65.000, porque se mantiene como valor de 16-bits. Hay sólo una lista global. Esta es la razón por la cuál se pueden crear hasta 65.000 variables solamente. Puesto que los alias y los nombres de objetos (hasta Visual FoxPro 6.0) también se incluyen en esta lista, el número real de variables es reducido por el número de objetos instanciados y áreas de trabajo asignadas.

El manejo de esta lista se ha optimizado en las distintas versiones de FoxPro. Cuando se libera un nombre cerrando una tabla o porque no se necesita más una variable, Visual FoxPro no quita la entrada inmediatamente. Solamente marca la entrada como inválida, como se hace con los registros eliminados.

Los items finalmente son quitados por un proceso llamado Garbage Collection (recolección de basura). Este término se refiere al proceso de quitar entradas inválidas de las listas, liberar entradas desactualizadas de la caché, compactar la memoria moviendo bloques de memoria alrededor, comprobando el acceso a los ficheros temporales, y así sucesivamente. Visual FoxPro ejecuta la recolección de basura en su bucle ocioso (idle loop). Se entra en este bucle siempre que Visual FoxPro está en una condición de espera causada por READ, READ EVENTS, INKEY() o WAIT WINDOW. Esto sigue siendo cierto si se utiliza el comando con las opciones de no esperar (NOWAIT). Se puede utilizar este truco para forzar a Visual FoxPro a limpiar la memoria usando un WAIT WINDOW "" NOWAIT.

No fue hasta Visual FoxPro 7.0 que SYS(1104) se hizo en la documentación aún cuando la función está disponible desde FoxPro 2.6, por lo menos. SYS(1104) dispara manualmente una recolección de basura. No es lo mismo que el bucle ocioso, aunque, como en el bucle ocioso Visual FoxPro hace más que ejecutar la recolección de basura, como adicionalmente procesar los mensajes de Windows. Durante la ejecución del programa, Visual FoxPro no está en un bucle ocioso y por lo tanto no realiza una recolección de basura. Hasta Visual FoxPro 5.0 esto tenía el efecto de que más y más entradas en la tabla de nombres han estado marcadas como inválidas, pero no se liberaban.

Esto tenía consecuencias de gran envergadura en la performance, pero también en la estabilidad. Cada vez que una nueva entrada se agrega a la tabla de nombres, Visual FoxPro tiene que buscar todas las entradas existentes para encontrar nombres conflictivos. Para las variables esto implica comprobar si existe una nueva variable con un alcance más bajo, porque no se puede, por ejemplo, crear una variable PÚBLICA cuando existe una variable LOCAL con el mismo nombre. Para los alias esto implica verificar que el nombre del alias no esté usado en la sesión actual de datos. Este proceso de búsqueda ha causado la degradación exponencial de la performance. Mientras que se podría medir la creación del primer objeto en milisegundos o incluso nanosegundos, a Visual FoxPro le tomó varios minutos para crear el objeto 60,000.

Las aplicaciones que nunca alcanzaban un estado ocioso, como las rutinas de importación o los proveedores de servicio, se hacían más lentos cuanto más tiempo funcionaban. Algunas funciones, también, exigían una nueva entrada en la tabla de nombres, como la función REQUERY al recargar una vista. La desaceleración no era el único problema. Cuanto más se acercaba una aplicación al límite, más inestable se volvía. Si se era afortunado, Visual FoxPro indicaba un error de "demasiados nombres", pero generalmente simplemente se colgaba.

Visual FoxPro 6.0 mejoró perceptiblemente este comportamiento. Cuando el número de items en la tabla de nombres se acerca al 95% del límite, Visual FoxPro inicia automáticamente una recolección de basura. En un programa que crea 65.000 objetos se nota como un descanso más largo al crear ese objeto.

Una mejora importante vino en Visual FoxPro 7.0. Todas las versiones anteriores contaron objetos.


28 de noviembre de 2006

Buscar direcciones postales en Google Maps con VFP

Como buscar direcciones postales en Google Maps desde un formulario VFP, mostrando mapas e imágenes de satélite.

Hace unos días, Luis María Guayán publicó el artículo Google Maps en un formulario de VFP que mostraba un ejemplo de como acceder a diferentes localizaciones en Google Maps mediante sus coordenadas terrestres

Al ver y probar el código publicado, enseguida me surgió la inquietud de adaptarlo a una búsqueda por la dirección postal en lugar de por sus coordenadas que, normalmente, no conozco.

Luis María me remitió a otro artículo Integrando VFP con Microsoft Virtual Earth que mostraba un ejemplo muy completo de como realizar búsquedas (y muchas más cosas) sobre Virtual Earth.
Como el código de Luis María era más sencillo (y se ajustaba mejor a mis necesidades), decidí revisar la API de Google Maps para implementar la búsqueda por la dirección postal.

Antes de pasar al código, debo hacer varias aclaraciones sobre éste:

En primer lugar, este código puede ser ejecutado con do "MiPrograma.prg" con lo que, al abrirse el formulario, podremos introducir la dirección a buscar, o bien podremos ejecutarlo con do "MiPrograma.prg" with "direccion a buscar", con lo que, al abrirse el formulario, se realizará la búsqueda de la dirección introducida directamente.

En segundo lugar, vereis que al ejecutar el código, el formulario abierto no tiene barra de título, pero esto no significa que no pueda ser movido. Sólo teneis que pinchar sobre una zona libre del formulario y arrastrar con el ratón. El truco reside en dos llamadas a la API en el Load del formulario y el evento MouseDown de este.

También, al hacer las diferentes pruebas, me encontré con que las búsquedas de direcciones que contenían "eñes" no se realizaban correctamente pero, si las sustituía por "enes", la búsqueda era correcta. Ese es el motivo de haber utilizado la función strtran, sólo a efectos de búsqueda.

Por último, mi agradecimiento a todas las personas que en el foro contestaron a mis dudas, y en especial a Luis María Guayán, sin cuya colaboración y apoyo, este mi primer artículo, no habría visto nunca la luz.

Y ahora, el código completo de este ejemplo es el siguiente:

Do MiPrograma With "Puerta de Alcala, Madrid, España"

*
* MiPrograma.prg
*
Procedure MiPrograma
  Lparameters MiAddress
  Public oMiForm
  oMiForm = Createobject("MiForm", MiAddress)
  oMiForm.Show
  Return
Endproc

Define Class MiForm As Form
  Height = 560
  Width = 625
  AutoCenter = .T.
  Caption = "Ejemplo con Google Maps"
  Name = "MiForm"
  SetPoint = ""
  SetDecimals = 2
  ShowWindow = 0
  TitleBar = 0
  BorderStyle = 2
  Closable = .F.
  MaxButton = .F.
  MinButton = .F.

  Add Object Descrip As TextBox With ;
    HEIGHT = 24, Left = 12, Top = 12, Width = 330, ;
    STYLE = 0, Name = "Descrip", SelectOnEntry = .T., Enabled = .T.

  Add Object cmdMostrar As CommandButton With ;
    TOP = 10, Left = 350, Height = 27, Width = 100, ;
    CAPTION = "Mostrar mapa", Name = "cmdMostrar"

  Add Object cmdCerrar As CommandButton With ;
    TOP = 10, Left = 573, Height = 27, Width = 40, ;
    CAPTION = "Salir", Name = "cmdCerrar"

  Add Object oleIE As OleControl With ;
    TOP = 48, Left = 12, Height = 500, Width = 600, ;
    NAME = "oleIE", OleClass = "Shell.Explorer.2"

  Procedure Load
    Sys(2333,1)
    This.SetPoint = Set("Point")
    This.SetDecimals = Set("Decimals")
    Set Point To .
    Set Decimals To 8
    Set Safety Off
    Declare Integer ReleaseCapture In WIN32API
    Declare Integer SendMessage In WIN32API Integer, Integer, Integer, Integer
  Endproc

  Procedure Init(MiAddress)
    If Type('miaddress')<>'C'
      This.Descrip.Value=''
    Else
      This.Descrip.Value=miaddress
      Thisform.cmdMostrar.Click()
    Endif
  Endproc

  Procedure MouseDown
    Lparameters nButton, nShift, nXCoord, nYCoord
    Local lnHandle
    If nButton = 1
      ReleaseCapture()
      SendMessage(This.HWnd, 0x112, 0xF012,0)
    Endif
  Endproc

  Procedure cmdCerrar.Click
    Set Point To (Thisform.SetPoint)
    Set Decimals To (Thisform.SetDecimals)
    Thisform.Release
  Endproc

  Procedure cmdMostrar.Click
    If Empty(Alltrim(Thisform.Descrip.Value ))
      Thisform.Descrip.SetFocus()
      Return
    Else
      *
      lcClave = "http://maps.google.es/maps?file=api&v=2.x&" + ;
        "key=ABQIAAAAtOjLpIVcO8im8KJFR8pcMhQjskl1-YgiA" + ;
        "_BGX2yRrf7htVrbmBTWZt39_v1rJ4xxwZZCEomegYBo1w" 
TEXT TO lcHtml NOSHOW TEXTMERGE
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
    <title>Busqueda en Google Maps</title>
    <script src="<<lcClave>>"
    type="text/javascript"></script>
    <script type="text/javascript">
    //<![CDATA[
    var map = null
    var geocoder = null
    var address = "<<Strtran(ALLTRIM(ThisForm.Descrip.Value),'ñ','n',1,10,1)>>"

    function load()
    { if (GBrowserIsCompatible())
      { map = new GMap2(document.getElementById("map"),'G_SATELLITE_MAP');
        map.addControl(new GLargeMapControl());
        map.addControl (new GMapTypeControl());
        map.addControl(new GOverviewMapControl());

        geocoder = new GClientGeocoder();
        if (geocoder) {
            geocoder.getLatLng(address,
          function(point)
          { if (!point)
            { alert("No Encontrado");
             }
          else
            { map.setCenter(point, 20);
            map.setMapType(G_NORMAL_MAP);
            var marker = new GMarker(point);
            map.addOverlay(marker);
            }
          }
         );
       }
      }
    }
    //]]>
    </script>
  </head>
  <body  scroll="no" bgcolor="#CCCCCC" topmargin="0" leftmargin="0" onload="load()" onunload="GUnload()">
  <div id="map" style="width: 600px; height: 500px"></div>
  </body>
</html>
ENDTEXT
      *
      Strtofile(lcHtml,"MiHtml.htm")
      Thisform.oleIE.Navigate2(Fullpath("MiHtml.htm"))
    Endif
  Endproc
Enddefine

Espero volver por aquí.

Jose Antonio Blasco, Zaragoza, España

27 de noviembre de 2006

Visual FoxPro muestra su transparencia

Artículo original: Visual Foxpro shows its Glass!
weblogs.foxite.com/bernardbout/archive/2006/10/28/2765.aspx
Autor: Bernard Bout
Traducido por: Ana María Bisbé York


Vista está casi aquí con su nuevo tema Aero Glass (transparencia) y sus nuevas APIs. Se ha mostrado ya que VFP puede correr en Vista y Craig Boyd ha mostrado cómo lograr algunos de los efectos de transparencia en un formulario VFP. Si no lo ha hecho, vea aquí:

www.sweetpotatosoftware.com/SPSBlog/Vista's Aero Glass for Visual FoxPro

Muchos de nosotros no nos habremos actualizado aun posiblemente hasta el SP1 o incluso después, ya que el PC más nuevo y poderoso lo necesitará y ¿Quién tiene tanto suelto como para además agregarle el coste del propio Vista? He investigado en Internet y he encontrado algunos sitios que piden simular los efectos de transparencia en XP. Incluso, he descargado un ejemplo en .NET que realmente realiza ese efecto; pero el formulario es tan lento que era imposible utilizarlo en la práctica. Pero cuidado, Visual FoxPro es rápido. Nosotros sabemos eso. Entonces, decidí intentar por mi cuenta.

¿Qué tal si tomamos un formulario cortamos porciones de el, y hacemos que las partes visibles sean transparentes y colocamos una forma opaca con objetos dentro de ella? Podría entonces controlar su ubicación, movimiento, etc. En una pantalla podría tener una apariencia de un formulario transparente. Entonces, allá vamos ...

(Puede descargar el código completo así como el artículo del enlace adjunto al final de este escrito)

Crear Mainfrom.scx

Comienzo con una forma normal. El formulario tiene configuradas las siguientes propiedades:
BackColour = RGB(64,64,64) 
BorderStyle = 0 - Sin Borde
ShowWindow = 2 - Como formulario de Nivel superior
TitleBar = 0 – Desactivado
El formulario tiene también algunas propiedades y Métodos los cuales se pueden ver en el ejemplo que se incluye. El Nombre del formulario es MainForm.

Luego agregué algunos elementos de diseño (skin) como se muestra:

 (el cual que está estirado) y  en la parte superior.

En el mismo centro coloqué una forma y fijé su color de fondo en RGB(0,255,0) (verde brillante)
Para tener las esquinas redondeadas coloqué 4 imágenes en las 4 esquinas las que tienen esta apariencia, 5 píxeles por 5 píxeles. (agrandado):

 para el borde superior izquierdo y  para el borde superior derecho.

Otros dos similares se colocaron al final.

23 de noviembre de 2006

Utilizar el canal Alpha en imágenes Visual FoxPro

Artículo original: Using the Alpha Channel in Visual Foxpro Images
http://weblogs.foxite.com/bernardbout/2006/09/10/using-the-alpha-channel-in-visual-foxpro-images/
Autor: Bernard Bout
Traducido por: Ana María Bisbé York


Utilizar el canal Alpha en imágenes PNG puede provocar efectos muy interesantes y agradables.
Estuve trabajando en algunos seudo formularios empleando máscaras de canal Alfa de imágenes PNG así que decidí intentar.

Actualizado el 12/09/2006 (vea debajo)

Creé las imágenes en Paint Shop Pro; pero cualquier aplicación para tratamiento de imágenes como Photoshop, etc puede crear máscaras que lo hagan. Al cargar un objeto imagen VFP, estas imágenes muestran muchos efectos interesantes, así que decidí invertir algún tiempo extra explorando este aspecto y las pantallas siguientes muestran el resultado que alcancé. El Titlebar.png es utilizado por la barra de título, mainform.png es utilizada para el área principal del formulario. Agregué Vistabuttons.bmp y coloqué sobre el un botón de comando invisible, para las acciones. Todo esto está colocado en un contenedor transparente en un objeto imagen VFP y los otros como etiquetas, cuadros de texto, etc están colocados encima. Pueden ser instanciados utilizando ADDOBJECT() sobre el formulario. Las imágenes pueden ser guardadas haciendo clic en el botón secundario del ratón y SavePictureAs (Guardar imagen como)

Titlebar.png


Mainform.png


VistaButtons.bmp



22 de noviembre de 2006

Formularios transparentes en Visual FoxPro al descubierto

Artículo original: Transparent Forms in Visual FoxPro Revealed
http://weblogs.foxite.com/bernardbout/2006/08/27/transparent-forms-in-visual-foxpro-revealed/
Autor: Bernard Bout 
Traducido por: Ana María Bisbé York


Cómo se ha hecho

Mi post anterior fue algo vago a propósito porque yo quería despertar un poco más de interés. Sólo describir el método utilizado para describir el efecto podría ser mucho más sencillo; pero el interés hubiera terminado ahí.

Muchos lectores "picaron". Algunos pensaron significativamente diferente a lo que yo había hecho, no obstante existen muchas formas de cambiar el diseño. Otros sencillamente no lo hicieron.

En cualquier caso, en esta entrada voy a describir el método que he utilizado para alcanzar el efecto medio transparente de un formulario. Espero que los lectores piensen sobre las técnicas utilizadas y lleguen a su propia forma de hacerlo.

El formulario principal es un formulario SDI con mi clase de navegación "Outlook". Básicamente seleccionando una opción al hacer clic en los botones azules llena un árbol debajo con las ventanas disponibles. Haga clic sobre uno de estos abre una ventana a la derecha donde tiene lugar la acción. Hasta aquí todo se entiende. El formulario SDI tiene una gran imagen de fondo.



Justo debajo de "Product ordering" se puede ver "X". Esto, normalmente tiene Visible = .F. y lo empleo como marcador.

20 de noviembre de 2006

Visual FoxPro es muy bueno

Artículo original: VFP is very very cool
http://weblogs.foxite.com/bernardbout/2006/08/16/vfp-is-very-very-cool/
Autor: Bernard Bout
Traducido por: Ana María Bisbé York

Son sólo artimañas

Se han ido los días de los controles grises. Ahora las pantallas necesitan tener tanta apariencia visual como su funcionalidad. Microsoft comenzó la evolución al diseñar la interfaz de Outlook 2000 y mejoró luego la interfaz de Outlook 2003, que emplea temas. En todas partes de la red vemos el efecto del color en las páginas Web.

Bueno, tenemos la capacidad de utilizar temas en VFP y en la interfaz del Outlook se presta con facilidad a copiar y agregar una apariencia similar y sentirlo como interfaz VFP.

Debo recalcar en el hecho que estas imágenes tienen el copywrite y pertenecen a la empresa para la que trabajo y no pueden ser utilizadas sin permiso.

Las dos pantallas siguientes pertenecen a una aplicación que he diseñado y que se emplea en mi compañía. Hay algunos puntos a resaltar aquí.

Como he mencionado en mi post anterior, mi filosofía de programación es programar una interfaz. Por tanto, no me preocupo en añadir funcionalidad que no será empleada, por tanto los botones que están a la izquierda están fijos y no les he agregado ninguna funcionalidad para añadirlos o eliminarlos.

Vamos a revisar la interfaz.

El formulario principal es un formulario de nivel superior SDI. No hay nada especial aquí.

Arriba a la izquierda, hay un control ActiveX calendar cuyo propósito en este caso es rellenar el espacio. Es completamente funcional así que se puede hacer búsqueda de fecha. Pero para la aplicación, tengo mi propio control para capturar la fecha.

Más abajo, en la mitad izquierda, es un área para la selección de pantallas que van a aparecer a la derecha. Es una clase de usuario que yo desarrollé empleando el control Treeview. Los elementos  que aparecen aquí se leen de una BDF local,  son completamente configurables incluyendo las imágenes.

Debajo a la izquierda están los famosos botones degradados de Outlook. A diferencia de la clase de usuario que yo desarrollé, estos botones serán configurables en tiempo de diseño; pero no se podrán cambiar en tiempo de ejecución.

Al final hay otro botón control de barra de estado de usuario que yo desarrollé. Es sencillo, muestra la hora, algunos mensajes y tiene un remolcador para el redimensionamiento del formulario. En esta aplicación el redimensionamiento no está permitido ya que esto rompería el efecto de las pantallas así como la colocación de los objetos. Está simplemente ahí y se ve bien.

En su lugar habitual, encima del todo, hay un menú que es un menú estándar VFP y añade Copy/Paste (Copiar/Pegar), además de alguna otra funcionalidad no disponible en el Treeview de la vista del Outlook.

A la derecha está la ventana principal que muestra una imagen - en este caso es un entrenador personal que ayuda a una persona que se entrena.

La interfaz trabaja según lo esperado. El usuario selecciona un botón  desde la banda de la izquierda que llena el árbol desde una dbf dando al usuario acceso a la pantalla que está debajo de ese grupo.

Todo esto trabaja de forma similar a un menú donde selecciona un elemento de menú y otro formulario que aparece ...




Ahora viene la mejor parte.

Hemos ejecutado los ejemplos que vienen con VFP y visto los "Formularios transparentes" donde los formularios y todo su contenido se vuelven transparentes utilizando API SetLayeredWindowAttributes. Aunque está bonito, utilizarlo requiere un formulario de nivel superior y provoca que TODO el contenido se vuelva transparente, incluyendo los controles y todo en el formulario. No tiene un aspecto muy agradable, ¡incluso el texto que se escribe también es transparente!

El formulario es, después de todo, una representación bidimensional y muestra qué es lo que quiere mostrar. El efecto transparente tiene lugar; pero ¿en un formulario donde incluso los controles se vuelvan transparentes? Esto es un No rotundo. Pero, ¿qué tal si pudiéramos hacer que el formulario fuera medio transparente mientras mantenemos los controles y todo lo que está dentro en opaco?

Para el usuario, el efecto que estoy tratando de lograr, es un formulario medio transparente, donde el fondo es parcialmente visible; pero todos los controles son opacos y las entradas de texto son claramente visibles. Cómo lo hago no importa en realidad dado el efecto final que se ve.

Obviamente SetLayeredWindowAttributes no es lo que se emplea aquí, porque esta función hace que todo se vea transparente, incluyendo los controles. Este no es el efecto que deseo, así es que la descarto. ¿Cómo puedo lograr este efecto? La clave aquí es saber lo que se quiere lograr.

Después de experimentar un poco llegué a la solución que se muestra debajo. El usuario selecciona una acción desde el árbol y se abre el formulario a la derecha. Este formulario tiene todos los controles opacos; pero aun así se puede ver la imagen al fondo. El formulario es medio transparente mientras que los controles son opacos, como cualquier formulario normal. Esto es lo que ve el usuario. Este es el efecto que yo quiero.




Para enfatizar la traslucidez he dejado una pequeña abertura de unos 3 pixels a la izquierda del formulario. Esto muestra la imagen de fondo y crea la ilusión de que el formulario es transparente.



Pero, ¿Cómo se hizo esto?

Ahora le dejo saber mi pequeño secreto. Todo esto es una ilusión. Si los dibujos del pavimento pueden aparentar 3D, lo cual no es nada, sino una ilusión óptica, entonces el formulario puede aparentar que es traslúcido. La pequeña abertura dejada a la izquierda añade la impresión de que el usuario está viendo una imagen a través del formulario que está por encima de ella.

Los magos emplean artimañas para lograr sus efectos. Yo utilizo VFP y un poco de imaginación. El formulario no tiene que ser transparente para aparentar que lo es. Sólo el efecto final da esa impresión.

Se completó la ilusión.

Ahora, ¿quien dice que el VFP no es genial? VFP puede ser tan bueno como usted quiere que sea. En mi caso ...

Ahora dejo a su imaginación flotar y diseñar algunas pantallas deslumbrantes. Envíenme algunas imágenes cuando lo tengan hecho.

¿Está interesado en seguir el tema? Escriba un comentario en mi Blog y su correo.

17 de noviembre de 2006

Visual FoxPro hace transparencias

Artículo original: Visual Foxpro does Glass!
http://weblogs.foxite.com/bernardbout/2006/06/15/visual-foxpro-does-glass/
Autor: Bernard Bout 
Traducido por: Ana María Bisbé York


Ha sido liberado el CTP de Windows vista y ha sido descargado por todo aquel que tenga buen ancho de banda. Una de las "golosinas" de los Temas de Vista es algo llamado Formulario de cristal (Glass Forms). Cuando se aplica, muestra un efecto de formulario transparente, como cristal.

Sin embargo, no se apure y piense que sus gráficos actuales no van a soportar este requerimiento. Hay una recomendación muy clara sobre que únicamente una parte de la ventana debe ser transparente.

Entonces, ¿qué debemos hacer los que utilizamos XP? ¿Soportará VFP o tendrá soporte para estos efectos tal y como tenemos el soporte para temas actual? ¡ Quién sabe !

En cualquier caso, yo quise mostrar aquí algunas golosinas en las que he estado trabajando con Visual FoxPro 9 en mi tiempo libre. Esta es una Beta, muy muy beta aun; pero muestra algunas posibilidades, especialmente ¡ porque corre en el viejo XP plano, y en el aburrido y viejo Visual FoxPro !

Si alguien está interesado en seguir con el desarrollo de esta idea, que me envíe una línea y podemos seguir a partir de aquí.

Sin más, tengo algunas pantallas de mi entorno VFP. El fondo fue fijado temporalmente para mostrar el efecto "CRISTAL". En ese momento, yo puedo mover el formulario; pero el efecto está un poco agitado. Hay algunos errores con ACTIVATE, etc.

Recuerde que este es mi formulario Beta ejecutándose en XP, no sobre Vista o alguna tarjeta gráfica de alta resolución.

Lo fundamental es que recuerde que esto es VFP.

¡ Viva el Fox !

Las siguientes ventanas no están ejecutándose en Vista sino en XP. No han sido dañados animales en la producción ni se emplean temas especiales. Todo esto es puro VFP. Haga clic sobre la imagen para ver la versión más grande.

Formulario de cristal VFP



Formulario transparente VFP.

Observe que todos los objetos son transparentes. No se ve muy bonito.



Otra vista del formulario cristalino. Todos los controles son opacos.



16/06/2006

Existe confusión sobre lo que representa un efecto "Cristal"

El ejemplo de formulario transparente es simplemente eso, un formulario medio transparente que emplea API SetLayeredWindowsAttributes y que se puede lograr con apenas 4 líneas de código para un par de APIs. No hay efecto "Cristal", solamente es "Transparente"

El efecto Cristal se usa en referencia de los formularios Vista, es donde parte de los formularios es transparente y otra parte no. Todos los objetos que se colocan en un formulario transparente asumirán para sí mismos la misma transparencia que el formulario padre. Esto no es así en un formulario de Cristal. Dentro de un formulario de cristal, los objetos van a permanecer opacos.

13 de noviembre de 2006

La percepción con VFP. ¡ Hazlo bonito !

Artículo original: With VFP its Perception. Make it cool
http://weblogs.foxite.com/bernardbout/2006/06/06/with-vfp-its-perception-make-it-cool/
Autor: Bernard Bout
Traducido por: Ana María Bisbé York


Todo está en la percepción

Craig Baley en su blog habló sobre cómo hacer para que VFP se vea bonito. Como el dijo, todo está en la mente - percepción - VFP es xBase y es una tecnología vieja, los controles VFP tienen apariencia antigua, etc. Necesitamos cambiar esto. Esto es lo que debemos leer:

http://craigbailey.blogspot.com/2006/05/vfp-how-to-make-visual-foxpro-cool.html

(Nota del editor: Este artículo se encuentra traducido  bajo el título: Como hacer a Visual FoxPro bonito)

Entonces, ¿VFP es genial o qué? Existen componentes que pueden ser utilizados para ver y sentir que VFP es genial como cualquier otra aplicación que conocemos.

Eche un vistazo a estas pantallas de Office 2007 y vea el control "Ribbon" (banda). Se ven mucho mejor logrados que las fichas (tabs). ¿Qué hay de los famosos controles OneNote? Otro conjunto de fichas y algunas selecciones caprichosas desde una barra expandible, similar a lo que tenemos en Outlook 2003. ¿Puede hacerse esto en VFP? Seguro, ¿Por qué no? Y no se verían genial.

He aquí los ejemplos de pantallas de algunos controles Ribbon y OneNote de Office 2007







¿Pero, qué hay de controles VFP nativos?

No hay necesidad de correr e implementar TODAS las funcionalidades de estos controles. Después de todo usted es desarrollador y puede hacer tanto como necesite para que su aplicación sea buena e incluirle tanta funcionalidad como necesite. La banda (Ribbon) puede tener las funciones; pero si no las necesita, entonces, ahorre tiempo. Su aplicación trabajará bien tanto con ellas, como sin ellas.

Word tiene muchas funciones porque se dirige a una audiencia muy amplia. Pero ¿Cuántos de nosotros utilizamos TODAS estas funciones? El 40%, 50%, si usted utiliza el 60% de las funciones es realmente un usuario fuerte.

Entonces, al crear controles en VFP que se vean "bien", ¿por qué debemos brindar su funcionalidad global sin que lo necesite? Si tiene en planes vender sus controles, entonces el público al que va dirigido es mayor y espera todo esto. Pero, para sus aplicaciones por qué perder tiempo y esfuerzo creando esto cuando en realidad no lo necesita. Cree solamente lo que necesite.

Esta es la filosofía que he utilizado al crear los controles que utilizo en mis aplicaciones. Les coloco solamente la funcionalidad suficiente que necesito, no más.

He aquí algunos de los controles que he creado recientemente. Seguro que se parecen a los controles de Microsoft Outlook. Pero esto es porque están creados para que tengan dicha apariencia. Tienen código 100% VFP, sin siquiera una llamada a API o ActiveX. La percepción es todo. ¡ VFP ES genial !

Mi versión de Minibar Cerrada y abierta con la con la sección "Programs" seleccionada

   

La sección "Maintence", en naranja, está cerrada.



Y algunas vistas de mi control OneNoteTab en código puro VFP:







Puedo especificar tantas fichas como yo quiera:



VFP  Rocks!

30 de octubre de 2006

Numerar los registros de un Select

Un truco para numerar los registros retornados por un comando SELECT-SQL.

Este truco fue tomado del artículo "Una consulta interesante escrita para SQL Server 2000 y SQL Server 2005" del amigo Miguel Egea (SQL Server MVP y Webmaster de PortalSQL).

Muchas veces he leído en diversos mensajes en el Foro de VFP, que se necesita numerar los registros retornados por un SELECT para pintar los registros alternados de un control Grid, o también para enumerar los documentos por clientes, o los ítems por factura. Con este truco, esto se puede lograr en una sola sentencia SELECT.

La idea es hacer un JOIN contra la misma tabla y contar los registros que tengan el identificador mayor o igual que la segunda tabla del JOIN.

Para hacer mas gráfica la idea, tomemos el ejemplo de enumerar los Clientes de la tabla Customers de la base de datos Northwind:
OPEN DATABASE (HOME(2) + "Northwind\Northwind")
SELECT COUNT(*) AS Registro, c1.CustomerId, c1.CompanyName ;
  FROM Customers C1 ;
  INNER JOIN Customers C2 ON C1.CustomerId >= C2.CustomerId ;
  GROUP BY C1.CustomerId, C1.CompanyName ;
  ORDER BY Registro

Con esta mismo concepto, también podemos hacer "cortes" en la numeración y reinicializarla según una condición.

En este otro ejemplo numeramos las Ordenes de la tabla Orders y hacemos un corte por Cliente:
OPEN DATABASE (HOME(2) + "Northwind\Northwind")
SELECT COUNT(*) AS Registro, O1.OrderId, O1.CustomerId, O1.ShipName ;
  FROM Orders O1 ;
  INNER JOIN Orders O2 ON O1.CustomerId = O2.CustomerId ;
  AND O1.OrderId >= O2.OrderId ;
  GROUP BY O1.OrderId, O1.CustomerId, O1.ShipName ;
  ORDER BY O1.CustomerId, Registro

A partir de Visual FoxPro 9.0, las mejoras incorporadas en el comando SELECT-SQL nos permite retornar el mismo conjunto de datos utilizando subconsultas.

Los mismos 2 ejemplos anteriores con sintaxis para VFP9:
OPEN DATABASE (HOME(2) + "Northwind\Northwind")
SELECT (SELECT COUNT(*) ;
  FROM Customers C2 ;
  WHERE C1.CustomerId >= C2.CustomerId) AS Registro, C1.* ;
  FROM Customers C1 ;
  ORDER BY Registro
OPEN DATABASE (HOME(2) + "Northwind\Northwind")
SELECT (SELECT COUNT(*) ;
  FROM Orders O2 ;
  WHERE O1.OrderId >= O2.OrderId AND ;
  O1.CustomerId = O2.CustomerId) AS Registro, O1.* ;
  FROM Orders O1 ;
  ORDER BY O1.CustomerId, Registro

Nota: No utilizar este truco con tablas que contengan muchos registros, ya que la performance del JOIN contra la misma tabla puede ser muy pobre. Antes de implementar esta solución, es conveniente comprobar que la velocidad de respuesta no afecte considerablemente los tiempos de nuestra aplicación.

Hasta la próxima,

Luis María Guayán

28 de octubre de 2006

West Wind Internet & Client Tools 5.0 liberado



Ya está disponible la versión 5.0 de West Wind Internet & Client Tools, un paquete integrado que proporcionan funcionalidad de Internet y mucho mas, a aplicaciones VFP.

West Wind Internet & Client Tools es un paquete integrado de clases de VFP muy útiles que proporcionan funcionalidad de Internet, HTTP, Mail, FTP, acceso TCP/IP y mucho mas. También incluye un juego de utilidades que proporcionan acceso orientado a objetos a datos de SQL Server y Web, gráficas básicas para aplicaciones de escritorio y Web, y una rica librería de funciones útiles en el desarrollo diario con VFP.

West Wind Internet & Client Tools es una herramienta shareware. La versión registrada tiene un costo de U$S 199 y es royalty free.
Para mas información y descargar una copia de West Wind Internet & Client Tools haga clic aquí.

26 de octubre de 2006

Integrando VFP con Microsoft Virtual Earth

Microsoft Virtual Earth es la herramienta de Microsoft, que compite con Google Maps.

En este artículo de Dave Crozier (Replacement-Software), se muestra un excelente ejemplo de como integrarlo con Visual FoxPro.

Hace aproximadamente un mes se ha publicado en PortalFox el artículo "Google Maps en un formulario de VFP", donde se incorpora una página HTML a un formulario de VFP que nos permitía navegar en Google Maps.

Con Microsoft Virtual Earth también se puede obtener un resultado similar, como se muestra en el Blog de Dave Crozier, donde se ha publicado un artículo, sobre como incorporar esta herramienta de Microsoft en un formulario de Visual FoxPro, con varias e interesantes opciones, y con su código fuente completo para descarga.

Para leer el artículo "VFP integration with Microsoft Virtual Earth" de Dave Crozier (en Inglés), y descargar el ejemplo haga clic aquí.


(*) Imagen tomada del Blog de Dave Crozier

Dave promete mejorar este código y publicarlo cuando este terminado. ¡A estar atentos!

17 de octubre de 2006

Detectar el estado de un informe

Hace algunas semanas en un mensaje en el Grupo de Noticias en Español de Visual FoxPro se consultaba sobre como detectar si un informe estaba en modo de vista previa o imprimiéndose, para así agregar una condición de impresión a ciertos controles (campos, etiquetas, imágenes, líneas, etc.) que deben por ejemplo visualizarse en la vista previa, pero no imprimirse.

A partir de Visual FoxPro 8, esto es muy fácil con la función SYS(2040) que nos indica si hay un informe activo, si está en vista previa o se está imprimiendo.

SYS(2040) retorna un caracter "0" si no hay ningún informe activo; "1" si el informe esta en vista previa; ó "2" si el informe se está enviando a la impresora o a un archivo.

Por ejemplo si solo necesitamos visualizar un control en modo de vista previa, se debe agregar como condición de impresión la expresión SYS(2040)="1"

Otra opción, para campos y etiquetas, es utilizar una expresión tipo:

IIF(SYS(2040)="1", "Vista Previa", "Impresora")

Para mas detalles vea la ayuda de la Función SYS(2040) en la documentación de Visual FoxPro.

Luis María Guayán

13 de octubre de 2006

Formularios con fondos degradados con GDI+ Parte 2

Artículo original: Gradient Backgrounds in your forms with GDI+ Part 2
http://weblogs.foxite.com/vfpimaging/archive/2006/06/22/1906.aspx
Autor: Cesar Ch.
Traducido por: Ana María Bisbé York


En mi entrada anterior con el mismo título, mostré cómo crear fondos con degradado VERTICAL en formularios. (Nota de la traductora: El autor se refiere a "Gradient Backgrounds in your forms with GDI+" que está traducido al español bajo el título "Formularios con fondos de colores degradados con GDI+")

Como explicó Malcolm Greene, (http://weblogs.foxite.com/vfpimaging/archive/2006/06/13/1825.aspx#comments) la misma técnica puede ser utilizada para contenedores VFP.

De acuerdo con MSDN, "La clase LinearGradientBrush define una brocha que pinta un color degradado en el que el color cambie uniformemente desde la línea de límite que comienza el degradado hasta la línea final del límite de la brocha. Las líneas de borde de un degradado lineal son dos líneas rectas paralelas. El degradado de color es perpendicular a las líneas de borde, cambiando gradualmente a través del movimiento desde la línea de borde inicial a la línea de borde final. El color degradado tiene un color en la línea de borde inicial y otro en la línea de borde final."

11 de octubre de 2006

Creando una aplicación VFP como un servicio

Normalmente, la creación de un servicio es bastante compleja, pero la creación de una aplicación VFP como un servicio es bastante simple gracias al par de herramientas INSTSRV.EXE y SRVANY.EXE

Artículo en inglés en el Blog de Calvin Hsia.

-- Creating a VFP application as a service --
http://blogs.msdn.com/calvin_hsia/archive/2004/12/13/282351.aspx

10 de octubre de 2006

Formularios con fondos de colores degradados con GDI+

Artículo original: Gradient Backgrounds in your forms with GDI
http://weblogs.foxite.com/vfpimaging/archive/2006/06/13/1825.aspx 
Autor: Cesar Ch.
Traducido por: Ana María Bisbé York 

Este escrito va para Bernard Bout.

Después de leer este post, vea también Gradient Backgrounds in your forms with GDI+ Part 2 (traducido al español en PortalFox como "Formularios con fondos de colores degradados con GDI+ Parte 2").





GDI+ permite crear muchos efectos tales como degradado de colores. Esta característica no se incluyó en _gdiplus.vcx; pero se puede acceder fácilmente con una única llamada a la función Flat de API. La mayor parte del código que se muestra a continuación relacionado con la brocha con Degradado es de Bob Durban.

Aplicarlo en formularios es realmente sencillo. Agregue el código que está debajo a los eventos del formulario: LOAD, RESIZE y DESTROY

Load Event
LOCAL lcGradFile
lcGradFile = ADDBS(SYS(2023))+SYS(2015)+".bmp"
This.AddProperty("cTempGradFile",lcGradFile)
IF FILE(lcGradFile)
  CLEAR RESOURCES (lcGradFile)
ENDIF
LOCAL lnRGBColor1
lnRGBColor1 = RGB(60,30,180) && Blue

* Crear la imagen para el degradado con GdipCreateLineBrushI 
SET CLASSLIB TO HOME() + "ffc/_gdiplus.vcx" ADDITIVE

* Declarar API
DECLARE Long GdipCreateLineBrushI IN GDIPLUS.dll ;
  String point1, String point2, ;
  Long color1, Long color2, ;
  Long wrapMode, Long @lineGradient

* Crear un objeto color y guarda los valores de color ARGB en variables
LOCAL loClr AS GpColor OF HOME() + "ffc/_gdiplus.vcx"
LOCAL lnColor1, lnColor2
loClr = CREATEOBJECT("gpColor")
loClr.FoxRGB = lnRGBColor1
lnColor1 = loClr.ARGB
loClr.FoxRGB = RGB(255,255,255) && White
lnColor2 = loClr.ARGB

* Crear un bitmap
LOCAL loBmp AS GpBitmap OF HOME() + "ffc/_gdiplus.vcx"
loBmp = CREATEOBJECT("gpBitmap")
loBmp.Create(1,Thisform.Height)

* Obtener un objeto gráfico bitmap 
LOCAL loGfx AS GpGraphics OF HOME() + "ffc/_gdiplus.vcx"
loGfx = CREATEOBJECT("gpGraphics")
loGfx.CreateFromImage(loBmp)

* Obtener una brocha con degradado
LOCAL loBrush as GpBrush OF HOME() + "ffc/_gdiplus.vcx"
LOCAL hBrush && Brush Handle
hBrush = 0
GdipCreateLineBrushI(BINTOC(0,"4rs")+BINTOC(0,"4rs"), ;
  BINTOC(0,"4rs")+BINTOC(Thisform.Height,"4rs"), ;
  lnColor1, lnColor2, 0, @hBrush)
loBrush = CREATEOBJECT("gpBrush")
loBrush.SetHandle(hBrush, .T.)

* Llenar nuestro mapa de bits con el degradado
loGfx.FillRectangle(loBrush,0,0,1,Thisform.Height)
loBmp.SaveToFile(lcGradFile,"image/bmp")
Thisform.AddObject("ImgBackGround","Image")
WITH Thisform.ImgBackGround
  .Stretch = 2
  .Width = Thisform.Width
  .Height = Thisform.Height
  .Picture = lcGradFile 
  .Visible = .T.
ENDWITH
RETURN

Resize Event
Thisform.ImgBackGround.Width = Thisform.Width
Thisform.ImgBackGround.Height = Thisform.Height

Destroy Event
WITH Thisform
  IF FILE(.cTempGradFile)
    CLEAR RESOURCES (.cTempGradFile)
    ERASE (.cTempGradFile)
  ENDIF
ENDWITH

Las nuevas clases GDI+

El código que he presentado en el evento LOAD funciona muy bien; pero tiene una apariencia bastante fea, al compararlo con lo que seremos capaces de hacer cuando sean liberadas las nuevas clases Sedna-X GDI+

El código que aparece a continuación va a sustituir todo el código relacionado con la creación del color degradado en el evento LOAD.

¡Es que no puedo esperar a que estén finalizadas estas clases!

Su trabajo es realmente brillante, y va a añadir mucho más que una clase envoltorio (wrapper)
* Crear una imagen con degradado lineal GDI+ utilizando 
* las clases xfc GDI+ de Bo Durban y Craig Boyd
System = NEWOBJECT("xfcSystem","system.vcx")
LOCAL loBmp AS xfcBitmap
LOCAL loGfx AS xfcGraphics
LOCAL loBrush AS xfcBrush
WITH System.Drawing 
  loBmp = .Bitmap.New(1, Thisform.Height) 
  loGfx = .Graphics.FromImage(loBmp) 
  loBrush = .Drawing2D.LinearGradientBrush.New( ; 
    .Rectangle.New(0, 0, 1, Thisform.Height), ; 
    .Color.FromRGB(lnRGBColor1), .Color.White, 1) 
  loGfx.FillRectangle(loBrush, loBrush.Rectangle) 
  loBmp.Save(lcGradFile, .Imaging.ImageFormat.Bmp) 
ENDWITH





Haga clic para descargar el código fuente de este formulario de ejemplo

9 de octubre de 2006

Añadir un "sizing grip" a un formulario de nivel superior

Este código demuestra como añadir un "sizing grip" a un formulario de nivel superior.

El sizing grip se muestra en el estilo "clásico" de Windows, es decir, sin temas, y solamente tiene funcionalidad en formularios de nivel superior. Pero el código es tan simple, que me parece vale la pena verlo.
PUBLIC oForm1
oForm1 = NEWOBJECT("Form1")
oForm1.SHOW
RETURN

DEFINE CLASS Form1 AS FORM
  HEIGHT = 269
  WIDTH = 348
  SHOWWINDOW = 2
  CAPTION = "Demo de sizing grip"
  AUTOCENTER = .T.
  NAME = "Form1"

  ADD OBJECT label1 AS LABEL WITH ;
    NAME = "Label1", CAPTION = "Label1", ;
    HEIGHT = 17, LEFT = 204, TOP = 204, WIDTH = 40

  PROCEDURE Label1.MOUSEDOWN
    LPARAMETERS nButton, nShift, nXCoord, nYCoord
    #DEFINE WM_NCLBUTTONDOWN 0xA1
    #DEFINE HTBOTTOMRIGHT 17
    SendMessage(THISFORM.HWND, WM_NCLBUTTONDOWN, HTBOTTOMRIGHT, 0)
  ENDPROC

  PROCEDURE Label1.INIT
    THIS.HEIGHT = SYSMETRIC(15)
    THIS.WIDTH = SYSMETRIC(14)
    THIS.FONTNAME = [Marlett]
    THIS.FONTSIZE = 12
    THIS.FORECOLOR = RGB(128, 128, 128)
    THIS.ANCHOR = 0
    THIS.LEFT = THISFORM.WIDTH - THIS.WIDTH - 1
    THIS.TOP = THISFORM.HEIGHT - THIS.HEIGHT - 1
    THIS.ANCHOR = 12
    THIS.MOUSEPOINTER = 8
    THIS.CAPTION = [o]
    DECLARE INTEGER SendMessage IN user32 ;
      INTEGER HWND,INTEGER wMsg,INTEGER wParam,INTEGER LPARAM
  ENDPROC
ENDDEFINE
Carlos Alloatti

8 de octubre de 2006

Librería VFPCompression (Zip/Unzip para Visual FoxPro)

El MVP Craig Boyd (SweetPotato Software, Inc.) pone a disposición de toda la comunidad una librería para compactar y descompactar archivos con Visual FoxPro.

La librería VFPCompression y su documentación están disponibles para libre descarga y uso en el Blog de Craig.

Existen tres entradas en su Blog con las versiones previas y definitiva, la documentación y funciones agregadas, y algunos comentarios sobre su desarrollo, y de la cooperación recibida de Bo Durban (Moxie Data, Inc.)

Los enlaces a las entradas en su Blog son los siguientes:
¡Gracias Craig por tu excelente trabajo y tu continua dedicación hacia la comunidad de VFP!

Ana María Bisbé York

7 de octubre de 2006

Clase ctl32_scontainer para VFP9 (Contenedor con barras de desplazamiento)

Ya está disponible la clase ctl32_scontainer. Esta clase consiste en un objeto container, con barras de desplazamiento.

Descripción, imágenes y documentación en el siguiente enlace:

ctl32_scontainer_class

Descargas:
ctl32_20080903.zip



Carlos Alloatti

6 de octubre de 2006

Dibujar o indexar formatos de píxel a imágenes con GDI+

Artículo original: DRAWING ON GIFS OR INDEXED PIXEL FORMAT IMAGES WITH GDI+
http://weblogs.foxite.com/vfpimaging/2006/03/18/drawing-on-gifs-or-indexed-pixel-format-images-with-gdi-/
Autor: Cesar Ch.
Traducido por: Ana María Bisbé York


Es muy común que necesitemos dibujar algunas formas, textos o aplicar algunos efectos a las imágenes.

Pero GDI+ tiene limitaciones al trabajar con imágenes creadas en formatos de índices pixelados, tales como 1 bppIndexed (0x00030101), 4bppIndexed (0x00030402) and 8bppIndexed (0x00030803). Estos son formatos de pixel utilizados también por GIF (Graphics Interchange Format). Si intenta dibujar en este tipo de imágenes puede recibir un error de este tipo:



Una solución muy sencilla es cargar el GIF utilizando GpImage, y obtener algunas propiedades, tales como Width y Height. Luego, crea un objeto imagen nuevo y vacío con el mismo tamaño que el GIF original; pero utilizando formato indizado pixel. _gdiplus.vcx utiliza PIXELFORMAT_32bppPARGB como predeterminado.

El siguiente paso es "dibujar" el GIF original en el bitmap creado en un formato pixel no indizado utilizando el procedimiento DrawImage. Ahora puede dibujar libremente sobre esta nueva imagen.
Finalmente, puede guardar la imagen nueva como GIF o cualquier otro tipo.

Un detalle importante que debe conocer es que cuando le pide a GDI+ que lo almacene como GIF, lo convertirá automáticamente la imagen nuevamente 8 a un formato 8bppIndexed, que es el predeterminado para GDI+.

He aquí un ejemplo de código que realiza esto, dibujando dos elipses y un rectángulo sobre una imagen.
#DEFINE GDIPLUS_PIXELFORMAT_1bppIndexed 0x00030101
#DEFINE GDIPLUS_PIXELFORMAT_4bppIndexed 0x00030402
#DEFINE GDIPLUS_PIXELFORMAT_8bppIndexed 0x00030803
#DEFINE GDIPLUS_PIXELFORMAT_16bppGrayScale 0x00101004
#DEFINE GDIPLUS_PIXELFORMAT_16bppRGB555 0x00021005
#DEFINE GDIPLUS_PIXELFORMAT_16bppRGB565 0x00021006
#DEFINE GDIPLUS_PIXELFORMAT_16bppARGB1555 0x00061007
#DEFINE GDIPLUS_PIXELFORMAT_24bppRGB 0x00021808
#DEFINE GDIPLUS_PIXELFORMAT_32bppRGB 0x00022009
#DEFINE GDIPLUS_PIXELFORMAT_32bppARGB 0x0026200A
#DEFINE GDIPLUS_PIXELFORMAT_32bppPARGB 0x000E200B
#DEFINE GDIPLUS_PIXELFORMAT_48bppRGB 0x0010300C
LOCAL lcSource, lcDest
lcSource = GETPICT("gif")
IF EMPTY(lcSource)
  RETURN
ENDIF
lcDest = ADDBS(JUSTPATH(lcSource))+ "_" + JUSTSTEM(lcSource)

*** Carga la imagen y verifica si está indizada :
LOCAL loImage AS GPIMAGE OF ffc/_gdiplus.vcx
loImage = NEWOBJECT('gpImage',HOME() + 'ffc/_gdiplus.vcx')
loImage.CreateFromFile(lcSource)
wImg = loImage.Imagewidth
hImg = loImage.ImageHeight
lcPixFormat = GetPixFormatName(loImage.PixelFormat)
IF NOT "INDEXED" $ UPPER(lcPixFormat)
  MESSAGEBOX("Dibuje directamente sobre la imagen, ya que no tiene formato " + ;
    "de pixel indizado !",64, "UTILICE LA TÉCNICA HABITUAL")
  RETURN
ENDIF

*** Crea un nuevo bitmap con las mismas dimensiones :
LOCAL loBitmap AS GpBitmap OF ffc/_gdiplus.vcx
loBitmap = NEWOBJECT("GpBitmap", HOME() + "ffc/_gdiplus.vcx")
LOCAL loGraph AS GpGraphics OF HOME() + ffc/_gdiplus.vcx
loGraph = NEWOBJECT('GpGraphics', HOME() + "ffc/_gdiplus.vcx")
loBitmap.CREATE(wImg, hImg, GDIPLUS_PIXELFORMAT_16bppRGB555 )

*** Pega la imagen original sobre la nueva imagen recién creada :
loGraph.CreateFromImage(loBitmap)
loGraph.DrawImageScaled(loImage, 0, 0, wImg, hImg)

*** Ahora podemos dibujar lo que deseemos sobre la imagen :
LOCAL loBlue, loRed, loGreen AS GpColor OF ffc/_gdiplus.vcx
LOCAL loPen AS GpPen OF HOME() + ffc/_gdiplus.vcx
loBlue = NEWOBJECT("GpColor", HOME() + "ffc/_gdiplus.vcx","",40,40,240 )
loRed = NEWOBJECT("GpColor", HOME() + "ffc/_gdiplus.vcx","",240,40,40 )
loGreen = NEWOBJECT("GpColor", HOME() + "ffc/_gdiplus.vcx","",40,230,40 )
loPen = NEWOBJECT("GpPen", HOME() + "ffc/_gdiplus.vcx")
loPen.CREATE(loBlue, 12)
loGraph.DrawEllipse( loPen, 0, 0, wImg, hImg)
loPen.PenColor = loRed
loGraph.DrawEllipse( loPen, 0+12, 0+12 , wImg-24 , hImg-24)
loPen.PenColor = loGreen
loGraph.DrawRectangle( loPen, 0+30, 0+30 , wImg-60 , hImg-60)

*** Guarda la imagen en GIF y JPG :
loBitmap.SaveToFile(lcDest+".jpg","image/jpeg","quality=100")
loBitmap.SaveToFile(lcDest+".gif","image/gif")
RETURN
PROCEDURE GetPixFormatName(nPix)
  DO CASE
    CASE nPix = 0x00030101
      RETURN "1bppIndexed"
    CASE nPix = 0x00030402
      RETURN "4bppIndexed"
    CASE nPix = 0x00030803
      RETURN "8bppIndexed"
    CASE nPix = 0x00101004
      RETURN "16bppGrayScale"
    CASE nPix = 0x00021005
      RETURN "16bppRGB555"
    CASE nPix = 0x00021006
      RETURN "16bppRGB565"
    CASE nPix = 0x00061007
      RETURN "16bppARGB1555"
    CASE nPix = 0x00021808
      RETURN "24bppRGB"
    CASE nPix = 0x00022009
      RETURN "32bppRGB"
    CASE nPix = 0x0026200A
      RETURN "32bppARGB"
    CASE nPix = 0x000E200B
      RETURN "32bppPARGB"
    CASE nPix = 0x0010300C
      RETURN "48bppRGB"
    CASE nPix = 0x001C400E
      RETURN "64bppPARGB"
    OTHERWISE
      RETURN "No identificado"
  ENDCASE
ENDPROC
Este procedimiento tiene un gran inconveniente, ya que en esta conversión automática, GDI+ escribe el archivo utilizando una paleta de medio tono por lo que el objeto imagen tendrá el color reducido. GDI+ hace la conversión a color desde 32 bits-por-pixel (32 BPP) al escribir la imagen al archivo.

De acuerdo con Szaak Priester: "Cuando GDI+ guarda un bitmap en formato GIF, realiza una forma cruda de determinación de los valores (cuantización) de los colores. Siempre emplea la misma paleta de colores, se rellena fundamentalmente con los 216 "colores Web-safe" Al inicio de Internet, estos colores eran los únicos mostrados consistentemente por la mayoría de los examinadores, de ahí su nombre... los 216 colores web-safe fueron escogidos básicamente por sus méritos técnicos (dividen uniformemente el espacio d color RGB) y no debido a sus cualidades visuales. Como consecuencia, la paleta web-safe (llamada también "paleta de medio tono") contiene fundamentalmente púrpuras casi perceptibles, muchos verdosos y marrones turbios, por tato, mucha parte utilizable del espectrum es seriamente poco poplado."

Debajo, puede ver el resultado que se obtiene utilizando la técnica que se ha comentado aquí.

GIF - Imagen original GIF - 8bppIndexed Image JPEG - 32bppARGB

Preste atención a la diferencia entre la calidad de estas tres imágenes.

Esta técnica puede ser utilizada también para redimensional cualquier imagen, incluyendo GIFs. Con el objeto de redimensionar, todo lo que necesitamos es cambiar en el código anterior las instrucciones que tratan del tamaño de imagen, loBitmap.Create(NewWidth, NewHeight, PixelFormat) y al pegar la imagen original en la creada nueva, loGraph.DrawImagePortionat(loOriginalImage, 0, 0, NewWidth, newHeight).

Necesitas solamente conocer las limitaciones de GIFs y decidir si guardará la imagen como GIF o como otro formato de imagen.

He aquí algunas diferencias entre formatos GIFs y JPEGs, de acuerdo con MSDN:
GIF vs. JPEG

¿Debe guardar las imágenes en formato GIF o JPEG? Aunque esta pregunta no está relacionada directamente con la administración de la paleta, confunde a mucha gente, y es de alguna manera relevante para nuestro tema.

Existen dos diferencias fundamentales entre imágenes GIF y JPEG:
Las imágenes GIF están comprimidas de forma que conservan todos sus datos, mientras las imágenes JPEG están comprimidas de forma que pierden muchos de sus datos.
Las imágenes GIF están limitadas como máximo a 256 colores, mientras las imágenes JPEG no están limitadas por la cantidad de colores que utilizan.
Pero ... ¿existe una forma de convertir una imagen en un formato GIF sin emplear una paleta de colores mejor distribuida en lugar de una paleta de medio tono que estropea las imágenes? Por supuesto; pero esto ya es tema para otro artículo ...

5 de octubre de 2006

Exportar Cursor VFP a EXCEL

Hace un tiempo atrás encontré en este portal una rutina que me fue muy útil, pues exportaba tablas o cursores de VFP a un Libro Excel.

El inconveniente que se me presentó es que cuando utilizaba exportaciones de tablas con demasiados registros, la espera era eterna. Producto que la rutina en cuestión escribía celda a celda los datos de cada registro de la tabla.

Pues bien, buscando siempre mejorar los métodos, les presento esta rutina de exportación de tablas y/o cursores que se apoya en la importación de datos tipo texto de Microsoft Excel:

********************************************************************
********************************************************************
*!* FUNCTION Exp2Excel( [cCursor, [cFileSave, [cTitulo]]] )
*!*
*!* Exporta un Cursor de Visual FoxPro a Excel, utilizando la
*!* técnica de importación de datos externos en modo texto.
*!*
*!* PARAMETROS OPCIONALES:
*!* - cCursor  Alias del cursor que se va a exportar.
*!*            Si no se informa, utiliza el alias
*!*            en que se encuentra.
*!*
*!* - cFileName  Nombre del archivo que se va a grabar.
*!*              Si no se informa, muestra el libro generado
*!*              una vez concluída la exportación.
*!*
*!* - cTitulo  Titulo del informe. Si se informa, este
*!*            ocuparía la primera file de cada hoja del libro.
********************************************************************
********************************************************************
FUNCTION Exp2Excel( cCursor, cFileSave, cTitulo )
  LOCAL cWarning
  cWarning = "Exportar a EXCEL"
  IF EMPTY(cCursor)
    cCursor = ALIAS()
  ENDIF
  IF TYPE('cCursor') # 'C' OR !USED(cCursor)
    MESSAGEBOX("Parámetros Inválidos",16,cWarning)
    RETURN .F.
  ENDIF
  *********************************
  *** Creación del Objeto Excel ***
  *********************************
  WAIT WINDOW 'Abriendo aplicación Excel.' NOWAIT NOCLEAR
  oExcel = CREATEOBJECT("Excel.Application")
  WAIT CLEAR

  IF TYPE('oExcel') # 'O'
    MESSAGEBOX("No se puede procesar el archivo porque no tiene la aplicación" ;
      + CHR(13) + "Microsoft Excel instalada en su computador.",16,cWarning)
    RETURN .F.
  ENDIF

  oExcel.workbooks.ADD

  LOCAL lnRecno, lnPos, lnPag, lnCuantos, lnRowTit, lnRowPos, i, lnHojas, cDefault

  cDefault = ADDBS(SYS(5)  + SYS(2003))

  SELECT (cCursor)
  lnRecno = RECNO(cCursor)
  GO TOP

  *************************************************
  *** Verifica la cantidad de hojas necesarias  ***
  *** en el libro para la cantidad de datos     ***
  *************************************************
  lnHojas = ROUND(RECCOUNT(cCursor)/65000,0)
  DO WHILE oExcel.Sheets.COUNT < lnHojas
    oExcel.Sheets.ADD
  ENDDO

  lnPos = 0
  lnPag = 0

  DO WHILE lnPos < RECCOUNT(cCursor)

    lnPag = lnPag + 1 && Hoja que se está procesando

    WAIT WINDOWS 'Exportando cursor '  + UPPER(cCursor)  + ' a Microsoft Excel...' ;
      + CHR(13) + '(Hoja '  + ALLTRIM(STR(lnPag))  + ' de '  + ALLTRIM(STR(lnHojas)) ;
      + ')' NOCLEAR NOWAIT

    IF FILE(cDefault  + cCursor  + ".txt")
      DELETE FILE (cDefault  + cCursor  + ".txt")
    ENDIF

    COPY  NEXT 65000 TO (cDefault  + cCursor  + ".txt") DELIMITED WITH CHARACTER ";"
    lnPos = RECNO(cCursor)

    oExcel.Sheets(lnPag).SELECT

    XLSheet = oExcel.ActiveSheet
    XLSheet.NAME = cCursor + '_' + ALLTRIM(STR(lnPag))

    lnCuantos = AFIELDS(aCampos,cCursor)

    ********************************************************
    *** Coloca título del informe (si este es informado) ***
    ********************************************************
    IF !EMPTY(cTitulo)
      XLSheet.Cells(1,1).FONT.NAME = "Arial"
      XLSheet.Cells(1,1).FONT.SIZE = 12
      XLSheet.Cells(1,1).FONT.BOLD = .T.
      XLSheet.Cells(1,1).VALUE = cTitulo
      XLSheet.RANGE(XLSheet.Cells(1,1),XLSheet.Cells(1,lnCuantos)).MergeCells = .T.
      XLSheet.RANGE(XLSheet.Cells(1,1),XLSheet.Cells(1,lnCuantos)).Merge
      XLSheet.RANGE(XLSheet.Cells(1,1),XLSheet.Cells(1,lnCuantos)).HorizontalAlignment = 3
      lnRowPos = 3
    ELSE
      lnRowPos = 2
    ENDIF

    lnRowTit = lnRowPos - 1
    **********************************
    *** Coloca títulos de Columnas ***
    **********************************
    FOR i = 1 TO lnCuantos
      lcName  = aCampos(i,1)
      lcCampo = ALLTRIM(cCursor) + '.' + aCampos(i,1)
      XLSheet.Cells(lnRowTit,i).VALUE=lcname
      XLSheet.Cells(lnRowTit,i).FONT.bold = .T.
      XLSheet.Cells(lnRowTit,i).Interior.ColorIndex = 15
      XLSheet.Cells(lnRowTit,i).Interior.PATTERN = 1
      XLSheet.RANGE(XLSheet.Cells(lnRowTit,i),XLSheet.Cells(lnRowTit,i)).BorderAround(7)
    NEXT

    XLSheet.RANGE(XLSheet.Cells(lnRowTit,1),XLSheet.Cells(lnRowTit,lnCuantos)).HorizontalAlignment = 3

    *************************
    *** Cuerpo de la hoja ***
    *************************
    oConnection = XLSheet.QueryTables.ADD("TEXT;"  + cDefault  + cCursor  + ".txt", ;
      XLSheet.RANGE("A"  + ALLTRIM(STR(lnRowPos))))

    WITH oConnection
      .NAME = cCursor
      .FieldNames = .T.
      .RowNumbers = .F.
      .FillAdjacentFormulas = .F.
      .PreserveFormatting = .T.
      .RefreshOnFileOpen = .F.
      .RefreshStyle = 1 && xlInsertDeleteCells
      .SavePassword = .F.
      .SaveData = .T.
      .AdjustColumnWidth = .T.
      .RefreshPeriod = 0
      .TextFilePromptOnRefresh = .F.
      .TextFilePlatform = 850
      .TextFileStartRow = 1
      .TextFileParseType = 1 && xlDelimited
      .TextFileTextQualifier = 1 && xlTextQualifierDoubleQuote
      .TextFileConsecutiveDelimiter = .F.
      .TextFileTabDelimiter = .F.
      .TextFileSemicolonDelimiter = .T.
      .TextFileCommaDelimiter = .F.
      .TextFileSpaceDelimiter = .F.
      .TextFileTrailingMinusNumbers = .T.
      .REFRESH
    ENDWITH

    XLSheet.RANGE(XLSheet.Cells(lnRowTit,1),XLSheet.Cells(XLSheet.ROWS.COUNT,lnCuantos)).FONT.NAME = "Arial"
    XLSheet.RANGE(XLSheet.Cells(lnRowTit,1),XLSheet.Cells(XLSheet.ROWS.COUNT,lnCuantos)).FONT.SIZE = 8

    XLSheet.COLUMNS.AUTOFIT
    XLSheet.Cells(lnRowPos,1).SELECT
    oExcel.ActiveWindow.FreezePanes = .T.

    WAIT CLEAR

  ENDDO

  oExcel.Sheets(1).SELECT
  oExcel.Cells(lnRowPos,1).SELECT

  IF !EMPTY(cFileSave)
    oExcel.DisplayAlerts = .F.
    oExcel.ActiveWorkbook.SAVEAS(cFileSave)
    oExcel.QUIT
  ELSE
    oExcel.VISIBLE = .T.
  ENDIF

  GO lnRecno

  RELEASE oExcel,XLSheet,oConnection

  IF FILE(cDefault + cCursor + ".txt")
    DELETE FILE (cDefault + cCursor + ".txt")
  ENDIF

  RETURN .T.

ENDFUNC

***
***
Saludos desde Chile,

OSCAR CALDERON FUENTES

29 de septiembre de 2006

Nuevas clases GDI+ en VFP-X

Artículo original: New VFP- X GDI+ classes
http://weblogs.foxite.com/vfpimaging/archive/2006/08/10/2194.aspx
Autor: Cesar Ch.
Traducido por: Ana María Bisbé York


En las últimas dos semanas, he estado realizado pruebas extensas y creando algunos ejemplos utilizando la nueva biblioteca VFP-X GDIPlusX. Debo decir que la clase está tremenda. Esta biblioteca está realmente haciendo mi vida con GDI+ mucho más sencilla.

Cuanto más la utilizo, más me gusta. Me estoy divirtiendo mucho.

Como dijo Craig Boyd en una de sus entradas de blog: (http://www.sweetpotatosoftware.com/SPSBlog/PermaLink,guid,c5ff0b43-a24d-43a6-b290-94fe9cfd4492.aspx) "La biblioteca GDIPlusX es una reproducción en código Visual FoxPro puro del espacio de nombre Drawing en .NET. Hemos escrito más de 40 000 líneas de código VFP y la librería consiste en más de 80 clases, por ahora: cerca del 95% está completo. Es seguro decir que no hay otra biblioteca en el planeta que brinde a los desarrolladores VFP la funcionalidad y el poder que esta brinda cuando se trabaja con GDI+." - Es totalmente cierto.

Si está planificando comenzar un nuevo proyecto y necesitará utilizar GDI+, o está interesado en ayudar en el proyecto, escribiendo código, haciendo pruebas, sugerencias, recomiendo fuertemente entrar en http://www.codeplex.com/Wiki/View.aspx?ProjectName=VFPX, seleccionar la ficha "Releases" http://www.codeplex.com/Release/ProjectReleases.aspx?ProjectName=VFPX, y descargar la versión más estable actualmente disponible.

Comience ejecutando el archivo DEMO.PRG en la carpeta Samples, y va a tener una mejor idea del poder de la clase.

Bo ha sugerido que desde ahora debemos mantener nuestra comunicación solamente vía CodePlex, utilizando las secciones Discussions y Issue Tracker. De esta forma, como dijo Bo "otras personas pueden contribuir y estar envueltos si ellos ven que aquí hay alguna actividad y que es muy sencillo para ellos colaborar también."

A propósito, ¡ El proyecto VFP-X ahora tiene también un nuevo logo !

vfpxbanner_small.gif
A partir de ahora, emplearé este espacio para mostrar algunos ejemplos de utilización de estas clases. Puede emplear el espacio de comentarios de este blog para hacer este tipo de peticiones.

EJEMPLOS DE CÓDIGO

Mi primer ejemplo viene de ayudar a Bernard Bout, a crear una imagen que será utilizada en su ejemplo "blueglass". Puede obtener más datos de esta información en: http://weblogs.foxite.com/bernardbout/archive/2006/06/15/1838.aspx

Crear una imagen de 2x2 píxeles, con un fondo azul y un píxel magenta en (0,0)

He aquí el código:
** Crea una imagen para ser utilizada en el ejemplo "blueglass" 
** por Bernard Bout
** imagen 2x2 píxeles, con fondo azul y un píxel magenta en (0,0)

_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx","vcx")))

* Define los colores a ser utilizados
lnRGBPointClr = RGB(0,0,255) && Azul
lnRGBBackgClr = RGB(255,0,255) && Magenta
WITH _SCREEN.System.Drawing
LOCAL loBitmap AS xfcBitmap
LOCAL loColor AS xfcColor
LOCAL loGfx AS xfcGraphics
* Crea un nuevo bitmap  2x2 en el predeterminado PixelFormat - 32bppARGB
loBitmap = .Bitmap.New(2,2)
* Crea un objeto Graphics para que sea capaz de utilizar la función Clear,
* que va a llenar todo el bitmap con el color deseado.
* Para este caso esto no es fundamental, porque el bitmap es
* pequeño, entonces podemos llamar "SetPixel" 4 veces para cubrir el bitmap.
loGfx = .Graphics.FromImage(loBitmap)
loGfx.Clear(.Color.FromRgb(lnRGBBackGClr))
* Dibuja el pixel
loBitmap.SetPixel(0,0, .Color.FromRgb(lnRGBPointClr))
* Guarda una imagen  PNG
loBitmap.Save("c:\BlueGlass.png", .Imaging.ImageFormat.Png)
ENDWITH



¡No puedo esperar por el siguiente entrada de blog de Bernard utilizándola!

27 de septiembre de 2006

Google Maps en un formulario de VFP

Como crear un formulario en Visual FoxPro, que nos muestre mapas e imágenes satelitales de Google Maps.

Ya es por todos conocido que con Visual FoxPro podemos hacer muchas cosas. Una de ellas es incorporar un control Explorador Web en un formulario y utilizar código HTML y JavaScript. Esto ya lo publicó mi amigo Antonio Muñoz de Burgos y Caravaca en su artículo "Control del Explorador Web (HTML & JS & VFP)".

En este artículo, vamos a ver un ejemplo de como incorporar dinámicamente código HTML y JavaScript, que nos permita navegar por Google Maps y poder desplazarnos virtualmente a diversos lugares del mundo a bordo de nuestro velóz y querido zorro ;-)

Lo primero que vamos a utilizar en este ejemplo, es una tabla con algunos lugares de interes, con sus respectivas coordenadas georeferenciales (latitud y longitud) y el nivel de acercamiento. Con estos datos, generaremos una página HTML que mostraremos en un control Explorador Web que incluiremos en nuestro formulario.

El código completo de este ejemplo es el siguiente:

PUBLIC oMiForm
oMiForm = CREATEOBJECT("MiForm")
oMiForm.SHOW
RETURN

DEFINE CLASS MiForm AS FORM
  HEIGHT = 365
  WIDTH = 475
  AUTOCENTER = .T.
  CAPTION = "Ejemplo con Google Maps"
  NAME = "MiForm"
  SetPoint = ""
  SHOWWINDOW = 2

  ADD OBJECT cboDescrip AS COMBOBOX WITH ;
    ROWSOURCETYPE = 6, ROWSOURCE = "MisLugares.descri", ;
    HEIGHT = 24, LEFT = 12, TOP = 12, WIDTH = 330, ;
    STYLE = 2, NAME = "cboDescrip"

  ADD OBJECT cmdMostrar AS COMMANDBUTTON WITH ;
    TOP = 10, LEFT = 350, HEIGHT = 27, WIDTH = 112, ;
    CAPTION = "Mostrar mapa", NAME = "cmdMostrar"

  ADD OBJECT oleIE AS OLECONTROL WITH ;
    TOP = 48, LEFT = 12, HEIGHT = 300, WIDTH = 450, ;
    NAME = "oleIE", OLECLASS = "Shell.Explorer.2"

  PROCEDURE LOAD
    SYS(2333,1)
    THIS.SetPoint = SET("Point")
    SET POINT TO .
    SET SAFETY OFF
    *-- Creo el cursor con los datos
    CREATE CURSOR MisLugares (Descri C(40), Lat N(12,6), Lon N(12,6), Zoom I(4))
    INSERT INTO MisLugares VALUES ("Torre Eiffel (Francia)", 48.858333, 2.295000, 17)
    INSERT INTO MisLugares VALUES ("Basílica de San Pedro (Vaticano)", 41.902102, 12.456400, 17)
    INSERT INTO MisLugares VALUES ("Estatua de la  Libertad (EEUU)", 40.689360, -74.044400, 17)
    INSERT INTO MisLugares VALUES ("Estadio Monumental (Argentina)", -34.545277, -58.449722, 17)
    INSERT INTO MisLugares VALUES ("Estadio Azteca (Mexico)", 19.302900, -99.150400, 17)
    INSERT INTO MisLugares VALUES ("Estadio Camp Nou (España)", 41.380906, 2.123330, 17)
    INSERT INTO MisLugares VALUES ("Cementerio de aviones (EEUU)", 32.174247, -110.855874, 14)
  ENDPROC

  PROCEDURE DESTROY
    SET POINT TO (THIS.SetPoint)
  ENDPROC

  PROCEDURE cboDescrip.INIT
    THIS.LISTINDEX = 1
  ENDPROC

  PROCEDURE cmdMostrar.CLICK
    TEXT TO lcHtml NOSHOW TEXTMERGE
    <html> <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <title>Google Maps</title>
    <script src="http://maps.google.com/maps?file=api&v=2&key=123" type="text/javascript"></script>
    <script type="text/javascript">
    //<![CDATA[
    function load()
    { if (GBrowserIsCompatible())
      { var map = new GMap2(document.getElementById("map"),G_SATELLITE_MAP);
      map.addControl(new GLargeMapControl());
      map.addControl(new GMapTypeControl());
      map.addControl(new GOverviewMapControl());
      map.setCenter(new GLatLng(<<ALLTRIM(STR(MisLugares.Lat,12,6))>>,
      <<ALLTRIM(STR(MisLugares.Lon,12,6))>>),<<TRANSFORM(MisLugares.Zoom)>>);
      map.setMapType(G_HYBRID_MAP);
    } }
    //]]> </script> </head>
    <body scroll="no" bgcolor="#CCCCCC" topmargin="0" leftmargin="0"
    onload="load()" onunload="GUnload()">
    <div id="map" style="width:450px;height:300px"></div>
    </body> </html>
    ENDTEXT
    STRTOFILE(lcHtml,"MiHtml.htm")
    THISFORM.oleIE.Navigate2(FULLPATH("MiHtml.htm"))
  ENDPROC

ENDDEFINE

Los resultados de ejecutar el código son los siguientes:





Google Maps posee muchísimas opciones que permiten añadir controles para el desplazamiento, etiquetas, marcadores, escalas, etc. Lo mejor para aprender a utilizarlo, es leer la documentación disponible en el sitio Google Maps API, como así también leer y ajustarse a los Términos de Uso.

Hasta la próxima.

Luis María Guayán