Archivo

Entradas Etiquetadas ‘Ejemplos’

FMX Linux – Primeros proyectos

lunes, 15 de julio de 2019 4 comentarios

Hace poco publiqué una primera entrada introductoria sobre FMX Linux. En ella explicaba los primeros pasos para su instalación y configuración. En mi caso lo hice utilizando un sistema Ubuntu 18.04 desktop.

FMX Linux – Configuración

Captura_2019-07-10 13_37_51

Para las pruebas estoy utilizando una máquina virtual preconfigurada sobre VirtualBox. Es una buena solución, además de práctica y rápida de comenzar con un sistema ya configurado y listo para funcionar. De esta forma me puedo centrar en lo que realmente importa para estas entradas.

Captura_2019-07-10 10_46_56Para esto he utilizado la web descargarmaquinasvirtuales.com que permite descargar (como su nombre indica) máquinas de diferentes sistemas ya configuradas (tanto para VirtualBox como para VMWare), de forma que podemos tenerlas en marcha y funcionando en unos minutos.

Una vez instalado y funcionando, la idea es ir ejecutando diferentes proyectos realizados en Delphi para ver cómo se desempeñan en Linux. Son proyectos que desde que apareció Firemonkey he ido desarrollando, algunos para entradas del blog, otros de ejemplo y otros como parte de aplicaciones más grandes, un poco de todo.

La primera prueba ya la comenté en la entrada anterior, y se trata del programa “Hola Mundo!”. En este caso funciona sin problemas (como era de esperar). También comenté que con este programa realicé un Debug paso a paso básico.

Forms y animaciones

La siguiente prueba, también bastante sencilla ha sido probar con varios formularios. Abrir, cerrar y liberarlos sin problemas. Además en el primero de ellos he añadido al color de fondo una animación.

Podéis ver que el color de fondo utiliza un gradiente y una animación en loop.

El botón de cerrar también tiene un efecto de blur aplicado.

Leer más…

Internacionalizar aplicaciones con DKLang

viernes, 10 de enero de 2014 6 comentarios

1326079952_297898748_1-Traductor-Espanol-Rumano-Rumano-Espanol-Coche Ya he hablado anteriormente de varios sistemas y paquetes de  componentes para internacionalizar aplicaciones en el blog. Por ejemplo, aquí y aquí. A raiz de un comentario de otro usuario, esta vez, por necesidad en este proyecto (IBCC) he decidido probar los componentes DKLang.

A primera vista me han sorprendido,  pues cuenta con versión para XE5 incluyendo aplicaciones móviles (Android/iPhone) así como para VCL y FMX. Más adelante hablaré de la versión para XE5 (versión 5), aunque en esta entrada y para este proyecto he usado la versión 3, que es la que soporta Delphi 6. 

Para la compilación del componente, al menos en esta versión (Delphi 6), hacen falta los componentes TNT para unicode. Gratuítos y con código fuente que podéis bajar desde esta misma web o desde internet sin problemas.

Imagen1138 La compilación e instalación de los packages se hace sin problemas. Basta con añadir un par de directorios al path para que encuentre los ficheros necesarios. Una vez instalado el package de diseño, ya me aparece el componente DKLanguageController en la pestaña de System.

He echado en falta algún manual sencillo para poder seguir a la hora de empezar con un proyecto nuevo. En la web y en la ayuda no hay nada que de una guía rápida de funcionamiento. Aun así, no lleva más de 10 minutos ver cómo se maneja el componente.

Todos los ejemplos que se incluyen están realizados utilizando los componentes TNT, aunque es fácil sustituirlos en el DFM por componentes estándar y el funcionamiento es correcto.

Leer más…

Testeando XE2… (soporte nativo para ficheros ZIP)

miércoles, 14 de septiembre de 2011 16 comentarios

ZIPFile   Otra de las nuevas funcionalidades que trae la versión de XE2 es el soporte para ficheros ZIP. No es que sea especialmente transcendental, pero en mi caso la he utilizado bastante en mis desarrollos y hasta ahora lo había tenido que hacer mediante componentes externos (he  probado varios paquetes).

   Para ello se ha añadido a la unit System.ZIP, todo lo necesario para el trabajo con este tipo de ficheros.

   En sistema de trabajo es bastante sencillo. Para trabajar con un fichero de este tipo tenemos que crear un objeto de la clase TZIPFile y abrirlo (utilizando la función Open). Tenemos diferentes modos de apertura dependiendo de las operaciones a realizar con el fichero (read,write o readwrite).

// Abrir un fichero
procedure Open(ZipFileName: string; OpenMode: TZipMode); overload;
procedure Open(ZipFileStream: TStream; OpenMode: TZipMode); overload;

Leer más…

Categories: Delphi, Ejemplos, XE2 Tags: , , ,

