Otras mandangas…

julio 24, 2011 en Código, Consejo, Delphi, Entrada Diario, Mis cosas, Taller práctico

 Dicen diversos diccionarios acreditados que una mandanga, en sentido familiar, es algo así como una excusa, como en “no me vengas con mandangas y ponte a trabajar…”. También puede ser un cuento, chisme o tontería, como “¡oye! ¡siempre sales con mandangas!. Incluso, alguna vez la escuché en un sentido figurado, de persona con ganas de pleito y barullo, como en “¡Mira que te gusta la mandanga!“… :-) Y en el caso de la entrada de mi blog, pienso que anda mas cerca de esa segunda acepción, propia de quien se aburre y sale con relicarios.  :-)

En esta ocasión, he rebuscado en el baul y he subido al servidor un pequeño ejemplo que, aunque no tenga valor en si mismo, por aquello de que fuera exquisito su código,  mezclaba un tanto de todo, y me daba algo de tiempo mientras reviso y buceo en otros temas. Los días van pasando demasiado deprisa y las vacaciones hacen que se huela el descanso y el ocio, lo cual va un poco reñido con el mantenimiento del blog. =:-O 

Además, siendo justos, dado que estamos aqui como quien dice entre amigos, diría verdad si reconociera que me fue util. Pero bueno… mejor que deciros que es o que no es, lo veais vosotros mismos.

Un segundo que prepare unas escenas. Upsssss
 

 ¿Veis…? Al ejecutar el fichero Impresion.exe (el cual queda residente y visible en la barra del sistema) me permitía ir procesando el contenido de unos ficheros de texto plano, de forma que, a medida que iban siendo creados, se leyera su contenido y se dispusiese en una lista de tareas.

Los ficheros siempre tenían la misma extensión. Para nuestro ejemplo los carácteres “sjc”.

En el video, se puede ver como copio al portapapeles uno de los ficheros (concretamente el primero de ellos lanzará la ventana del explorador con la dirección del blog) y lo pego en la carpeta donde reside el ejecutable. Y como resultado sucederá lo comentado. En la vida real, no pegaba ni cortaba ningun fichero sino que lo generaba desde mi aplicación principal, permitiendo delegar esa tarea a esa especie de “servicio”.

Estas eran las lineas que generaban el fichero, cuyo nombre era siempre aleatorio: 

procedure TDemoBlog.VerWebExecute(Sender: TObject);
var 
  Parametros, FFile: String;
begin 
 inherited;

  Parametros:= '11111#Probando un ventana explorer'+
               '#3#1#http://www.sjover.com/delphi#';
  FFile:= Utilidades.PalabraAleatoria(10) +'.sjc';
  Utilidades.SaveFile(FFile, parametros);
...

Y cada fichero generaba una linea de tarea que se mostraba en la ventana de la aplicación. En la imagen inferior podeis ver la ventana en la que existe un sencillo TListbox para mostrarlas.

 Las dos funciones PalabraAleatoria( ) y SaveFile(  ), pueden ser encontradas facilmente en internet con distintas variaciones.  

procedure SaveFile(const FileName: TFileName;
                   const content: string);
begin 
  with TFileStream.Create(FileName, fmCreate) do 
    try 
      Write(Pointer(content)^, Length(content));
    finally 
      Free;
    end;
end;
function PalabraAleatoria(Longitud: integer): string;
const
  Letras = '01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var
 n : integer;
begin
  Result:='';
  for n:=1 to Longitud do
    Result:=Result+Letras[1+Random(Length(Letras))];
end;

 Si tenéis curiosidad por ojear el codigo fuente lo podeis descargar desde el enlace siguiente: 

Descargar fuentes de la entrada

Dentro de las fuentes, leed por favor el archivo leeme.txt donde os comento unas anotaciones necesarias.

