2 de febrero de 2012

Enlaces de descarga reparados

Hola a todos, como se deben haber dado cuenta, con el cierre de MEGAUPLOAD y sus dominios asociados, todos los enlaces de descarga del Blog quedaron inhabilitados.

Sin duda, el cierre MEGAUPLOAD  y la detención de sus máximos ejecutivos, constituye medida represiva sin precedentes y una clara advertencia de la policía del mundo, protectora de los intereses de las grandes corporaciones que manejan el cine, la música y el software hacia los las empresas que ofrecen servicios de descarga, por no "monitorear" ni "censurar" las actividades de los usuarios por si alguno de ellos decide quebrantar alguna ley que afecte sus intereses.

Ahora, no se si los tribunales de EEUU actuarán con el mismo tesón para resarcirme por el tiempo y las molestias causadas al dejar inaccesibles enlaces de descarga para aquellos archivos gratuitos, y aquellos de mi propiedad intelectual que si deseo compartir, y que no violan ninguna ley ni perjudican los intereses de nadie.

Bueno, lo importante es que los links ya están repuestos y disponibles a través de MEDIAFIRE y FILESERVE por si acaso, en todo caso, estoy evaluando la posibilidad de habilitar una cuenta en Dropbox o a través de Skydrive para mayor seguridad.



Saludos a todos,



Rodrigo Dinamarca M.

17 de octubre de 2011

¿Quién es el Alias?

Para todos quienes programamos en algún lenguaje xBase, sin duda son indispensables las cláusulas SELECT a la hora de  cambiarnos de área de trabajo al manipular varios archivos (o tablas) de datos.  Es así como cada archivo que utilizamos en una aplicación tiene su SELECT o área de trabajo asignada y que utilizaremos cuando debemos realizar alguna operación sobre la tabla (APPEND BLANK, REPLACE, LIST, DELETE, etc.).

Sin embargo, y en especial ahora que estoy abordando el tema de la migración hacia Harbour Minigui, quiero compartir este excelente artículo de René Flores, creador de "Objeto Persistente", al cual he dedicado un enlace en dBFast Chile desde que inicié este Blog.

René es un antiguo desarrollador de Clipper en Mexico, y a través de su empresa CiberTec, se ha especializado en la consultoría, enseñanza y desarrollo de aplicaciones con Fivewin, Harbour, xHarbour y otras tecnologías xBase, centrándose en la actualidad en Xailer.

El artículo titulado "¿Quién es al Alias?" rompe algunos mitos y paradigmas con respecto al uso de los SELECT en la administración de áreas de trabajo y nos entrega una nueva visión que sin duda nos ayudará a optimizar el uso de tablas de datos en nuestras aplicaciones, y aunque no le he pedido permiso a René para transcribir el artículo en este Blog, estoy seguro de que no se  molestará. 

Acá les dejo la transcripción,:

Tengo 20 años programando en Clipper, y mas o menos 15 dando clases de programación y análisis y diseño de sistemas, de los cuales 5 estuve dando clases en la Universidad La Salle de México, como catedrático de la Escuela de Ingeniería en la carrera de Ingeniería en Cibernética y Ciencias de la Computación.

He dirigido algunos trabajos de tesis sobre bases de datos, 2 de ellos específicamente sobre lenguajes Xbase y hasta una tesis sobre programación orientada al Objeto con FiveWin, sí sí, aunque ustedes no lo crean FiveWin fue en su momento (hace como 8 años) el tema de un trabajo de titulación profesional de 2 de mis alumnos.

El caso es que en todos estos años pegándole a la tecla con Clipper y ahora con (x)Harbour, me he encontrado con que casi nadie saber manejar los archivos DBF en condiciones.

Actualmente la idea es ir mas por el tema del SQL, pero hay muchas personas que todavía manejan archivos DBF y que conociendo un poco mas sobre ellos se pueden hacer cosas muy interesantes.

Pero vamos a ver..... ¿ como que nadie sabe usar los DBFs ?, si cientos de programadores del mundo los han usado durante años..... de acuerdo, pero de todos esos cientos de programadores, que levanten la mano aquellos que consideren que saben manejar los DBF BIEN.

Si levantaste la mano, continúa leyendo, espero poder enseñarte algo nuevo, si no la levantaste, también continúa leyendo, seguro vas a aprender algo nuevo.

El mito de las 256 "áreas" de trabajo.

Los que venimos del dBase III recordamos que en los libros ponían, dBase puede abrir hasta 256 "areas" de trabajo, entendiendo por "areas de trabjo" un espacio de memoria en donde se "abría" la tabla DBF (está mal llamarla "base de datos"), lo que en el SQL moderno de hoy se llama "cursor".

Estas "areas" estaban numeradas, de tal forma que cuando queríamos abrir una tabla, todos hacíamos (y algunos siguen haciendo) la forma erronea de abrir la tabla:

SELECT 1 // para seleccionar el "area" donde vamos a abrir la tabla
USE archivo.dbf

Esto quería decir que la tabla ARCHIVO.DBF estaría abierta en el "área" 1, así pues siempre que quisiéramos hacer referencia a los campos o a alguna operación sobre ARCHIVO.DBF, antes que nada tendríamos que hacer primeramente un salto al "área" donde está abierta dicha tabla, algo como esto:

SELECT 1
GO TOP
DO WHILE ! EOF()
? Recno()
SKIP 
ENDDO

