Boletín Pascal #23
Los ejemplos completos de código fuente de este número están disponibles para descargar.
![]() |
![]() |
Boletín Pascal #23 - 23-JUN-2001 INDICE 1. UNAS PALABRAS DEL EDITOR 2. HASH TABLES - Por Alirio A. Gavidia B. - Cómo tomar una decisión - Las alternativas – orden - Más cerca del "case..of" - Pros y contras - Las listas o tablas "Hash" - Enfrentando la manera de hallar el índice - Método de multiplicación - Manipulando cadenas de caracteres - Conclusión 3. KYLIX DESKTOP DEVELOPER EDITION POR SOLO $199! 4. CARGANDO UN ARCHIVO CON UNA IMAGEN JPEG EN UN BITMAP 5. PE EXPLORER 6. PESTPATROL 7. DIVISION DE PALABRAS (GUIONADO) 8. GREATIS TFORMDESIGNER 3.0 9. EL NOVATO Y EL EXPERTO - Por Alejandro Giraldo Niño 10. OBTENIENDO EL ICONO DE UNA APLICACION O DOCUMENTO ¡AYUDENOS! ________________________________________________________________________ 1. UNAS PALABRAS DEL EDITOR Antes de comenzar, quisiera decir que lamento el retraso, pero debido a razones laborales no he podido sacar antes esta edición del boletín. Para aquellos que estén interesados en una comparación entre Delphi y Visual Basic, dénle una mirada a la última edición de nuestro Boletín para Desarrolladores: http://www.latiumsoftware.com/es/developers/0016.php De la mano de Alejandro Giraldo Niño vuelve la sección "El Novato y el Profesional", dedicada a mostrar a los más principiantes en Delphi algunas características un poco más avanzadas, en este caso mostrándonos cómo usar la propiedad Tag para identificar un componente de un formulario. El Grupo Albor está en campaña solicitando a Borland un Delphi en español. No adherimos a los términos de la campaña, pero sí a la intención: http://www.grupoalbor.com/castellano/Castellano.htm ¡En Latium Software estamos celebrando nuestro primer aniversario! Con sus picos y valles, y casi sin darnos cuenta, ha transcurrido ya todo un año... Recuerdo que en su primera emisión este boletín comenzó con no más de 100 suscriptores, y a un año del lanzamiento, ya suman más de 2.650! Me gustaría agradecerles a todos ustedes, especialmente a quienes nos han acompañado desde el principio, a los webmasters que enlazaron a nuestro sitio, a aquellos que colaboraron con el boletín con sus artículos (particularmente a Alirio Gavidia), a quienes nos contactaron para felicitarnos por el boletín y nos animaron a seguir con este esfuerzo, a todos los que nos recomendaron a sus colegas, a todos los que votaron por nosotros en los rankings, y finalmente no me quiero olvidar de todas las empresas que confiaron en nosotros para presentar sus productos. En este nuevo año habrá muchos cambios, y esperamos poder cubrir sus expectativas mucho mejor. La publicación "AbracaDelphi!" ya no se emite más, pero les haremos un lugar en este boletín a los más principiantes, comenzando en el próximo número con un ejemplo de una aplicación de bases de datos. También va un agradecimiento muy especial por su participación a los miembros de nuestro Foro para Programadores en Delphi en Yahoo! Groups, y por supuesto también para los del Foro para Desarrolladores de Software. Es para mí un honor un placer compartir los foros con estas personas. A propósito, aprovecho para recordarles las direcciones e invitarlos a todos a participar: Programadores en Delphi http://espanol.groups.yahoo.com/group/delphi-intermedio Suscripción: delphi-intermedio-subscribe@gruposyahoo.com Cantidad de miembros: 140 Promedio de mensajes diarios: 3,6 Desarrolladores de Software http://es.groups.yahoo.com/group/desarrolladores-software Suscripción: desarrolladores-software-subscribe@yahoogroups.com Cantidad de miembros: 51 Promedio de mensajes diarios: 1,3 Muchas gracias a todos y sigan con nosotros! Ernesto De Spirito eds2004 @ latiumsoftware.com ________________________________________________________________________ JfControls Lib. Multilenguaje. Multiapariencia. Skins. Privilegios. Más de 40 componentes integrados y personalizables. Múltiples problemas de programación resueltos. Administración centralizada de recursos. Para Delphi 3-7 y C++ Builder 3-6. http://www.jfactivesoft.com/spindex.htm ________________________________________________________________________ 2. HASH TABLES - Por Alirio A. Gavidia B. <alirio@gavidia.org> Cómo tomar una decisión ======================= Un problema común en la programación está en la toma de un rumbo basado en el valor de una secuencia literal de caracteres. Esto es simplemente poder ejecutar una opción si por ejemplo la variable "Pago" es 'efectivo', 'cheque', 'tarjeta' o 'transferencia'. Podríamos diseñar una secuencia de código como la siguiente: Const STransfer = 'transferencia'; SCheck = 'cheque'; SCard = 'tarjeta'; SCash = 'efectivo'; SPayMode = 'Forma de pago'; SUnknow = 'desconocido'; If pago=SCash then ... If pago=SCheck then ... If pago=SCard then ... If pago=STransfer then ... Esto funcionaría, y para ser sincero no me preocuparía mucho por ello. Es práctico. Sin embargo si hablamos de más opciones (unas 32, se me ocurre) se puede volver algo incómodo y ciertamente poco elegante. La opción de un "case" es, en este nivel, inutilizable. Simplemente se requiere tipos matemáticamente "contables" (enteros, letras y enume- rados). Esto permite que el "case" sea realmente eficiente (a diferencia del "Case" de lenguajes xBase). Mejorando el ejemplo anterior podemos ir a: If pago=SCash then ... else If pago= SCheck then ... else If pago= SCard then ... else If pago= STransfer then ... Esto cambio evita que se hagan "n" lecturas para igual número de opciones, pero, para el caso de la última opción, o peor aún, el caso en que la opción sea diferente a las esperadas, igual se realizan "n" consultas. Las alternativas – orden ======================== El orden parece ser la vía. Se establece una lista ordenada y se realiza una búsqueda binaria (divide y conquista). Supongamos tenemos 32 elementos: Dividimos en dos de 16 y seleccionamos, dividimos en dos de 8 y seleccionamos, dividimos en dos de 4 y seleccionamos, dividimos en dos de 2 y seleccionamos, y se escoge el final. Total: 5 decisiones (nótese el hecho de que 2 elevado a la 5ta potencia es 32). Hasta ahora la búsqueda binaria nos garantiza para 32 opciones un máximo de 5 (log N) consultas (en realidad, 50% de las búsquedas se resolverían en 5 consultas, piénselo). Los "if" anidados nos darían en promedio 16 consultas (n/2). Más cerca del "case..of" ======================== Una idea que siempre se presenta, y que he visto usar, es tomar la primera letra. Para el ejemplo inicial esto nos dejaría usar una estructura tipo "Case", pero al llegar a la letra "t" de "tarjeta" y de "trasferencia" (o "c" de "cash", "check"o "card" en inglés) necesita- ríamos, al menos, colocar un "if". Otra aproximación sería crear una función que nos dé un número por identificador. Propongo para el ejemplo dado lo siguiente: Function IdentIndex(AIdent: String): integer; Begin Result := length(AIdent) + Ord(AIdent[3]) end sCash 109 // en español SCheck 107 // en español SCard 121 // en español STransfer 110 // en español sCash 119 // en inglés SCheck 106 // en inglés SCard 118 // en inglés STransfer 105 // en inglés Ahora es posible usar "Case": Var Ident : string; begin Ident := 'efectivo'; if InputQuery('Hash', SPayMode, Ident) then case IdentIndex(Ident) of 109: ShowMessage(SCash); 107: ShowMessage(SCheck); 121: ShowMessage(SCard); 110: ShowMessage(STransfer); else ShowMessage(SUnknown); end end; Pros y contras ============== El método es interesante ante el hecho de reducir el número de consultas necesarias a una. Sin embargo tenemos ahora la sobrecarga de determinar la función adecuada que devuelva los índices. Una incorrecta determi- nación de esta fórmula nos presenta los siguientes problemas: * Repetición de índices * Índices esparcidos * Lentitud en evaluación de la fórmula El último punto es muy obvio y no lo comentaré. La repetición de índices nos obliga a pasos adicionales dentro del "Case". Si tenemos dos identificadores generando el mismo índice hay que hacer al menos un análisis adicional para reconocer cuál es el camino correcto. Los índices esparcidos son un inconveniente si pretendemos construir una tabla. En este caso podríamos diseñar un arreglo como el que a conti- nuación ejemplifico: Begin : FillChar(JumpTable,SizeOf(JumpTable), 0); JumpTable[IdentIndex(SCash)] := PayModeCash; JumpTable[IdentIndex(SCheck)] := PayModeCheck; JumpTable[IdentIndex(SCard)] := PayModeCard; JumpTable[IdentIndex(sTransfer)] := PayModeTransfer; : End; Var JumpTable : array [100..130] of TRoutine; Index : Integer; Ident : string; Begin Ident := SCash; InputQuery('Hash', SPayMode, Ident); Index := IdentIndex(Ident); if (Index in [100..130]) and (Assigned(JumpTable[Index])) then JumpTable[Index] end; Tenemos JumpTable como un arreglo de rutinas que se ejecutan dado un índice. Pero hay un desperdicio ya que utilizamos desde 30 posibles valores sólo cuatro. Lo ideal sería que "IdentIndex" nos otorgara cuatro valores consecutivos. A veces tenemos que vivir con eso, pero a veces no. En el caso del analizador de un compilador normalmente es posible dedicar esfuerzo para determinar la fórmula adecuada sin desperdicio. Pero cuando son muchas palabras, o no se conocen todas de antemano, tenemos que reenfocar todo este asunto. Las listas o tablas "Hash" ========================== La fórmula para determinar el índice debe por lo general ser limitada dado que no solemos estar en capacidad de crear arreglos con índices de 14 dígitos. La solución más simple es dividir. Si queremos limitarnos a sólo 8 posibilidades podríamos dividir entre 8 y obtener el resto. Así los nuevos valores serán sCash 5 // en español SCheck 3 // en español SCard 1 // en español STransfer 6 // en español sCash 7 // en inglés SCheck 2 // en inglés SCard 6 // en inglés STransfer 1 // en inglés Demasiado bueno, pero a veces no resulta así, por ejemplo limitándolo a cuatro se hace presente la repetición de los índices. sCash 1 // en español SCheck 3 // en español SCard 1 // en español STransfer 2 // en español sCash 3 // en inglés SCheck 2 // en inglés SCard 2 // en inglés STransfer 1 // en inglés El límite sugerido para una tabla suele ser un número primo que no sea muy cercano a una potencia de 2. Para tablas con repetición de índices normalmente se obtiene el índice, se consulta la tabla, y si la celda esta libre se toma, sino se busca la siguiente libre de manera linear. Cada falla en esta búsqueda reduce la eficiencia del método. Las listas de hashing se construyen como listas de listas. Cada nodo de la lista principal apunta a la lista de identificadores que corresponden al índice dado. Hay otras soluciones a este problema, pero no las analizaré aquí. Enfrentando la manera de hallar el índice - Método de multiplicación ==================================================================== Me gusta esta manera de resolver el problema cuando tenemos un número en un rango amplio y queremos reducirlo en un rango menor. Para número de posibles valores 2^N (2 elevado a la N). "Key" es un valor generado directamente al evaluar el identificador. La fórmula se determina de la siguiente manera: Hash := ((K*Key) and M) mod S; Dónde: - K es 158 para índices de 8 bits, 40503 para índices de 16 bits y 2654435769 para índices de 32 bits. - S es 2 elevado la diferencia del número de bits y N. - M es el tamaño de la tabla menos 1. (2^N-1). Para N=10 tenemos K=40503 (16 bits) S=2^(16-10) = 2^6 = 64 M=1023 Hash := ((40503*Key) and 1023) mod 64; El resultado será un valor desde 0 hasta 63 (de una rango original de 0 a 1023). Manipulando cadenas de caracteres ================================= Usualmente se puede simplemente tomar cada carácter de una cadena, sumarlos y tomar el modulo con 256. Sin embargo esto genera problemas con los anagramas ('arroz' y 'zorra' por ejemplo) y palabras similares. Una solución es utilizar xor ("o" exclusivo) en lugar de la adición y agregar una tabla intermedia de valores pseudo-aleatorios que nos den una mejor distribución, además considerar en el algoritmo la posición de cada letra y no sólo su valor intrínseco. Conclusión ========== Utilizar "hash tables" puede dar resultados espectaculares en situa- ciones donde se tiene conocimiento de los identificadores a buscar y/o dónde podemos desperdiciar memoria a cambio de velocidad. En lo particular, los compiladores e interpretadores son, a mi parecer, los mejores casos. También es útil en sistemas de base de datos donde el problema no sea espacio sino la necesidad de mínima cantidad de lecturas. Sin embargo un problema a tener en cuenta es la eliminación de registros en la tabla, algo usual en bases de datos. ------------------- El código fuente que acompaña este artículo puede descargarse desde: http://www.latiumsoftware.com/descarga/p0023.zip ------------------ Para aprender más: * D. E. Knuth, 1973, The Art of Computer Programming, Vol. 3: Sorting and Searching, Reading, MS: Addison-Wesley. ------------------------------------ Copyright © 2001 por Alirio A. Gavidia B. Todos los derechos reservados. Se permite la publicación de este material por cualquier medio por parte de cualquiera siempre que este no sea modificado en contenido y se cite la fuente original. ________________________________________________________________________ 3. KYLIX DESKTOP DEVELOPER EDITION POR SOLO $199! Borland está tratando de vender tantas licencias de Kylix como sea posible y por un tiempo limitado ha bajado significativamente el precio de Kylix DDE de US $999 a solamente US $199. Sí, una licencia de Kylix DDE cuesta solamente US $199! Si está intereado en el desarrollo de aplicaciones bajo Linux, este es definitvamente el mejor momento para comprar: http://shop.borland.com/Product/0,1057,3-15-CQ100479,00.html La oferta expira el 23 de Agosto del 2001. El formulario de orden de pedido permite direcciones de facturación internacionales, pero hay que proveer una dirección en los EE.UU. para el despacho. Si no tiene una dirección en los EE.UU. -ya sea propia o provista por un tercero- puede preguntar a su representante Borland local para ver a cuánto lo están vendiendo. Generalmente cuesta un poco más, pero no tiene que preocu- parse del traslado, aduanas, etc. ________________________________________________________________________ 4. CARGANDO UN ARCHIVO CON UNA IMAGEN JPEG EN UN BITMAP Esta es una pregunta recurrente en foros y grupos de noticias. Para cargar una imagen JPEG podemos usar la unidad "jpeg" que viene con Delphi (si no la tiene instalada, creo que puede encontrarla en el directorio "Extras" en el CD ROM de Delphi). Puede encontrar alterna- tivas en la red, pero voy a usar esta porque viene con Delphi y todos la tienen. Para cargar una imagen JPEG en un componente Image tiene que hacer algo como lo siguiente: uses jpeg; procedure TForm1.Button1Click(Sender: TObject); var jpg: TJpegImage; begin jpg := TJpegImage.Create; try jpg.LoadFromFile('d:\path\file.jpg'); Image1.Picture.Assign(jpg); // Carga la imagen como JPEG finally jpg.Free; end; end; La imagen es cargada como JPEG, lo que está bien, a menos que queramos acceder a las propiedades Pixels y ScanLine del Bitmap. Por ejemplo, lo siguiente no funcionará: Image1.Canvas.Pixels[0,0] := clWhite; // Pixel sup-izq en blanco Si pretendemos realizar algún procesamiento de imagen deberemos cargar la imagen JPEG como un bitmap. Para hacer eso tenemos que usar el método Assing de un Bitmap, no un Picture, así que sólo tenemos que hacer un pequeño cambio en el procedimiento que presentáramos arriba: Image1.Picture.Bitmap.Assign(jpg); // Carga la imagen como BMP ________________________________________________________________________ 5. PE EXPLORER PE Explorer es un analizador de código fuente, herramienta de recursos y desensamblador. PE Explorer le permite ver, editar y reparar la estructura interna y recursos de archivos PE (Portable Executable) tales como EXE, DLL, DRV, BPL, DPL, SYS, CPL, OCX, SCR y otros ejecutables Win32. Su capacidad de edición visual le permite modificar rápidamente los recursos sin tener que escribir ningún script. Aplicación : PE Explorer v1.20 Creador : Heaven Tools http://www.heaventools.com Licencia : Shareware (evaluación de 30 días) Descarga : http://www.heaventools.com/download/pexsetup.zip (~1Mb) ________________________________________________________________________ 6. PESTPATROL PestPatrol detecta y remueve más de 16.000 pestes no-virus, y está diseñado para ser usado con software anti-virus para proveer protección completa. Corre rápido, detectando y removiendo programas y documentos maliciosos o no deseados incluyendo Bombas ANSI, relacionados con Anarquía, Molestias, Pestes AOL, Puertas Traseras, Tarjetería, Negación de Servicio, Exploits, Explosivos, Java y ActiveX Hostiles, Guarda- claves, Bombarderos de Correo, Herramientas de Craqueo, Herramientas de Craqueo y Captura de Contraseñas, Herramientas Phreak, Escaneadores de Puertos, Control/Administración Remota, Husmeadores, Burlones, Espías, Troyanos y Herramientas de Creación de Troyanos, War Dialers, Gusanos, etc. Aplicación : PestPatrol Creador : SaferSite.com, Inc. http://www.safersite.com Licencia : Tryware (detecta pero no remueve) Descarga : http://safersite.com/Downloads/Eval/SetupPestPatrolEval.EXE (~1.35 Mb) ________________________________________________________________________ 7. DIVISION DE PALABRAS (GUIONADO) A veces necesitamos mostrar o imprimir un texto, y quisiéramos dividir con guiones las palabras largas que no caben en al final de una línea, evitando que "caiga" entera en la línea siguiente dejando demasiado espacio sin usar. El problema principal que se presenta es cómo dividir una palabra en sílabas. Para ello he escrito la siguiente función que separa una palabra española en sílabas. Es imperfecta, pero al menos constituye una base: procedure Syllabify(Silabas: TStringList; s: string); const Consonantes = ['b','B','c','C','d','D','f','F','g','G', 'h','H','j','J','k','K','l','L','m','M','n','N', 'ñ','Ñ','p','P','q','Q','r','R','s','S','t','T', 'v','V','w','W','x','X','y','Y','z','Z']; VocalesFuertes = ['a','A','á','Á','e','E','é','É', 'í','Í','o','ó','O','Ó','ú','Ú']; VocalesDebiles = ['i','I','u','U','ü','Ü']; Vocales = VocalesFuertes + VocalesDebiles; Letters = Vocales + Consonantes; var i, j, n, m, guion: integer; begin j := 2; s := #0 + s + #0; n := Length(s) - 1; i := 2; Silabas.Clear; while i <= n do begin guion := 0; // No dividir if s[i] in Consonantes then begin if s[i+1] in Vocales then begin if s[i-1] in Vocales then guion := 1; end else if (s[i] in ['s', 'S']) and (s[i-1] in ['n', 'N']) and (s[i+1] in Consonantes) then begin guion := 2; end else if (s[i+1] in Consonantes) and (s[i-1] in Vocales) then begin if s[i+1] in ['r','R'] then begin if s[i] in ['b','B','c','C','d','D','f','F','g', 'G','k','K','p','P','r','R','t','T','v','V'] then guion := 1 else guion := 2; end else if s[i+1] in ['l','L'] then begin if s[i] in ['b','B','c','C','d','D','f','F','g', 'G','k','K','l','L','p','P','t','T','v','V'] then guion := 1 else guion := 2; end else if s[i+1] in ['h', 'H'] then begin if s[i] in ['c', 'C', 's', 'S', 'p', 'P'] then guion := 1 else guion := 2; end else guion := 2; end; end else if s[i] in VocalesFuertes then begin if (s[i-1] in VocalesFuertes) then guion := 1 end else if s[i] = '-' then begin Silabas.Add(Copy(s, j, i - j)); Silabas.Add('-'); inc(i); j := i; end; if guion = 1 then begin // Dividir aquí Silabas.Add(Copy(s, j, i - j)); j := i; end else if guion = 2 then begin // Dividir después inc(i); Silabas.Add(Copy(s, j, i - j)); j := i; end; inc(i); end; m := Silabas.Count - 1; if (j = n) and (m >= 0) and (s[n] in Consonantes) then Silabas[m] := Silabas[m] + s[n] // Ultima letra else Silabas.Add(Copy(s, j, n - j + 1)); // Ultima sílaba end; Para probar este procedimiento puede poner un Textbox y una Label en un formulario y en el evento Change del Textbox escribir: procedure TForm1.Edit1Change(Sender: TObject); var Silabas: TStringList; begin Silabas := TStringList.Create; try Syllabify(Silabas, Edit1.Text); Label1.Caption := StringReplace(Trim(Silabas.Text), #13#10, '-', [rfReplaceAll]); finally Silabas.Free; end; end; Ahora que tenemos un procedimiento de silabeo, debemos notar que no podemos guionar una palabra en cualquier corte de sílaba. Usualmente es correcto y/o deseable unir las sílabas pequeñas de los extremos izquierdo y/o derecho de una palabra para garantizar por ejemplo que haya por lo menos dos sílabas de cada lado de la palabra cuando sea dividida, o -como en el siguiente ejemplo- asegurarse que tengamos por lo menos cuatro caracteres de cada lado: procedure ApplyRules(Silabas: TStringList); // Garantizar que haya al menos cuatro letras en los extremos // izquierdo y derecho de las palabra begin with Silabas do begin if Count = 1 then exit; while Count > 1 do begin if Length(Strings[0]) >= 4 then break; Strings[0] := Strings[0] + Strings[1]; Delete(1); end; while Silabas.Count > 1 do begin if Length(Strings[Count-1]) >= 4 then break; Strings[Count-2] := Strings[Count-2] + Strings[Count-1]; Delete(Count-1); end; end; end; Finalmente, llega la hora de recorrer el texto separando las líneas de un párrafo determinando qué palabras deberían ser guionadas. El siguiente ejemplo hace eso con un texto a mostrar en un Memo: procedure Hyphenate(Memo: TMemo; OriginalText: TStrings); var parrafo, i, j, k, m, n, MaxLineWidth: integer; s, line: string; Bitmap: TBitmap; Canvas: TCanvas; Silabas: TStringList; begin Silabas := TStringList.Create; try // Necesitamos un lienzo (canvas) para usar su método TextWidth // para saber el ancho del texto y ver si cabe en el área // cliente o no. La clase TMemo no tiene una propiedad Canvas // así que por eso creamos uno nosotros. Bitmap := TBitmap.Create; Canvas := Bitmap.Canvas; try Canvas.Font := Memo.Font; MaxLineWidth := Memo.ClientWidth - 6; // Ancho máximo Memo.Lines.Clear; for parrafo := 0 to OriginalText.Count - 1 do begin // Por cada párrafo s := OriginalText[parrafo]; // Obtener el párrafo original // Obtener las líneas en que tenemos que cortar un párrafo while Canvas.TextWidth(s) > MaxLineWidth do begin // Primero encontramos (en "j") el indice del comienzo de la // primera palabra que no cabe (la que hay que dividir) j := 1; n := Length(s); i := 2; while i <= n do begin if (s[i-1] = ' ') and (s[i] <> ' ') then j := i; // ultimo comienzo de una palabra if Canvas.TextWidth(Copy(s, 1, i)) > MaxLineWidth then break; // alcanzado un ancho que no entra inc(i); end; // ¿Dónde ocurre el corte? if s[i] = ' ' then begin // Bien! Cortamos en un espacio Memo.Lines.Add(Copy(s, 1, i - 1)); // Agregamos la línea s := Copy(s, i + 1, n - i); // Removemos la línea end else begin // Cortamos en una palabra. Ahora encontramos (en "k") el // primer espacio después de la palabra (k) k := j + 1; while (k <= n) and (s[k] <> ' ') do inc(k); // Dividir la palabra en sílabas Syllabify(Silabas, Copy(s, j, k - j)); ApplyRules(Silabas); // Chequear (en "m") cuántas sílabas entran m := 0; Line := Copy(s, 1, j-1); while Canvas.TextWidth(Line + Silabas[m] + '-') <= MaxLineWidth do begin Line := Line + Silabas[m]; inc(m); end; if (m <> 0) and (Silabas[m-1] <> '-') then begin // Guionar Line := Line + '-'; j := Length(Line); if Silabas[m] = '-' then inc(j); end; Memo.Lines.Add(Line); // Agregamos la línea s := Copy(s, j, n - j + 1); // Removemos la línea end; end; Memo.Lines.Add(s); // Agregar la última línea (entra) end; finally Bitmap.Free; end; finally Silabas.Free; end; end; Para probar el procedimiento, coloque un componente Memo en un formu- lario, hágalo alinear por ejemplo a la parte superior del formulario (Align = alTop) y escriba el siguiente código en el evento Resize del formulario: procedure TForm1.FormResize(Sender: TObject); var TextoOriginal: TStringList; begin TextoOriginal := TStringList.Create; try TextoOriginal.Add('Si se ha preguntado cómo hacen los ' + 'programas procesamiento de textos para dividir palabras ' + 'con de guiones al final de una línea, he aquí un ' + 'ejemplo sencillo (en comparación con los que usan las ' + 'aplicaciones de procesamiento de textos).'); TextoOriginal.Add('Este es un segundo párrafo que se provee ' + 'con fines de ejemplo.'); Hyphenate(Memo1, TextoOriginal); finally TextoOriginal.Free; end; end; El código fuente completo de este artículo podrá encontrarlo adjunto a este boletín. ________________________________________________________________________ 8. GREATIS TFORMDESIGNER 3.0 ¿Qué es Greatis TFormDesigner? ------------------------------ TFormDesigner es un componente para Delphi 3-5 y C++ Builder 3-5 que le permite mover y cambiar el tamaño de cualquier control de su formulario. Sólo coloque un componente TFormDesigner en su formulario, establezca su propiedad Active en True, y disfrute - no tiene que preparar su formu- lario para usar TFormDesigner. Lo lindo de TFormDesigner es que funciona en tiempo de ejecución, mientras que el diseñador estándar funciona sólo en tiempo de diseño. Para facilitar su uso, TFormDesigner replica la cara del diseñador de formularios estándar de Delphi y C++ Builder. Una vez que TFormDesigner haya sido activatado, puede seleccionar cualquier control en su formulario con la tecla Tab o con un clic del ratón, y luego moverlo o modificar su tamaño. Características --------------- - Selección múltiple - Compatibilidad con formularios ActiveX - Compatibilidad con formularios MDI - Bloque y protección de controles - Diálogo de Alineación - Diálogo de Tamaño - Paleta de Alineación - Manejadores de arrastre configurables - Rejilla de diseño configurable - Pistas de tamaño/coordenadas - Manual de Usuario imprimible - Versión de evaluación para Delphi 3-5 y C++ Builder 3-5 - Editor de formularios totalmente funcional como demo gratis Descarga -------- Un EXE-demo compilado, documentación imprimible en formato PDF y la versión de prueba de TFormDesigner para Delphi 3-5 y C++ Builder 3-5 se incluyen en el kit demostrativo, que puede descargar de la siguiente dirección: http://www.greatis.com/formdesdemo.zip (~756K) Licencia -------- TFormDesigner cuesta US$ 29.95 para una licencia de un solo usuario. Más información --------------- Vea más información en la página web de TFormDesigner: http://www.greatis.com/formdes.htm Para más información, contacte a Greatis Software <b-team@greatis.com> ________________________________________________________________________ 9. EL NOVATO Y EL EXPERTO - Por Alejandro Giraldo Niño Algunas veces, cuando estamos aprendiendo, buscamos ser algo iterativos en nuestro código. La propiedad TAG que tienen todos los componentes nos permite identificar el Componente de manera única, incluso hasta podemos hacer grupos de Componentes utilizando el mismo valor de TAG. Este es un buen ejemplo del uso iterativo de la propiedad TAG. Supongamos, que necesitamos almacenar en un fichero *.ini todos los parámetros que el usuario modificó en un formulario que contiene 30 Instancias del Componente (TCheckBox), de los cuales, por ejemplo 15 pertenecen a una sección "Activos" y los otros 15 a la sección de "Pasivos". NOVATO: uses IniFiles; procedure TForm1.GuardarParametros; var Ini: TIniFile; begin Ini := TIniFile.Create(ExtractFilePath(Application.ExeName) +'Prueba.ini'); // Guardar Activos Ini.WriteBool('Activos','Activo01',Checkbox1.Checked); Ini.WriteBool('Activos','Activo02',Checkbox2.Checked); Ini.WriteBool('Activos','Activo03',Checkbox3.Checked); Ini.WriteBool('Activos','Activo04',Checkbox4.Checked); Ini.WriteBool('Activos','Activo05',Checkbox5.Checked); Ini.WriteBool('Activos','Activo06',Checkbox6.Checked); Ini.WriteBool('Activos','Activo07',Checkbox7.Checked); Ini.WriteBool('Activos','Activo08',Checkbox8.Checked); Ini.WriteBool('Activos','Activo09',Checkbox9.Checked); Ini.WriteBool('Activos','Activo10',Checkbox10.Checked); Ini.WriteBool('Activos','Activo11',Checkbox11.Checked); Ini.WriteBool('Activos','Activo12',Checkbox12.Checked); Ini.WriteBool('Activos','Activo13',Checkbox13.Checked); Ini.WriteBool('Activos','Activo14',Checkbox14.Checked); Ini.WriteBool('Activos','Activo15',Checkbox15.Checked); // Guardar Pasivos Ini.WriteBool('Pasivos','Pasivo01',Checkbox16.Checked); Ini.WriteBool('Pasivos','Pasivo02',Checkbox17.Checked); Ini.WriteBool('Pasivos','Pasivo03',Checkbox18.Checked); Ini.WriteBool('Pasivos','Pasivo04',Checkbox19.Checked); Ini.WriteBool('Pasivos','Pasivo05',Checkbox20.Checked); Ini.WriteBool('Pasivos','Pasivo06',Checkbox21.Checked); Ini.WriteBool('Pasivos','Pasivo07',Checkbox22.Checked); Ini.WriteBool('Pasivos','Pasivo08',Checkbox23.Checked); Ini.WriteBool('Pasivos','Pasivo09',Checkbox24.Checked); Ini.WriteBool('Pasivos','Pasivo10',Checkbox25.Checked); Ini.WriteBool('Pasivos','Pasivo11',Checkbox26.Checked); Ini.WriteBool('Pasivos','Pasivo12',Checkbox27.Checked); Ini.WriteBool('Pasivos','Pasivo13',Checkbox28.Checked); Ini.WriteBool('Pasivos','Pasivo14',Checkbox29.Checked); Ini.WriteBool('Pasivos','Pasivo15',Checkbox30.Checked); Ini.Free; end; EXPERTO: uses IniFiles; procedure TForm1.GuardarParametros; var Ini: TIniFile; I: Integer; begin Ini := TIniFile.Create(ExtractFilePath(Application.ExeName) +'Prueba.ini'); for I := 0 to ComponentCount -1 do if Components[I] is TCheckbox then if TCheckBox(Components[I]).Tag = 1 then // Guardar Activos Ini.WriteBool('Activos', Components[I].Name, TCheckBox(Components[I]).Checked) else // Guardar Pasivos Ini.WriteBool('Pasivos', Components[I].Name, TCheckBox(Components[I]).Checked); Ini.Free; end; EXPLICACION: El Componente TForm hereda de la clase TComponent la propiedad ComponentCount. Esta propiedad devuelve la cantidad de componentes que tiene el formulario y nosotros simplemente la utilizamos para saber a cuántos componentes le tenemos que preguntar si son o no de tipo TCheckBox. La propiedad Components, también heredada de la clase TComponent, es la que apunta a todos los componentes del formulario y la utilizamos para saber cual es el TAG de cada Componente, y de esta forma saber qué tenemos que hacer... Okay, este es un pequeño ejemplo de cómo utilizar un mismo procedimiento OnClick para 5 los botones del formulario. Claro esta!! utilizando la propiedad TAG. procedure TForm1.Button1Click(Sender: TObject); begin case TButton(Sender).Tag of 1: Showmessage('Soy el boton 1'); 2: Showmessage('Soy el boton 2'); 3: Showmessage('Soy el boton 3'); 4: Showmessage('Soy el boton 4'); 5: Showmessage('Soy el boton 5'); end; end; A cada uno de los Botones hay que especificarles, en el Inspector de Objetos, en lo eventos que tomen el evento "Button1Click" y modificarles el Tag. ------------------ Copyright (c) 2001 Alejandro Giraldo Niño. alejogn@hotmail.com, alejogn@yahoo.com, UINICQ:6414205. ________________________________________________________________________ 10. OBTENIENDO EL ICONO DE UNA APLICACION O DOCUMENTO Marian Hubinsky me envió el siguiente código en respuesta a mi artículo "Obteniendo el icono de una aplicación o documento", diciéndo me que GetAssociatedIcon (la función que yo escribí) "no extrae correctamente algunos iconos pequeños, esta otra es más chica y funciona un poco mejor": // obtener icono del Shell uses ShellApi; function SHSmallIcon(xpath : string; getopen : boolean) : hicon; var fili : TSHFileInfo; aka : Integer; begin aka := SHGFI_ICON or SHGFI_SMALLICON; if getopen then aka := aka or SHGFI_OPENICON; SHGetFileInfo(Pchar(xpath), 0, fili, sizeof(TSHFileInfo), aka); Result := fili.hIcon; end; // ejemplo de uso: procedure TForm1.Button3Click(Sender: TObject); var smi : hicon; begin smi := SHSmallIcon('c:\windows', true); DrawIconex(Form1.Canvas.Handle, 10, 10, smi, 0, 0, 0, 0, DI_normal); DestroyIcon(smi); end; // Marian Hubinsky <treeumph@tn.sknet.sk> http://wtools32.szm.com Agradezco a Marian por su colaboración. ________________________________________________________________________ ¡TÚ PUEDES AYUDARNOS! Necesitamos tu ayuda para que este boletín pueda continuar y crecer. Una forma en que puedes ayudarnos es enviando este enlace a tus amigos: http://www.latiumsoftware.com/es/pascal/index.php Otra forma es votándonos en alguno de estos rankings para darle más visibilidad a nuestro sitio web y aumentar así el número de suscrip- ciones al boletín, que esperamos en el futuro se traduzca también en un mayor número de colaboraciones de artículos, trucos, etc.: http://www.sandbrooksoftware.com/cgi-bin/TopSite2/rankem.cgi?id=latium http://news.optimax.com/topdelphi/links.exe/click?id=70C517ECAE6E http://www.programmingpages.com/?r=latiumsoftwarecomenpascal http://www.top219.org/cgi-bin/vote.cgi?delphi&83 http://top100borland.com/in.php?who=20 http://top200.jazarsoft.com/delphi/rank.php3?id=latium http://213.65.224.200/cgi-bin/toplist.cgi/hits?Id=80 http://www.programacion.net/votar-enlace.php?id=474 http://www.lawebdelprogramador.com/buscar/enlace.php?id=615 Por favor vota. Son sólo unos segundos para ti que REALMENTE pueden hacer la diferencia. Necesitamos tu ayuda para poder continuar. ________________________________________________________________________ Si no has recibido el archivo con el código fuente completo de los ejemplos que se presentan en este boletín, puedes descargarlo de la siguiente dirección: http://www.latiumsoftware.com/descarga/p0023.zip ________________________________________________________________________ Página principal: http://www.latiumsoftware.com/es/pascal/index.php Página del grupo: http://espanol.groups.yahoo.com/group/boletin-pascal/ Para suscribirse / apuntarse: boletin-pascal-subscribe@gruposyahoo.com Para cancelar / removerse: boletin-pascal-unsubscribe@gruposyahoo.com Para reportar problemas con la suscripción: eds2004 @ latiumsoftware.com ________________________________________________________________________ Este boletín se provee "TAL Y COMO ESTA", sin garantía de ninguna clase. Su uso implica la aceptación de nuestros términos de licencia y de la ausencia de garantía que puedes leer en nuestro sitio web. Allí también encontrarás una nota sobre marcas registradas. Te animamos a que redis- tribuyas este boletín, siempre y cuando lo hagas en forma completa (incluyendo la información de copyright), sin modificaciones y de manera gratuita. Los artículos son copyright de sus respectivos autores y se reproducen aquí con el permiso de los mismos. ________________________________________________________________________ Latium Software http://www.latiumsoftware.com/es/index.php Copyright (c) 2001 por Ernesto De Spirito. Todos los derechos reservados ________________________________________________________________________ |
Los ejemplos completos de código fuente de este número están disponibles para descargar.
![]() |
¿Errores? ¿Omisiones? ¿Comentarios? Por favor contáctanos!