Básicamente, existen dos clases: la clase que representa a la tarea a procesar (TTrabajo) y la clase que representa a la cola de tareas (TColaImpresion), con características similares a cualquier abstacción de tipo Cola. Mas abajo podeis ver un extracto del interfaz. La clase TColaImpresion, se apoya en una instancia de la clase TStrings, o lista de cadenas, que suele ser un recurso bastante comun y recurrido, para almacenar las tareas hasta que sean ejecutadas. Y finalmente, dos piezas importantes en el esquema son por un lado el componente Notificador (TShellNotification), que nos ayuda a descubrir que ha sido generado un nuevo fichero, y por otro una instancia descendiente de TTherad que va consumiendo, en un hilo secundario, las tareas de la cola, siguiendo el orden de antiguedad. 

  TColaImpresion = class(TComponent)
      ...
  protected 
    procedure DoAddTaskEvent(ATrabajo: TTrabajo); virtual;
    procedure DoDeleteTaskEvent(ATrabajo: TTrabajo); virtual;
    procedure DoHacerTaskEvent(Sender: TObject; ATrabajo: TTrabajo;
    AModulo, AFuncion: Integer); virtual;
    function DoLinkFileCreateEvent(Sender: TObject;
     Path: String): TShNotificationEvent; virtual;
    procedure Delete(Index: Integer);
  public     { Public declarations } 
    constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;
    procedure Add(const ATextoAMostrar: String; ATrabajo: TTrabajo);
    function Count: Integer;
    function ExtraeItem: TTrabajo;
    procedure VaciarCola;
    procedure UpdateCola;
    procedure DoHacer;
    procedure LeeFicheroImpresion(const AFileName: String);
    property Strings[Index: Integer]: string read Get; default;
    property Path: String read FPath write SetPath;
    property Active: Boolean read FActive write SetActive;
    property OnAddTaskEvent: TNotifyTaskEvent read FOnAddTaskEvent write SetOnAddTaskEvent;
    property OnDeleteTaskEvent: TNotifyTaskEvent read FOnDeleteTaskEvent write SetOnDeleteTaskEvent;
    property OnDoHacerEvent: TNotifyDoHacerEvent read FOnDoHacerEvent write SetOnDoHacerEvent;
  end;

La pregunta que me queda responder es quizás el por qué.   :-)

No… no es que estuviera aburrido… :-)    Habitualmente uno escribe código porque existen nuevas necesidades o requerimientos que satisfacer, en un marco de trabajo mutable y caprichoso. Es lo mas normal. Sin embargo, en ocasiones, esas circunstancias pueden ser simplemente, que estemos encarando la actualización del entorno de desarrollo, y exista -a nuestro pesar- la necesidad de replantearnos el uso en el proyecto de un determinado paquete de componentes por otro que pueda ofrecer mejoras -o sean libres, o sea requerimiento del cliente, etc…-. Así, de la noche a la mañana y sin quererlo ni beberlo, nos veamos obligados a sustituir todo el codigo que afecta a los antiguos componentes por el codigo nuevo y con la necesidad de que el sistema siga funcionando, el nuevo entorno permita compilar sin los paquetes sustituidos y ademas, uno tenga el tiempo de hacer eso sin tener que abandonar el mundo y encerrarse en una cueva… Y eso es otra historia. Y además, por experiencia, amarga…  :-)    

Realmente, estas líneas de codigo las escribí en el contexto de plantearme el cambio del sistema de impresión, de la suit de componentes que utilizaba. El proyecto contenía tropecientos mil informes distintos a cada cual mas intrincado y los generadores de informes, pese a ser muy similares unos de otros, son el peor sitio para perderse cuando uno tiene prisa. 

Así que estas lineas fueron simplemente una estrategia para dilatar ese problema en el tiempo, de forma que fuera llevadero y asumible. Las unidades que iba a extraer del proyecto fueron aisladas dentro del modulo de impresión, de forma que el proyecto compilaba sin las referencias a los paquetes que dejaba, ya en el marco del entorno actualizado. Seguia teniendo el problema de la migración, porque eso no me lo resolvía, pero sí podía compilar e iniciar la sustitución progresiva de las misma. Por otro lado, tampoco era tan mala idea. He visto otras aplicaciones trabajar así. El servidor de fax de la empresa tiene un sistema similar: Habilita una carpeta compartida al alcance de todos los clientes de fax del sistema. y para enviar un fax, son creados simultaneamente un documento de texto que parametriza el envío y otro documento que representan los datos a enviar. Y poco mas. El servidor de fax se limita a monitorizar la carpeta y va procesando los ficheros anotando el exito o fallo de la operación de envío.

