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.  

 

 

La web de JM

julio 4, 2011 en Código, Componentes, Delphi, Enlace interesante, Entrada Diario, Nos deja la semana...

Quisiera compartir con vosotros lo que me parece una buena noticia para nuestra comunidad y se que algunos compañeros se van a alegrar. Y es que, tras cruzar unos correos con mi amigo Jose Manuel Navarro, me ha permitido reactivar su blog dentro de mi servidor, de forma que pueda ser accesible de nuevo.

:-)   

Así que aprovecho estas lineas para darle las gracias a Jose Manuel. Ya he comentado anteriormente, en las paginas de mi blog, que es un gran amigo, que conozco desde hace ya bastantes años.  

Hay muchos compañeros que seguían al dia de hoy solicitandole código existente en su blog (yo he recibido recientemente uno de los correos que fue el que realmente me alertó de esa posibilidad de alojar sus paginas), y a mi particularmente, me parecía que, dada la calidad de sus artículos, merecia ser rescatado del silencio, aunque realmente no se haga ésto para darle continuidad.  También quisiera destacar que antes de mi, fue Jose Luis en el rinconcito, quien alojó una gran parte de los mismos. Yo he preferido mantener íntegra la apariencia de su blog, de forma que resulta hasta curioso porque para muchos va a resultar como ver una fotografia congelada en el tiempo, alla por el año 2006.

Espero que os gusta la iniciativa y que me vayais comentando -o directamente a Jose- si existe algun problema con alguno de los enlaces.

La web de JM 

Para el código, Jose Manuel, tambien ha habilitado un repositorio en el que se han añadido tanto código como artículos en https://github.com/jmnavarro, y que puede servir en un futuro para alojar mas contenido propio, sea o no de nuestra herramienta o comunidad. Seguro que, de cualquier forma, será tan interesante su lectura como lo fue la de los articulos que escribió para Delphi y C++ Builder.

No quisiera despedir la entrada sin comentar que lamentablemente, toda la parte que hace referencia a sus escritos vinculados con Bolivia, que reflejan su humanidad y su solidaridad, finalmente no pude incluirlos, dada su extensión y la cantidad de referencias que habían de rehabilitarse, en una estructura web clásica como lo era su blog. Lo digo porque muchos de los que visitaron su pagina posiblemente se pregunten donde están y realmente me apenó verme en la tesitura de excluirlos.

Nada mas, me despido deseando que los disfruteis de nuevo.

TPanelMiniaturas (primera versión v1.1110)

noviembre 21, 2010 en Código, Componentes, Delphi, Entrada Diario

Para los programadores que trabajamos habitualmente solos, y ese ha sido mi caso durante algunos años, resulta verdaderamente agradable iniciar cualquiera de las actividades o colaboraciones que te permiten salirte del rutinario aislamiento o soledad. Es complicado no poder compartir con otras personas algunas inquietudes del día a día, por decir algo, o que las cosas haya que hacerlas de determinada forma y no de otra, porque realmente muchos de los que te rodean ignoran lo que haces, y ni siquiera lo comprenden, y te ven como el individuo que está encerrado en una habitación con varios servidores, como si uno fuera un domador y se encerrara en la función diaria con sus fieras. :-)

Así que si ha sido también vuestro caso, podréis entender que sin ningún motivo realmente importante le comentara a Neftalí la idea de crear un componente. La idea… ¡divertirse!… ¡claro!… divertirse y aprender. El artículo que planteaba la creación de las miniaturas en tiempo de ejecución era una buena excusa :-D . Y con el blog pasa algo parecido, que acaban siendo ventanas hacia el exterior, buscando aire fresco e ideas que nos hagan salir de la rutina y nos motiven. Si hay algo que he aprendido durante estos años, es que el trabajo en equipo es la primera piedra del aprendizaje y que siempre se nos pega algo, bueno o malo, de la persona con la que has compartido algo.

