may 182013
 

Buenas,

Como ya sabemos (y sino lo sabréis ahora), Firebird (y Interbase) guardan la información de todo el metadato de una base de datos en unas tablas especiales llamadas tablas de sistema (o system tables en inglés). En este artículo quiero mostrar cómo extraer información de estas tablas para así poder, por ejemplo, personalizar mensajes o mostrar información al usuario o lo que creamos oportuno.

Tenemos que tener claro que estas tablas las usaremos sólo de consulta, nunca modificaremos una manualmente (sobre todo si no sabemos qué tocamos) ya que su modificación puede conllevar a la rotura de nuestra base de datos.

Dado que no he encontrado información de la relación entre las diferentes tablas de sistema y éstas lo único que tienen son índices únicos (no tienen claves primarias ni relaciones de integridad para saber con qué tabla se relacionan), he creado un pequeño diagrama de la mayoría de ellas que, más o menos, creo es el correcto. Si alguien discrepa, estaré encantado de leer su propuesta y hacer los cambios al modelo que sean necesarios. Para una mayor comprensión he eliminado el RDB$ de delante del nombre de las tablas y de los campos.

Firebird System Tables

Referente al diagrama, cabe decir que las relaciones se hacen todas a través del campo con el mismo nombre en ambas tablas, a excepción de RELATION_FIELDS que usa FIELD_SOURCE para relacionarse con la tabla FIELDS.

Puesto que existe una muy buena documentación de qué contiene cada una de las tablas, el objetivo de esta entrada no será el explicarlo, sino el facilitar una herramienta que ya haga el trabajo de extraer esa información. Para ello he creado una clase con una serie de métodos que lanzan las sentencias SQL necesarias para devolver la información deseada en cada caso. Dado que hay que lanzar sentencias SQL, necesitamos un componente tipo “query” para poder realizar las consultas. Debido a ésto, la clase debería ser dependiente de unos determinados componentes de acceso a Firebird. Para evitarlo, he definido un par de métodos abstractos para crear y gestionar el objeto “query”, así las clases descendientes sólo necesitarán redefinir estos dos métodos y será muy sencillo hacerlo accesible a cualquier juego de componentes.

La clase padre está definida como sigue:

  TFBMetaData = class
  private
    FDataBase: TCustomConnection;
    FQuery: TDataSet;
  protected
    procedure Iterator(SQL: string; SL: TStringList); overload;
    procedure Iterator(SQL: string; SL1, SL2: TStringList); overload;
    procedure SetQuery; virtual; abstract;
    function AssignSQL(SQL: string): Boolean; virtual; abstract;
    function ExecSQL(SQL: string): Boolean; virtual;
  public
    constructor Create(DataBase: TCustomConnection); virtual;
    destructor Destroy; override;

    procedure GetTables(TablesName: TStringList);
    procedure GetViews(ViewsName: TStringList);
    procedure GetViewSource(ViewName: string; ViewSource: TStringList);
    procedure GetTableFields(TableName: string; FieldsName: TStringList); overload;
    procedure GetTableFields(TableName: string; FieldsName, FieldsType: TStringList); overload;
    procedure GetPrimaryKeyFields(TableName: string; FieldsName: TStringList); overload;
    procedure GetPrimaryKeyFields(TableName: string; FieldsName, FieldsType: TStringList); overload;
    procedure GetDependOnObject(ObjectName: string; DependObjects, ObjectsType: TStringList);
    procedure GetObjectDependsOn(ObjectName: string; DependObjects, ObjectsType: TStringList);
    procedure GetExceptions(ExceptName, ExceptMessage: TStringList);
    procedure GetFieldArrayDimension(TableName, FieldName: string; LowerBound, UpperBound: TStringList);
    procedure GetUDF(UDF: TStringList);
    procedure GetGenerators(Generators: TStringList);
    procedure GetIndexFields(IndexName: string; IndexFields: TStringList);
    procedure GetIndex(TableName: string; IndexName: TStringList; OnlyActive: Boolean = False; Unique: Boolean = False);
    procedure GetProcedures(ProcNames: TStringList);
    procedure GetProcedureSource(ProcName: string; ProcSource: TStringList);
    procedure GetProcedureInParam(ProcName: string; ProcInParam: TStringList);
    procedure GetProcedureOutParam(ProcName: string; ProcOutParam: TStringList);
    procedure GetRoles(RolesName: TStringList; NoSystemRoles: Boolean = True);
    procedure GetTriggers(TableName: string; TriggerType: TTriggerType; TriggerName: TStringList);
    procedure GetTriggerSource(TriggerName: string; TriggerSource: TStringList);
  end;

Aquí vemos como el constructor recibe como parámetro un database de conexión a nuestra base de datos. Éste es de tipo TCustomConnection, por lo que sólo nos servirán aquellos componentes que su componente de conexión derive de éste (que son la gran mayoría).