En este caso simplemente habia reservado una carpeta local para depositar los ficheros que representaban cada informe. Seguro que se os ocurren algunos detalles más (que un fichero de texto plano se puede compartir, que la aplicación se comparte con cualquier otra aplicación que sea capaz de escribir en un fichero de texto, y que además, se centraliza el uso de los recursos de impresión, siendo susceptible de monitorizar el consumo del mismo, etc, etc…).

Nada mas por hoy. Espero que estas lineas os puedan ser de alguna utilidad.  

 

 

Expresiones regulares: Novedad en XE (y Parte 3.2)

mayo 31, 2011 en Delphi, Enlace interesante, Entrada Diario, Taller práctico, XE

Con esta última entrada que vamos a compartir, creo que podemos dar por cerrada la serie sobre Expresiones Regulares, al menos en lo que respecta estrictamente al blog. Sin embargo, si que me gustaría anticiparos y compartir con vosotros, de la misma forma que lo he hecho con el grupo que se abrió en Facebook, que voy a intentar mantener vivos y dinámicos algunos de los contenidos relacionados con las entradas, de forma que puedan ser consultados o comentados cuando ya no sean de inmediato interés o las entradas ya no habiliten la posibilidad de comentar porque ha caducado el plazo. Es por eso que existe una nueva opción en el menú “Temas de discusión”, inaccesible de momento, pero que se activará si Dios quiere en un breve espacio de tiempo, (que ahora mismo no os puedo confirmar): ¿una semana? ¿un mes? Ni idea. Posiblemente, sea necesario ser miembro del grupo de Delphi Básico en Facebook para acceder al mismo.

De momento he creado cuatro temas de discusión que me parecen interesantes y que están directamente relacionados con entradas de estos últimos meses o del año anterior: el framework del conjunto de entradas de Un día con los mayores, el módulo de control de presencia, el componente TPanelMiniaturas que pudimos compartir con Germán y por último, el que representa a esta última serie sobre Expresiones Regulares.

Todo esto esta un poco en lo alto, madurando y casi diría fermentando… :-)

Cualquier comenario que deseeis hacer sobre esto, lo podeis escribir en el muro del grupo de facebook (no en mi cuenta de facebook personal por favor).

Y por fin llegaron los postres…

Delphi 2007: PerlRegEx y su componente TPerlRegEx

Si hemos optado por Delphi 2007 o cualquiera de las versiones anteriores incluidas en los componentes que descargamos. ¡Enhorabuena!. Has sido un tipo listo, porque no tienes por que esperar a la renovación del IDE para disfrutar de ”algo” que te pueda ahorrar un esfuerzo y un trabajo, con independencia de que este sea más o menos grande. No… no es que ahora sea todo reducido a una expresión regular. No… la idea es mas sencilla, más simple: Si encuentras un motivo para usarla, hazlo, porque posiblemente sea una buena inversión, ya que su uso va a ahorrarte bastantes lineas de código y además no va a ser algo que va a crearte problemas en esa posible actualización del entorno, si decides actualizarte a XE.

Vamos a entender este punto:

Una vez instalado el componente en el IDE podremos usarlo de la forma habitual, como cualquier otro componente de la paleta. En mi caso, y en el primero de los ejemplos, hemos hecho uso en tiempo de diseño de forma que nos despreocupamos tanto de la creación como de la destrucción. Tan solo debemos centrarnos en 3 aspectos claves:

  • Necesitamos asignar la expresión que se va a usar.   
    • (pre.RegEx:= edCadenaBusqueda.Text;)
  • Necesitamos asignar el texto que se va a evaluar de acuerdo a esa expresión
    • (pre.Subject:= s;)
  • Y finalmente, necesitamos invocar el procedimiento que pone en marcha el proceso de analisis y compilación de la expresión
    • (if pre.Match then...) 

 A partir de ese momento, ya nos es posible evaluar si ha tenido exito gracias al método FoundMatch. También podemos conocer qué posición ocupa en primer caracter encontrado, que cumple la expresión, gracias a MatchedExpressionOffset y que longitud tiene el término MatchedExpressionLength.

 Y en el caso de querer seguir buscando la expresión, simplemente sería algo tan sencillo como usar MatchAgain, nos avanzaría hasta la siguiente posición encontrada, de la misma forma que hemos comentado en el parrafo anterior.