Ha sido muy enriquecedor colaborar con Germán, por diversos motivos, entre los cuales quisiera resaltar su profesionalidad, ya que cuando uno es capaz de tomarse con seriedad y respeto las cosas pequeñas, como puedan ser las cuatro lineas que hemos implementado para el componente, también lo hará con los proyectos de envergadura. Y eso siempre ha sido algo que valoré desde que me conozco. No hay clientes pequeños o grandes, ni proyectos grandes ni pequeños. Quien no es capaz de tratar con respeto a un cliente pequeño tampoco lo hará a uno grande y quién no sabe abordar de forma profesional un proyecto pequeño tampoco lo hará en uno grande. Esas milongas de quien dice: ¡tranquilo que cuando esto sea grande lo haré mejor son solo eso… milongas!  :-)

En fin… He disfrutado mucho estas ultimas semanas colaborando con Neftalí.

No voy a extenderme mucho mas, ya que la idea es que sirva de excusa para generar nuevas entradas, muchas de ellas a través de facebook, medio al que estoy ahora mismo dando prioridad sobre el blog, de forma que podamos tener a través de facebook y twitter el día a día y reservar las entradas propiamente dichas al blog de Delphi Básico.

Estas son las rutas al componente y a la demo que hemos preparado para que veais en acción a nuestra fiera.

;-D

Germán estaba preocupado de que la demo fuera lo mas completa y ha puesto mucho interés en que además tuviera una apariencia lo mas agradable posible.

Sources TPanelMiniaturas

Demo EXE TPanelMiniaturas

Faltan muchas cosas todavía que revisar y otras funcionalidades que implementar. Y para que veais un poco como lo hemos vivido, os he añadido una tabla que representa la evolución de cambios en el componente. Estás anotaciones existen en el código fuente pero de forma adicional le propuse a Germán mantener un registro de cambios accesible desde cualquier lugar por lo que los ubicamos en una de las base de datos de mi servidor, de forma que pudiera ser consultada por ambos.

Esta es la tabla:

! Tarea Fecha
Solicitud
Tipo Solicitado por Notas Hecho
TPanelMiniaturas
Recuperación resumen bugs y mejoras pendientes proxima versión Resumen bugs pendientes y mejoras. X
11/11/2010 Gestiones Admon VERSION 2011.11.10
Mascara protección anticopia Añadir mascara tipo imagen y propiedad activación a nivel de panel y contenedor
11/11/2010 Nueva PROX. VERSION
Funcionalidad Rotar Imagen Display 11/11/2010 Nueva
Activacion multiples filas y/o columnas de miniaturas
11/11/2010 Nueva PROX. VERSION
Funcionalidad Invertir Imagen Display 11/11/2010 Nueva Funcionalidad invertir la imagen.
Separacion de las constantes en un modulo independiente Seria conveniente separar las constantes en una unidad independiente
10/11/2010 Mejora
Funcionalidad Mover paneles Se desea poder en tiempo de ejcucion la posicion de los paneles. Nota: Ya existe en miniaturas.
10/11/2010 Nueva PROX. VERSION
Detectado problema persistencia Evaluar problema persistencia diseño con extensiones jpeg, gif
10/11/2010 Arreglar fallo PROX. VERSION
Evaluar diseño correcto ascendencia TPanelMiniaturas ¿Se ha elegido correctamente el ascendiente de TPanelMiniaturas?
10/11/2010 Mejora PROX. VERSION
Copiar y pegar imagenes desde Portapapeles Añadir una imagen desde el portapapeles.
10/11/2010 Nueva PROX. VERSION
Evaluar cambios en diseño componente por imagenes de gran tamaño
10/11/2010 Mejora
Evaluar el uso de hilos de ejecucion 10/11/2010 Mejora
Funcionalidad seleccion multiple Uso de la propiedad MultipleSelection para acciones en bloque
10/11/2010 Nueva
Funcionalidad Miniatura con ancho y alto variable Ampliar la limitacion de ancho fijo en la miniatura
10/11/2010 Mejora
Añadido evento ONCHANGE Añadido evento OnChange para cambio panel activo X
10/11/2010 Mejora VERSION 2011.11.10
Integración timer en componente Integrado timer para rotaciones automáticas. Añade propiedades Animate y AnimateInterval. X
10/11/2010 Mejora VERSION 2011.11.10
Funcionalidad eliminar panel 10/11/2010 Nueva VERSION 2011.11.10 procedimiento DELETEPANEL X
Cambio parámetros procedimiento ADDIMAGESFROMFOLDER Los parámetros no era definitivos y se revisan X
10/11/2010 Mejora VERSION 2011.11.10
Correccion bug procedimiento SETTEXT. Detectado loop en tiempo de ejecución X
10/11/2010 Arreglar fallo VERSION 2011.11.10
Propiedades ASSOCIATEDIMAGE Y ASSOCIATEDTEXT Permitiran asociar un componente externo para el display y el texto. X
10/11/2010 Nueva VERSION 2011.11.10
Funcionalidad arrastrar ficheros desde EXPLORADOR WINDOWS Permitir arrastrar ficheros desde explorador. Mas funcionalidad y utilidad para el componente X
09/11/2010 Nueva VERSION 2011.11.9
Correccion Metodo NEXT Si Itemindex = -1 decidir que hará next. Se ha tenido en cuenta estetica y se han hecho pruebas. X
09/11/2010 Arreglar fallo VERSION 2011.11.9
Ampliada funcionalidad arrastre ficheros a directorios Se amplia el arrastre de carpetas dentro del arrastre de ficheros X
09/11/2010 Mejora VERSION 2011.11.9
Propiedad SELECTIONCOLOR Y SELECTIONTYPE Reorganizacion propiedades que afectan al foco X
09/11/2010 Nueva VERSION 2011.11.9
Corrección situacion panel con respecto al scroll Añadido calculo para reubicación X
09/11/2010 Arreglar fallo VERSION 2011.11.8
Propiedad AUTOSLIDE Permite que el componente miniaturas se mueva de forma dinamica con el scroll X
08/11/2010 Nueva VERSION 2011.11.8
Liberar memoria an la seleccion visual de miniaturas Reparado bug por prevención de memoria no liberada X
08/11/2010 Arreglar fallo VERSION 2011.11.8
Correccion Procedures SELECCIONAITEM y DOCLICKPANEL Añadido codigo para no ejecutar si no ha cambiado indice. X
08/11/2010 Arreglar fallo VERSION 2011.11.8
Doble buffer Mejora en la eliminación del parpadeo X
08/11/2010 Nueva VERSION 2011.11.8
Funcionalidad resaltar foco Rutinas graficas para resaltar foco. Añadida propiedad TIPOSELECCION para gestionar tipo resalte. X
05/11/2010 Nueva VERSION 2011.11.5
Correccion Funcionalidad Metodo Click panel Se redefine el procedimiento DoClickPanel para no anular el evento. X
05/11/2010 Mejora VERSION 2011.11.5
Correccion Propiedad SETINDEXSELECTION No saltaba método. Se cambia asignacion. Ver comentarios en unidad componente. X
05/11/2010 Arreglar fallo VERSION 2011.11.5
Propiedad ITEMINDEX (gestiona foco y navegación) Esta propiedad es requerida en multiples areas. X
03/11/2010 Nueva VERSION 2011.11.3
Funcionalidad navegacion (FIRST, PRIOR, NEXT Y LAST) Procedimientos de navegacion sobre miniaturas. X
03/11/2010 Nueva VERSION 2011.11.3
Correccion lineas ADDPANEL Son eliminadas lineas redundantes que ya eran invocadas en UpdatePanels X
03/11/2010 Arreglar fallo VERSION 2011.11.3
Metodo GETCLIENTHEIGHTPANEL para calculo del Height del Panel Calculo correcto del ancho del panel. Adicion constantes margenes. X
03/11/2010 Mejora VERSION 2011.11.3
Metodos ADDIMAGESFROMFOLDER, ADDIMAGESFROMFILE Y CLEARIMAGES Nuevas funcionalidades para version release componente. X
03/11/2010 Nueva VERSION 2011.11.3
Primer borrador estructura componente Primer borrador componente para discusión X
29/10/2010 Nueva VERSION 2011.11.1
Funcionalidad Grafica Componente Ampliacion funcionalidad base metodo PAINT X
29/10/2010 Nueva VERSION 2010.11.1
Discusion diseño COMPONENTE Discusion preliminar sobre diseño proyecto y componente. Objetivos. Requerimientos, etc… X
29/10/2010 Gestiones Admon VERSION 2010.11.1
Demo Visor Imagenes
Finalizar depuracion previo al release primera version Ultimas revisiones demo previas a la primera release X
10/11/2010 Arreglar fallo VERSION 2011.11.10
Documentacion TPanelMiniaturas
Tarea elaboracion primera documentación 10/11/2010 Nueva VERSION 2011.11.10 X
Añadida primera documentación al componente X
05/11/2010 Nueva VERSION 2011.11.5
TPanelImagen
Propiedades INDEXSELECTION y MULTIPLESELECTION Funcionalidad base selección foco en componente. Creacion del índice de referencia.Ver documentación X
03/11/2010 Nueva VERSION 2011.11.3
TPanelMiniaturas Diseño
Menu contextual para edición colecciones Integración edición colección con IDE X
06/11/2010 Nueva VERSION 2011.11.10