Testeando XE2… (Estilos)

martes, 13 de septiembre de 2011 15 comentarios

Otra de las cosas de las que ya he hablado y que llaman la atención en esta nueva versión de Delphi, son los estilos. Es una primera implementación y es mejorable en muchos aspectos, pero es una paso más y un avance más para poder personalizar nuestras aplicaciones.

Hay que decir que el funcionamiento general es muy sencillo.

estilos1

Estaría bien, por ejemplo, mejorar el generador de estilos para que se pudieran importar estilos desde otros archivos similares. En mi caso el generador se ha mostrado “poco estable” cuando he intentado hacer algo más complejo (cambio de imágenes) que intentar modificar los colores del estilo cargado.

Por internet podéis descargar varios videos sobre el uso del «VCL Style Designer», que así es como se llama la herramienta para crear y modificar los estilos. Aunque es bastante sencilla e intuitiva. Una vez cargado el estilo, podemos modificar los diferentes elementos. Se puede exportar la imagen del estilo completo, modificarla desde un editor externo y luego volverla a cargar, para poder así asignar elementos de esa nueva imagen a los diferentes componentes. Se pueden cargar varias imágenes y combinar elementos de varias de ellas; Pero como he dicho en estas operaciones se ha mostrado «inestable»

Los componentes que derivan de los estándar cogen de forma correcta los estilos.  Lo que no he podido probar (y no parece que exista ahora solución -digo esto sin habérmelo mirado mucho-) es qué posibilidades hay para componentes «externos» que no deriven de uno estándar.

Una creado o modificado un estilo podemos cambiar los campos que identifican al creador, desde la misma herramienta de creación (autor, versión, página web y correo electrónico). Mientras que la previsualización desde el «Style Designer» funciona bastante bien (salvo cuando hay algun error que no muestra nada -tampoco el error-), desde el IDE de Delphi la previsualización es defectuosa (podrían haber utilizado el mismo método que desde el Designer) o «floja» por decirlo así.

En resumen, aunque el sistema tiene algunos detalles todavía faltos de pulir, en general me parece un acierto el poder dotar de esta posibilidad a las aplicaciones que se realicen. El sistema es muy sencillo de utilizar y aunque tiene algunas limitaciones, funciona bastante bien.

Actualmente al cargar instalar Delphi XE2 contamos contamos con 5 estilos que podemos utilizar.

Desde la web de Rodrigo podeís descargar algunos que él ya ha creado y es de esperar que los miembros de la comunidad vayan «colgando» los suyos propios a medida que los vayan creando (para eso supongo que se ha distribuido). Desde ahí mismo podéis consultar la explicación detallada de cómo crear nuevos estilos a partir de los existentes.

El código para cargar el estilo es bastante simple a partir de su nombre:

Var
  Estilo:TStyleManager;
  i:integer;
begin
  Estilo := TStyleManager.Create;
  try
    i := Random(6);
    Estilo.SetStyle(lEstilos[i]);
  finally
    Estilo.Free;
  end;</p>

En mi caso cargo los estilos de forma aleatoria a partir de un array donde los tengo definidos.

// Ini estilos</p>
lEstilos[0] := 'Windows';
lEstilos[1] := 'Aqua Graphite';
lEstilos[2] := 'Aqua Light Slate';
lEstilos[3] := 'Emerald Light Slate';
lEstilos[4] := 'Golden Graphite';
lEstilos[5] := 'Ruby Graphite';</p>

Hasta aquí esta primera visión. En general me ha parecido positivo. Algunos aspectos por pulir, pero creo que es un detalle más, que hace subir puntos a esta versión.

Un saludo.

Categories: Ejemplos, XE2 Tags: , , ,

Testeando XE2… (FireMonkey)

martes, 13 de septiembre de 2011 11 comentarios

Pues ya tengo Delphi XE2 en marcha. Que ya lo tengo instalado ¡Vamos!

No se a vosotros, pero a mi me pasa (y no sólo con esta versión) que una vez que la tengo instalada, lo primero que se me ocurre es probarla. Probar las nuevas características y ver qué tal funcionan.

¡¡Eso es lo que hace todo el mundo!! Diréis.

Bueno, sí y no. Me refiero a «probarlas en frío». Sin saber nada más. Sin leer nada más.
Se trata de abrir un proyecto en blanco y empezar a probar cosas. Cosas totalmente nuevas. Cosas que no existían hasta ahora. Cosas de las que he oído hablar, pero no he leído nada.

Eso me da una idea de cómo son de fáciles a la hora de asimilar. Difíciles, complicadas, rebuscadas,… Busco documentación y veo cómo se llega a ella, si la hay, cómo es de clara,…

Todo el mundo habla estos días de FireMonkey.  ;-D    ¡¡Pues allá que voy yo!!