En el ejemplo se puede ver claramente como avanzamos de forma progresiva a lo largo del texto, hasta encontrar todas las palabras que cumplen la expresión regular.

 Término   Posición   Longitud 
Mancha 19 6
hidalgo 95 7
rocín 149 5
rocín 702 5
hidalgo 760 7

 Guardar en la mente que estamos trabajando con cadenas no Unicode sino AnsiString. Esto lo vamos a ver de reojo en el apartado próximo. Pero antes, y volviendo al comentario que iniciaba éste, la mecánica de trabajo que vamos a utilizar por ejemplo desde Delphi XE, a nivel de la unidad RegularExpressionsCore va a ser similar. Cambian los collares pero los perros son los mismos.

Podemos comparar esos chuchos:   :-)

Salvando que ahora necesitamos crear y destruir dinámicamente la instancia de TPerlRegEx, puesto que ya no tenemos un componente sino un objeto, los metodos que usamos tienen nombres similares como era de esperar. A fin de cuenta estamos usando una importación de la librería y ya se han abstraido los conceptos aplicados a la funcionalidad. Asi pues tenemos de igual forma las propiedades RegEx y Subject, que esperan respectivamente la expresión y el texto a evaluar. Seguimos teniendo Match y MatchAgain. Y tan solo respecto a nuestro ejemplo, nos ha variado que MatchedExpressionOffset ahora es MatchedOffset y MatchedExpressionLength se convierte en MatchedLength.

No parecen unos cambios muy complejos…  :-)

 

RegularExpressionsCore Vs RegularExpressions 

 

Si nos decidimos a ejecutar el ejemplo 2, que utiliza el módulo RegularExpressionsCore, obtenemos un resultado un tanto diferente del ejemplo anterior. Permitidme unos cambios rápidos en el código… veamos… cambiad tanto en el cuerpo de la accion Iniciar como en Seguir las dos lineas, de forma que sean similares a las que teníamos en el primer ejemplo:

      rchContenedorTexto.SelStart:= ConvertirOffSet(pre.MatchedOffset-1);
      rchContenedorTexto.SelLength:= Length(UTF8ToString(pre.MatchedText)); 

 por

      rchContenedorTexto.SelStart:= pre.MatchedOffset-1;
      rchContenedorTexto.SelLength:= pre.MatchedLength; 

Esta es la nueva lectura que podemos hacer: 

 Término   Posición   Longitud 
Mancha 19 6
hidalgo 96 7
rocín 150 6
rocín 717 6
hidalgo 776 7

 ¡Veis! Comparad unas y otras. Ya no nos coinciden y por lo tanto, las selecciones de texto en nuestro componente RichEdit andan digámoslo como desenfocadas. 

:-D

Esa es la magia de Unicode frente a AnsiString. Las vocales acentuadas que aparecen en algunas palabras del texto (como vivía, rócín, más, etc…) van a retornar un Offset y un valor de Length aparentemente incorrectos y ciertamente, esto no es exactamente así. Por tal motivo he utilizado en el ejemplo 2 un array de enteros y una función que corrige la posición.

El motivo de esta contrariedad es simple y de forma llana porque estamos trabajando con cadenas unicode UTF8String, y la codificación de éstas varía entre 1 y 4 bytes según el punto de codigo que consideremos. Para todos aquellos caracteres con mas de 1 byte, ese byte posterior al primero, tanto si consideramos el de dos, tres o cuatro bytes, tiene siempre la estructura binaria [10xxxxxx] donde esos primeros 2 bits (10) son fijos y el resto variable, dependiendo en conjunto de la codificacion del resto de bytes. Eso nos ha permitido de forma sencilla recontar los caracteres (descartando en el conteo los caracteres que representaban ese byte posterior)  y recalcular el offset (ConvertirOffSet( ))  