Cualquier comentario, propuesta de mejora, será siempre bienvenida. Así como si quisierais participar y compartir la experiencia con nosotros. :-)

Bueno. Nada mas. Seguid las entradas que vaya publicando Neftalí desde su blog porque son verdaderamente interesantes. Tanto el como yo, iremos complementando la información sucesivamente.

Ahhh… un comentario que olvidaba. En la tabla que os he incluido, figuran las versiones numeradas por la fecha. Finalmente, con la que es la release, decidimos que siguiera otra pauta y cambiamos el año por el numero de versión, por lo que aparece como versión v1.1110, que tomaremos así, de ahora en adelante con motivo de seguir añadiendo y complementando el componente. No tiene mayor importancia el que sea de una forma o de otra.

Enlaces relacionados:

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.

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.

TDBLookupComboBox al escenario (Parte I)

junio 17, 2010 en Advertencia, Código, Delphi, Entrada Diario, Taller práctico

Llevaba varias semanas con la idea de hacer un par de comentarios breves sobre el componente TDBLookupComboBox y mira por donde, hoy parece que tengo unos minutos que voy a intentar aprovechar.

Así que vamos a ello. La idea es ayudar con algunas ideas a los compañeros que dan sus primeros pasos.

Este componente es bastante clásico (no es nuevo). Y está ubicado en la paleta “DataControl”, la paleta que Borland reservó en sus primeros días para alojar todos los controles de datos y se justifica, en un contexto donde el usuario selecciona un item entre los valores mostrados en una lista ligada a un conjunto de datos, para asignar un valor (como resultado de esta selección), en un campo del dataset destino. Así pues, este valor que va a ser asignado, es por regla general distinto del valor que es mostrado al usuario y también siguiendo el mismo razonamiento, formaría parte de un dato significativo de una tabla maestra respecto a su detalle (eso es algo lógico ¿no?). Digo “por regla general” ya que realmente no existe ninguna restricción que impida que los campos Keyfield y ListField se asignen al mismo nombre de campo pero esto no tendría demasiado sentido. Sería similar a la funcionalidad ofrecida por el componente TDbComboBox, salvando el hecho de que este obtiene su lista de selección sobre una lista de cadenas, cargada en memoria mediante algunos de los métodos que publica la clase TStrings.

