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.





6 comentarios:

  1. Hola Rodrigo,

    Me parece fantástico tu articulo sobre HMG y tu punto de vista entre dBfast y HMG, yo estado durante varios meses probando y mirando HMG, pero he llegado a la conclusión de que HMG tiene mucho puntos de riesgo en su futuro a medio/largo plazo, porque no tiene detrás una importante empresa que suponga un sustento de apoyo del cual los programadores podamos confiar en su futuro y que nos vuelva a pasar algo parecido a lo que le ha pasado a dBfast.

    La verdad, ya se que es un cambio brutal, pero estoy casi decidido a continuar mi camino como programador al lado de Microsoft Visual Studio + SQL server y para pequeñas cosas utilizar Access + SQL Server.

    Lo que si tengo claro, y por mi mismo, es que acompañare a dBFast esta el final, hasta el día que nadie pueda ejecutar cualquier programa hecho en dBFast, que sin duda y como muchas veces hemos dicho, dBFast fue, a sido y sera el mejor invento para continuar con el lenguaje basado en "xBase", ahora estoy mas seguro que nunca de decir que “Computer Associates” se equivoco y no sabe cuanto en no continuar con el desarrollo de esta fantástica herramienta de programación, como sabéis dejo de continuar su desarrollo, allá por el año 1996 (más o menos) y ahora que estamos en 2011 y si no fuera porque Microsoft a dejado de continuar con la ejecución de programas de 16 bits a partir de Windows 7 64 bits, hoy en día y sin tener soporte de "CA" hemos podido realizar programas con una fiabilidad como otros lenguajes de programación que si han tenido soporte de sus creadores.

    Para terminar os diré que me siento muy triste de tener que decidirme por otros lenguajes de programación pues dBfast a sido un buen compañero de viaje...

    ResponderEliminar
  2. Hola Ramón, tal como dices, es realmente un cambio "brutal" pasar desde dBFast a Visual Studio, y desde los DBF a Access y SQL, pero el cambio asegurará la "vigencia" de tus aplicaciones a futuro, sin embargo, discrepo un poco contigo en cuanto a las razones.

    Primero, creo que una empresa importante no garantiza el futuro de un producto a mediano o largo plazo y te explico el por qué: en la época de dBFast, Computer Associates era una gran e importante empresa que además adquirió los derechos de Clipper, sin embargo, los ejecutivos determinaron que era más rentable apostar por Visual Objects en lugar de potenciar las herramientas existentes y que eran probadamente exitosas. Otro ejemplo, Microsoft con su producto Visual Foxpro acaparó un segmento importante en el mercado del desarrollo xBase, y sin embargo, decidió descontinuar un producto evidentemente evolucionado, 100% visual, orientado a objetos e integrado con SQL, ODBC, ADO, etc., para potenciar alternativas más rentables como Visual Studio, y que a diferencia de Visual Foxpro, requieren por separado el soporte para base de datos. Otro ejemplo, la compra de Macromedia por parte de Adobe, que descontinúa rápidamente a Freehand, herramienta líder en el diseño gráfico, para posicionar a Illustrator o InDesign, además, descontinuaron Flashpaper para asegurar el dominio del formato adobe PDF.

    Bueno, ejemplos hay muchos y es porque "business are business", y la continuidad de un producto dependerá de su rentabilidad o de los lineamientos que determine la empresa para el futuro.

    Por otro lado, me gusta (x)Harbour Minigui (Extended) o sus variantes por ser productos Open Source, y esa es la gracia, los proyectos Open Source no dependen de la rentabilidad sino más bien de la pasión y de las ganas de crear algo bueno y compartirlo. Basta ver a Linux y sus distintas distribuciones, Openoffice e IBM Lotus Symphony, Mozilla Firefox, MySQL y OpenERP, entre otros, basta ver su evolución para darse cuenta de que los esfuerzos individuales o de equipos de personas con un interés común, pueden generar alternativas de calidad y con vigencia en el tiempo.

    Pero como la intención es ayudar a quienes necesiten migrar de Clipper DOS o DBFAST a algún lenguaje de 32 bits, (x)Harbour MiniGui (Extended) y su IDE cumplen a cabalidad con este cometido, además tienen la gracia de introducir al programador en los lenguajes Visuales y la programación orientada a objetos de manera sencilla y utilizando un lenguaje familiar. Otra ventaja de MINIGUI, es que utiliza la API de Windows para generar las aplicaciones, y que Harbour no genera un ejecutable .EXE, genera un módulo en C que debemos volver a compilar con un compilador de lenguaje C como Mingw, Bcc o Pelles C, lo cual contribuye a la portabilidad de las aplicaciones.

    Por último, si con dBFast hemos podido hacer maravillas durante 20 años a pesar de todas sus limitaciones, sin soporte y con los avances tecnológicos en contra, utilizando una herramienta más potente las posibilidades se multiplican, si por algo insisto siempre en que "la mejor herramienta de programación es el ingenio del programador".

    Un gran saludo,


    Rodrigo Dinamarca M.

    ResponderEliminar
  3. Se va entendiendo incluso por los leguleyos ve, es fascinante como todo está cambiante y el desafío es ir saltando de instrumento a instrumento para mantener le movimiento y el sentido.

    ResponderEliminar
  4. Buenas tardes Rodrigo, vengo de visual foxpro y rqbasic actualmente me dedico a la docencia, pero todavia no puedo dejar mis raices de programador y estoy probando hmg 3.1.1, mira encontre tu blog porque queria un ejemplo de busqueda incremental y grata sorpresa, corri el ejecutable y todo bien pero al volver a compilar, las capturas que hago no aparecen en el browse, pero si se estan guardando. Espero que tengas un poco de tiempo para orientarme. Gracias.

    ResponderEliminar
  5. Rodrigo, Hola. Primero que nada excelente aporte, tuve la suerte de trabajar con Dbfast hace mucho tiempo.
    Mi pregunta es, al querer bajar este ejemplo, los link estan roto... desde donde se pueden bajar ?

    ResponderEliminar
  6. Paquete de xBase en 2017.
    http://harbourdeveloper.blogspot.com.br/
    Visitar el sitio y ver el contenido disponible.
    Si programa en Clipper o Harbour / xHarbour en modo de texto, o incluso el programa en modo gráfico (Windows) pero no utiliza la base de datos relacional (MySQL), el paquete xBase es ideal para acercar o ayudar en el desarrollo de sus sistemas . Todos los sistemas vienen con el código fuente, compiladores y manual para crear el entorno de programación.
    Gracias por su atención !
    Marcelo Neves
    +55 41 99786-3995
    Skype: msdn.xbase
    e-mail: marcelo.souza.das.neves@gmail.com

    ResponderEliminar