Hay un ejemplo muy bueno en Wikipedia que explica visualmente este punto. Os aconsejo la lectura así como la del libro de Marco Cantú “La guia de Delphi” (2010) donde se habla abiertamente del cambio a Unicode en nuestro entorno de desarrollo.

 http://es.wikipedia.org/wiki/UTF8

http://es.wikipedia.org/wiki/Archivo:Codificaci%C3%B3n_UTF-8.svg

 

Todo esto comentado, va a influir en nuestra decisión de elegir entre las dos formas de trabajo distintas: una por decirlo de alguna forma de más bajo nivel, RegularExpresionsCore, que manipulará tanto a nivel de parámetros como retorno de funciones cadenas UTF8String. De hecho, en el constructor de la clase, se ocultan las opciones de creación que por defecto, para que esto no pueda ser cambiado, por razones obvias. Notad como estas constantes no estan definidas en el conjunto de opciones TPerlRegExOptions, que tambien van a combinarse previa a la compilación.

constructor TPerlRegEx.Create;
begin
  inherited Create;
  FState := [preNotEmpty];
  FCharTable := pcre_maketables;
  FPCREOptions := PCRE_UTF8 or PCRE_NEWLINE_ANY;
end;

Y por otro lado RegularExpressions, una capa por encima de la anterior, permitiendo que trabajemos directamente con nuestras cadenas habituales String unicode, de forma que para nuestra selección del texto, no nos ha hecho falta ningun mecanismo adicional que haga el recalculo. Por lo demas, podeis usar sin problema tanto una forma como otra. Si os hace falta obtener el mapa de coincidencias, lo mas sencillo es que os decidais por la capa de nivel superior.

 Bueno. Nos despedimos aquí. Es posible que queden algunas dudas. Mi consejo en ese caso es que activeis la depuración de los dcus en las opciones del compilador y recorrais con puntos de parada también las clases que participan. Es una buena forma de comprender que sucede realmente entre bastidores.

 Espero que os haya gustado y que os pueda ser útil.

 

Entradas relacionadas: 

Join the forum discussion on this post

Expresiones regulares: Novedad en XE (Parte 3.1)

mayo 28, 2011 en Delphi, Entrada Diario, Taller práctico, XE

Seguimos avanzando en la serie…

Como os comentaba en la parte segunda, existía un pequeño problema, que mientras se corrige y no se corrige, nosotros podemos salvar de formas distintas. Vamos a buscar la mas sencilla para este caso concreto.

Veamos..

En nuestro caso, vamos a eliminar el boton [Seguir] dejando en una sola acción el recorrido de la búsqueda, de forma que la instancia no se sale de ambito hasta que llega al final del método, momento en el que son liberados los recursos asociados a la instancia creada de TRegEx. Eso lo podéis comprobar de una forma muy sencilla poniendo un punto de parada y comprobando que efectivamente es destruida la instancia de TPerlRegEx que es la clase que en ultima instancia liberará la memoria del proceso.

Este tipo de errores son bastante dificiles de detectar en un ambiente de trabajo normal (me refiero a fuera de lo que puede ser un ejemplo de pocas lineas) pues no siempre tienen que generar excepciones inmediatas y suelen ofuscarse entre las lineas del entorno inmediato que la rodean.

Así pues… eliminamos la acción asociada a Seguir y el componente TBitBtn que la disparaba. Tampoco nos hará falta la variable FSeguir ni los update de las acciones. Y seguidamente hacemos unos pequeños cambios en el cuerpo del procedimiento vinculado a la accion acIniciar. Nos basta alojar un el bucle que recorra las coincidencias y dejarle al usuario la opción de seguir o no. Esto último nos ha hecho valorar que lo que era un procedimiento, que habíamos llamado EvaluarResultado( ) ahora sea una función y devuelva el deseo de seguir adelante o no de nuestro usuario.

Estas son las lineas que hemos modificado: 