He ido al menú de fichero y he creado una nueva aplicación de este tipo (Fire Monkey HD Application).

A priori todo parece normal; el formulario se ve algo diferente, pero todo lo demás tiene «la misma pinta».

Pongo algunos componentes, unos botones, un checkbox, una statusBar y una imagen. Mi primera sorpresa viene cuando voy a buscar la propiedad Caption del botón y no está.   8-|

¡No puede ser! Vuelvo a buscar y efectívamente no está.

En su lugar veo la propiedad Text. Pruebo y efectívamente pertenece al caption del TButton.

No parece que tenga mayor importancia…

Pongo un checkbox y al ir a buscar la propiedad Checked…  ¿??¿?¿   ¡¡No está!!    8-||

Vuelvo a revisar las propiedades y…  encuentro una propiedad IsChecked que pertenece a lo que normalmente se hace con la propiedad Checked.

Dejando de lado esto (cuestión de la adaptación de los nuevos componentes adquiridos por Embarcadero-me imagino-) el resto de cosas que he probado son bastante intuitivas y algunas bastante impresionantes. Aplicar un efecto (para lo que antes necesitábamos bastante código o componentes especializados) o realizar una rotación de una imagen es algo «trivial» (por su sencillez).

La velocidad es muy buena y la primera impresión muy positiva.

Os dejo este primer ejemplo; Bastante «tonto» por su funcionalidad, pero también por la complejidad que me ha conllevado (bastante poca).

Aquí los fuentes y el ejecutable, aunque sinceramente no creo que tengan mucho interés, desde en punto de vista de la codificación.

NOTA: Sigo buscando cómo asignarle un texto a la StatusBar que he puesto en el formulario…    ;-D

Un saludo.

Cargar fichero GPX (XML) y acceder a los datos

viernes, 4 de febrero de 2011 12 comentarios

Desde hace un tiempo estoy trabajando con ficheros de rutas o «Tracks» que provienen de dispositivos GPS (Global Positioning System). Un track o una ruta, no es más que una sucesión de coordenadas de Latitud y Longitud. Adicionalmente esos puntos pueden contener más información como Elevación, Fecha y hora,…

Existen infinidad de formatos para almacenar «Tracks» muchos de ellos provenientes de marcas concretas de receptores GPS (Garmin, DeLorme, Holux, Magellan, Tomtom…) y otros provenientes de programas de software conocidos como OziExplorer, CompeGPS o los relacionados con Google Maps.

Además existe también un formato genérico y bastante aceptado como estandard, que almacena puntos con estructura XML. Es el formato GPX (GPS eXchange Format).

La idea de esta entrada es generar código para trabajar desde delphi con un track en formato genérico GPX. Poder abrirlo y cargar los valores sin problemas y posteriormente exportarlo a KML. Aunque una vez recuperados los datos desde Delphi, la exportación a otros formatos será muy similar.

Leer un archivo GPX

Lo primero que vamos a hacer para poder leer un archivo de tipo GPX es generar una pieza intermedia que nos permita fácilmente trasladar los datos desde un XML (con una estructura determinada) a un Dataset (en nuestro caso un TClientDataset).

NOTA: Para utilizar como ejempos, en la web podéis encontrar infinidad de archivos de este tipo; Yo para las pruebas utilizaré un par de archivos de rutas distintos que podeís descargar aquí.

Para ello utilizaremos la herramienta XML Mapper de Delphi, accesible desde el menú de Tools (o también desde el Menu de programa, en el mismo grupo donde está instalado Delphi).

PASO1: Abrir fichero GPX; Aunque la extensión no es XML, sí lo es la estructura del fichero, así que la herramienta reconocerá los campos y en la parte izquierda del programa podremos ver la estructura del fichero con los diferentes campos disponibles. En este caso vemos que el fichero contiene una serie de campos correspondientes al track, como creator, versión,… y luego un «Nested Dataset»; Un Dataset «anidado», que contiene todos los puntos del track con los campos Lat (latitud), Lon (longitud), Ele (altura o elevación) y time (fecha/hora).

PASO2: Generar un Datapacket; Una vez que la estructura se ha leído correctamente podemos generar lo que XML Mapper llama un DataPacket; Que viene a ser algo así como los datos que tenemos en el XML, pero en formato de Dataset y además nos permitirá guardar el archivo de transformación (que es realmente lo importante).
Este archivo de transformación nos permitirá posteriormente leer cualquier fichero XML con la misma estructura que este y realizar la traducción a «formato dataset» de forma automática.

Para ello Desde el menú contextual seleccionamos todos los campos (Select All) y en el menú de Create escogemos Datapacket from XML (Ctrl+D). Con ello habremos generado nuestro fichero de transformación y el Dataset final.