También vemos los dos métodos a redefinir en los descendientes, que son AssignSQL (el cual asignará la sentencia SQL al componente “query”) y SetQuery (que creará el componente “query”).

Así mismo vemos todos los métodos públicos disponibles. El propio nombre indica qué información devuelve cada uno, así que no creo que valga la pena comentarlos. La forma de devolver la información es en un parámetro (o dos) de tipo TStringList. Por ejemplo, GetTables devolverá en el parámetro TablesName las tablas definidas en la base de datos.

Para ilustrar cómo se haría un descendiente de esta clase, acompañan el ejemplo 2 clases más, una para FireDAC y otra para IBX.

Para FireDAC quedaría de la siguiente manera.

  // clase para los componentes FireDAC
  TFBMDFireDAC = class(TFBMetaData)
  protected
    procedure SetQuery; override;
    function AssignSQL(SQL: string): Boolean; override;
  public
    constructor Create(DataBase: TADConnection); reintroduce; virtual;
  end;

Como vemos, se redefinen los dos métodos abstractos para ponerles el código específico de estos componentes. También redefino el constructor para forzar que el componente database que se pase por parámetro sea el específico de los componentes con los que quiero trabajar.

Del mismo modo he hecho el específico para los IBX.

  // clase para los componentes IBX
  TFBMDIBX = class(TFBMetaData)
  protected
    procedure SetQuery; override;
    function AssignSQL(SQL: string): Boolean; override;
  public
    constructor Create(DataBase: TIBDatabase); reintroduce; virtual;
  end;

Otros componentes como DBExpress, IBObjects o ADO, funcionarían de la misma manera.

Todo esto es válido al menos hasta la versión 2.5 de Firebird, que es con la que he realizado las pruebas (Firebird 2.5.1.26351). Imagino que también lo será para Interbase, aunque no he hecho ninguna prueba.

Si alguien no tiene Firebird/Interbase instalado, le recomiendo que se descargue la versión Embedded de Firebird si no se quiere instalar el motor.

Descargar la demo y la unit aquí.

Espero os sea de utilidad.

Nos leemos

may 112013
 

Buenas,

Post in two languages / Mensaje en dos idiomas:


spanish flag En este artículo quiero explicar cómo personalizar el SplashScreen del IDE de RadStudio, es decir, cómo poner un icono y un mensaje personalizado en la pantalla de inicio del IDE. Esto es útil más que nada para los creadores de componentes o expertos para Delphi y válido desde la versión 2005.

Splash Screen IDE

Hacerlo es realmente sencillo. Para ello tenemos que seguir los siguientes pasos:

  • Escogemos una imagen en formato bmp y de tamaño 24×24 que queramos poner. El píxel inferior izquierdo se tomará como el color de transparencia.
  • La añadimos a un archivo de recursos. Si no sabemos cómo, podéis mirar la entrada anterior de este mismo blog.
  • Una vez echo ésto, sólo tendremos que usar la variable global SplashScreenServices y su método AddPluginBitmap en, por ejemplo, el procedimiento Register.

SplashScreenServices es de tipo IOTASplashScreenServices (Interface Open Tools API SplashScreen Services) definidos los dos (variable y interface) en la unit ToolsAPI, por lo que deberemos añadirla a la cláusula uses. Para no tener errores de compilación tendremos que incluir en el requires del proyecto la bpl designide (designide.dcp).

AddPluginBitmap recibe una serie de parámetros:

  • ACaption: texto que aparecerá en el splashscreen. En el ejemplo “GoogleMaps Library VCL (1.0.1 Final)”.
  • ABitmap: handle de la imagen a mostrar.
  • AIsUnRegistered: especifica si está o no registrado. En caso de no estarlo (True) se pintará en rojo.
  • ALicenseStatus: texto a mostrar después de ACaption. En el ejemplo “Licence LGPL – Copyright Xavier Martínez – cadetill”.
  • ASKUName: texto que se añadirá al caption.

Con estos sencillos pasos ya tenemos el SplashScreen del IDE personalizado con nuestros componentes o expertos.

Un ejemplo de procedimiento Register podría ser este:

procedure Register;
{$IFDEF DELPHI2005}
var
  Bmp: HBITMAP;
{$ENDIF}
begin
  // registrar componentes y editores
  RegisterPropertyEditor(TypeInfo(TAboutFrm), nil, '', TAboutGMLib);
  RegisterComponentEditor(TGMBase, TGMBaseEditor);
  RegisterComponentEditor(TGMLinkedComponent, TGMLinkedComponentEditor);

  {$IFDEF DELPHI2005}
  ForceDemandLoadState(dlDisable);
  if Assigned(SplashScreenServices) then
  begin
    Bmp := LoadBitmap(FindResourceHInstance(HInstance), 'TGMMap');
    try
      SplashScreenServices.AddPluginBitmap('GoogleMaps Library 1.0.1 Final', Bmp, False, 'Licence LGPL');
    finally
      DeleteObject(Bmp);
    end;
  end;
  {$ENDIF}