procedure TBusquedasConExpRegulares.acIniciarExecute(Sender: TObject);
var
  pre: TRegEx;
begin
   //vaciamos la lista de resultados
   lbxResultados.Items.Clear;
   //inciamos la creación personalizada de la expresión regular
   pre:= CrearExpresionRegular;
   // obteniendo la colección de resultados tras ejecutar la expresión
   CollOfMatch:= pre.Matches(rchContenedorTexto.Text);
   //nos valemos de un enumerador para recorrer la estructura
   EnumCollOfMatch:= CollOfMatch.GetEnumerator;
   //evaluamos el resultado de la busqueda
   while EnumCollOfMatch.MoveNext and EvaluarResultado do;
end;
function TBusquedasConExpRegulares.EvaluarResultado: Boolean;
begin
   with EnumCollOfMatch.Current do
   begin
    //añadimos la subcadena encontrada
    lbxResultados.Items.Add('La palabra ' +
                            Value + ' ' +
                            'ha sido encontrado en la posición ' +
                            IntToStr(Index-1)
                            );
    rchContenedorTexto.SelStart:= Index-1;  //Su posición en el texto inicial
    rchContenedorTexto.SelLength:= Length;  //Su longitud
    TextoBarraEstado(Length, Index-1);      //Y lo mostramos además en la barra
   end;                                     //de estado
   Result:= MessageDlg('Coincidencia encontrada...', mtWarning, [mbYes, mbNo], 0, mbYes) = mrYes;
end;

Entonces quedaría de la siguiente forma ya con los cambios que hemos hecho:

Interfaz con los cambios

unit UBusquedasConExpRegulares;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, CheckLst, ComCtrls, ActnList, RegularExpressions;
type
  TBusquedasConExpRegulares = class(TForm)
    edCadenaBusqueda: TEdit;
    bnIniciar: TButton;
    Label2: TLabel;
    Label1: TLabel;
    chlxOpciones: TCheckListBox;
    lbxResultados: TListBox;
    rchContenedorTexto: TRichEdit;
    Label3: TLabel;
    Label4: TLabel;
    Acciones: TActionList;
    acIniciar: TAction;
    sbBusqueda: TStatusBar;
    Button2: TButton;
    acReemplazar: TAction;
    Label5: TLabel;
    edCadenaAReemplazar: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure acIniciarExecute(Sender: TObject);
    procedure acReemplazarExecute(Sender: TObject);
  private
    { Private declarations }
    procedure TextoBarraEstado(ALength, AOffset: Integer);
    function EvaluarResultado: Boolean;
    function CrearExpresionRegular: TRegEx;
  public
    { Public declarations }
  end;
var
  BusquedasConExpRegulares: TBusquedasConExpRegulares;
  CollOfMatch: TMatchCollection;
  EnumCollOfMatch: TMatchCollectionEnumerator;
implementation
{$R *.dfm}
procedure TBusquedasConExpRegulares.acIniciarExecute(Sender: TObject);
var
  pre: TRegEx;
begin
   //vaciamos la lista de resultados
   lbxResultados.Items.Clear;
   //inciamos la creación personalizada de la expresión regular
   pre:= CrearExpresionRegular;
   // obteniendo la colección de resultados tras ejecutar la expresión
   CollOfMatch:= pre.Matches(rchContenedorTexto.Text);
   //nos valemos de un enumerador para recorrer la estructura
   EnumCollOfMatch:= CollOfMatch.GetEnumerator;
   //evaluamos el resultado de la busqueda
   while EnumCollOfMatch.MoveNext and EvaluarResultado do;
end;
procedure TBusquedasConExpRegulares.acReemplazarExecute(Sender: TObject);
var
  pre: TRegEx;
begin
  //vaciamos la lista de resultados
  lbxResultados.Items.Clear;
  //inciamos la creación personalizada de la expresión regular
  pre:= CrearExpresionRegular;
  //reemplazamos todas las coincidencias
  rchContenedorTexto.Text:= pre.Replace(rchContenedorTexto.Text,
                                        edCadenaAReemplazar.Text);
