![]() |
![]() |
| Volver al índice | Por
Luis Roche |
Si, si ya lo sé. No creais que lo he olvidado. Habíamos quedado que la unidad 10 trataría del tema de los editores de componentes.
Lo que pasa es que para explicar este tema... ¡necesitamos un componente apropiado! De modo que esta unidad tratará sobre un nuevo componente sobre el cual explicaremos el tema de los editores de componentes en la unidad 11. Además, así matamos dos pájaros de un sólo tiro. Me explico: últimamente en el grupo de mensajes de Delphi han aparecido varias consultas sobre como evitar el parpadeo que se produce al dibujar imágenes que cambian frecuentemente. Este parpadeo (flicker) se produce al dibujar directamente las imganes en la pantalla. La solución es sencilla: basta con dibujar previamente la imagen en un bitmap oculto (off-screen) y cuando el dibujo este finalizado, volcarlo a la pantalla.
De este modo se evita el parpadeo aunque estemos dibujando el gráfico muchas veces por segundo.
Vamos a aplicar esta técnica al componente TDigitalDisplay. Se trata de un display digital tipo calculadora.
Nuestro componente tendrá las siguientes características:
Comenzemos a diseñar nuestro componente. Nuestro display constará de un número de dígitos configurable por el usuario, por lo que necesitamos una propiedad Digits que se encargue de este aspecto. Cada dígito estará formado por siete segmentos (volveremos sobre esto en la siguiente sección). Cada segmento puede estar encendido (propiedad DigitOnColor) o apagado (propiedad DigitOffColor). Otra propiedad será el color de fondo (propiedad BckgndColor).
Por supuesto, necesitaremos almacenar el valor a representar (propiedad Value) y, además, le dotaremos de alineación (propiedad Alignment), de la posibilidad de rellenar con ceros a la izquierda (propiedad LeadingZeros) y de estar o no visible (¿adivinais? propiedad Visible).
Por último le dotaremos de unos cuantos eventos estándar: OnClick, OnDragOver, OnDragDrop, OnMouseUp... y de un evento personalizado: OnDigitClick, que se disparará cuando se haga click sobre un dígito (si se hace click sobre parte del componente que no sea un dígito se disparará el evento OnClick).
Nuestro componente descenderá de TGraphicControl y la tárea de dibujado del mismo se llevará a cabo en el método Paint, el cuál habrá que redefinir (override). Por cierto ¿es "redefinir" la traducción adecuada de "override"? No estoy seguro, así que, por favor, sacarme de dudas :(
Con esto ya tenemos el marco en el que desarrollaremos el componente, asi que ¡manos a la obra!
![]() |
Cómo hemos mencionado en el punto anterior, cada dígito está formado por siete segmentos. En el dibujo se han numerado en color azul. Además de los siete segmentos constituyentes de cada dígito, se han añadido tres segmentos más: uno para representar el punto decimal(.) y otros dos para la separación horaria (:). |
Se ha tomado como origen el punto 0,0 y el punto más lejano del origen (el vértice inferior derecho del segmento 3) tiene como coordenadas (26,32), es decir, he dibujado los segmentos sobre una plantilla de 26x34 pixels. Este tamaño es arbitrario, ya que luego cada vértice se le multiplicará por la escala correspondiente. De este modo he definido dos matrices: una para las coordenadass x y otra para las coordenadas y. Por supuesto se podría haber hecho con una sóla matriz, pero como hay que añadirle otra dimensión a la matriz (el indice del segmento) queda un poco más claro así.
Const
{Coordenadas de los puntos}
{Coordenadas de los 10 (1..10) segmentos posibles: 7 segmentos + 1 punto decimal (.), 2 puntos de hora (:)
Cada segmento tiene 6 (0..5) vértices}
{ _ }
{ . |_| }
{ : |_| }
Px : Array[1..10,0..5] of integer = ((9,9,24,24,20,13),(26,26,26,24,22,22),(26,26,26,22,22,24),
(24,24,9,9,13,20),(7,7,7,9,11,11),(7,7,7,11,11,9),
(11,13,20,22,20,13),(2,2,5,5,5,2),(2,2,5,5,5,2),(2,2,5,5,5,2)) ;
Py : Array[1..10,0..5] of integer = ((0,0,0,0,4,4),(2,2,14,16,14,6),(20,20,32,28,20,18),
(34,34,34,34,30,30),(32,32,20,18,20,28),(14,14,2,6,14,16),
(17,15,15,17,19,19),(32,32,32,32,34,34),(22,22,22,22,25,25),(10,10,10,10,13,13));
{Para cada digito del 1 al 9, 1 representa segmento encendido, 0 apagado}
DigitsArray : Array[0..9] of string = ('1111110','0110000','1101101','1111001','0110011','1011011',
'1011111','1110000','1111111','1111011');
Seg_dot = 8; {Segmento correspondiente al .}
Seg_DotDown = 9; {Segmento correspondiente al punto inferior de :}
Seg_DotUp = 10; {Segmento correspondiente al punto superior de :}
Hemos definido las dos matrices constantes px y py con las coordenadas
de los vértices. Además, se define la matriz DigitsArray
de 10 elementos (0..9) que contiene que segmentos están encendidos
y apagados para cada dígito. Por ejemplo, el dígito 1 tiene
encendidos los segmentos 2y 3 y apagados el resto. Aquí no se tienen
en cuenta los segmentos extras (. y :).
Por último a los segmentos correspondientes a el punto decimal y a los puntos de la hora se les asigna un número y una constante para luego poder referirse a ellos con mayor facilidad.
Ya tenemos las bases necesarias para poder pintar el display en el método Paint. Eso sin olvidar que debemos eliminar el parpadeo...
Dibujando
sin parpadeos: el método Paint
Cómo en todo componente gráfico el método Paint se encarga de dibujar el componente en pantalla. Recordemos que este método es llamado por Delphi cada vez que se necesita un redibujado del componente, bien sea en respuesta a un mensaje de Windows o a una llamada nuestra, tal y como hacemos en los métodos Set de las diferentes propiedades, al llamar a repaint provocamos el redibujado del componente (Más adelante volveremos sobre este aspecto).
Para evitar el parpadeo no dibujaremos directamente sobre el canvas del componente (esto lo hicimos en el componente TGradiente, unidad 5), sino que lo haremos sobre otro bitmap. el proceso es el siguiente:
Bien, una vez conocida la teoría, es cuestión de aplicarla. Este es el método Paint de nuestro componente:
procedure TDigitalDisplay.Paint;
Var
ex, ey : single; {Escala a la que dibujar el display}
i, Digito : byte;
Incx, x,y, offsetx, offsety :integer;
DigitSpace : integer;
SrcRect : TRect;
Digitos : byte;
Caracter : char;
FillString : string;
begin
SrcRect:=Rect(0,0,Width,Height); {Obtenemos el rectángulo del componente}
FBitmap.Width:=Width; {Ajustamos nuestro bitmap}
FBitmap.Height:=Height;
{Cálculo del offset a dejar alrededor}
offsetx:=Trunc(0.2*width);
offsety:=trunc(0.2*height);
{Calculamos el espacio disponible para cada dígito}
DigitSpace:=Trunc((Width-offsetx) / FDigits);
{Posicionamos la x e y}
x:=offsetx div 2;
y:=offsety div 2;
{Cálculo de la escala}
ex:=DigitSpace/27;
ey:=(Height-offsety)/35;
{Inicializamos el nº de digitos dibujados}
Digitos:=0;
{Asignamos colores}
FBitmap.Canvas.Brush.Color:=FBckgndColor;
FBitmap.Canvas.FillRect(SrcRect);
{Asignamos a la variable text el valor a representar}
Text:=FValue;
{Creamos una cadena auxiliar de relleno por la izquierda}
if FLeadingZeros then
FillString:='000000000000000000000000000000'
else
FillString:=' ';
{Rellenar la variable text con ceros o espacios (FillString) según sea la alineación}
case FAlignment of
taRightJustify : Text:=Copy(FillString,1,FDigits-GetNumberOfDigits)+FValue;
taCenter : Text:=Copy(FillString,1,(FDigits-GetNumberOfDigits) div 2)+FValue;
end;
{Bucle para el dibujo de cada dígito}
for i:=1 to Length(Text) do
begin
Caracter:=Copy(Text,i,1)[1]; {Caracter a dibujar}
Case caracter of
'0'..'9': begin
Digito:=StrToInt(Caracter);
DrawDigit(Digito,x,y,ex,ey);
x:=x+DigitSpace;
Inc(Digitos);
if Digitos = FDigits then break;
end;
':' : begin
DrawDot(seg_DotUp,x,y,ex,ey);
DrawDot(seg_DotDown,x,y,ex,ey);
end;
'.',',': DrawDot(seg_Dot,x,y,ex,ey);
' ': x:=x+DigitSpace;
end;
end;
{Una vez dibujado totalmente el display en el bitmap off-screen, lo volcamos
al canvas del componente. Resultado ¡No hay parpadeo!}
Canvas.Draw(0,0,FBitmap);
end;
Como se puede ver el método Paint utiliza una serie de rutinas auxiliares
para el dibujado del display. Estos métodos son:
Vamos ahora con el último aspecto interesante del componente: hacer que otros desarrolladores puedan descender sus propios componentes de nuestro Display original
Preparando
la herencia: métodos dinámicos.
Cuando pretendemos que un componente pueda ser un futuro "padre" de futuros componentes debemos diseñar juiciosamente que aspectos debemos permitir que los hijos reimplementen y que aspectos deben permanecer ocultos a los mismos.
En una primera aproximación queda claro que todos los métodos Set y Get de las propiedades serán privados, ya que los hijos deben acceder a las propiedades directamente, no a través de estos métodos. Por ello declaramos estos métodos en las sección privada de la declaración del componente, lo mismo que los campos "F" asociados a estas propiedades (FBackgndColor, FDigitOnColor...).
Por otra parte, tanto el constructor como el destructor deben ser públicos así que con ellos no hay problema. Pero, ¿qué hacemos con el método Paint? Este método si que puede ser redefinido por futuros componentes hijos, por ejemplo, si un desarrollador quiere utilizar otro método para dibujar los digitos. Podríamos dejar el método Paint en la parte privada, pero entonces la única manera que tendría el desarrollador de escribir este nuevo método ¡sería en el componente original! Pero ¿y si no tiene el código fuente, sino tan sólo el fichero .dcu? Pues que no puede hacerlo :(
De modo que no seamos crueles y declaremos el procedimiento Paint como protected; así un componente hijo para implementar su propio método de dibujado del display sólo tendrá que escribir:
procedure Paint; override;
Que, dicho sea de paso, es lo mismo que hemos tenido que hacer nosotros :) Y es que no olvidemos que nuestro componente, a su vez, es hijo de TGraphicControl y ¡gracias a Dios! en dicho componente el método Paint se declaro protected y no private ;)
Nos queda por implementar un evento, OnDigitClick que se debe disparar cuando el usuario del componente haga click sobre un digito del display. Hay que distinguir entre el click sobre un dígito (que disparará el evento OnDigitClick) y el click sobre el resto del display (que disparará el evento OnClick).
Lo primero es declarar un campo FOnDigitClick para el nuevo evento. Además declararemos el tipo de procedimiento del evento:
TOnDigitClick = procedure(Sender : TObject; Digit : integer) of object;
Para disparar este evento, redefiniremos el procedimiento MouseDown. Para ello declararemos el método en la parte protected (ya sabeis, para que los hijos...) y añadiremos la palabra override. Esta es la implementación de dicho método:
procedure TDigitalDisplay.MouseDown(Button : TMouseButton; Shift : TShiftState; X,Y : Integer);
{Método que se encarga de determinar si se ha pulsado con el ratón sobre un dígito del display.
En caso afirmativo se dispara el evento correspondiente}
var
i, xt, xoff, yoff, DigitSpace : integer;
begin
inherited MouseDown(Button,Shift,X,Y);
{Cálculo del offset horizontal}
xoff:=Trunc(0.2*width);
xt:=Trunc(0.2*width) div 2;
{Cálculo del espacio ocupado por un dígito}
DigitSpace:=Trunc((Width-xoff) / FDigits);
{Vamos comprobando si el click se ha ha hecho sobre el dígito (i)}
for i:=1 to FDigits do
begin
if (x>=xt) AND (x<=xt+DigitSpace) then
DigitClick(i);
xt:=xt+DigitSpace;
end;
end;
Lo primero que hace el método es llamar al antecesor del mismo mediente
la llamada inherited. A continuación se calcula si se ha hecho click
sobre un dígito. En caso afirmativo se ejecuta el procedimiento
DigitClick pasándole como parámetro el número
de dígito sobre el que se ha hecho click. El método DigitClick,
por su parte, se encarga de disparar el evento anteriormente definido:
procedure TDigitalDisplay.DigitClick(Digit : integer);
{Método que dispara el evento OnDigitClick}
begin
if Assigned(FOnDigitClick) then
FOnDigitClick(Self,Digit);
end;
Y aquí esta el meollo de la cuestión. ¿Por qué
hemos definido el método DigitClick y no hemos escrito las
sentencias correspondientes dentro del método MouseDown?
La respuesta es reusabilidad. Si un usuario del componente quiere que en
respuesta a un click sobre un dígito dicho dígito cambie,
codificara las sentencias necesarias en respuesta al evento OnDigitClick.
Pero si es un nuevo programador el que esta desarrollando un hijo de TDigitalDisplay
no puede hacer esto. A este programador el procedimiento MouseDown le sirve
perfectamente y tan sólo tiene que reimplementar el procedimiento
DigitClick y poner allí lo que quiera. Si lo hubieramos codificado
todo en un único procedimiento esto no sería posible. De
ahí que mantengamos separados el procedimiento que disparará
el evento y el disparo del evento en sí. De este modo aseguramos
una gran flexibilidad a futuros desarrolladores: por ejemplo, un hijo reimplementara
DigitClick para incrementar el valor del dígito (por ejemplo, para
ajustar la hora), mientras que otro lo reimplementara para poner el valor
a cero (en un cronómetro). En ambos casos el método MouseDown
será el mismo y bastará con trabajar sobre DigitClick directamente.
El último detalle importante es que el método DigitClick se ha declarado dynamic, ya que si no se declara así, no se puede reimplementar, ya que los métodos estáticos no se pueden redefinir, sólo los dinámicos (ya sean dynamic o virtual). En otras unidades profundizaremos más sobre este tema. Mientras os refiero a la ayuda en línea de Delphi para más información sobre este tema.
Con esto queda terminado el componente. Quizás haya sido una explicación un poco breve pero considero que con el nivel que ya tenemos no debeis teneis problema para entender el funcionamiento del mismo. Os recomiendo que estudies la forma de trabajar con off-screen bitmaps, ya que es un tema que se utiliza frecuentemente en la programación gráfica bajo Windows. Y Recordar que este componente nos servira en la próxima unidad para desarrollar el tema de los editores de propiedades.
unit DigitalDisplay;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
{Tipo para el evento OnDigitClick}
TOnDigitClick = procedure(Sender : TObject; Digit : integer) of object;
TDigitalDisplay = class(TGraphicControl)
private
FBitmap : TBitmap; {Bitmap oculto (off-screen) para el dibujo del display}
FDigits : byte; {Número de digitos a representar}
FValue : string; {Valor a arepresentar}
FBckgndColor, FDigitOnColor, FDigitOffColor : TColor; {Colores}
FAlignment : TAlignment; {Alineación horizontal}
FLeadingZeros : boolean; {Rellenar con ceros}
FOnDigitClick : TOnDigitClick; {Campo para el evento OnDigitClick}
procedure DrawDot(index : byte;x,y : integer;ex,ey : single);
procedure DrawDigit(Digit : byte; x,y : integer;ex,ey : single);
procedure SetDigits(Value:byte);
procedure SetValue(Value:String);
procedure SetBckgndColor(Value : TColor);
procedure SetDigitOnColor(Value : TColor);
procedure SetDigitOffColor(Value : TColor);
procedure SetAlignment(Value : TAlignment);
procedure SetLeadingZeros(Value : boolean);
function GetNumberOfDigits : byte;
protected
procedure Paint; override;
procedure MouseDown(Button : TMouseButton; Shift : TShiftState; X,Y : Integer); override;
procedure DigitClick(Digit : integer); dynamic;
public
constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
published
property LeadingZeros : boolean read FLeadingZeros write SetLeadingZeros default False;
property Alignment : TAlignment read FAlignment write SetAlignment default taRightjustify;
property Digits : byte read FDigits write SetDigits default 4;
property Value : string read FValue write SetValue;
property BckgndColor : TColor read FBckgndColor write SetBckgndColor;
property DigitOnColor : TColor read FDigitOnColor write SetDigitOnColor;
property DigitOffColor : TColor read FDigitOffColor write SetDigitOffColor;
property OnDigitClick : TOnDigitClick read FOnDigitClick write FOnDigitClick;
property Visible;
property OnClick;
property OnDragDrop;
property OnDragOver;
property OnEndDrag;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
end;
procedure Register;
implementation
Const
{Coordenadas de los puntos}
{Coordenadas de los 10 (1..10) segmentos posibles: 7 segmentos + 1 punto decimal (.), 2 puntos de hora (:)
Cada segmento tiene 6 (0..5) vértices}
{ _ }
{ . |_| }
{ : |_| }
Px : Array[1..10,0..5] of integer = ((9,9,24,24,20,13),(26,26,26,24,22,22),(26,26,26,22,22,24),
(24,24,9,9,13,20),(7,7,7,9,11,11),(7,7,7,11,11,9),
(11,13,20,22,20,13),(2,2,5,5,5,2),(2,2,5,5,5,2),(2,2,5,5,5,2)) ;
Py : Array[1..10,0..5] of integer = ((0,0,0,0,4,4),(2,2,14,16,14,6),(20,20,32,28,20,18),
(34,34,34,34,30,30),(32,32,20,18,20,28),(14,14,2,6,14,16),
(17,15,15,17,19,19),(32,32,32,32,34,34),(22,22,22,22,25,25),(10,10,10,10,13,13));
{Para cada digito del 1 al 9, 1 representa segmento encendido, 0 apagado}
DigitsArray : Array[0..9] of string = ('1111110','0110000','1101101','1111001','0110011','1011011',
'1011111','1110000','1111111','1111011');
Seg_dot = 8; {Segmento correspondiente al .}
Seg_DotDown = 9; {Segmento correspondiente al punto inferior de :}
Seg_DotUp = 10; {Segmento correspondiente al punto superior de :}
constructor TDigitalDisplay.Create(AOwner : TComponent);
begin
inherited Create(AOwner);
ControlStyle:=ControlStyle+[csOpaque]; {Necesario para evitar el parpadeo}
FBitmap:=TBitmap.Create; {Creamos el bitmap que no servirá para dibujar el display off-screen}
{Valores por defecto}
FDigits:=4;
FValue:='1997';
FBckgndColor:=clBlack;
FDigitOnColor:=clLime;
FDigitOffColor:=$4000;
FAlignment:=taRightJustify;
Width:=108;
Height:=35;
end;
destructor TDigitalDisplay.Destroy;
begin
inherited Destroy;
FBitmap.Free; {Liberamos el bitmap off-screen}
end;
procedure TDigitalDisplay.SetAlignment(Value : TAlignment);
begin
if Value<>FAlignment then
begin
FAlignment:=Value;
repaint;
end;
end;
procedure TDigitalDisplay.SetDigits(Value : byte);
begin
if Value<>FDigits then
begin
FDigits:=Value;
repaint;
end;
end;
procedure TDigitalDisplay.SetLeadingZeros(Value : boolean);
begin
if Value<>FLeadingZeros then
begin
FLeadingZeros:=Value;
repaint;
end;
end;
procedure TDigitalDisplay.SetValue(Value : string);
begin
if Value<>FValue then
begin
FValue:=Value;
repaint;
end;
end;
procedure TDigitalDisplay.SetBckgndColor(Value : TColor);
begin
if FBckgndColor<>Value then
begin
FBckgndColor:=Value;
repaint;
end;
end;
procedure TDigitalDisplay.SetDigitOnColor(Value : TColor);
begin
if FDigitOnColor<>Value then
begin
FDigitOnColor:=Value;
repaint;
end;
end;
procedure TDigitalDisplay.SetDigitOffColor(Value : TColor);
begin
if FDigitOffColor<>Value then
begin
FDigitOffColor:=Value;
repaint;
end;
end;
procedure TDigitalDisplay.Paint;
Var
ex, ey : single; {Escala a la que dibujar el display}
i, Digito : byte;
Incx, x,y, offsetx, offsety :integer;
DigitSpace : integer;
SrcRect : TRect;
Digitos : byte;
Caracter : char;
FillString : string;
begin
SrcRect:=Rect(0,0,Width,Height); {Obtenemos el rectángulo del componente}
FBitmap.Width:=Width; {Ajustamos nuestro bitmap}
FBitmap.Height:=Height;
{Cálculo del offset a dejar alrededor}
offsetx:=Trunc(0.2*width);
offsety:=trunc(0.2*height);
{Calculamos el espacio disponible para cada dígito}
DigitSpace:=Trunc((Width-offsetx) / FDigits);
{Posicionamos la x e y}
x:=offsetx div 2;
y:=offsety div 2;
{Cálculo de la escala}
ex:=DigitSpace/27;
ey:=(Height-offsety)/35;
{Inicializamos el nº de digitos dibujados}
Digitos:=0;
{Asignamos colores}
FBitmap.Canvas.Brush.Color:=FBckgndColor;
FBitmap.Canvas.FillRect(SrcRect);
{Asignamos a la variable text el valor a representar}
Text:=FValue;
{Creamos una cadena auxiliar de relleno por la izquierda}
if FLeadingZeros then
FillString:='000000000000000000000000000000'
else
FillString:=' ';
{Rellenar la variable text con ceros o espacios (FillString) según sea la alineación}
case FAlignment of
taRightJustify : Text:=Copy(FillString,1,FDigits-GetNumberOfDigits)+FValue;
taCenter : Text:=Copy(FillString,1,(FDigits-GetNumberOfDigits) div 2)+FValue;
end;
{Bucle para el dibujo de cada dígito}
for i:=1 to Length(Text) do
begin
Caracter:=Copy(Text,i,1)[1]; {Caracter a dibujar}
Case caracter of
'0'..'9': begin
Digito:=StrToInt(Caracter);
DrawDigit(Digito,x,y,ex,ey);
x:=x+DigitSpace;
Inc(Digitos);
if Digitos = FDigits then break;
end;
':' : begin
DrawDot(seg_DotUp,x,y,ex,ey);
DrawDot(seg_DotDown,x,y,ex,ey);
end;
'.',',': DrawDot(seg_Dot,x,y,ex,ey);
' ': x:=x+DigitSpace;
end;
end;
{Una vez dibujado totalmente el display en el bitmap off-screen, lo volcamos
al canvas del componente. Resultado ¡No hay parpadeo!}
Canvas.Draw(0,0,FBitmap);
end;
procedure TDigitalDisplay.DrawDot(index : byte;x,y : integer;ex,ey : single);
{Dibuja un punto en la posición x,y con la escala determinada por ex y ey}
Var
j : byte;
Puntos : array[0..5] of TPoint;
begin
FBitmap.Canvas.Pen.Color:=DigitOnColor;
FBitmap.Canvas.Brush.Color:=DigitOnColor;
for j:=0 to 5 do
Puntos[j]:=Point(x+Trunc(Px[index,j]*ex),y+Trunc(Py[index,j]*ey));
FBitmap.Canvas.Polygon([Puntos[0],Puntos[1],Puntos[2],
Puntos[3],Puntos[4],Puntos[5]]);
end;
procedure TDigitalDisplay.DrawDigit(Digit : byte; x,y : integer;ex,ey : single);
{Dibuja el dígito (0..9) pasado en la posición x,y con la escala determinada por ex y ey}
Var
i, j : byte;
Puntos : array[0..5] of TPoint;
begin
for i:=1 to 7 do
begin
if Copy(DigitsArray[Digit],i,1) = '0' then
begin
FBitmap.Canvas.Pen.Color:=FDigitOffColor;
FBitmap.Canvas.Brush.Color:=FDigitOffColor;
end
else
begin
FBitmap.Canvas.Pen.Color:=FDigitOnColor;
FBitmap.Canvas.Brush.Color:=FDigitOnColor;
end;
for j:=0 to 5 do
Puntos[j]:=Point(x+Trunc(Px[i,j]*ex),y+Trunc(Py[i,j]*ey));
FBitmap.Canvas.Polygon([Puntos[0],Puntos[1],Puntos[2],
Puntos[3],Puntos[4],Puntos[5]]);
end;
end;
function TDigitalDisplay.GetNumberOfDigits : byte;
{Devuelve el número de digitos numéricos de la propiedad Value}
Var
i : byte;
begin
Result:=0;
for i:=1 to Length(FValue) do
if Copy(FValue,i,1)[1] in ['0'..'9'] then
Inc(Result);
end;
procedure TDigitalDisplay.MouseDown(Button : TMouseButton; Shift : TShiftState; X,Y : Integer);
{Método que se encarga de determinar si se ha pulsado con el ratón sobre un dígito del display.
En caso afirmativo se dispara el evento correspondiente}
var
i, xt, xoff, yoff, DigitSpace : integer;
begin
inherited MouseDown(Button,Shift,X,Y);
{Cálculo del offset horizontal}
xoff:=Trunc(0.2*width);
xt:=Trunc(0.2*width) div 2;
{Cálculo del espacio ocupado por un dígito}
DigitSpace:=Trunc((Width-xoff) / FDigits);
{Vamos comprobando si el click se ha ha hecho sobre el dígito (i)}
for i:=1 to FDigits do
begin
if (x>=xt) AND (x<=xt+DigitSpace) then
DigitClick(i);
xt:=xt+DigitSpace;
end;
end;
procedure TDigitalDisplay.DigitClick(Digit : integer);
{Método que dispara el evento OnDigitClick}
begin
if Assigned(FOnDigitClick) then
FOnDigitClick(Self,Digit);
end;
procedure Register;
begin
RegisterComponents('Curso', [TDigitalDisplay]);
end;
end.
Como ejemplo de uso del componente y para demostraros que está realmente libre de flickering, en un form en blanco coloca un componente TDigitalDisplay y un botón. En respuesta al evento OnClick del botón codifica lo siguiente:
procedure TForm1.Button1Click(Sender: TObject); var contador : integer; begin for contador:=0 to 1000 do begin DigitalDisplay1.Value:=IntToStr(Contador); Application.ProcessMessages; end; end;¡Ejecutar el programa y disfrutar! ;)
Otros Links de Interés:
Cocina - Videos - Juegos Gratis - Postales cachondas - Cine - Programas Gratis - Letras de Canciones
Listas de todos los Tutoriales Gratis. 1998- 2007 - -
Los
tutoriales y cursos aquí reunidos son una recopilación de los mejores encontrados en
Internet.
El crédito y copyright de los mismos si lo hubiere corresponde al autor de cada
uno de ellos.
Si tu tutorial o curso está aquí, y deseas darlo de baja de esta recopilación o
quieres añadir el tuyo,
envíanos un mensaje desde
aquí