end;

Espero que os sea de utilidad.



english flag Customizing the IDE SplashScreen

In this article I would to explain how customizing the IDE SplashScreen from RadStudio, ie how to put an icon and a personalized message on start up IDE screen. This can be useful for components or experts developpers for Delphi and valid from 2005 version.

Doing this is easy. For this we need follow the following steps:

  • Seleccione una imagen con formato BMP y con tamaño de 24×24 que desea mostrar. El píxel inferior izquierda indica el color transparente.
  • Add it to a resource file. If you don’t know how, you can see the previous entry in this blog.
  • Once done this, you only need to use the global variable SplashScreenServices and their method AddPluginBitmap into, for example, the Register procedure.

SplashScreenServices is from IOTASplashScreenServices type (Interface Open Tools API SplashScreen Services) defined both (variable and interface) into the ToolsAPI unit, so you need to add it to the uses clause. To avoid compilation errors you need to add the designide bpl into the requires project (designide.dcp).

AddPluginBitmap have some parameters:

  • ACaption: text to show on splashscreen. In the exemple “GoogleMaps Library VCL (1.0.1 Final)”.
  • ABitmap: handle of the image to show.
  • AIsUnRegistered: specifies if it is or not registered. If not (True) will be painted red.
  • ALicenseStatus: text to show after the ACaption. In the example “Licence LGPL – Copyright Xavier Martínez – cadetill”.
  • ASKUName: will be appended to the caption.

With these simple steps already we have the IDE SplashScreen personalized with our components or experts.

A demo of Register procedure can be:

procedure Register;
{$IFDEF DELPHI2005}
var
  Bmp: HBITMAP;
{$ENDIF}
begin
  // register components and editors
  RegisterPropertyEditor(TypeInfo(TAboutFrm), nil, '', TAboutGMLib);
  RegisterComponentEditor(TGMBase, TGMBaseEditor);
  RegisterComponentEditor(TGMLinkedComponent, TGMLinkedComponentEditor);

  {$IFDEF DELPHI2005}
  ForceDemandLoadState(dlDisable);
  if Assigned(SplashScreenServices) then
  begin
    Bmp := LoadBitmap(FindResourceHInstance(HInstance), 'TGMMap');
    try
      SplashScreenServices.AddPluginBitmap('GoogleMaps Library 1.0.1 Final', Bmp, False, 'Licence LGPL');
    finally
      DeleteObject(Bmp);
    end;
  end;
  {$ENDIF}
end;

I hope you find it useful.

Regards

may 052013
 

Buenas,

Un archivo de recursos es un tipo de archivos que nos permite almacenar datos que posteriormente se añadirán a nuestra aplicación en la compilación para ser usados por ésta.

Lo primero que necesitaremos es un archivo de texto (script) en el que se especificarà el nombre del recurso, el tipo de recurso y el recurso propiamente dicho. Los tipos de recurso pueden ser los siguientes:

RT_ACCELERATOR Accelerator table
RT_BITMAP Bitmap resource
RT_DIALOG Dialog box
RT_FONT Font resource
RT_FONTDIR Font directory resource
RT_MENU Menu resource
RT_RCDATA Application-defined resource (raw data)
RT_STRING String-table entry
RT_MESSAGETABLE Message-table entry
RT_CURSOR Hardware-dependent cursor resource
RT_GROUP_CURSOR Hardware-independent cursor resource
RT_ICON Hardware-dependent icon resource
RT_GROUP_ICON Hardware-independent icon resource
RT_VERSION Version resource

 

Estos archivos de script llevan extensión .rc y han de tener un formato como el que sigue:

TGMMap         BITMAP    "GMMap.bmp"
RES_MAPCODE    RCDATA    "map.html"

Donde la primera columna es el nombre del recurso, la segunda es el tipo de recurso y la tercera es el recurso propiamente dicho.

Una vez tenemos nuestro fichero .rc terminado, tenemos que compilarlo. Para ello usaremos el compilador de recursos de Borand (brcc32.exe) que se distribuye con cualquier versión de Delphi y está localizado en la carpeta bin de la instalación. Así pues, desde línea de comandos ejecutamos la siguiente instrucción:

brcc32 MiArchivoDeRecursos.rc

Esto generará un archivo con el mismo nombre pero con extensión .res que será el archivo de recursos que añadiremos a nuestra aplicación. Por lo general, lo que suele hacerse es crear un archivo de comandos con esta instrucción para facilitarnos el trabajo.