end;
function TBusquedasConExpRegulares.CrearExpresionRegular: TRegEx;
var
  i: Integer;
  fOp: TRegExOptions;
begin
   //vaciamos el conjunto de opciones para aplicar
   fOp:= [];    // y procedemos a obtener las validas
   for i := 0 to chlxOpciones.Count - 1 do
   begin
      if chlxOpciones.Checked[i] then
         case i of
           0: fOp:= fOp + [roIgnoreCase];
           1: fOp:= fOp + [roMultiLine];
           2: fOp:= fOp + [roSingleLine];
           3: fOp:= fOp + [roIgnorePatternSpace];
           4,5:;
           //4: fOp:= fOp + [preAnchored];
           //5: fOp:= fOp + [preUnGreedy];
           6: fOp:= fOp + [roExplicitCapture];
         end;
   end;
   // Creamos el registro para manejar la expresión regular
   Result:= TRegEx.Create(edCadenaBusqueda.Text, fOp);
end;
function TBusquedasConExpRegulares.EvaluarResultado: Boolean;
begin
   with EnumCollOfMatch.Current do
   begin
    //añadimos la subcadena encontrada
    lbxResultados.Items.Add('La palabra ' +
                            Value + ' ' +
                           'ha sido encontrado en la posición ' +
                           IntToStr(Index-1)
                           );
    rchContenedorTexto.SelStart:= Index-1;  //Su posición en el texto inicial
    rchContenedorTexto.SelLength:= Length;  //Su longitud
    TextoBarraEstado(Length, Index-1);      //Y lo mostramos en la barra
   end;                                           //de estado
   Result:= MessageDlg('Coincidencia encontrada...¿Seguir?',
                             mtWarning, [mbYes, mbNo], 0, mbYes) = mrYes;
end;
procedure TBusquedasConExpRegulares.FormCreate(Sender: TObject);
begin
  rchContenedorTexto.Lines.LoadFromFile('texto.txt', TEncoding.UTF8);
end;
procedure TBusquedasConExpRegulares.TextoBarraEstado(ALength, AOffSet: Integer);
begin
 with sbBusqueda do
 begin
   sbBusqueda.Panels[0].Text:= 'Length:'+ IntToStr(ALength);
   sbBusqueda.Panels[1].Text:= 'OffSet:'+ IntToStr(AOffSet);
 end;
end;
end.

Seguimos avanzando…

Panel de miniaturas

octubre 31, 2010 en Código, Componentes, Delphi, Taller práctico

Hace unos días, leía con interés una de las entradas de Neftalí. Como siempre, su lectura resulta útil,   y creo que eso es algo que compartiréis. :-)

En esa entrada, cuyo enlace incluyo mas abajo, se dejaban unas lineas de código que crean un ejemplo de visor de imágenes, con un control a través de las miniaturas creadas. Ese tipo de código que suele nacer a raíz de una pregunta en un foro y que luego, tras resolverla y discutir sobre ella, mueve nuestra curiosidad y nuestro ánimo a retocarlo para compartirlo con otros compañeros en un blog.

Este era el enlace de la entrada y del código fuente:

http://neftali.clubdelphi.com/?p=1087

Código completo de la entrada de Neftali

Imagen visor

A mi me pasó mas o menos lo mismo tras leer su entrada. Las dos semanas anteriores había repasado conceptos del diseño de componentes y a la vista de ese código, pensé que me sería “educativo” verme en la tesitura de trasladarlo a un componente para encapsularlo y usarlo desde el IDE. Y en eso me puse.

El blog llega a estar inactivo durante días o semanas en esos casos. Pasa algo similar a cuando me “enfrasco” con alguno de los artículos, que acaban absorbiendo el tiempo. :-D

Con Germán ya tengo algo de amistad, así que le envié un primer borrador de ese pequeño componente para ver si quería compartirlo y participar en la tarea, y de esa forma lo colgaba el también en su blog, junto a  la entrada que lo había motivado.