En la parte derecha de la pantalla podemos ver la misma estructura que teníamos en el XML pero en «formato datapacket». Podemos comprobar cómo se ha realizado la transformación y que los datos se leen correctamente utilizando un botón que hay en la parte inferior de la pantalla llamado «Create and Test Transformation«.

Esto nos abre un Grid y nos muestra los datos provenientes del Dataset que se ha generado utilizando el fichero de transformación. Esto es lo que posteriormente nosotros queremos repetir en nuestro programa. Además como la estructura XML posee un «DataSet anidado», si pulsamos doble click sobre el campo trkpt, se abre un segundo Dataset que contiene todos los registros de los puntos que hay en la ruta.

En este ejemplo hemos realizado una traducción directa, pero el programa (antes de generar la transformación) nos permite modificar los campos que queremos «mapear» entre el origen (el XML) y el destino (Datapacket/Dataset), de forma que podemos aplicar determinados cambios, variaciones o filtros a los datos de entrada.

PASO3: Guardar el fichero de transformación; Una vez realizado esto sólo nos queda (lo más importante) guardar el fichero de transformación, que nos asegura que a partir de este momento podremos repetir esta misma transformación (con cualquier otro fichero de entrada que sea del mismo formato que este). Desde el Menú de File seleccionamos Save Transformation. Asignaremos anuestro fichero el nombre de GPX_To_DP.xtr (GPX to DataPacket).

Utilizar un fichero de transformación

Una vez que hemos generado nuestro fichero de tranformación GPX_To_DP.xtr, vamos a ver cómo nos puede ser útil en un programa delphi para tratar ficheros de rutas guardaos en formato GPX. Para ello utilizaremos 2 TClientDataset, no para la ruta y otro para los puntos de la ruta (a modo de Master-Detail).

Creamos un un nuevo proyecto y un formulario con los siguientes componentes:

  • Un DBGrid, un TDatasource y un TClientDataset para los puntos
  • Un DBGrid, un TDatasource, un TClientDataset y un TXMLTransformProvider para los datos del track.

Cada uno de los DBGrid va ligado al TDataset y cada uno de estos al TClientDataset.

En el XMLTransformProvider deberemos colorcar en la propiedad TransformRead, el fichero de transformación que hemos generador mediante la herramienta XML Mapper.

Como hemos visto, el campo trkpt del TClientDataset asociado al track es un campo de tipo TDataset (en concreto un TDatasetField). Para mostrarlo utilizaremos el segundo TClientDataset que hemos colocado en el formulario (cdsPoints).

En la propiedad DatasetField del TClientDataset cdsPoints asignamos el campos del primer Dataset (en este caso trkpt). Con esto visualizaremos en el segundo DBGrid los puntos que contiene el track.

Una vez hecho esto, bastará con las siguentes líneas de código para conseguir leer el contenido de cualquier fichero con formato GPX. Únicamente hay que asignar el fichero de transformación y el fichero fuente XML.

var
  b:Boolean;
  path:string;
begin
  // Abrir el diálogo
  b := OpenDialog1.Execute;
  if (b) then begin
    // Asignar fichero de transformación
    path := ExtractFilePath(Application.ExeName) + 'GPX_To_DP.xtr';
    XMLTransformProvider.TransformRead.TransformationFile := path;
    // Fichero XML (GPX) de ruta
    XMLTransformProvider.XMLDataFile := OpenDialog1.FileName;
    // Abrir el Dataset
    cdsTrack.Open;
  end;
end;

El resultado son los 2 DBGrid con los datos del Track:

Podéis descargar los ficheros de ejemplo de las rutas y el código fuente y binarios en los siguientes enlaces:

<Rutas de ejemplo>
<SOURCES DEL EJEMPLO>
<BINARIOS DEL EJEMPLO>

Una vez que tenemos los datos en formato accesible (fácil) para nosotros, lo siguiente que vamos a proponer es exportarlos a un formato KML (Google Maps) y visualizar la ruta/track sobre un Mapa de Google Maps.

Algo como lo que se ve a continuación, aunque eso lo dejo para la próxima entrada:

Un saludo.

Os adjunto algunas direcciones de interés…

_____________________________________________________________________
Referencias:

http://en.wikipedia.org/wiki/GPS_eXchange_Format
Categories: Ejemplos, Midas Tags: , , , ,

Crear accesos directos a un menú

martes, 25 de enero de 2011 4 comentarios

Hace tiempo me topé con un tema similar a este, aunque con un enfoque diferente; En aquel caso se trataba de que el usuario pudiera crear su propio menú personalizado dentro de una aplicación. Es decir, que además de las opciones propias de la aplicación, el usuario pudiera configurarse un menú con las opciones que más deseara utilizar o tener más «a mano». En este caso, y a partir de este hilo en los foros del Clubdelphi, se ha planteado la posibilidad de que un usuario pueda crearse sus propios «accesos directos» a opciones del menú.