Para añadirlo a la aplicación, Delphi dispone de la directiva de compilación $R que podemos usar de la siguiente manera:

{$R MiArchivoDeRecursos.RES}

Para más información sobre esta directiva visitar la documentación en Embarcadero.

Ahora ya sólo nos queda usar los recursos del fichero desde la aplicación. Para ello, la unit Windows nos provee una serie de funciones llamadas LoadXXXX donde XXXX es el tipo de recurso a cargar, como por ejemplo:

function LoadBitmap
function LoadCursor
function LoadAccelerators
function LoadString
.....

Para ver cómo funcionan, lo mejor es un ejemplo, en el cual mostraré cómo cargaríamos nuestra imagen “GMMap.bmp” llamada “TGMMap” en un TImage.

var 
  Bmp: TBitmap;
begin
 Bmp := TBitmap.Create;
 try
  Bmp.Handle := LoadBitmap(hInstance, 'TGMMap');
  Image1.Width := Bmp.Width;
  Image1.Height := Bmp.Height;
  Image1.Canvas.Draw(0,0,Bmp);
 finally
  Bmp.Free;
 end;
end;

Lo primero que hace es crear un TBitmap en el que cargaremos la imagen mediante la función LoadBitmap. Esta función recibe 2 parámetros (como la mayoría de las demás funciones), el primero es el handle a la instancia (hInstance es una variable global) y el segundo el nombre del recurso. Luego le da las medidas al TImage y muestra la imagen mediante el Canvas. Y para finalizar, libera el bitmap creado.

No obstante esto, hay muchos componentes Delphi que ya están preparados para cargar de forma directa uno de nuestros recursos encapsulando así este trabajo, como por ejemplo el propio TImage:

begin
 Image1.Picture.Bitmap.LoadFromResourceName(hInstance,'TGMMap');
end;

Con los archivos de recursos, la imaginación es nuestra única limitación a la hora de acompañar nuestra aplicación de características, ya que podemos  incluir desde una simple cadena, a archivos de música, pasando por imágenes, cursores,…. Y todo ello en un mismo archivo (nuestra aplicación) por lo que ya estará todo cargado en memoria cuando queramos usarlo.

Espero os sea de utilidad.

Nos leemos

abr 232013
 

Buenas,

Post in two languages / Mensaje en dos idiomas:


spanish flag Por defecto, FireDAC ofrece acceso a diversos motores de bases de datos tales como Oracle, DB2, SQLServer o Interbase (entre otros) pero, como cabía esperar, no trae ninguno preconfigurado para Firebird. Así pues tendremos que hacerlo nosotros (y es muy sencillo).

Para ello tenemos 2 opciones y las dos son igual de sencillas.

Modificar el driver para IB que viene por defecto

Si sólo trabajamos con Firebird, ésta puede ser nuestra opción. Para ello seguiremos estos sencillos pasos:

  • Iremos a la carpeta DB de la instalación de FireDAC.
  • Abriremos con un editor de texto el archivo ADDrivers.ini
  • Añadiremos las siguientes líneas
    [IB]
    VendorLib=<path_to_bin_folder_of_Firebird>\fbclient.dll
  • Guardamos el fichero y reiniciamos el IDE.

Con estos sencillos pasos ya tenemos configurada nuestra conexión a Firebird.

Crear nuestro propio driver para Firebird

En el caso de que queramos desarrollar tanto para Interbase como para Firebird, no nos va a quedar otra opción que crear nuestro propio driver. Para ello procederemos de la siguiente manera:

  • Igual que antes, iremos a la carpeta DB de la instalación de FireDAC.
  • Abriremos con un editor de texto el archivo ADDrivers.ini
  • Añadiremos las siguientes líneas
    [FB25_SERVER]
    BaseDriverID=IB
    VendorLibWin32=<path_to_bin_folder_of_Firebird>\fbclient.dll
  • Guardaremos el fichero y reiniciaremos el IDE.

En este caso lo que hemos hecho es crear un driver (FB25_SERVER) basado en otro (BaseDriverID=IB).

Como siempre, espero que os sea de utilidad.

Nos leemos



english flag Accessing Firebird with FireDAC

By default, FireDAC provides access to some database engines such as Oracle, DB2, SQLServer or Interbase (inter alia) but, as expected, don’t have any preconfigured to Firebird. Therefore we have to do it ourselves (and it is very easy).

For this we have two options and both are equally easy.

Modify the IB driver that comes standard

If we only work with Firebird, this can be our option. For this we follow these easy steps:

  • Go to the DB folder of FireDAC installation.
  • Open with a text editor the ADDrivers.ini file.
  • Add these lines
    [IB]
    VendorLib=<path_to_bin_folder_of_Firebird>\fbclient.dll
  • Save file and restart IDE.