Por poner un ejemplo que nos sirva de referencia en esta entrada, podemos imaginar una tabla de avisos muy sencilla, que nos servirá como recordatorio de una tarea, y en la que podemos anotar la fecha-hora,  el cliente, el comentario o la observación a recordar y si dicha tarea está activa o no lo está. La estructura habitual de un tabla así, podría ser similar a la generada por este script de sql:

CREATE TABLE [dbo].[Avisos] (
  [IDAviso] int IDENTITY(1, 1) NOT NULL,
  [IDCliente] int NOT NULL,
  [Fecha] datetime NOT NULL,
  [Comentario] varchar(80) NULL,
  [Activo] char(1) NULL,
  PRIMARY KEY CLUSTERED ([IDAviso])
)
ON [PRIMARY]
GO

Y siguiendo el ejemplo, se supone que debería existir otra tabla ligada a los clientes, que vamos a limitar a tres campos que puedan ser significativos, en este contexto (el identificador del cliente, el código del cliente y finalmente su nombre).

CREATE TABLE [dbo].[CLIENTES] (
  [IDCliente] int IDENTITY(1, 1) NOT NULL,
  [Codigo] int NOT NULL,
  [Nombre] varchar(40) NOT NULL,
  PRIMARY KEY CLUSTERED ([IDCliente])
)
ON [PRIMARY]
GO

Podría además existir definida en la base de datos la relación de clave ajena que permite vincular los valores almacenados en el campo IDCliente de la tabla avisos con los valores existentes en la tabla de clientes, pero a efectos de estos comentarios no es algo relevante, por lo que yo creo que podemos obviarlo y simplemente suponer que existe.

Para acompañar la entrada, he querido también preparar un pequeño ejemplo que recree lo que estamos compartiendo pero, como no he querido usar una conexión a base de datos, para no complicar el escenario con algo que tampoco es imprescindible, he añadido en el módulo de código un procedimiento para recrear la creación de nuestros datasets, que se ejecuta durante la creación del formulario.

procedure TMain.InicializacionDatosEjemplo;

Ya tenemos casi todos los ingredientes.

Si ojeais el procedimiento para inicializar los datos y la estructura del dataset, vereis que la tabla de Avisos contiene tanto el IDCliente como los campos Codigo y Nombre, aunque no pertenezcan a dicha tabla sino a la de Clientes.  Os incluyo una imagen que os muestra los campos que contienen cada ClientDataSet (en el codigo de inicialización he incluido para que sea mas didáctico, unas rutinas para crear los campos persistentes que están comentadas, ya que la creación la hice en tiempo de diseño)

Convenimos que podría ser algo razonable, fruto de una consulta sql similar a:

SELECT
  dbo.Avisos.IDAviso,
  dbo.Avisos.IDCliente,
  dbo.CLIENTES.Codigo as CodigoCliente,
  dbo.CLIENTES.Nombre as NombreCliente,
  dbo.Avisos.Fecha,
  dbo.Avisos.Comentario,
  dbo.Avisos.Activo
FROM
dbo.Avisos INNER JOIN dbo.Clientes ON
(dbo.CLIENTES.IDCliente = dbo.Avisos.IDCliente)

Esto no es nada extraño. Sería un efecto similar al haber definido los campos CodigoCliente y NombreCliente como Lookup vinculados a una consulta abierta al dataset del cliente, en lugar de haber sido creados como tipo Data, que es el caso referido. No… no digo que esté mal o bien  :-)  y si estas dando tus primeros pasos quizás lo veas hasta lógico que sea indicado. A mi también me pasaba… Pero la picaresca es muy útil y nos basta una pequeña “trampa” legal para evitar que los susodichos campos sean actualizables, obra y gracia de nuestro TClientDataSet. ¿Hay alguna razón de que todos los campos sean actualizables, contrastables y participen en la sentencia retornada a la base de datos…?

