A la búsqueda de imágenes/iconos

lunes, 28 de febrero de 2011 4 comentarios
Share Button

Muchas veces nos encontramos en nuestros programas con que necesitamos imágenes o iconos para ilustrar acciones o elementos de nuestros menús o barras de botones. Personalmente utilizo alguno que tengo como «librería habitual»; Pero otras veces necesitamos algo diferente o las características del proyecto obligan a buscar algo nuevo.

Hay muchas librerías profesionales de iconos (de muy alta calidad), pero también hay lugares donde se pueden conseguir infinidad de iconos de forma gratuita.

He aquí un pequeño repaso de los que yo visito con mayor asiduidad.

(29/06/2015) Actualizado; He eliminado algunas webs que ya no están disponibles del listado anterior y he añaduido también algunas nuevas.

____________________________________________________________

EasyIcon EASYICON
(http://www.easyicon.net/)

Un poco lenta a aveces, pero posee gran cantidad de iconos disponibles. Una vez seleccionada una palabra para la búsqueda, posee selectores para afinar los resultados en cuanto a tamño, color (imprtante),…

_____________________________________________________________

IconGenerator

PICOL – Icon generator
(http://www.picol.org/)

Esta web en un poco diferente al resto, puesto que tal y como indica su nombre es una especie de utilidad para generar iconos personalizados (dentro de unos límites). Nos solicita un tamaño, un color, un icono «base» y un pequeño icono «badge» para la zona inderior derecha (como la flecha de un acceso directo). A partir de todo esto, ‘podemos descargar el icono generado.

_____________________________________________________________

FlatIcons

FLATICON
(http://www.flaticon.com/)

Como su nombre indica, es una web enfocada a iconos con estilo «plano» que están utilizandose más últimamente con las inteficies de las nuevas versiones de Windows. Faltan selectores por categorías o tamaños como tienen otras webs, pero es un buen lugar donde buscar si necesiotamos iconos con la característica comentada (flat).

_____________________________________________________________

IconFinderICONFINDER
(http://www.iconfinder.com)

Muy buena interface una vez realizada la búsqueda. La selección de tamaños, la visualización en pantalla,… se puede realizar una vez que se muestran los resultados y es muy ágil. La visualización de los iconos una vez seleccionados en muy buena y mustra diferentes tamaños del mismo icono junto al resultado.

_____________________________________________________________

VeryIcon
VERYICON
(http://www.veryicon.com/)

Muestra menos resultados que los anteriores. La información cuando muestra el icono seleccionado es buena y prioriza mostrar iconos del mismo «icon-set» que el seleccionado.
_____________________________________________________________


ICONS-SEARCH
(http://icons-search.com/)

Personalmente encuentro que devuelve menos resultados que los primeros buscadores que he indicado. No es de los que tiene mejor interface, pero cumple bien su función.

_____________________________________________________________

ICONSPEDIA
(http://www.iconspedia.com/)

Uno de los últimos que he descubierto. Me resulta demasiado recargado el entorno y la búsqueda de iconos. La página está muy «espesa y el número de iconos que devuelve no está entre las páginas con más resultados retorna.
_____________________________________________________________

ICONSEEKER
(http://www.iconseeker.com)

Esta también se encuentra entre las páginas que devuelve más resultados. La página de respuesta es bastante limpia y permite algun filtro (por tamaño) una vez que los resultados ya se han mostrado. No me gusta mucho que los resultados salgan mezclados en cuanto al tamaño de las imágenes, pero como ya he dcho es fácil filtrarlas una vez que estamos en los resultados.
Una vez seleccionado el icono la pantalla es clara y de las más cuidadas. También está entre mis preferidas.
_____________________________________________________________

MrIcons

Mr. ICON
(http://www.mricons.com)

Es de las últimas que he descubierto y me da la sensación de que es de las más nuevas. No es de las que más resultados devuelve y la forma de mostrarlos tampoco es de las que más me agrade. Mezcla los resultados por tamaños y no permite filtrar entre ellos de forma fácil. La página de resultados es liosa, pero en cambio una vez seleccionado el icono la página que se muestra es más clara y hay acceso rápido a iconos relacionados (por tamaño y por «icon-set»).
_____________________________________________________________

ICONARCHIVE
(http://www.iconarchive.com)

Esta se encuentra también entre las últimas que he descubierto. No ses de las que más resultados muestra (de las que menos en realidad), pero posee los mejores filtros una vez que se accede a los resultados; La página está muy cuidada y la información se muestra de una forma muy clara.
_____________________________________________________________

FINDICONS
(http://findicons.com)

También es de los últimos que he encontrado, y aunque no es de los que devuelve más iconos, los filtros a posteriori una vez completada la búsqueda son los mejores junto con los de IconArchive. La página que muestra el icono seleccionado también es muy clara y da mucha información.
_____________________________________________________________

ICONZA
(http://iconza.com/)

Es web es algo diferente al resto, pero la he incluído aquí porque en algun caso también me ha servido. Se trata de una serie de 112 iconos (generales) que se pueden personalizar para obtener muchas combinaciones. A partir de los iconos base se puede seleccionar el tamaño, el color (tonalidad general del icono) y el fondo, de forma que se obtienen muchas combinaciones a partir de los 112 originales.
_____________________________________________________________

 

 

Una vez encontrados los iconos, hay que mostrar especial atención a la licencia de cada uno de ellos, porque hay variantes.

Recomiendo que si usáis las páginas no es quedéis con una (tal vez tampoco con todas), pero realizar una búsqueda en 3 o 4 de ellas y revisar los resultados combinados suele dar muy buenos resultados (al menos eso es lo que hago yo  ;-) ).

Espero que os se a útil.

AÑADO: Cualquier enlace que no se encuentre en la lista y que consideréis interesante será bienvenido.

Un saludo.

Categories: General Tags: , ,

RxLib para RadStudio XE

jueves, 17 de febrero de 2011 8 comentarios
Share Button

A la sección de «Recopilatorio de las RxLib», he subido una nueva versión (oficiosa como las últimas) que compila bien en la nueva versión de Dephi (RAD Studio XE). Los ficheros de INCLUDE están actualizados para la nueva versión y he añadido alguna directiva de compilación para evitar errores segun diferentes configuraciones.

He modificado algunas units en la demo principal (RxDemo) de forma que compile para las últimas versiones.

Un saludo y a disfrutarla.  ;-)

TidHTTP; Descargar imágenes de una Web

jueves, 10 de febrero de 2011 Sin comentarios
Share Button

Haciendo pruebas con el componente TidHTTP me ha surgido este ejemplo y me parece interesante mostrar el proceso completo que he seguido para descargar contenido desde una web. En este caso he accedido a las imágenes en formato PNG, pero es extensible a cualquier otros contenidos.

En un primer paso se trata de obtener el contenido (source) desde una página web y a partir de ahí, extraer los links (en mi caso correspondientes a imágenes en formato PNG) y descargar los ficheros para almacenarlos en disco. Para ello utilizaremos el control TidHTTP de la Indy.

Para obtener el código fuente de la página podemos utilizar un código como este:

  // URL de la página
  StrURL := ... ;
  // Crear componente para acceder
  IdH := TIdHTTP.Create(nil);
  // proteccion para liberar
  try
    // proteccion por error
    try
      // Obtenemos la pagina
      Resp := IdH.Get(Trim(StrURL));
      // recuperamos la respuesta
      TSContent.Text  := Resp;
    except
      on E:Exception do begin
        MessageDlg('Error al acceder a la página. '+#13+#10+
                         'Mensaje: ' +
                         E.Message, mtError, [mbOK], 0);
      end;
    end;  // try
  finally
    IdH.Free;
  end;

Una vez que obtenemos la respuesta del componente almacenamos el contenido de la página en un TStrings. Si accedemos a la página: http://neftali.clubdelphi.com/temporal/ejemplo1.html

Obtendremos como resultado del Get el siguiente texto, que corresponde al código fuente de la página:

Lo siguiente que vamos a hacer es un simple «parseo» buscando los enlaces que nos interesen. En nuestro ejemplo queremos links a imágenes en formato png. Utilizando funciones de la clase SysUtils podemos obtener sin mayor problemas los links contenidos en el código; Si probáis con la página de ejemplo, deberíais obtener un único link:

http://neftali.clubdelphi.com/images/GLibWMI_paleta_1.8b.png

Por último, nos queda ver el código necesario para descargar la imagen y almacenarla en disco. Para ello podemos utilizar de nuevo el componente TidHTTP. En mi caso, y en previsión de que pueda haber varias imágenes a descargar, he creado una clase derivada de TThread para ello:

  {: Clase para descargar una imagen y almacenarla en disco.}
  TDownImageThread = class(TThread)
  private
    FURLImage: string;
    FPathImage: string;
    FFileNameImage: string;
    // Internas
    ImageName: string;
    PathURL: string;
    // Componente
    idH:TidHTTP;
  public
    // redefinir métodos
    constructor  Create(AURL:string; AOutPathImages:string);
    destructor Destroy; override;
    procedure Execute; override;
    {: URL de la imagen a descargar. }
    property URLImage:string read FURLImage write FURLImage;
    {: Path de disco local donde voy a almacenar la imagen.}
    property PathImage:string read FPathImage;
    {: Nombre completa (path+Nombre) de la imagen almacenada en disco local}
    property FileNameImage:string read FFileNameImage;
  end;

Al crear el Thread (método Create) ya pasamos como parámetros, la URL de la imagen a descargar y el Directorio inicial donde se van a guardar las imágenes encontradas.

El método Execute creamos un componente TidHTTP (igual a como lo hemos hecho antes), pero en este caso utilizamos un TFileStream para recoger el fichero que descargamos y almacenarlo en disco.

Utilizamos el Path de la URL para generar diferentes directorios para las diferentes imágenes:

···················································································································

URL: http://neftali.clubdelphi.com/images/GLibWMI_paleta_1.8b.png

Path: images

FileName: GLibWMI_paleta_1.8b.png

···················································································································

//: recupara la imagen y la guarda en disco
procedure TDownImageThread.Execute();
var
  Stream:TFileStream;
  IdH:TidHTTP;
  path:string;
  dir:string;
begin
  // Directorio de salida
  dir := AnsiReplaceText(PathURL, '/', STR_EMPTY);
  // Nombre vacío
  if (ImageName = STR_EMPTY) then begin
    Exit;
  end;
  // Path de salida
  path := IncludeTrailingBackslash(IncludeTrailingBackslash(PathImage)
          + dir) + ImageName;
  // Crearlo por si no existe
  ForceDirectories(ExtractFilePath(path));
  try
    // Stream para la imagfen
    Stream  := TFileStream.Create(path, fmCreate);
    try
      //recuperar la imagen
      IdH := TidHTTP.Create(nil);
      IdH.AllowCookies := True;
      // proteccion
      try
        IdH.Get(Trim( FURLImage), Stream);
      except
        // Error al descargar la imagen
        //..  Volcarlo al log
      end;
    finally
      // Liberar
      idH.Free;
      Stream.Free;
    end;
    // Path de salida
    FFileNameImage := path;
  except
    // error al crear el fichero
    //...  Log
  end;
end;

El resultado se puede ver en este ejemplo.

Una vez descargadas las imágenes se muestran en un componente en la misma aplicación, y desde ahí podemos acceder a la información de la imagen y realizar algunas acciones sobre ellas.

Para realizar pruebas podéis introducir, por ejemplo direcciones como:

Como siempre podéis descargar los fuentes y los binarios de ejemplos.

<DESCARGAR SOURCES>

<DESCARGAR BINARIO>


ACTUALIZACIÓN (26/02/2015): La página del ejemplo ha cambiado y ahora se accede por https. por lo tanto el enlace es:

https://sites.google.com/site/gmapsdevelopment/Home

Por lo tanto para poder descargar tanto la página inicial (de donde se extraen los enlaces),  como las imágenes en si, hay que modificar ligeramente el programa.

Hay que utilizar el componente TIdSSLIOHandlerSocket para poder utilizar SSL y añadir al proyecto as librerías de acceso a SSL.

No hay muchas modificaciones en el código, pero si queréis verlas, dejaré el código antiguo y el nuevo (compilado en Delphi 7) para que podáis compararlos.

<DESCARGAR SOURCES v2 HTTPS>

<DESCARGAR BINARIO v2 + DLL ssl>

<DESCARGAR DLL’s para acceder SSL>

NOTA: Para generar las imágenes en disco PNG he utilizado el componente “Portable Network Graphics Delphi” de Gustavo Huffenbacher Daud, que podéis encontrar y descargar de forma gratuita de Internet.


 

Un saludo.

Cargar fichero GPX (XML) y acceder a los datos

viernes, 4 de febrero de 2011 12 comentarios
Share Button

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
Share Button

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>

Utilizar ADO con Threads

viernes, 14 de enero de 2011 1 comentario
Share Button

Los threads (o hilos de ejecución), en general, se ven como un tema peliaudo; Son unas «cosas» que están ahí, son buenas (eso dice todo el mundo), pero cuando más se tarde en tener que tacarlos, mejor…  ;-D

Recuerdo que cuando empecé en esto de la programación, los threads o la «programación con múltiples hilos de ejecución» sonaba algo así como «muy difícil». A medida que va pasando el tiempo, uno se da cuenta que que no es así, y cuando has hecho unos cuantos ejemplos te das cuenta de la potencia que aportan y de que realmente sólo hay que tener algunas «precauciones» a la hora de utilizarlos.

En este misma página podéis encontrar algunos ejemplos («ping usando threads», Ejemplo visual y Ejemplo visual ampliado) de programación de hilos de ejecución con Delphi. Ejemplos bastantes sencillos y bastante inútiles, porqué no decirlo, exceptuando la utilidad de aprender cómo funcionan y poder ver un código simple de utilización.

La idea de esta entrada es ir un poco más allá. A veces nos encontramos en una aplicación, con determinadas consultas que tardan mucho tiempo y que no es imprescindible esperar a su finalización para poder continuar con la ejecución normal del programa. Las más comunes serías típicas consultas de Listados, estadísticas o determinadas operaciones que podríamos hacer en 2º plano. Esas consultas serían las candidatas ideales para poder lanzarlas en un Thread independiente del hilo principal del programa.

Para los ejemplos voy a utilizar los componentes ADO y accederemos a la Base de Datos dbdemos.mdb que viene con Delphi.

Para trabajar con ADO utilizando threads, o para lanzar consultas dentro de threads, la única condición es que la conexión (TADOConnection) se cree dentro del mismo thread. Utilizaremos para ello una «cadena de conexión» como propiedad del Thread.

La estructura de la clase podría ser algo así:

TADOSQLThread = class(TThread)
  private
    FADOQ: TADOQuery;
    FSQL: string;
    FTotalTime:string;
  public
    constructor Create(CreateSuspended:Boolean; AConnString:String;
                       ASQL:string);
    destructor Destroy; override;
    procedure Execute(); override;
    property SQL:string read FSQL write FSQL;
    property ADOQ:TADOQuery read FADOQ write FADOQ;
    property TotalTime:string read FTotalTime;
end;

Para lanzar desde una aplicación una consulta utilizando nuestra clase TADOSQLThread , debería bastar con asignar la conexión, la cadena SQL, lanzar nuestro thread y esperar a que acabe. El código podría ser este:

  //crear el Thread; Pasamos los parámetros de conexión y SQL
  th := TADOSQLThread.Create(True, AConnection, ASQL);
  // Evento finalizacion; Al finalizar el control me llegará hasta este evento.
  th.OnTerminate := TerminateThread;
  // Ejecutarlo (ponerlo en marcha)
  th.Resume;

El código operativo del thread es sencillo, se encuentra en el método Execute y lo único que hace es ejecutar la consulta; En el constructor cremos la nueva Query (con una nueva conexión) y asignamos la SQL.

constructor TADOSQLThread.Create(CreateSuspended:Boolean;
           AConnString:String; ASQL:string);
begin
  // Creamos el thread inicialmente suspendido (para asignarle las props.)
  inherited Create(CreateSuspended);
  // No liberar automáticamente
  Self.FreeOnTerminate := False;
  //crea el query
  FADOQ := TAdoquery.Create(nil);
  FADOQ.ConnectionString := AConnString;
  FADOQ.SQL.Add(ASQL);
  Self.FSQL:= ASQL;
end;
 
procedure TADOSQLThread.Execute();
begin
  inherited;
  // Ejecutar la consulta
  Self.FADOQ.Open;
end;

Ahora haría falta probar si en la ejecución de una serie de sentencia SQL con y sin threads se aprecian diferencias visibles. Hay que tener en cuenta que el utilizar o no threads también implica otras cosas.

No todo en este escenario son ventajas, hay que tenerlo en cuenta y entender el funcionamiento para sopesar si en cada caso concreto es beneficioso utilizar threads. Hay 2 grandes inconvenientes que a priori se detectan fácilmente cuando se ejecuta y se prueba un ejemplo como el que vamos a realizar.

Gasto de conexiones: En una ejecución normal, las consultas que se lanzan utilizan todas la misma conexión (ADOConnection); Una premisa que hemos marcado para trabajar con threads, es que cada thread debe funcionar con su conexión propia. Esto implica que en un caso estamos utilizando una única conexión y en el otro X conexiones concurrentes. Esto puede ser un problema en segun qué sistemas.

Sobrecarga de tiempo: El segundo problema (derivado en cierta manera del primero) es la sobrecarga de tiempo que la creación y activación de las nuevas conexiones provoca. Crear, activar (sobre todo este) y liberar las conexiones de cada thread es un tiempo añadido que hay que tener en cuenta.

Estos 2 problemas no se puede solucionar (del todo), pero sí mitigar utilizando un «pool de conexiones«; No es un tema para desarrollar ahora (tal vez más adelante), pero la idea explicada de forma sencilla, es que podemos utilizar un número máximo de conexiones (no tantas como threads). De esta forma, se asigna una conexión libre a un thread cuando la necesita, mientras haya conexiones libres; Cuando ya no quedan libres, el thread debe esperar a que una finalice para que le sea asignada. De esta forma podemos fijar el número máximo de conexiones que se utilizan y además optimizar tiempo, ya que esas conexiones se pueden «reaprovechar» de forma que no exista la necesidad de crear/activar/destruir cada una de ellas.

¿Cuando usar threads y cuando no?

La regla sencilla sería: «Cuanto más grandes y pesadas sean las consultas, más a cuenta sale utilizar threads».

Si lanzamos 20 consultas que tardan muy poco tiempo, el retraso en crear/activar las conexiones de cada una de ellas puede hacer que el tiempo de preparación sea mayor que el de la propia consulta; En ese caso estaremos «gastando» mas tiempo en «preparar» que en «consultar. Por el contrario si esas 20 consultas tardan 30 segundos cada una, el tiempo de extra de conectar para cada una de ellas puede pasar desapercibido (cuando mayor sea el tiempo de consulta, más eficiente este sistema).

Resultado de las pruebas

En las pruebas he lanzado una serie de consultas de forma secuencial. Hay que notar que el tiempo total (para consultas grandes) es sensiblemente menos cuando utilizamos threads; Pero no sólo hay que tener en cuenta el tiempo total, sino el intervalo en que tenemos acceso al resultado de cada consulta.

De forma secuencial, si la primera consulta tarda 10, la segunda 5 y la tercera 7; El tiempo total es de 22, pero los tiempos de acceso a los resultados son 10, 15 y 22 segundos respectívamente; En cambio si esto se hiciera con threads, aun suponiendo que el tiempo total fuera el mismo, los tiempo de acceso a los resultados serían 10, 5 y 7 segundos.

Select * from CustomerSelect * from Employee
Select * from CountrySelect * from items
Select * from Parts

Select * from VendorsSELECT employee.* FROM employee ORDER BY Salary,
LastName DESC , FirstName DESC , HireDate DESC

SELECT employee.* FROM employee ORDER BY Salary DESC

SELECT customer.*, orders.*, items.*, parts.*, vendors.*,
vendors.State, items.Discount, orders.SaleDate, *
FROM vendors INNER JOIN (parts INNER JOIN ((customer INNER JOIN orders
ON customer.CustNo = orders.CustNo)
INNER JOIN items ON orders.OrderNo = items.OrderNo)
ON parts.PartNo = items.PartNo)
ON vendors.VendorNo = parts.VendorNo
ORDER BY vendors.State, items.Discount DESC , orders.SaleDate

La primera prueba consta de una serie de consultas que tardan muy poco tiempo, con la Base de Datos de Access DBDEMOS.MDB (que se adjunta con Delphi). En este caso se puede ver que los tiempos de las consultas individuales son sensíblemente más bajos sin threads que con threads, debido a que las consultas con threads incluyen el tiempo de conexión. Finalmente aunque los tiempos individuales son mayores (con threads) el tiempo total queda bastante igualado (se compensa la ejecución con threads con la pérdida en las conexiones).

DATOS SIN THREADS.

DATOS CON THREADS

¿Qué pasaría si lanzáramos algunas consultas que tarden más tiempo?

Para el ejemplo he utilizado datos propios conectando a SQL Server, ya que los de la Base de Datos DBDEMOS sólo nos sirven para realizar pequeñas pruebas. Os animo a que cambieis la conexión ADO que viene en el ejemplo y configuréis vuestra propia conexión y vuestras propias consultas para realizar las pruebas.

Para la conexión basta con pulsar el botón que aparece en la parte derecha de la conexión:

Y para las consultas,  basta con tener la precaución de colocar el caracter @ al inicio de cada una de las SQL (sólo cuando empieza la consulta, no en el salto de línea).

En este caso, vemos que los resultados sí cambian sensiblemente; Lo primero que nos llama la atención, es la diferencia de tiempo total de la serie de consultas (con y sin threads); He realizado unas cuantas ejecuciones, alternando primero unas y luegos las otras y los resultados de tiempos son estos; Los primeros son las consultas normales y los segundos con threads:

Sin threads:

·············································
Tiempo total(todo): 01:19:359
Tiempo total(todo): 01:18:516
Tiempo total(todo): 01:04:500
Tiempo total(todo): 01:08:969
Tiempo total(todo): 01:09:718
·············································

Con threads:

·············································
Tiempo total con threads(todo): 01:00:000
Tiempo total con threads(todo): 00:46:800
Tiempo total con threads(todo): 00:45:484
Tiempo total con threads(todo): 00:53:078
·············································

Posteriormente he lanzado, para variar, 4 ejecuciones concurrentes de ejemplo; 2 con threads y 2 sin threads y el resultado ha sido similar (en cuanto a la diferencia):

·············································
Tiempo total(todo): 01:48:984
Tiempo total(todo): 01:50:860
·············································
·············································
Tiempo total con threads(todo): 01:27:593
Tiempo total con threads(todo): 01:32:860
·············································

CONCLUSIÓN: Aunque el ejemplo es bastante sencillo, y la clase que implementa los threads tiene poca «chicha» yo creo que se ven las posibilidades de utilizar esta opción. También debe quedar claro que no es algo para usar «siempre»; Hemos visto que dependiendo del escenario donde se utiliza puede resultar inútil e incluso contraproducente, ya que gasta más recursos que la técnica sin threads. Como ventaja tenemos que la utilización de threads, en general, nos aporta paralelismo y mayor control en la ejecución del programa (ya que evitamos el «bloqueo» en el caso de una consulta muy costosa).

El código fuente del ejemplo, el binario podeís descargarlo desde aquí.

<DESCARGAR CODIGO FUENTE>

Un saludo.

 
Categories: Artículos, Delphi, OOP Tags: , ,

[APP] IBPIndex v.1.7b; Información sobre Tracks (GPS)

sábado, 1 de enero de 2011 33 comentarios
Share Button

ACTUALIZACIÓN:  Debido a cambios en la política de Google Maps hay una nueva actualización del programa IBPIndex.  

IBPIndex es una aplicación que permite obtener información y visualizar sobre un mapa, rutas en formato GPX (GPS eXchange Format), GTM (GPS Trackmaker), KML (Google Earth), PLT (OziExplorer), TCX (Garmin Training Center), GDB (Mapsource), TRK (CompeGPS), TRL (Alan Map500 TrackLog), MPS (Garmin Mapsource), UPT (Magellan File -eXplorist-) y XOL (Swiss Map 25/50/100).

Está pensado (o enfocado) a la carga de archivos que provienen desde un dispositivo GPS.

TrackInfo

INFORMACIÓN:

  • Funciona en todas las versiones de Windows.
  • NEW Versión 1.7 (beta)  del 15/12/2011
  • La aplicación se puede usar y distribuir de forma totalmente gratuíta.

 

CARACTERÍSTICAS:

  • Visualización de los puntos de ruta sobre un gráfico; Información de puntos. Zoom, impresión, exportación…
  • Permite abrir y convertir tracks en los siguientes formatos:

GPS Trackmaker (*.GTM)
Google Earth (*.KML)
OziExplorer (*.PLT)
Garmin Training Center (*.TCX)
Mapsource (*.GDB)
CompeGPS (*.TRK)
GPS Exchange Format (*.GPX)
Alan Map500 TrackLog (*.TRL)
Garmin Mapsource (*.MPS)
Magellan File -eXplorist- (*.UPT)
Swiss Map 25/50/100 (*.XOL)

  • Exportación con interface a GPSBabel a otros formatos
  • Configuración de múltiples idiomas y fácil creación de nuevos (aplicación).
  • Gráficos de altura y velocidad de la ruta.
  • Información puntual para los puntos de la ruta.
  • Animación de rutas/recorridos gráficamente.
  • Visualización de las rutas sobre Google Maps. Exportación e impresión de mapas.
  • Visualización de Perfil de altura de los datos el track. Exportación como imagen.
  • Interface Ribbon.
  • Información del IBPIndex de la ruta (www.ibpindex.com); Impresión y exportación a PDF.
  • Actualizaciones automáticas desde Internet.
  • Visualización en Google Maps en 3D utilizando pluging de Google Earth.

 

IMAGENES:

Click sobre las imagenes para agrandar.

Interface Ribbon de la aplicación (principal) .

Interface Ribbon

Otras ventanas de la aplicación (principal de Datos, Gráfico, Google Maps, Perfil, IBPIndex,…)

Ventana principal de DatosVentana de gráficoGoogle MapsPantalla IBPIndexConversión de rutas

INSTALACIÓN:

La aplicación se distribuye en un fichero instalable (setup_IBPIndex.exe). Basta con lanzar la instalación desde Windows y seguir las instrucciones del programa.

NOTA: En Vindows Vista y Windows 7 es necesario ejecutar la instalación con privilegios de administrador.

Si tiene problemas para instalar el programa, puede consultar aquí.

TRADUCCIONES

Si deseas añadir y cooperar en el desarrollo de la aplicación con nuevos idiomas, puedes hacerlo con unos sencillos pasos:

  1. Crea un nuevo fichero de idioma basándote en algunos de los ficheros de idioma presentes en la aplicación (es.lng, ca.lng, en.lng,…).
  2. Con un editor de texto (NOTEPAD, por ejemplo) traduce las constantes manteniendo la misma estructura que tienen; Especial atención a los símbolos especiales (&  *|* ).
  3. Envíamelo a la dirección <german_ral[ARROBA]hotmail.com> con tus datos.
  4. Una vez revisada y comprobada, la añadiré al programa con tus datos como traductor.

 

DESCARGA:

La aplicación se puede descargar desde esta misma web, o desde la web de IBPIndex (sección Aplicaciones).

Descarga desde aquí.  

Descarga desde IBP Index.

 

APORTA…

Esta aplicación es gratuíta y está realizada para utilizarla a nivel personal y por puro ocio. Me gusta la programación y me gusta la actividad de BTT, así que el resultado ha sido este.

Espero que a otras personas les pueda ser útil y aceptaré/agradeceré propuestas, ideas, correcciones, sugerencias,… para mejorarla y ampliarla.

 

HISTORIAL

Versión 1.7b (15/12/2001)

* Corregidos varios errores.
* mejoras en la visualización de mapas (Google Maps).

Versión 1.4b (12/05/2011)

ERRORES CORREGIDOS
* Corregidos errores de multiidioma. Se utiliza un sistema nuevo más abierto que permite añadir y traducir a nuevos idiomas mçás fácilmente
* Corregidos problemas al cargar ficheros con caracteres extraños en el nombre.
* Corregido error al mostrar elevación de ruta.
* Corregido error en el comando de conversión.
* Corregidos bugs menosres.

MEJORAS AÑADIDAS:
* Mejoras en el multiidioma (Cambio a un sistema más abierto).
* Admite rutas el multiples formatos a la hora de cargar.
* Carga e importa rutas más rápido (optimizado)
* Índice de carga de ruta
* Ajuste de columnas.
* Añadida traducción al Inglés y catalán.

 

Versión 1.2 (20/04/2011)

ERRORES CORREGIDOS:
* Error en el cuadro de arrastrar ruta; No admite arrastre.
* Corregido error en algunos equipos, que se quejan de que no exiaste MIDAS.DLL
* Corrección a la hora de actualizar la aplicación. En algunos casos no se descargaba
correctamente el fichero.
* Errores varios en la consulta del IBPIndex.
* Errores en los datos de rutas.

MEJORAS AÑADIDAS:
* Añadidos gráficos de velocidad y altura
* Sincronización de los puntos de cada gráfico
* Animación de la ruta visualmente.
* Información extendida de los puntos de la ruta.
* Título completo para la aplicación (incluyendo versión)
* Añadida información al gráfico referente a los puntos (Hora, distancia, acumulados,…).
* Ampliados los formatos de conversión de rutas.

Versión 1.1  (01/04/2011)

Versión inicial.

 

Un saludo.

 

 

¡Feliz Navidad!

viernes, 24 de diciembre de 2010 3 comentarios
Share Button

Aprovecho este espacio para desearte unas buenas fiestas y extender mis mejores deseos para el próximo año.

¡Feliz Navidad!

Categories: Offtopic Tags:

Versión 1.5 del componente TSelectOnRuntime

viernes, 3 de diciembre de 2010 4 comentarios
Share Button

Hace unos días, a partir de unos comentarios y alguna petición sobre este componente por parte de usuarios, he corregido algun bug en el componente y he añadido algo de código. Actualizado la ayuda y recompilado las demos.

También he comprobado que funciona perfectamente en la nueva versión de Delphi, RAD Studio XE sin cambio alguno.

Corregidos unos problemas relacionados con los Anchors del componente y también cuando los componentes seleccionados tenían alineación alClient.

Para acceder a este componente puedes ir a la página de TSelectOnRuntime o acceder a la librería GLib completa desde aquí.

Categories: Componentes, Delphi Tags: ,

Presentación RAD Studio XE en Barcelona (24 Nov.)

jueves, 25 de noviembre de 2010 Sin comentarios
Share Button

El día 24 fue la presentación en Barcelona del nuevo RAD Studio XE. Siempre interesantes para conocer las novedades de las últimas versiones que las herramientas de Embarcadero. En este caso centrada en Delphi.

En la página de Facebook de Jordi Coll (Thundax Software) hay un par de fotos. No son muy buenas (supongo que Jordi las hizo desde su puesto con un teléfono), pero son muuuucho «mejores» que las que os puedo ofrecer yo.   ;-D

Dan una vista de la sala donde se ha celebrado los últimos años la presentación.

La verdad es que me hubiera gustado que Danysoft hubiera hecho alguna más y las hubieran publicado, o nos las hubiera distribuído para publicarlas en los blogs (petición que ya he hecho para la próxima vez).

La presentación fue a cargo de Luis Alfonso Rey; Interesante como siempre. Los ejemplos y demostraciones que suele mostrar no se ajustan a las típicas cosas que se ven en otras presentaciones. Suele intentar que estas sean originales y sobre temas no triviales; Lo que hace que en la mayoría de los casos tengan bastante interés (en algun otro caso resultan un poco «vagas», pero son lo que menos).  ;-)

En esta caso las demos y gran parte de la presentación ha girado en torno a DataSnap, donde parece que se encuentran algunas mejoras importantes que presenta la nueva versión. Además es un tema que por sus posibilidades «está de moda».



Por otro lado se encuentran las herramientas «auxiliares» que embarcadero ha decidido añadir a la versión XE, que merecen tema a parte y que también han tenido algo de tiempo en esta presentación.

AQTime del cual se ha realizado una pequeña demo para mostrar sus cualidades; Lanzar este profiler sobre una pequeña aplicación de muestra, para determinar los tiempos empleados por los diferentes procedimientos y comparar diferentes versiones de resultados, es insuficiente a todas luces, para mostrar las bondades y excelencias de esta herramienta. Y digo esto con conocimiento de causa, ya que lo he utilizado en su versión 3.5 y 4; Ya lo he comentado aquí antes, pero los calificativos que se me ocurren son «fantástica» e «imprescidible». Todo un acierto haber incluído esta herramienta con la nueva versión.
Actualmente se distribuye la versión 7, así que supongo que debe haber mejorado «algo» desde la versión que tuve el placer de utilizar.

FinalBuilder, que se distribuye también en su versión 7; No he tenido el placer de probarlo, pero por lo que se ha visto en la demo, es una herramienta que está condenada a utilizarse (u otra similar) en medianos/grandes proyectos. Ya no sólo por la comodidad que puede aportar al programador (generador de versiones), sino por la seguridad y robustez que puede aportar a este paso del proceso.

CodeSite. No tengo experiencia con él, pero lo que sí puedo decir es que he tenido que programar soluciones similares en alguno de mis proyectos (medianos/grandes); Así que si funciona mínimamente bien, creo que me evitará el trabajo de hacerlo por mi mismo en un futuro.

Ha habido en este caso también, tiempo para presentar algunas novedades en el IDE.  Me ha parecido «curiosa» e interesante la posibilidad de depurar los hilos de ejecución. También se ha hecho una breve reseña a las mejoras relacionadas con el tratamiento de expresiones regulares. Desconocidas para algunos de nosotros (aquí me incluyo yo, hasta ahora) e «indispensables» para otros, segun la naturaleza de los proyectos con los que estemos trabajando.

El tema de las licencia el algo aparte. De verdad, que al final vamos a tener que hacer un «Master» para poder enterarnos de cómo funcionan. Embarcadero cada vez (o al menos esa impresión me da a mi) complica más este tema. Se ha hablado en la presentación de ToolCloud en relación a esto, y que segun la web de Embarcadero es:

«Embarcadero ToolCloud centrally provisions and manages Embarcadero’s leading database management and application development tools».

Pues eso. Espero que os haya quedado ahora la cosa más clara (o al menos más que a mí).

Reseñas también a las mejoras que Embarcadero está realizando sobre el compilador para que sea más estable y rápido (descargar diapositiva PDF), en los esfuerzos para que en la web exista más material de ayuda y documentación (NOTA) y para corregir errores.
NOTA: En este caso parece que sigue olvidándose del Español como idoma a incluir es este bloque.  ;-(

Por lo demás, pues siempre es agradable cambiar el «ambiente» de la mesa de trabajo por uno diferente y ver a antiguos compañeros de trabajo para compartir experiencias con ellos.

Un saludo.

Categories: Delphi, Eventos, Offtopic Tags: ,