With these easy steps we have configured our connexion to Firebird.

Create our own driver for Firebird

In case you want to develop for both Interbase and Firebird, we have to create our own driver. For this proceed as follows:

  • As before, go to the DB folder of FireDAC installation.
  • Open with a text editor the ADDrivers.ini file.
  • Add these lines
    [FB25_SERVER]
    BaseDriverID=IB
    VendorLibWin32=<path_to_bin_folder_of_Firebird>\fbclient.dll
  • Save file and restart IDE.

In this case that we have done is create a driver (FB25_SERVER) based on another (BaseDriverID = IB).

As always, I hope you find it useful.

Regards

abr 152013
 

Buenas,

Post in two languages / Mensaje en dos idiomas:


spanish flag Después de mucho trabajo, tanto en lo laboral como en los componentes, por fin puedo lanzar la versión 1.0.0 final de los mismos. No es que aquí se congelen, sino que he llegado ha realizar todo lo que quería hacer cuando empecé el proyecto. Seguiré evolucionándolos, actualizándolos a las nuevas versiones del API de Google Maps, corrigiendo bugs y, por qué no, añadiendo algún que otro componente/clase/funcionalidad que aun les faltan para terminar de dar cabida a toda el API de Google Maps.

Esta nueva versión trae muchos cambios, por eso os remito al archivo ChangesLog.txt que encontraréis en el paquete, no obstante sí que voy a mencionar aquí los más relevantes:

  • Cambio de licencia, pasando de ser GNU GPL (GENERAL PUBLIC LICENSE) a GNU LGPL (LESSER GENERAL PUBLIC LICENSE). De esta manera se podrán usar sin restricciones en todo tipo de aplicaciones (desde freeware a comerciales).
  • Nuevo componente TGMGroundOverlay para mostrar imágenes personalizadas en los mapas.
  • Nuevo paquete para XE.
  • Nueva clase TGMGenFunc con funciones de clase de carácter general. Se encuentra en la unit GMFunctions.
  • Se ha completado la clase TGeometry.
  • Se ha rehecho el componente TGMGeoCode para usar el API de GoogleMaps y no usar el WebService.
  • Se ha añadido un nuevo método a TGMMarker para cargar marcadores desde un DataSet (LoadFromDataSet).
  • Se han añadido los idiomas alemán y ruso (gracias Sascha y Teltikov).
  • Añadido método ZoomToPoints a las clases TPolyline, TRectangle, TGMMarker y TGMMap para aplicar el zoom necesario para ver la figura o los marcadores.
  • El método ZoomMapToAllMarkers de TGMMarker se marca como “deprecated”. En su lugar usar ZoomToPoints.
  • Se ha añadido la propiedad StreetView a TGMMap para el manejo de las características del panorama StreetView (por ahora sólo la visibilidad).
  • Se ha añadido el método OpenCloseInfoWin para poder mostrar/ocultar por código el InfoWindows relacionado a un TLinkedComponent (TMarker, TRectangle, TCircle,…).

Quiero aprovechar este mensaje para presentar también el nuevo formato de la página de la GMLib, con información más detallada y más explicaciones. Además, podremos encontrar la página en 2 idiomas, español y inglés (recordar que mi inglés no es muy bueno, así que agradeceré correcciones en la traducción).

Para terminar, dejar enlaces de interés referente a los componentes:

Como siempre, podéis descargaros esta nueva versión desde los sitios habituales.

Saludos



english flag After a lot of work, in my job and with the components, finally I can launch the new release 1.0.0 final. I will not freeze the components here, but that now has to do all I wanted to do when I started the project. I will continue their develop, updating them to new versions of Google Maps API, bug fixing and, why not, adding more components/class/functionalities that are missing to complete all Google Maps API.

This new release have a lot of changes, so I refer you to ChangesLog.txt file that you can find into the package, however I will mention here the most important:

  • Licence change, from GNU GPL (GENERAL PUBLIC LICENSE) to GNU LGPL (LESSER GENERAL PUBLIC LICENSE). So you can use it without restrictions in all type of applications (from freeware to commercials).
  • New TGMGroundOverlay component to show custom images into the maps.
  • New package for XE.
  • New TGMGenFunc class with function class general. Into GMFunctions unit.
  • The TGeometry class are finished.
  • The TGMGeoCode component is recoded to use GoogleMaps API instead of WebService.
  • New method added into TGMMarker to load markers from a DataSet  (LoadFromDataSet).
  • Added the German and Russian languages​​ (thanks Sascha and Teltikov).
  • Added ZoomToPoints method to TPolyline, TRectangle, TGMMarker and TGMMap classes for zooming required to view the figure or markers.
  • The ZoomMapToAllMarkers method of TGMMarker is marked as “deprecated”. Instead use ZoomToPoints.
  • StreetView property has been added to TGMMap for handling features of StreetView panorama (for now only the visibility).
  • Added OpenCloseInfoWin method to show/hide the InfoWindows related to a TLinkedComponent by code (TMarker, TRectangle, TCircle, …).