Ya sabeis por donde voy ¿no? Nos basta una simple asignacion a false en los valores pfInUpdate y pfInWhere de las opciones providerflags de cada uno de los dos campos. Y quedan los dos a nuestra merced, para que puedan ser alterados durante el proceso de grabación y confirmación de cambios (sin que afecten a nada). Eso sí… la sentencia sql debería referir en la parte que vincula una tabla con otra tras el FROM, siempre en primer lugar la tabla que va a ser actualizada, que en este caso concreto es la tabla de Avisos.

Seguimos avanzado en el ejemplo y diseñamos un formulario, que va a mostrar una rejilla en la parte superior y lo que pudiera ser una ficha editable en la parte inferior. El poner la rejilla es para hacerlo un poco más gráfico y visual y que se aprecie como dos cosas distintas, puesto que en todos los frameworks que hemos compartidos han existido estos dos elementos.

Esta es la imagen de nuestro único formulario:

Descargar el fichero fuente

Ahora nos resta escribir unas lineas de código, en el Evento OnValidate del campo CodigoCliente:

procedure TMain.cdsAvisosCodigoClienteValidate(Sender: TField);
begin
  if not Sender.IsNull then
  begin
     if cdsClientes.Locate('Codigo', Sender.Value, []) then
     begin
       cdsAvisos.FieldByName('IDCliente').Value:= cdsClientes.FieldByName('IDCliente').Value;
       cdsAvisos.FieldByName('NombreCliente').Value:= cdsClientes.FieldByName('Nombre').Value;
     end
     else raise Exception.Create('No se ha encontrado el cliente');
  end
  else begin
     cdsAvisos.FieldByName('IDCliente').Clear;
     cdsAvisos.FieldByName('NombreCliente').Clear;
  end;
end;

El efecto que estamos logrando es el de coordinar de una forma sencilla, tanto que pueda haber sido usado el selector de clientes como la casilla en la que el usuario va a introducir el código, dato que en un porcentaje alto de casos es sabido de antemano por el usuario.

Eso es algo bastante normal y corriente, que existan junto con la clave primaria, otras claves naturales que conviven y se garantizan mediante indices únicos. Imaginemos una tabla de pedidos (por poner un ejemplo claro), con una clave primaria que podría ser de tipo entero, incremental. Y una clave natural formada por el Ejercicio, la Empresa y el numero de pedido (y quizás la serie también si la empresa hiciera uso de series). La clave primaria en esos casos nunca es del dominio del usuario sino que es usada internamente por la aplicación para apuntar a los registros, mientras que la clave natural, en cambió, si es usada por el usuario al interactuar con el interfaz de la aplicación.

Pensareis: si hubieramos apuntado el campo KeyField de nuestro TDBLookupComboBox a IDCliente tambien hubiera funcionado sin ese código añadido en el evento OnValidate ¿no?.  Pues sí, pero fácilmente crearíais referencias circulares entre ambos, al intentar coexistir y modificarse uno a otro tras la acción del usuario. Al apuntar los dos componentes a CodigoCliente, cualquier modificación en uno de ellos, automáticamente modificará al otro.

Finalmente, habría que asignar los valores de las propiedades, tanto en el TDBEdit como en el TDBLookupComboBox. Veamoslo:

Primero en el TDBEdit. (el punto rojo nos indica la asignación)

También modificamos los valores de las propiedades implicadas en el selector. La conexión con el componente ClientDataSet vinculado a la tabla clientes se hace a través de ListSource, ListField y KeyField.

Esta podría ser una captura de la ejecución del código fuente que, aunque no resulte deslumbrante puesto que estamos compartiendo algo muy básico, al menos sí puede ser de utilidad para quien haya tenido dudas porque se enfrente por primera vez a montar estructuras similares. Descargad el código y comentáis lo que creáis oportuno.

Por hoy nada mas. Espero que os haya servido de ayuda.

La parte II, que posiblemente cerrará estos comentarios,  nos valdrá para reconocer algunas carencias del componente y ahondar en estas reflexiones que compartimos.