Estas dos imágenes, se corresponden con unas lineas de código para testearlo y probarlo. Al pulsar sobre la miniatura visualizaría la imagen sobre el display pero realmente el componente principal es el TScrollBox que contiene dichas miniaturas y el resto de controles utilizan los métodos y eventos de dicha clase TPanelMiniaturas para acceder a su funcionalidad.

Presentación

Y esta imagen, al pulsar sobre la imagen de las palmeras, haciendo unas pruebas.

Imagen cargada a través de su miniatura

Mas abajo, se puede ver el componente sobre un formulario cualquiera.  Como permite cargar las imágenes en tiempo de diseño, ya que utiliza las típicas colecciones para recrear las miniaturas, podemos cargar cualquier imagen y hacer uso de la persistencia para que se guarde y sea cargada al inicio.

Vista de diseño. Componente TPanelMiniaturas

Iremos publicando el código y las ideas que nos parezcan oportunas que puedan enriquecer el pequeño ejemplo en versión componente. Estamos en ello  :-)   El único deseo que nos mueve es colaborar y ser útiles a nuestra comunidad.

En mi caso, os iré informando a través de Facebook de los pasos que vamos dando.

Antes de finalizar, quería recordaros que ya se encuentran disponibles los videos de las sesiones de Coderage 5. Aunque se encuentran disponibles en inglés, hay algunos muy interesantes y os aconsejo que les deis un vistazo. Podéis bien descargarlos en formato mp4 o visionarlos desde la misma web de Embarcadero.

Esta es la dirección:

http://www.embarcadero.com/coderage5/sessions

Un saludo a todos.

Delphi For Android Sneak (Preview 2)

septiembre 10, 2010 en Delphi, Enlace interesante, Entrada Diario, Taller práctico, Videos

Hoy he añadido al blog el enlace de Lennie De Villiers. Y me visto muy gratamente sorprendido por el video, por lo que no he tardado demasiado a resaltarlo, tanto en el facebook, twitter como aquí mismo.

Aunque está en inglés, si yo me he enterado de la película, vosotros también…

:-D

Módulo de control de presencia

septiembre 2, 2010 en Código, Delphi, Entrada Diario, Taller práctico

Vamos a ponernos un poco las pilas ahora que ya han terminado las vacaciones. ¡Volvemos a lo nuestro!, a este breve tiempo que compartimos y que nos mantiene en contacto y vivos. Y como hacía ya bastante tiempo que no subía unas lineas de codigo, hoy casualmente, rebuscando en las carpetas perdidas, he encontrado un pequeño ejemplo que tenía preparado para el blog y que accidentalmente perdí.

Lo dicho. Estas lineas domían el sueño de los justos. :-) Son ni mas ni menos que del 2007, por lo que tienen la friolera de unos dos años.  Creo recordar que se prepararon en el transcurso de ese año, mientras colaboraba con Jose Luis Freire en el Boletín de Delphi, y casí con seguridad hubieran formado parte de los siguientes boletines, ya que finalmente se suspedió su publicación al no existir suficientes medios humanos para mantenerlo. Bueno. No nos engañemos. :-( Cualquier iniciativa gratuita corre ese peligro. Todos vamos sobrecargados de trabajo y al no ser una actividad que tenga una recompensa económica pues es dificil encontrar gente que colabore de forma regular. Pero bueno… se pudieron publicar bastantes numeros gracias a la constancia y a la entrega de Jose Luis, de ir detrás de unos y de otros, recordándonos que faltaban pocos días para la publicación.  :-)

¡Qué tiempos aquellos!  -parezco un viejete contando batallitas-

jejejeje

Sigamos…

En este caso vamos a hacer algo distinto. Normalmente preparo unos artículos introductorios para explicar el código y al final se acompaña, en el último de los artículos que forman parte de la serie. Como no tengo preparado nada, os voy a incluir el fichero con el codigo fuente del ejemplo y prepararé las entradas si es que me enviais alguna pregunta. :-D

Es un código muy sencillito. Leed el archivo leeme contenido en el zip que os cuenta un poco la película.

¿vale?

Imagen de la aplicación

Codigo

Nada mas por hoy. Espero que tengais un buen día y que seais, como se suele decir, muy felices.