I want to take this message to present also the new format of the GMLib page with more detailed information and explanations. Besides, we can find the page in 2 languages​​, English and Spanish (remember that my English is not very good, so I appreciate corrections in translation).

Finally, leave links of interest regarding the components:

As always, you can download the new version from the usual places.

Regards

feb 142013
 

18 añosBuenas,

Hoy estamos de celebración, nuestra querida herramienta Delphi se hace mayor y cumple los 18 años. ¡¡Felicidades Delphi!!

Quizás ya no nos acordemos, los que tenemos más años, de las primeras versiones de Delphi, es más, de los primero programas hechos en Turbo Pascal. ¡¡Qué tiempos aquellos!!

Fue a mediados de los noventa, en 1995, cuando Borland lanzó la primera versión de Delphi, el Delphi 1 para 16 bits, haciendo que el lenguaje Pascal pasara a ser un RAD (Rapid Application Development) gracias a su potente VCL (Visual Components Library).

Desde entonces ha llovido mucho y Delphi ha pasado por varias manos (Borland – CodeGear – Embarcadero) hasta llegar a lo que es hoy, con la versión XE3 lanzada por Embarcadero el año pasado, acercándonos cada vez más a la plataforma iOS gracias a Firemonkey (o FMX).

En fin, que muchas felicidades Delphi y gracias a Borland, CodeGear y Embarcadero por darnos la posibilidad de poder tener esta herramienta durante todos estos años (y los que vengan).

happy birthday

Nos leemos

feb 102013
 

Buenas

Post in two languages / Mensaje en dos idiomas:


spanish flag Para demostrar el uso de la unidad Zip, he realizado una pequeña aplicación, la WinZLB. En ella demuestro cómo abrir o crear archivos comprimidos con esta unidad, así como añadir o extraerlos.

El funcionamiento es muy sencillo. Primero crearemos un objeto de tipo TZipFile

FZLB := TZipFile.Create;

Para crear un nuevo archivo comprimido, usaremos el método Open con TZipMode zmWrite.

FZLB.Open(SaveDialog1.FileName, zmWrite);

Para abrir un archivo existente, usaremos el método Open con TZipMode zmReadWrite

FZLB.Open(OpenDialog1.FileName, zmReadWrite);

Para añadir archivos al fichero comprimido usaremos el método Add

FZLB.Add(OpenDialog1.Files[i]);

Y para extraer los ficheros, el método Extract

FZLB.Extract(lvData.ItemIndex, FileOpenDialog1.FileName);

No tenemos que olvidarnos de la destrucción del objeto.

A parte de esto, podemos jugar con los métodos de compresión y con la información de los archivos comprimidos.

Para más información acerca de esta unidad, podéis consultar la wiki de Embarcadero.

Puedes descargar los fuentes desde aquí.



english flag To show how to use the Zip unit, I did a very small application, the WinZLB. In this application I show how open or create compressed files with this unit, as well as add files or extract it.

The operation is very simple. First we need to create an object of type TZipFile

FZLB := TZipFile.Create;

To create a new compressed file, we will use the Open method with TZipMode zmWrite.

FZLB.Open(SaveDialog1.FileName, zmWrite);

To open an existing file, we will use the Open method with TZipMode zmReadWrite

FZLB.Open(OpenDialog1.FileName, zmReadWrite);

To add files to the compressed file we will use the Add method

FZLB.Add(OpenDialog1.Files[i]);

And to extract files, the Extract method

FZLB.Extract(lvData.ItemIndex, FileOpenDialog1.FileName);

We must not forget the destruction of the object.

Apart from this, you can play with the compression methods and with the information of compressed files.

For more information about this unit, see the wiki from Embarcadero.

You can download source code from here.

ene 132013
 

Buenas,

Post in two languages / Mensaje en dos idiomas:


spanish flag Ha costado pero al final ha llegado la nueva versión de la GMLib para Delphi/BCB, la 0.1.9 beta.

Esta nueva versión trae muchos cambios, pero el más destacado es que ahora es compatible con FMX (lo cual ha implicado muchos cambios internos), y que he realizado una ayuda (formato CHM) en español y inglés mediante el programa DelphiCodeToDoc. Me he decantado por éste por ser, de los que he visto de código abierto, el que más me ha gustado. Si conocéis algún otro generador de documentación gratuito mejor que DelphiCodeToDoc, no dudéis en comentármelo para que le de un vistazo. Este programa permite generar documentos PDF, HTML y CHM (incluiré sólo la ayuda en CHM para que el paquete no engorde demasiado). La ayuda la podéis encontrar en la carpeta .\Docs\GMDoc\ES para español y .\Docs\GMDoc\EN para inglés. Como mi nivel de inglés no es muy bueno, si encontráis alguna mala traducción no dudéis en decírmelo para corregirla.