La cosa comienza a ponerse interesante cuando requieres usar mas tablas simultáneamente, digamos unas 5 tablas, entonces erroneamente haces:

SELECT 1
USE archivo1.dbf
SELECT 2
USE archivo2.dbf
......
SELECT 5
USE archivo5.dbf

Mal, mal, mal, esto te lleva a hacer lo que yo he llamado "código ping-pong", es decir selección de área, operación, selección de otra área, otra operación, selección de otra area mas, otra operación mas, y luego vamos de vuelta al área original, lo cual nos lleva a un código lleno de SELECTs por todos sitios, que es complicado de mantener.

Si a esto aunamos que a 16 bits el número de áreas de trabajo estaba limitado por la memoria (las 256 áreas de trabajo que prometía dBase nunca fueron ciertas), entonces nuestro código se volvía un maremagnum de saltos entre áreas, de tener que cerrar tablas para abrir otras en las mismas "áreas" de tener que acordarte en que número de área quedaba tal o cual tabla DBF de tal o cual proceso, vamos, un rollo.

Y todo por no utilizar el "alias" de los archivos DBF.

¿ Y quien o qué es ALIAS ?

En todos los lenguajes de programación no orientados a datos, como Pascal, "C" o el mismo Basic, cuando se manejan archivos de datos, siempre se les asignan 2 nombres, el nombre "físico" que es el nombre del archivo tal cual se almacena en el disco, y el nombre "lógico", que es el nombre con el cual haremos referencia dentro del código de nuestro programa.

Clipper/(x)Harbour no es una excepción a esta regla, porque en realidad, SIEMPRE estas manejando 2 nombres cuando abres una tabla DBF para trabajar sobre ella, es decir, cuando haces algo como esto :

USE archivo.dbf

En realidad estas generando un "ALIAS", es decir, un "apodo" para hacer referencia al archivo abierto durante la ejecución de tu programa.

Si revisas la sintaxis del comando USE, te encontrarás varias cosas interesantes:

USE [ ; 
[INDEX ] ;
[ALIAS ] ;
[EXCLUSIVE | SHARED] ; 
[NEW] ; 
[READONLY] ; 
[VIA 

El comando USE tiene una clausula ALIAS, que si no fuera importante, creeme, no la pondrían como parte de la sintaxis.

Esta clausula ALIAS se utiliza para darle un "nombre lógico" a tu tabla para que puedas manejarla en memoria, si tu omites este nombre, se asigna automáticamente como "alias" el mismo nombre de la tabla, sin extensión.

El ALIAS de una tabla es una pieza muy importante de la programación de datos porque si sabes como utilizarlo, te ahorrarás lineas y líneas de SELECTS inútiles.

Existe un "operador ALIAS", representado por un "->" signo de menos + signo de mayor que, este operador nos servirá para hacer referencia al área de trabajo que deseamos usar sin necesidad de tener que hacer un SELECT

Por ejemplo:

USE archivo1.dbf
archivo1->(dbgotop())
DO WHILE ! archivo1->(EOF())
? archivo1->(RECNO())
archivo1->(DBSKIP())
ENDDO

En este ejemplo asumimos que no hemos usado la cáusula ALIAS, luego entonces el ALIAS asignado por omisión será "archivo1" (sin extensión), luego usaremos el operador "alias" (->) para indicar que operaciones deben realizarse sobre él.

Nota por favor que todas las operaciones sobre la tabla son funciones NO COMANDOS y que todas las operaciones van encerradas entre paréntesis.

Otra cláusula importante del comando USE es la claúsula NEW, esta clausula te ahorrará miles de lineas de SELECTS ya que esta clausula abre la tabla DBF en la primer área que se encuentre disponible, es decir, no tienes que hacer un SELECT previo a abrir un DBF, simplemente le pones NEW al momento de abrir, y dejas que Clipper/(x)Harbour se haga cargo del resto.

USE archivo1.dbf NEW
USE archivo2.dbf NEW

No tienes que hacer ningun SELECT para abrir las tablas, ahora, para hacer referencia a los campos de cada archivos, o bien realizar operaciones sobre las tablas, tampoco hay necesidad de hacer SELECT, el ALIAS nos soluciona la vida:

USE archivo1.dbf NEW
USE archivo2.dbf NEW

archivo1->(DBGOTOP())
archivo2->(DBGOBOTTOM())

DO WHILE ! archivo2->(BOF())
? archivo2->(fieldget(1))
? archivo1->(fieldget(5))
IF ! archivo1->(EOF())
archivo1->(DBSKIP())
ENDIF
archivo2->(DBSKIP(-1))
ENDDO

El manejo de los campos es muy simple, funciona igual, basta con que pongas el nombre del campo despues del operador alias SIN PARENTESIS:

? archivo1->nombre
? archivo2->iva

Los campos también son asignables usando el operador alias, tanto para leer sus valores, como para actualizar o agregar valores, sustituyendo con esto al comando REPLACE:

cNombre := archivo1->nombre
archivo2->(DBAPPEND()) // el comando APPEND BLANK se sustituye pro su función 
archivo2->nombre := cNombre // no hay REPLACE, el valor se asinga directamente

Esto de los alias puede ser tan complejo como quieras, un ejemplo típico es buscar en una segunda tabla el valor de un campo contenido en la primera:

CON SELECTS:

SELECT 1
cNombre := field->nombre
SELECT 2
SEEK cNombre

CON ALIAS:

archivo2->(DBSEEK(archivo1->nombre))

Efectivamente, con la línea anterior, basta con que hagas referencia a los alias y en vez de usar 4 líneas de código en una sola solucionas el problema y haces exactamente lo mismo.

Ahora esto del alias es mas versátil porque tu puedes dar TU PROPIO ALIAS

USE archivo1.dbf ALIAS MiAlias NEW
MiAlias->(DBGOTOP())
? MiAlias->nombre

Usando tu propio ALIAS puedes darle un nombre mas identificativo al manejo de tus tablas DBFs.

Veamos otro ejemplo mas complejo pero sin duda mucho mas útil:

USE archivo.dbf ALIAS ArchPant SHARED NEW
USE archivo.dbf ALIAS ArchRep SHARED NEW
USE archivo.dbf ALIAS ArchMovs SHARED NEW

NO, No hay ningún error, estoy abriendo LA MISMA TABLA (archivo.dbf) 3 veces AL MISMO TIEMPO..... ¿ que no causa esto un error del tipo ALIAS already in use ?.... pues va a ser que no.... y esto es algo de lo mas alucinante y útil de los archivos DBFs.

Una de las cosas que casi nadie usa es esta poderosa característica de los DBFs, poder abrir la misma tabla 2, 3, 4, o "n" veces al mismo tiempo, esto se logra CAMBIANDO EL ALIAS y abriendo la tabla como compartida (SHARED) como si fuesemos a programar para red.

Lo interesante de esta forma de usar los DBFs es que cada "alias" mantiene su puntero independiente, es decir puedes hacer algo tan loco como esto:

ArchPant->(DBGOTOP())
ArchMovs->(DBGOTO(5))
ArchRep->(DBGOBOTTOM())

El puntero de cada "alias" se moverá a distintas posiciones, sin importar que estemos hablando del mismo archivo DBF (recuerda, una cosa es el nombre físico y otra es el nombre lógico) Y si tu preguntas los valores de los campos :

ArchPant->nombre
ArchMovs->nombre
ArchRep->nombre

Seguramente obtendrás valores distintos en cada oportunidad, esto porque sin importar que sea el mismo archivo, para efectos prácticos en la memoria de la computadora esta "abierto" 3 veces bajo 3 alias distintos y cada alias guarda y mantiene su propio estado. Solo como comentario, Xailer a través de los DataSets facilita aun mas el manejo de los alias.

Pero volviendo al tema, para los que no tienen Xailer o tienen la versión personal, aun así podemos seguir haciendo cosas interesantes a base de usar solo los alias:

Si yo hago:

ArchMovs->(DbAppend())
ArchMovs->nombre := "Juan Perez"
ArchMovs->direccion := "Da igual donde viva, Viva El "
ArchMovs->(DBCommit())
ArchMovs->(DBUnlock())

El registro añadido será visible EN TODOS LOS ALIAS QUE APUNTEN AL MISMO ARCHIVO
FISICO, esto es maravilloso, porque es como dar de alta al mismo tiempo en 3 tablas distintas.

La pregunta que puede surgir en este momento es .... ¿ y esto como para que me serviría en la vida real ?.... pues para facilitarte la existencia:

Supongamos que tu quieres mostrar en pantalla un browse del archivo de clientes, donde tienes además 2 índices, uno por clave y otro por nombre, y que además quieres hacer operaciones de mantenimiento como altas bajas y cambios y para rematar quieres hacer reportes que pueden ir ordenados por clave o por nombre, entonces haríamos algo como esto:

USE clientes ALIAS CliBrow SHARED NEW
CliBrow->(OrdSetFocus("nombre")) // arrancamos ordenado por nombre del cliente
CliBrow->(DBGOTOP())

USE clientes ALIAS CliMovs SHARED NEW
CliMovs->(OrdSetFocus("clave")) // esta segunda instacia del archivo va ordenado por clave

USE clientes ALIAS CliReps SHARED NEW
CliRep->(OrdSetFocus("clave") // idem anterior.

Si yo quiero meter la primer instancia del archivo de clientes a un browse, basta con hacer:

CliBrow->(Browse()) 

¡¡¡Así de simple !!!!! El usuario podrá moverse sobre el browse y podrá cambiar los criterios de ordenación sin afectar a los otros 2 alias que apuntan a la misma tabla.

Ahora viene lo bueno, si yo quiero hacer un reporte o hacer un ABM sobre la tabla clientes NO LO HAGO SOBRE EL ALIAS DEL BROWSE, lo hago ya sea sobre el alias CliMovs o sobre el alias CliReps para no molestar al alias del browse.

Cuando trabajas con un solo alias, y quieres dar un mantenimiento a una tabla mostrada en un browse, entonces tienes que "guardar" el estado del área de trabajo que muestras en el browse, es decir, en que registro estás, sobre que indice estas trabajando, etc. y luego hacer las operaciones de mantenimiento sobre ese mismo alias que pueden vincular, mover el puntero sobre la tabla, agregar un registro nuevo, cambiar el orden del índice actual, realizar búsquedas, etc, y luego de la operación, debes regresar todo a como estaba antes. Esta técnica te evita eso precisamente.

Por ejemplo, si tu quieres agregar un registro el código sería algo como esto:

CliMovs->(RLOCK())
CliMovs->(DBGOBOTTOM())
nSiguienteClave := CliMovs->clave+1
CliMovs->(DbAppend())
CliMovs->clave := nSiguienteClave
CliMovs->nombre:= cVariableConElNuevoNombre
CliMovs->(DbCommit())
CliMovs->(DBUnlock())

Nota que toda la operación de mantenimiento la hacemos sobre el Alias CliMovs, no sobre el Alias CliBrow, de esta manera no tenemos que guardar posiciones ni estados, porque CliBrow no se va a mover su puntero y su índice se quedan como están, en cambio CliMovs hará toda la labor de mantenimiento y el cambio se verá reflejado automáticamente en CliBrow y en CliRep en cuanto haya terminado la actualización CliMovs, el Browse ni si quiera se va a mover, a menos claro, que la operación de mantenimiento afecte al registro en el cual estaba el puntero del alias CliBrow.

Lo mismo pasa para los reportes, si yo quiero un reporte, usualmente ese reporte abarca un rango de datos, para no molestar al Browse ni a su alias, pues el reporte lo emitimos sobre el alias CliRep:

CliRep->(OrdSetFocus("nombre"))
CliRep->(OrdScope(0,"A")) // reporte de todos los clientes cuyo nombre empiece con "A"
CliRep->(OrdScope(1,"A"))
CliRep->(DBGOTOP())
DO WHILE ! CliRep->(EOF())
... aqui val el codigo del reporte...
CliRep->(DBSKIP())
ENDDO

Y el CliBrow ni se va a enterar que hay un alias llamado CliRep con un Scope sobre la tabla moviendo el puntero para generar un informe.

Esta técnica de programación es ideal para Windows, sobre todo si estás trabajando en entornos MDI porque puedes asignar a cada ventana hija los alias que vaya a necesitar para trabajar, en los entornos MDI una de las cosas mas complicadas de manejar es precisamente esa, evitar que el puntero en la ventana "A" se mueva cuando en la ventana "B" se está realizando otra operación sobre la misma tabla DBF.

Puedes incluso abrir todas las tablas al momento de arrancar tu programa, porque en (x)Harbour no existe la limitación de las 256 "areas" de dBase, en (x)Harbour prácticamente no hay limite en la cantidad de archivos que puedas abrir simultáneamente.

En Xailer Pro y Enterprise no tienes los problemas de Alias cuando trabajas en entornos MDI porque Xailer te provee de los DataSets que usualmente ya van "atados" a cada formulario y como expliqué antes, te ahorran el tema de tener que lidiar con alias, en FiveWin o en la versión Personal de Xailer, el uso de esta técnica es indispensable para trabajar con entornos MDI, en (x)Harbour modo consola, esta ténica te puede ayudar a eliminar muchos vicios de programación y a ahorrar muchas líneas de código.

Cabe señalar que esto mismo es aplicable a Advantage DataBase Server, de hecho el manual de ADS recomienda que todas las tablas estén abiertas desde que arranca la aplicación, esto es aplicable tanto a archivos DBF con NTX o CDX o bien a tablas ADT con índices ADI.

En un próximo artículo discutiremos otra técnica relacionada con esta llamada "alias dinámicos" que hará que no tengas que preocuparte prácticamente por nada que tenga que ver con el acomodo de las tablas en memoria.



___________________________________________________________________________________


Espero les haya gustado y les sirva para replantear el diseño de sus aplicaciones a la hora de utilizar sus tablas DBF.


Les dejo el enlace al artículo original de René Flores en Objeto persistente:  http://objetopersistente.blogspot.com/2008/02/quien-es-alias.html



Saludos,





19 de septiembre de 2011

Migrando desde dBFast a Harbour Minigui

Como ya hemos visto en artículos anteriores, nuestro fiel dBFast, que ha sobrevivido por alrededor de 20 años prácticamente sin cambios, desde Windows 3.1 a Windows 7, no logrará sobrevivir a la nueva generación de tecnologías basadas en 64 bits que, en forma lenta pero constante, se han ido consolidando en el mercado tanto de la informática personal como en la informática de negocios.  Ante esta disyuntiva, nos hemos visto obligados a buscar un nuevo lenguaje de programación que nos permita desarrollar aplicaciones o migrar las que ya tenemos con una curva mínima de aprendizaje, y con la menor cantidad de cambios posibles. ¿Será pedir mucho?

En mi opinión, y según lo manifiesto en el artículo que escribí el 4 de mayo de 2011 (Ver artículo), la herramienta que reúne las condiciones mencionadas en el párrafo anterior es Harbour Minigui.  Como dije en ese artículo, Harbour es un compilador Open Source, 100% compatible con Clipper.  Minigui en tanto, es una librería desarrollada en xHarbour y C nativo por Roberto López (Argentina) que proporciona una interfaz gráfica a los programas escritos en (x)Harbour.  Minigui además proporciona un IDE que facilita el desarrollo de aplicaciones y la organización de nuestros proyectos (Sitio Oficial de Harbour Minigui).


Similitudes entre dBFast y Harbour Minigui


Afortunadamente, y aunque cueste creerlo, hay bastantes similitudes entre dBFast y Harbour Minigui.  Esto gracias a que Harbour = Clipper (100% compatibles), y Clipper es descendiente directo de dBase al igual que dBFast, por lo cual existe un gran porcentaje de compatibilidad entre las sintaxis de los dos lenguajes.  No debemos olvidar que tanto Clipper como dBFast son concebidos en sus inicios como compiladores para dBase.  Ambos lenguajes mantienen los comandos básicos para la apertura y administración de tablas, los comandos de red, tratamiento de variables y tipos de datos, estructuras de control, etc.; de modo que los cambios que veremos serán más bien de forma que de fondo.

De acuerdo a lo anterior, la principal dificultad que deberemos sortear al migrar desde dBFast a Harbour Minigui, está en la forma de programar.

En dBFast los programas tienen invariablemente la misma estructura, esto es:
  1. Comenzamos con las sentencias del preprocesador #define e #include, 
  2. Establecemos los parámetros de funcionamiento mediante el uso de los comandos SET
  3. Abrimos las tablas de datos
  4. Definimos Constantes y Variables
  5. Creamos Ventanas y Controles (incluidos los comandos SAY y GET)
  6. Creamos el ciclo dentro del cual se ejecutará el programa (DO WHILE - ENDDO) dentro del cual...
  7. Chequeamos los eventos, en qué control se producen y dependiendo de eso, llamamos los programas y/o funciones que deben ejecutarse,
  8. Cerramos las tablas de datos.
Veamos el siguiente ejemplo:


******************************************
* Programa: Ej_01.prg
******************************************
#DEFINE bOk "Aceptar"
#DEFINE bCancel "Cancelar"

SET DATE FRENCH
SET DELETED ON
SET CONFIRM ON

USE Personas.dbf

cNombre = SPACE(30)
nEdad = 0

CREATE WINDOW "Personas" FROM 10,10 TO 20,50

@01,02 SAY "Nombre:" GET cNombre PICTURE "@!" VALID .NOT. EMPTY(cName)
@03,02 SAY "Edad:"      GET nEdad PICTURE "99"
CREATE BUTTON bOk       AT 5,15 SIZE 1,10
CREATE BUTTON bCancel  AT 5 30 SIZE 1,10

SET EXIT BUTTON TO bCancel

DO WHILE .T.
    READ SAVE MODAL
    nEvent = EVENT()
    DO CASE
           CASE nEvent = 1
                      IF LASTKEY() = 27
                           EXIT
                      ENDIF
           CASE nEvent = 1
                      IF BUTTON() = bOk
                           APPEND BLANK
                           REPLACE NOMBPERS WITH cNombre
                           REPLACE EDADPERS  WITH nEdad
                           COMMIT

                           cNombre = SPACE(20)
                           nEdad = 0

                      ELSE
                           EXIT
                      ENDIF
    ENDCASE

ENDDO

CLOSE WINDOW "Personas"
CLOSE DATABASES

RETURN
******************************************

Si ponemos atención, veremos que a diferencia de la programación para DOS, en que la ventana y los controles debieran estar dentro de un ciclo para redibujar la pantalla cada vez que se requiera, en dBFast estos objetos permanecen invariables durante la ejecución del programa y no necesitan ser redibujados.  Por su parte, el ciclo solo evalúa los eventos (clic, doble clic, pulsación de teclas, etc.) y dependiendo del evento y del control en el cual se produce, realizamos una determinada acción a través de una llamada a función o procedimiento.

Por otra parte Harbour Minigui permite definir los objetos (controles en dBFast) en modo estándar o xBase y en modo alternativo.  El modo alternativo permite utilizar definiciones similares a la de otros lenguajes visuales como Visual dBase 7.5.  Veamos por ejemplo las dos maneras posibles de definir un Botón (Button):

Sintaxis Estándar:

      @ <nRow> ,<nCol>
            BUTTON <ControlName>
            [ OF | PARENT <ParentWindowName> ]
            CAPTION <cCaption> 
            PICTURE <cPictureName> [ TOP | BOTTOM | LEFT | RIGHT ]
            ACTION | ONCLICK | ON CLICK <ActionProcedureName> | <bBlock>
            [ WIDTH <nWidth> HEIGHT <nHeight> ]
            [ FONT <cFontName> SIZE <nFontSize> ]
            [ BOLD ] [ ITALIC ] [ UNDERLINE ] [ STRIKEOUT ]
            [ TOOLTIP <cToolTipText> ]
            [ FLAT ]
            [ NOTRANSPARENT ]
            [ ON GOTFOCUS <OnGotFocusProcedur> | <bBlock> ]
            [ ON LOSTFOCUS <OnLostFocusProcedure> | <bBlock> ]
            [ NOTABSTOP ]
            [ HELPID <nHelpId> ]
            [ INVISIBLE ]
            [ MULTILINE ]

Sintaxis alternativa: 

      DEFINE BUTTON <ButtonName>
            PARENT <ParentWindowName>
            ROW <nValue> 
            COL <nValue>
            CAPTION <cValue> 
            PICTURE <cValue>
            PICTALIGNMENT Top | Left | Right | Bottom
            ONCLICK <ActionProcedure>
            WIDTH <nValue>
            HEIGHT <nValue>
            FONTNAME <cValue>
            FONTSIZE <nValue>
            FONTBOLD <lValue>
            FONTITALIC <lValue>
            FONTUNDERLINE <lValue>
            FONTSTRIKEOUT <lValue>
            TOOLTIP <cValue>
            FLAT <lValue>
            TRANSPARENT <lValue>
            ONGOTFOCUS <ActionProcedure>
            ONLOSTFOCUS <ActionProcedure>
            TABSTOP <lValue>
            HELPID <nValue>
            VISIBLE <lValue>
            MULTILINE <lValue>
      END BUTTON

Veamos el siguiente ejemplo de un programa desarrollado en Harbour Minigui:

************************************
#include "hmg.ch"

Function Main

    DEFINE WINDOW Win_1 ;
        AT 0,0 ;
        WIDTH 400 ;
        HEIGHT 300 ;
        TITLE 'Tutor 04 TextBox Test' ;
        MAIN 

        DEFINE MAIN MENU
           POPUP "First Popup"
             ITEM 'Change TextBox Content' ACTION  Win_1.Text_1.Value := 'New TextBox Value'
             ITEM 'Retrieve TextBox Content' ACTION  MsgInfo ( Win_1.Text_1.Value)
             SEPARATOR
             ITEM 'Change Numeric TextBox Content' ACTION  Win_1.Text_2.Value := 100
             ITEM 'Retrieve Numeric TextBox Content' ACTION  MsgInfo ( Str(Win_1.Text_2.Value))
             SEPARATOR
             ITEM 'Change Numeric (InputMask) TextBox Content' ACTION  Win_1.Text_3.Value := 1234.12
             ITEM 'Retrieve Numeric (InputMask) TextBox Content' ACTION  MsgInfo ( Str(Win_1.Text_3.Value))


           END POPUP
        END MENU

        @ 40 , 120 TEXTBOX Text_1
        @ 80 , 120 TEXTBOX Text_2 NUMERIC
        @ 120 , 120 TEXTBOX Text_3 NUMERIC INPUTMASK '9999.99'

    END WINDOW

    ACTIVATE WINDOW Win_1

Return
************************************


Al ver el programa de ejemplo, algún programador de dBFast no familiarizado con los lenguajes visuales me preguntará ¿y aquí dónde está el ciclo DO WHILE - ENDDO? ¿en qué parte puedo chequear los eventos para llamar a mis funciones o procedimientos?

La verdad es que el programa está completo y no necesitamos ningún ciclo para chequear eventos.  Ocurre que lo que para dBFast eran Controles, para Harbour Minigui son Objetos, y los objetos tienen ciertas particularidades.  Por ejemplo, si definimos un botón utilizando la sintaxis alternativa (Todos los objetos creados con el IDE se definen utilizando la sintaxis alternativa) veremos que este posee Propiedades, responde a ciertos Eventos específicos, y posee ciertos Métodos equivalentes a "reacciones" que permiten modificar el objeto en cuestión.  En el fondo, basta con definir los controles, establecer sus propiedades y asignar una procedure o función a alguno de los eventos que genera el objeto.

Un botón específicamente posee las siguientes propiedades:

- Enabled
- Visible 
- Row
- Col
- Width
- Height
- Caption
- FontName
- FontSize
- FontBold
- FontItalic
- FontUnderline
- FontStrikeout 
- ToolTip
- Picture
- Name (R)
- Parent (D)
- Flat (D)
- TabStop (D)
- HelpId (D)
- Transparent (D)
- PictAlignment (D)
- MultiLine (D)

D: Disponible solo en tiempo de diseño
R: Reservado, no modificable. Elemento esencial para la definición del objeto


Un botón responde solo a los siguientes eventos:

- OnGotFocus (Cuando obtiene el foco)
- OnLostFocus (Cuando pierde el foco)
- OnClick o Action (Cuando hacemos clic sobre él.  Es en este evento en donde asignamos un procedimiento)


Un botón también posee los siguientes métodos (o reacciones)

- Show (Hacer visible el botón, el equivalente en dBFast es SHOW CONTROL)
- Hide (Ocultar el botón, el equivalente en dBFast es HIDE CONTROL)
- SetFocus (Llevar el foco hasta el botón, el equivalente en dBFast es SELECT CONTROL)
- Release (Eliminar el objeto botón, el equivalente en dBFast es CLOSE CONTROL)

Cuando queremos hacer referencia a un deterrminado objeto, por ejemplo el botón del cual hablamos, debemos utilizar la sintaxis de los lenguajes visuales, es decir:

NombreVentana.NombreControl.Método
o bien,
NombreVentana.NombreControl.Propiedad := Valor

Otra cosa importante que debemos tener en cuenta es que si utilizamos objetos para el ingreso de datos como por ejemplo, un control Textbox, no necesitamos una variable para recibir esos datos pues los objetos utilizan una de sus propiedades, por lo general la propiedad Value, para almacenar los datos. 

Por ejemplo, si queremos ingresar un nombre en dBFast , primero debemos definir la variable ( cNombre = SPACE(30) ) y luego ingresar el nombre utilizando cualquiera de las formas que se muestran a continuación:

- @01,02 SAY "Nombre:" GET cNombre 
- CREATE CONTROL TEXT "Text_Nombre" AT 2,10 SIZE  1,30 SAVE TO cNombre

Si posteriormente queremos guardar el nombre ingresado en el campo NOMPERS de la tabla PERSONAS.DBF, solo debemos hacer referencia a la variable en el comando REPLACE, es decir:

REPLACE NOMPERS WITH cNombre

Para hacer la misma operación con Minigui primero debemos definir el control de la siguiente forma (por cierto, es el IDE el que genera el código):

    DEFINE TEXTBOX Text_Nombre
        ROW    10
        COL    90
        WIDTH  300
        HEIGHT 24
        FONTNAME "Arial"
        FONTSIZE 9
        TOOLTIP ""
        ONCHANGE Nil
        ONGOTFOCUS Nil
        ONLOSTFOCUS Nil
        FONTBOLD .F.
        FONTITALIC .F.
        FONTUNDERLINE .F.
        FONTSTRIKEOUT .F.
        ONENTER Nil
        HELPID Nil
        TABSTOP .T.
        VISIBLE .T.
        READONLY .F.
        RIGHTALIGN .F.
        DISABLEDBACKCOLOR Nil
        DISABLEDFONTCOLOR Nil
        BACKCOLOR NIL
        FONTCOLOR NIL
        INPUTMASK Nil
        FORMAT Nil
        VALUE ""
    END TEXTBOX

Parece más complicado que en dBFast, pero en el fondo, en la definición del objeto solo se especifican las propiedades y lo que debe hacerse ante cada evento.  Esta tarea se facilita enormemente utilizando el Inspector de Objeto incorporado en el IDE de Minigui.  Ahora, si queremos almacenar el valor ingresado en el Textbox en el campo NOMPERS de la tabla PERSONAS.DBF, debemos hacer lo siguiente:

REPLACE NOMPERS WITH NombreVentana.Text_Nombre.Value

Se me olvidaba un detalle importante, cuando utilizamos un objeto BROWSE y desplazamos el puntero por los registros del control, la propiedad Value devuelve el número de registro seleccionado en el objeto, pero a diferencia de dBFast, NO mueve el puntero en la tabla.  Para actualizar la posición del puntero en la tabla debemos utilizar el comando GO de la siguiente forma:

GO NombreVentana.NombreBrowse.Value

Otra opción es agregar esta instrucción en el evento ONCHANGE del objeto Browse.   

A continuación les dejo una pequeña aplicación para administrar un archivo de Personas (Personas.Dbf) con dos tablas de índices, un cuadro de búsqueda incremental y codificación automática.  Este ejemplo utiliza una interfaz similar a los ejemplos anteriores desarrollados en dBFast y ha sido desarrollado íntegramente en Harbour Minigui versión 3.0.35.  El ejemplo contiene todos los archivos necesarios para ejecutar la aplicación, he tratado de documentar las funciones y he simplificado el código para una mejor comprensión de la lógica de programación.  

Capturas del ejemplo:







Descargar el programa de ejemplo desde MEGAUPLOAD:


MEDIAFIRE


FILESERVE




El ejemplo contiene los siguientes archivos:

Ejemplo_1.hbp (Archivo contenedor de proyecto)
Main.Prg (Módulo o programa principal de la aplicación)
Main.Fmg (Formulario principal de la aplicación)
Add_Record.Fmg (Formulario para agregar/modificar registros)
Personas.Dbf (Tabla para el almacenamiento de personas)

Se incluye también la carpeta "BMP" que contiene una gran cantidad de íconos en formato BMP para utilizarlos en botones y objetos que soporten imágenes.

El proyecto contiene dos formularios: Main (Ventana principal de la aplicación) y Add_Record (Ventana utilizada para agregar y modificar registros).  Estos formularios contienen los objetos desde los cuales se hacen las llamadas a las distintas funciones y procedimientos.  Todas las funciones y procedimientos asignadas a los distintos eventos de cada objeto están contenidas en el módulo Main.prg, y se detallan a continuación:


Formulario "Main":

Función OpenData() : evento ONINIT del formulario
Función CloseData() : evento ONRELEASE del formulario

Objeto Textbox "Text_Buscar" (Casilla de búsqueda incremental):
Función FindRecord() : evento ONCHANGE del objeto

Objeto RadioGroup "Radio_SetIndex" (Selector de índice activo):
Función SetNewIndex() : evento ONCHANGE del objeto

Objeto Button "B_New" (Botón "Nuevo")
Función AddRecord() : evento ACTION del objeto

Objeto Button "B_Edit" (Botón "Editar")
Función EditRecord() : evento ACTION del objeto

Objeto Button "B_Delete" (Botón "Eliminar")
Función DeleteRecord() : evento ACTION del objeto

Objeto Button "B_Close" (Botón "Cerrar")
Método Release del formulario "Main" (Main.Release) asociado al evento ACTION del objeto.


Formulario Add_Record:

En este formulario, no se han asignado funciones para los eventos de los objetos Textbox que reciben los datos.  Solo se han asignado eventos a los botones del formulario.

Objeto Button "B_Save" (Botón "Aceptar")
Función SaveRecord() : evento ACTION del objeto

Objeto Button "B_Cancel" (Botón "Cancelar")
Método Release del formulario "Add_Record" (Add_Record.Release)


Adicionalmente se utilizan las funciones:

SetCtrlState() : Activa/desactiva los controles del formulario "Main" en caso de que la tabla "Personas.dbf" no tenga registros.  Esta función es invocada desde las funciones OpenData(), SaveRecord() y DeleteRecord().

GetNewId() : Obtiene el nuevo ID automáticamente a partir del último ID ingresado.  Esta función es invocada desde la función AddRecord().


Espero que la información y el ejemplo les sean de utilidad, cualquier consulta pueden hacerla a través de los comentarios del Blog, o bien directamente a mi correo electrónico.  Les comento que estoy creando un pequeño tutorial con breves videos en HD que grafican los pasos a seguir en la construcción de aplicaciones con Harbour Minigui.

Saludos,



Rodrigo Dinamarca M.





13 de junio de 2011

RPV Printing System: Solucionar error SYSTRAY.OCX no está correctamente registrado

He recibido un par de consultas de personas que utilizan Windows Vista y Windows 7, que tienen problemas para trabajar con las versiones de RPV Printing System disponibles para descarga en este Blog.

Al abrir el visualizador de informes no hay problemas, pero cuando intentan abrir el servidor de impresión de RPV, el cual es fundamental para administrar la impresión de informes, aparece un mensaje de error indicando que el componente "systray.ocx" no está correctamente registrado.

La solución para este problema es instalar los runtimes de Visual Basic 5, los cuales están disponibles para descarga en la sección "Recursos" del Blog.


4 de mayo de 2011

Programa en dBFast modificado para Harbour + Minigui

Hace unos días, Ramón Denuc me reenvió un email de Jorge López (más conocido como Mustafa), antiguo desarrollador en dBFast que cansado de las limitaciones impuestas por la falta de soporte a raíz del abandono del proyecto por parte de sus creadores, sumadas a la consiguiente falta de actualizaciones que lo hicieran compatible con las tecnologías de 32 bits, y por la amenaza en ciernes que suponen los sistemas operativos de 64 bits para este viejo y fiel lenguaje, en marzo de 2010 anunció a la comunidad dBFast a través de su foro en "La web del Programador" que abandonaba el desarrollo de aplicaciones en dBFast.

Actualmente Jorge utiliza el compilador Harbour, totalmente compatible con Clipper, más la librería gráfica Minigui.

Jorge revisó un artículo que publiqué en este blog el día 13 de octubre de 2010, titulado: "Uso de solapas para administrar archivos de datos en una interfaz basada en CONTROL BROWSE y PICTURE BUTTONS", y modíficó el código dBFast del ejemplo para adaptarlo a Harbour + Minigui.   Para clarificar un poco más la idea, comparto con ustedes texto que Jorge le envió a Ramón: 

"Hola Ramon
Como estas ? como sigue dBfast ? 
He visto la Web que tiene el compañero RODRIGO DINAMARCA
y tiene un Sample que me gusto mucho de un Tab_Browse con
dos Bases de datos y un Buscador comun y los Botones conjuntos
Me gustaria que le hicieras llegar la version que yo he modificado 
para HMG que al estar compilada en C pero siendo Clipper si es 
de 32 Bits. que veo que es el tema que esta preocupando a los 
usuarios de dBfast.
Tienes que renombrar los ficheros con extension b_  a--> bat si 
tiens que compilar y  e_  a --> exe que es el ejecutable.

Sin otro particular un abrazo
Jorge"

La verdad es que me impresionó bastante el resultado obtenido por Jorge al compilar su aplicación con Harbour+Minigui.

Este es el ejemplo original desarrollado en dBFast:



Pueden ver el artículo y el ejemplo completo haciendo clic aquí


Este es el ejemplo modificado por Jorge López y compilado con Harbour Minigui:





Como siempre, pueden descargar el ejemplo modificado por Jorge desde MEGAUPLOAD


MEDIAFIRE


FILESERVE




Algunos datos interesantes sobre Harbour y Minigui:

Harbour Minigui es un proyecto Open Source (gratuito) iniciado originalmente por Roberto López (Argentina).  Minigui es una librería escrita en lenguaje xHarbour y C nativo que proporciona una interfaz gráfica a los programas escritos en xHarbour, actualmente se incorpora el compilador de C, MinGW que también es Open Source.  Para más información acerca de Minigui, pueden revisar su página web oficial en:  http://www.harbourminigui.com y pueden encontrar una definicion simple pero bastante completa en los  siguientes enlaces:

http://www.embalajesterra.com/misdoc/programas/minigui_doc1.html
http://www.carozodequilmes.com.ar/Projects/QAC/QAC_9.htm

Un aspecto interesante a considerar es que a diferencia de Clipper, en el cual compilábamos nuestros PRG para generar un módulo OBJ, que luego enlazábamos para crear un EXE, Harbour genera un código C que es compilado y posteriormente enlazado a las librerías para generar el EXE.  También es posible, dependiendo del compilador que utilicemos, generar aplicaciones para Pocket PC o para Linux, e incluso aplicaciones para 64 bits.

En lo personal, y a raíz del trabajo de Jorge López, descargué Harbour Minigui, y en los tiempos libres (que no son muchos porque cuando se tiene familia la cosa cambia), me he entretenido bastante probando a hacer pequeños programillas, y la verdad es me ha gustado mucho, el lenguaje es bastante sencillo y semi orientado a objetos, por su parte, el IDE es una gran ayuda a la hora de crear las interfaces, aunque si lo desean, pueden programar directamente el código sin utilizar el IDE.

Creo que Harbour Minigui es una excelente opción para aquellos que buscan una alternativa de migración con una curva de aprendizaje mínimo, para cuando dBFast haya cumplido su vida útil, de hecho, me han solicitado actualizar un sistema de gestión comercial y estoy pensando seriamente hacerlo con Harbour Minigui, pero tranquilos, eso no quiere decir que abandonaré dBFast o este Blog.

Saludos,


RODRIGO DINAMARCA M