La solución en aquel momento pasó por «volcar» el contenido del menú a otro componente (en ese caso un TreeView, de forma similar a cómo se ve en este ejemplo) y desde ese, generar la estructura del nuevo punto de menú arrastrando elementos.

Para el problema de generar accesos directos, se me antoja que se pueda usar un sistema similar.

(1) «Volcar» el contenido del menú hasta otro componente que nos permita trabajar con los elementos del menú (ya que ni el menú ni los ítems poseen opciones para arrastrar -Drag & Drop-). Este esta caso vamos a utilizar un componente (TListBox) donde almacenaremos los elementos y los apuntadores a los ítems del menú (propiedad Objects).

procedure TFormMain.Button1Click(Sender: TObject);
var
  i:integer;
  str:string;
 
  // Recursiva para obtener los subItems
  procedure GetItems(mi:TMenuItem);
  var
    i:Integer;
  begin
    for i := 0 to (mi.Count - 1) do begin
      Str := mi.Items[i].Caption;
      ListBox1.Items.AddObject(Str, mi.Items[i]);
      // SubItems de este
      GetItems(mi.Items[i]);
    end;
  end;
begin
 
  // Recorerr menu principal
  for i := 0 to (MainMenu1.Items.Count - 1) do begin
    Str := MainMenu1.Items[i].Caption;
    ListBox1.Items.AddObject(Str, MainMenu1.Items[i]);
    // SubItems de este
    GetItems(MainMenu1.Items[i]);
  end;
end;

Con este código poblamos el ListBox con los Caption(Text) de los elementos del menú, y lo que es más importante, los apuntadores a cada elementos que se guardar al utilizar AddObject.

(2) ¿Cómo crear un acceso directo que permita ejecutar una opción de menú? Para ello podemos utilizar un TImage que sobre el cual programaremos el evento OnDblClick/OnClick.

Crear el componente es sencillo, y se puede ver código de ejemplo de cómo hacerlo en estas entradas:

La idea es que cada «acceso directo» posea un apuntador al elemento de menú correspondiente para poder ejecutar el código programado en el OnClick o en la TAction asociada a ese elemento del menú. Lo lógico sería utilizar una propiedad del propio componente (Data, Object,….) que nos permitiera enlazar directamente. No es el caso del TImage, así que en el ejemplo utilizaremos el propio ListBox como el «contenedor» de los apuntadores (como una lista intermedia), aunque como he dicho, la solución ideal, sería que cada «acceso directo»  tuviera un puntero «directo» al TMenItem asociado.

El código paras crear el componente y gestionar esa asociación podría ser similar a este:

procedure TFormMain.Button2Click(Sender: TObject);
var
  img:TImage;
  mi:TMenuItem;
begin
  If  ListBox1.ItemIndex = -1 then begin
    MessageDlg('Selecciona un elemento de la llista', mtWarning, [mbOK], 0);
    Exit;
  end;
 
  // Item del menu
  mi := TMenuItem(ListBox1.Items.Objects[ListBox1.ItemIndex]);
 
  // Tiene asignado el OnClick?
  if Assigned(mi.OnClick) then begin
    // Nada
  end
  else begin
    // Tiene asignada la action?
    if Assigned(mi.Action) then begin
      //signado OnExecute
      if Assigned(mi.Action.OnExecute) then begin
        // Nada
      end
      else begin
        MessageDlg('Ese elemento no tiene nada que hacer asignado',
             mtWarning, [mbOK], 0);
        Exit;
      end;
    end
    else begin
      MessageDlg('Ese elemento no tiene nada que hacer asignado',
             mtWarning, [mbOK], 0);
      Exit;
    end;
  end;
 
  Randomize;
 
  // Elemento seleccionado
  img := TImage.Create(nil);
  img.Parent := Panel1;
  img.Height := 32;
  img.Width := 32;
  img.Left := Random(panel1.Width - img.Width);
  img.Top := Random(panel1.Height - img.Height);
  img.Stretch := True;
  img.Transparent := True;
  // El TAG es la posicion en la lista
  img.Tag := ListBox1.ItemIndex;
  // Item del menu
  mi := TMenuItem(ListBox1.Items.Objects[ListBox1.ItemIndex]);
  // Asignar la imagen
  ImageList1.GetBitmap(mi.ImageIndex, img.Picture.Bitmap);
  // Asignar el evento
  img.OnClick := MyImgClick;
end;

Primero se realizan unas comprobaciones para detectar si posee alguna acción asignada (sea OnClick o TAction) y posteriormente se crea el TImage, se configura y se asigna como TAG el ItemIndex del ListBox (que es este caso estamos utilizando como estructura intermedia para guardar el apuntador al TMenuItem).