Dado que se ha hecho compatible con FMX, también he intentado hacer compatible la GMLib con el navegador DECF1 (dado que es el único compatible con FMX que conozco). No obstante, y después de mucho probar, no he logrado hacerlo funcionar con la GMLib (de forma individual sí).

Por defecto, la GMLib sólo se compilará para el navegador TWebBrowser (ya que es el único que viene por defecto en Delphi). Si queréis incluir la compatibilidad con DCEF1 (por si tenéis la curiosidad y/o queréis probar de solucionar los problemas con él ya que incluyo todo el desarrollo que he hecho) tendréis que “activarla” en el archivo gmlib.inc que encontraréis en la carpeta raíz de los componentes. Este archivo contiene una estructura como esta:

{$IFDEF VERXXX}
  {$DEFINE WEBBROWSER}
  {.$DEFINE CHROMIUM}
{$ENDIF}

Donde VERXXX es la versión de Delphi. Para activar DCEF buscaremos primero la versión de Delphi donde lo tenemos instalado y quitaremos el punto (.) que hay delante del signo dolar ($) del DEFINE de CHROMIUM y/o CHROMIUMFMX y compilar los componentes después de haber instalado DCEF (que nadie lo haga antes eh!!!). Como se puede deducir viendo esas líneas, tenemos la posibilidad de desactivar el TWebBrowser.

También he aprovechado y he hecho “limpieza” de unidades, poniendo en una sola unidad todas las clases sencillas o de apoyo.

Otra novedad es la implementación parcial de la clase Geometry de Google Maps (encoding y poly totalmente y spherical parcialmente).

Como siempre, podéis ver más explicaciones y descargar los componentes en su página.



english flag It has cost but finally has come the new version of the GMLib for Delphi / BCB, the 0.1.9 beta.

This new version brings a lot of changes, but the most important is that now is compatible with FMX (which has involved many internal changes), and  I have done a help (CHM format) in spanish and english using the DelphiCodeToDoc program. I opted for it to be the best open source program that I tested. If you know another free documentation generator better than DelphiCodeToDoc, don’t doubt to say to me and I will test it. This program generates documents in PDF, HTML and CHM format (I will include only CHM format to reduce package). You can find the help into folder .\Docs\GMDoc\ES for spanish and .\Docs\GMDoc\EN for english. Because my english is not good enough (I’m learning), if you find a bad translation tell me to correct it.

Since it has been made ​​compatible with FMX, also I have tried to do GMLib compatible with DECF1 browser (because this is the unique compatible with FMX that I know). However, and after many testing, I failed to make it work with the GMLib (individually yes).

By default, the GMLib only is compiled for TWebBrowser browser (since is the only that comes with Delphi by default). If you want to include the DCEF1 compatibility (if you have curiosity and/or want to try to fix the problems with it as I include all the development I’ve done) you need to activate it on gmlib.inc file that you can find in the root folder of the components. This file have a structure like this:

{$IFDEF VERXXX}
  {$DEFINE WEBBROWSER}
  {.$DEFINE CHROMIUM}
{$ENDIF}

Where VERXXX is the version Delphi. To activate DCEF first you need to search the version Delphi where we have installed it and we delete the point (.)  that is before the dollar sign ($) of Chromium and/or CHROMIUMFMX DEFINE and compile the components after to install DCEF (you must not do it before!). As you might guess seeing those lines, we have the ability to disable the TWebBrowser.

Also I have taken advantage and have done “cleaning” of units, putting in one unit all simple or support classes.

Other novelty is the implementation partial of the class Geometry of Google Maps (encoding and poly fully and partially spherical).

As always, you can see more explanations and download the components in your page.

ene 072013
 

Buenas

Seguro que en alguna ocasión hemos tenido que controlar el tiempo que tarda en realizarse algún trozo de código o proceso, y seguro que para ello se ha recurrido a la función Now (definida en la unidad SysUtils) o a la función del API de Windows GetTickCount. Otra opción es crearnos una unit que contenga la clase expuesta por Zarko en uno de sus artículos de delphi.about.com (ver How to Accurately Measure Elapsed Time Using High-Resolution Performance Counter).

Lo que quizás no sepamos es que, a partir de Delphi 2010, disponemos de la unit Diagnostics (System.Diagnostics para XE2 o superior por lo que será compatible para los dos frameworks, VCL y FMX). Esta unit básicamente contiene un record que describe la clase escrita por Zarko en su artículo anteriormente mencionado, incluso han conservado el mismo nombre de la clase (TStopwatch) y de varios de sus métodos. El funcionamiento también es casi igual, aquí un ejemplo:

uses
  System.Diagnostics;

procedure TForm1.Button1Click(Sender: TObject);
var
  sw: TStopWatch;
  i: Integer;
begin
  sw := TStopwatch.StartNew; // inicializa, reset y start, toto en uno
  for i := 0 to 100000 do Application.ProcessMessages;
  sw.Stop;

  Caption := IntToStr(sw.ElapsedMilliseconds);
end;

Este record (al igual que la clase de Zarko) se basa en el uso de las funciones del API de Windows QueryPerformanceFrequency la cual inicializará la propiedad Frequency (para plataforma MAC se establece una frecuencia de 100 nanosegundos) y QueryPerformanceCounter (para plataforma MAC se usa la función AbsoluteToNanoseconds).

Una vez parado el contador podemos acceder a varias de sus propiedades para obtener el tiempo transcurrido. Si queremos saber los milisegundos bastará con consultar la propiedad ElapsedMilliseconds la cual nos dará el resultado de los milisegundos transcurridos.

Otra propiedad que podemos consultar es Elapsed. Ésta es un TTimeSpan (definido en la unit TimeSpan o System.TimeSpan para XE2 o superior). Esta estructura tiene algunas propiedades que nos darán algo más de precisión que ElapsedMilliseconds llegando a los microsegundos. Para ello accederemos a su propiedad TotalMilliseconds que, al ser un Double, podremos obtener fielmente esos microsegundos.

Pero si lo que queremos es más precisión, gracias a la frecuencia (Frequency) y a la propiedad ElapsedTicks podremos llegar a nanosegundos con una sencilla división (ElapsedTicks / Frequency nos daría los segundos, sólo es cuestión de ir dividiendo por 1000 hasta llegar a la unidad deseada – segundo -> milisegundo -> microsegundo -> nanosegundo -).

Pues nada, ya conocemos una unidad más de Delphi ;)

Hasta la próxima

Nos leemos

dic 282012
 

Buenas,

Los que tengan nociones de POO (Programación Orientada a Objetos) sabrán perfectamente cómo añadir funcionalidades a una clase creando una nueva.

Por ejemplo, imaginemos que tenemos la clase TMyClass definida de la siguiente manera:

type
  TMyClass = class
    procedure MyProc;
    function MyFunc: Integer;
  end;
....
procedure TForm1.Button1Click(Sender: TObject);
var
  C1: TMyClass;
begin
  C1 := TMyClass.Create;
  C1.MyProc;
  C1.MyFunc;
  C1.Free;
end;

Si queremos añadir funcionalidad a esta clase, lo primero que se nos puede venir a la cabeza es la herencia, es decir, crear una nueva clase que contenga las nuevas funcionalidades o especializaciones, quedando como ésto:

  TMyCustomClass = class(TMyClass)
    procedure AnotherProc;
  end;
.....
procedure TForm1.Button1Click(Sender: TObject);
var
  C1: TMyCustomClass;
begin
  C1 := TMyCustomClass.Create;
  C1.MyProc;
  C1.MyFunc;
  C1.AnotherProc; // <- hacemos uso del nuevo método
  C1.Free;
end;

El “inconveniente” de hacerlo así es que, como vemos, si queremos usar el método AnotherProc tendremos que crear un objeto de tipo TMyCustomClass. Pero, ¿qué pasa si tenemos un proyecto que hace uso masivo de TMyClass? En ese caso tendríamos que cambiar todas las referencias de TMyClass por TMyCustomClass, y eso podría ser un trabajo tedioso, y más si lo que queremos es ampliar la clase, no especializarla.

Para ese menester, Delphi (de echo la POO) nos brinda con las Helper Class (también aplicable a los records). Una Helper Class es una clase que, asociada a otra clase, añade nuevos métodos o propiedades a la clase asociada y que pueden ser usados por ésta. Es una forma de expandir una clase sin necesidad de herencia.

La sintaxis de las Helper Class es la siguiente:

type
   identifierName = class|record helper [(ancestor list)] for TypeIdentifierName
     memberList
   end;

La implementación para nuestro ejemplo sería así:

type
  TMyHelperClass = class helper for TMyClass
    procedure AnotherProc;
  end;
.......
procedure TForm1.Button1Click(Sender: TObject);
var
  C1: TMyClass; // <- objeto de la clase base....
begin
  C1 := TMyCustomClass.Create;
  C1.MyProc;
  C1.MyFunc;
  C1.AnotherProc; // <- ... pero que puede usar el nuevo método
  C1.Free;
end;

Para más información podéis consultar la docwiki de Embarcadero sobre este tema en Class and Record Helpers (Delphi).

Nos leemos