Finalmente sólo quedar crear el procedimiento MyImgClick, que ejecutará el código asignado al elemento del menú cuando se presione sobre la imagen asociada. Se incluyen comprobaciones similares a las anteriores, por si el elemento no tiene nada asignado y se tiene en cuenta también que haya código en el OnClick del TMenuItem o exista una TAction asociada.

var
  i:integer;
  str:string;
  mi:TMenuItem;
begin
  // Test del sender
  if not (sender is TImage) then begin
    Exit;
  end
  else begin
    i := TImage(Sender).Tag;
    Str := ListBox1.Items[i];
  end;
 
  // Acceder a la opción de menú
  mi := TMenuItem(ListBox1.Items.Objects[i]);
  // Asignado código?
  if Assigned(mi.OnClick) then begin
    mi.OnClick(nil);
    Exit;
  end
  else begin
    // Tiene asignada la action?
    if Assigned(mi.Action) then begin
      // Asignado OnExecute
      if Assigned(mi.Action.OnExecute) then begin
        mi.Action.OnExecute(nil);
        Exit;
      end;
    end
  end;
 
  MessageDlg('No hay nada asignado a esa opción...', mtInformation, [mbOK], 0);
end;

Se puede mejorar y «refinar» bastante más, pero creo que la idea queda clara. A partir de aquí cada uno que «añada» lo que quiera. Cualquier sugerencia será bien recibida.

El código del ejemplo se puede descargar desde aquí.

<DESCARGAR SOURCES>

Aplicación multiidioma con GNU Gettext

viernes, 5 de marzo de 2010 26 comentarios

He probado algunos sistema de traducción para programas Delphi, entre ellos multiidioma2he de destacar los componentes de TsiLang Component Suite , que me parecen muy buenos y completos.

Estos días necesitaba un paquete gratuíto y revisando páginas y foros he llegado a GNU Gettext for Delphi and C++ Builder. Me parece un paquete muy sencillo de utilizar y por lo que he probado hasta ahora, cómodo y eficaz.

En esta entrada voy a explicar desde cero los pasos que he seguido para conseguir una aplicación multiidioma.

PREPARATIVOS

(1) GNU Gettext

Descargar desde la página «GNU Gettext for Delphi and C++ Builder» el programa de instalación. También se puede encontrar la última versión en SourceForge.

Una vez descargado el ficchero e instalado tendremos lo necesario para generar nuestros ficheros de traducción e integrarlos en el programa.

NOTA: Hay una versión especial compatible con Delphi 2009 que podemos descargar desde aquí, si estamos utilizando esta versión de Delphi..

Si se ha instalado correctamente, nos pedirá reiniciar el sistema.
Una vez instalado, si desde el explorador de Windows pulsamos Click derecho con el ratón sobre una carpeta, nos debería aparecer una nueva opción «Extract Translations to Template«.
generar_traducciones

(2) Poedit for Windows

Necesitaremos una herramienta para poder traducir (ya seamos nosotros o una tercera persona) los ficheros de traducción a los diferentes idiomas. Esto está pensado para que esa posible tercera persona (que puede no tener Dephi instalado)  pueda traducir un fichero sólo con esta herramienta (y el fichero .po como veremos después).

Descargamos el programa desde la página (http://www.poedit.net/) y lo nstalaremos.

Una vez realizados estos dos pasos previos, crearemos un pequeño proyecto de ejemplo donde realizar las pruebas.

Para ello, desde Delphi comenzamos una nueva aplicación y en el formulario generaremos algo similar a esto:

TRADUCCIÓN

(a) Preparar el  proyecto.

Una vez tenemos nuestra aplicación funcionando debemos preparnos para comentar lo necesario para traducción.
(1) Lo primero es añadir la unit gnugettext.pas a nuestro proyecto. Las units que usesn traducciones deberán tenerla en el uses.

NOTA: Si habéis realizado la instalación estandard debería estar en:
«c:\Archivos de programa\dxgettext\gnugettext.pas»

(2) Añadir al OnCreate de nuestros formularios el siguiente código:

// Traducir el formulario
TranslateComponent(Self);

(3) Preparar la estructura de directorios para organizar las traducciones. Dentro del directorio de la aplicación crearemos una carpeta llamada locale y dentro de ella un árbol de directorios similar al que se ve en la imagen.

directorios1

En este ejemplos, yo voy a utilizar 4 idiomas (es, en, fr, ca); Contando que el original va a ser el Español(es) debemos crear la estructura para los otros 3 (inglés, francés y catalán). Nos quedará un arbol de directorios como este:

directorios2

(b) Generar fichero de traducciones (.po)

(1) Para generar el fichero de traducciones del proyecto se debe extraer todas las cadena «traducibles» y almacenarlas en un fichero; Para ello basta con que, desde el explorador de Windows, pulsemos Click derecho sobre la carpeta del proyecto y seleccionemos la opción «Extract Translations to template».
Esto generará un fichero default.po en el directorio de la aplicación.

generar_traducciones

(2) Copiaremos este fichero dentro de los directorios LC_MESSAGES; Tantas copias como directorios tengamos. Después de eso tendremos varios ficheros .po; Uno en el directorio de la aplicación y uno dentro de cada carpeta de traducción:

appdir/appTest.exe
appdir/default.po
appdir/locale/en/LC_MESSAGES/default.po (Traducciones al inglés)
appdir/locale/fr/LC_MESSAGES/default.po (Traducciones al francés)
appdir/locale/ca/LC_MESSAGES/default.po (Traducciones al catalán)

(c) Traducir los ficheros.

En este punto ya se pueden abrir cada uno de los ficheros .po y traducirlo utilizando poedit. Una vez abierto el fichero con poedit, veremos una pantalla como esta:


Una vez finalizada la traducción de palabras y cerrado el programa veremos que en cada directorio aparecen 2 ficheros; El  default.po (original) y el de traducción llamado default.mo.
Repetimos esto con todos los ficheros de traducción.

(d) Cómo utilizar las traducciones en nuestro programa

Una vez los ficheros de traducción estén generados, para utilizarlos desde la aplicación añadirenmos el siguiente código a los botones de activación de los idiomas.

botones_idiomas

// para el catalán
UseLanguage('ca');
RetranslateComponent(Self);
 
// para el inglés
UseLanguage('en');
RetranslateComponent(Self);
...

Si ejecutamos la aplicación veremos que al pulsar los botones cambia es aspecto del formulario.

Para finalizar, aun nos queda un detalle, que es, que la cadena que aparece al pulsar el botón «Hola Mundo!»  no aparece traducida.

Para ello utilizaremos la función de traducción  _().
La llamada que actualmente está así:

MessageDlg('Hola', mtInformation, [mbOK], 0);

Pasará a colocarse así:

MessageDlg(_('Hola'), mtInformation, [mbOK], 0);

PASOS FINALES

Con eso nuestro programa debería quedar traducido completamente. Si movemos el EXE y la carpeta locale a cualquier otro sitio veremos que el programa funciona correctamente. Si sólo movemos el EXE, el efecto es que no aparace nada traducido.

Queda un último detalle, que es integrar las traducciones en el EXE, para que todo quede en el ejecutable y no debamos preocuparnos del directorio locale.

integrar_traducciones

Basta con pulsar click derecho sobre el EXE de nuestra aplicación desde el explorador de Windows, Seleccionar la opción «Embeb Translations» y seleccionar los ficheros de traducción que queremos integrar.

integrar_traducciones2

Con esto debería ser todo.

UPDATE: (20/04/2011)

Pues haciendo pruebas he llegado a obtener el mismo proble que comenta Sil en sus comentarios (en mi caso utilizando Delphi XE). Se trata de que al intentar añadir las traducciones al exe se obtiene un error con la referencia: «6637DB2E-62E1-4A60-AC19-C23867046A89».

Revisando por Internet, parece que se solventa sustituyendo unas constantes que hay en gnugettext.pas.

(1) Buscar esta sección en la unit gnugettext.pas:

  // DetectionSignature: used solely to detect gnugettext usage by assemble
  DetectionSignature: array[0..35] of AnsiChar='2E23E563-31FA-4C24-B7B3-90BE720C6B1A';
  // Embedded Header Begin Signature (without dynamic prefix written by assemble)
  BeginHeaderSignature: array[0..35] of AnsiChar='BD7F1BE4-9FCF-4E3A-ABA7-3443D11AB362';
  // Embedded Header End Signature (without dynamic prefix written by assemble)
  EndHeaderSignature: array[0..35] of AnsiChar='1C58841C-D8A0-4457-BF54-D8315D4CF49D';
  // Assemble Prefix (do not put before the Header Signatures!)
  SignaturePrefix: array[0..2] of AnsiChar='DXG'; // written from assemble

(2) Y sustituirlas por estas:

  // DetectionSignature: used solely to detect gnugettext usage by assemble
  DetectionSignature: array[0..35] of AnsiChar='6637DB2E-62E1-4A60-AC19-C23867046A89';
  // Embedded Header Begin Signature (without dynamic prefix written by assemble)
  BeginHeaderSignature: array[0..35] of AnsiChar='';
  // Embedded Header End Signature (without dynamic prefix written by assemble)
  EndHeaderSignature: array[0..35] of AnsiChar='';
  // Assemble Prefix (do not put before the Header Signatures!)
  SignaturePrefix: array[0..16] of AnsiChar='#0#0#0#0#0#0#0#0';//'DXG'; // written from assemble

Con este ambio ha desaparecido el error y se integran perfectamente las traducciones.

Espero que haya sido útil.

Espero comentarios, sugerencias, errores,… y demás.

PING «threaded» usando ICMP.DLL

miércoles, 16 de diciembre de 2009 4 comentarios

Hay muchas formas de implementar un PING. Una de ellas es utilizando la librería ICMP.DLL (Internet Control Message Protocol).
Basándome en la Unit Ping.pas que utiliza esta librería extraída de delphi-Central, he creado un ejemplo que la utiliza combinando las llamadas con Threads.

La unit Ping.pas se puede descargar desde la página anterior o desde aquí (raw_ping). El ejemplo crea una clase llamada TPingThread con la siguiente estructura:

  TPingThread = class(TThread)
  private
    mmResult:TMemo;
  protected
    procedure Execute; override;
    procedure UpdateResult;
  public
    PingResult:TPingResult;
    Ready:Boolean;
    constructor Create(Ping:TPingResult);
  end;

En este caso hemos añadido un TMemo para poder usarlo en la sincronización y mostrar los resultados, aunque no seía necesario (sólo para mostrarlos en este ejemplo), ya que los resultados realmente se alamacenan en la variable PingResult.

El thread utiliza el procedimiento IcmpSendEcho para realiza el Ping.

Se puede descargar el código fuente y los binarios compilados con Delphi 6:
<CODIGO FUENTE>
<BINARIO>

Se puede comparar este con un ejemplo similar realizado sin threads.
<DESCARGAR BINARIO>

Categories: Delphi, Ejemplos Tags: , ,

Inventario de software instalado (WMI)

martes, 24 de noviembre de 2009 6 comentarios

Siguiendo con los ejempos y ahondando en las posibilidades de WMI (Windows Management Instrumentation), a raiz de este hilo en los foros del ClubDelphi, referente al software instalado en nuestro ordenador, he probado a obtener la lista de programas instalados utilizando la WMI.

Para ello podemos utilizar la clase Win32_Product.
Si accedemos a su descripción en MSDN, obtenemos la siguiente descripción de la clase:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Win32_Product : CIM_Product
{
  uint16   AssignmentType;
  string   Caption;
  string   Description;
  string   IdentifyingNumber;
  string   InstallDate;
  datetime InstallDate2;
  string   InstallLocation;
  sint16   InstallState;
  string   HelpLink;
  string   HelpTelephone;
  string   InstallSource;
  string   Language;
  string   LocalPackage;
  string   Name;
  string   PackageCache;
  string   PackageCode;
  string   PackageName;
  string   ProductID;
  string   RegOwner;
  string   RegCompany;
  string   SKUNumber;
  string   Transforms;
  string   URLInfoAbout;
  string   URLUpdateInfo;
  string   Vendor;
  uint32   WordCount;
  string   Version;
};

Podemos disponer así de toda la información, referente a cada uno de los programas/aplicaciones instalados en nuestro equipo (*NOTA*).

Para ello, lo primero que debemos hacer es Importar a Delphi la librería “Microsoft WMI Scripting v1.X Library (Version 1.X)“, si no lo hemos hecho ya.
En la Introducción sobre WMI, se explica un poco más detalladamente.

Una vez importada, lo siguiente (dentro de nuestro ejemplo) es conectar con nuestro proveedor (en este caso nuestra máquina local):

1
2
3
4
5
6
    // Create the Location object
    Locator := CoSWbemLocator.Create();
    // Connect to the WMI service, with the root\cimv2 namespace
    aServices := Locator.ConnectServer(STR_EMPTY, {Server}
        STR_CIM2_ROOT, {user}STR_EMPTY, {password}STR_EMPTY,
        STR_EMPTY,STR_EMPTY, 0, nil);

A continuación, si la conexión ha sido correcta, realizamos la consulta sobre la clase Win32_Product, que nos dará la información que necesitamos.

1
2
3
// realizar la consulta
ObjSet := Services.ExecQuery('SELECT * FROM Win32_Product',
 'WQL', wbemFlagReturnImmediately and wbemFlagForwardOnly , nil);

A partir de ahí basta con recorrer los elementos obtenidos y e nuestro caso formatear algunas propiedades para mostrarlas (Caption, InstallDate, Vendor y Version), aunque todas las demás están igualmente a disponsición del programador.

Recordad de colocar en el USES las dos units comentadas antes.

Se puede descargar el código de ejemplo desde aquí y el ejecutable compilado desde aquí.

<Ejemplo_sources>

<Ejemplo_Ejecutable>

(*NOTA*): Recordemos (link), que una de las características de WMI es que permite «Administración remota», con lo que si tenemos los suficientes permisos, este código también puede servir para inventariar otras máquinas.