Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: mmxngg - Febbraio 20, 2012, 05:20:01 pm

Titolo: Come faccio a...
Inserito da: mmxngg - Febbraio 20, 2012, 05:20:01 pm
- Da lontanissimi ricordi del pascal so che era possibile testare il valore di una sottostringa così : Stringa[2:3] = 'ABC'. Mi da errore in fase di compilazione. Esiste in maniera diversa da quella che ricordo o me lo sono inventato per facilitarmi la vita ?  ;D
- Questa è un po più tosta. Ho parecchie form che sostanzialmente contengono gli stessi componenti e anzichè scrivere lo stesso codice di gestione per ognuna ho implementato un sistema generico sugli eventi e fin qui tutto bene. Il problema è che in alcuni casi vorrei che fosse eseguito anche del codice specifico (in sostanza una specie di eredietarietà a runtime). Sono riuscito a farlo sul componente Application e ho visto che c'è qualcosa anche per gli altri componenti su alcune proprietà / eventi ma non su tutti. C'è un sistema ?

Allengo parte del codice (con un minimo di commento) per rendere meglio l'idea :

Codice: [Seleziona]
// Richiamato dall'evento OnCreate di ogni form
procedure TFormMain.GenericCatchSettings(Sender: TObject);
var
  N: Integer;
begin
  with (TForm(Sender)) do
    for N := $0 to Pred(ComponentCount) do
      case (Components[N].ClassName) of
        'TTrackBar': begin
          TTrackBar(Components[N]).OnChange := @GenericTBChange;
        end;
        'TStaticText': begin
          TStaticText(Components[N]).OnMouseDown := @GenericSTMouseDown;
          TStaticText(Components[N]).OnMouseMove := @GenericSTMouseMove;
        end;
      end;
end;

// Per ogni componente static text cerco il rispettivo trackbar
procedure TFormMain.GenericSTMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
  N, Delta: Integer;
  MNewPos: TPoint;
  TBName: string;
begin
  if (Shift = [ssLeft]) then
  begin
    GetCursorPos(MNewPos);
    Delta := (MNewPos.X - MOldPos.X) + (MNewPos.Y - MOldPos.Y);
    MOldPos := MNewPos;
    TBName := 'TB' + Copy(TStaticText(Sender).Name, $3, Length(TStaticText(Sender).Name) - $2);
    with (TForm(TStaticText(Sender).Owner)) do
      for N := $0 to Pred(ComponentCount) do
        if (Components[N].Name = TBName) then
          TTrackBar(Components[N]).Position := TTrackBar(Components[N]).Position + Delta;
  end;
end;

procedure TFormMain.GenericSTMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  GetCursorPos(MOldPos);
end;

// Qui vorrei che fosse eseguito questo codice + quello eventualmente presente nel componente
procedure TFormMain.GenericTBChange(Sender: TObject);
var
  STName: string;
  N: Integer;
begin
  STName := 'ST' + Copy(TTrackBar(Sender).Name, $3, Length(TTrackBar(Sender).Name) - $2);
  with (TForm(TTrackBar(Sender).Owner)) do
    for N := $0 to Pred(ComponentCount) do
      if (Components[N].Name = STName) then
        TStaticText(Components[N]).Caption := IntToStr(TTrackBar(Sender).Position);
end;

// Il codice da eseguire associato al componente TBProva
procedure TFormMain.TBProvaChange(Sender: TObject);
begin
  ShowMessage('Prova');
end;

In pratica anzichè assegnare l'evento con Evento := @NomeProc non c'è una specie di AddHandleOn qualcosa ?
Titolo: Re:Come faccio a...
Inserito da: xinyiman - Febbraio 20, 2012, 10:55:46 pm
Per quanto riguarda il punto 1 puoi usare questo codice:
Codice: [Seleziona]
function EstraiStringa(StringaIn: string; Da: integer; A: integer): string;
var
     i: integer;
     ret: string;
begin
          ret:='';
          for i:=Da to A
                ret:=ret + StringaIn[i];
          EstraiStringa:=ret;
end;
Per il secondo problema devo fare delle ricerche!
Titolo: Re:Come faccio a...
Inserito da: bonmario - Febbraio 21, 2012, 07:56:08 am
Oppure usare la funzione copy http://www.delphibasics.co.uk/RTL.asp?Name=Copy (http://www.delphibasics.co.uk/RTL.asp?Name=Copy)


Ciao, Mario
Titolo: Re:Come faccio a...
Inserito da: mmxngg - Febbraio 21, 2012, 09:46:48 am
Si alla fine ho usato la funzione Copy mentre non ho ancora trovato niente per risolvere il secondo problema se non ripristinare l'evento di default (quello assegnato in fase di design) e li richiamare anche quello generico.
Titolo: Re:Come faccio a...
Inserito da: Microges2000 - Febbraio 21, 2012, 02:46:08 pm
Per il secondo problema puoi usare il metodo virtual sulle procedure che ti interessano oppure l'override di metodi se vuoi completamente riscrivere gli eventi.

Facciamo degli esempi....

Metodo 1 - aggiunta di funzionalità:

Definamo una semplice classe...

TPadre = Class
           Procedure Evento1;
         Public
           Procedure FaiEvento1; Virtual;
         End;

ora Evento1 eseguirà una serie di codice e, poi, richiamerà FaiEvento1 che sarà una procedura vuota.
Quindi avremo

Procedure TPadre.Evento1;
Begin
  {
   .....
   Qui' il codice relativo all'evento 1
   .....
  }

  FaiEvento1;  // Qui' è richiamata la procedura che interessa 
End;

Procedure TPadre.FaiEvento1;
Begin
End;

come vedi FaiEvento1 è vuota in quanto il padre si occupa di tutto.

Se vogliamo aggiungere qualche riga di codice in futuro basta derivare l'oggetto padre e riscrivere solamente la funzione che ci interessa:

TFiglio = Class(TPadre)
         Public
           Procedure FaiEvento1; Override;
         End;
         
Procedure TFiglio.FaiEvento1;
Begin
  {
   .....
   Qui' il codice da aggiungere
   .....
  }
End;

Metodo 2 - redifinizione procedure:

La tecnica è simile alla precedente ma sovrascrive tutto il codice

Definamo la solita classse ma, in questo caso, ci serve solo una procedura...

TPadre = Class
         Public
           Procedure FaiEvento1;
         End;

l'oggetto figlio sarà:

TFiglio = Class(TPadre)
         Public
           Procedure FaiEvento1; Override;
         End;

in questo caso abbiamo riscritto totalmente l'evento - possiamo comunque richiamare quello del padre richiamando la procedura con la parola chiave hinerited cosi':

Procedure TFiglio.FaiEvento1;
Begin
  inHerited FaiEvento1;
  {
   .....
   Qui' il codice dell'oggetto figlio
   .....
  }
End;
Titolo: Re:Come faccio a...
Inserito da: mmxngg - Febbraio 21, 2012, 05:02:08 pm
Ok, questa è l'eredietarietà standard ma se l'applico così son costretto a crearmi un nuova classe per ogni componente che utilizzo, compilarla nell'IDE ecc. ecc.
E' poco pratico perchè mi ritroverei con 2 TTrackBar, 2 TStaticText ecc. ecc. con l'unica differenza che quelli personalizzati eseguono una porzione di codice in più su un determinato evento.  L'oggetto Application ha AddOnUserInputHandler che permette di aggiungere (in stack) del codice rispetto a quello eseguito di default. Mi chiedevo se c'era un metodo generico (e a runtime) senza passare per la ridefinizione delle varie classi.
Titolo: Re:Come faccio a...
Inserito da: mmxngg - Febbraio 21, 2012, 06:38:15 pm
Ho trovato come fare...cercando altro per giunta...vabbè  (fortuna che lazarus è open source e si può spulciare nei sorgenti) :P

Il metodo più semplice è :

Codice: [Seleziona]
var
  ExecShow: procedure of object;

  TMethod(ExecShow).Data := TForm(Sender);
  TMethod(ExecShow).Code := TForm(Sender).MethodAddress('FormShow');
  if not (TMethod(ExecShow).Code = nil) then
    ExecShow;

Questa è il codice da inserire nella parte generica. In questo caso l'evento è l'OnShow e la procedure che contiene il codice specifico si chiama FormShow (e deve chiamarsi allo stesso modo in tutti gli oggetti).  Se non esiste semplicemente ritorna null e va oltre.

P.S.

Aggiungo che funziona solo con le procedure dichiarate published (probabilmente per le altre non viene salvato il nome e quindi ritorna null).
Titolo: Re:Come faccio a...
Inserito da: xinyiman - Febbraio 22, 2012, 07:56:20 am
Buono a sapersi  ;)
Titolo: Re:Come faccio a...
Inserito da: Microges2000 - Febbraio 22, 2012, 08:16:26 am
Perfetto (e buono a sapersi) ma conta che è sempre un "truccaccio" e sicuramente non portabile.
Lazarus come Delphi è fortemente orientato agli oggetti e la cosa migliore è usarli nei modi, più o meno, classici in quanto le caratteristiche come quella che hai trovato tu possono essere modificate dagli autori.
Titolo: Re:Come faccio a...
Inserito da: mmxngg - Febbraio 22, 2012, 09:45:21 am
E' lo stesso metodo che utilizza l'IDE per importare le varie opzioni della schermata dell'ispettore degli oggetti ma dovrebbe anche essere il metodo di base con cui il free pascal richiama gli oggetti dato che è una proprietà della classe TObject (praticamente la prima classe da cui tutti ereditano qualcosa) quindi funziona su qualsiasi piattaforma e difficilmente può subire modifiche (è praticamente un pilastro della struttura). L'unico appunto è dichiarare la variabile con lo stesso prototipo della procedure, quindi nell'esempio ho sbagliato (apparentemente non da errore in realtà si brucia lentamente lo stack).

Codice: [Seleziona]
var
  ExecShow: procedure (Sender: TObject) of object;

Questo è quello corretto e rispecchia l'evento OnShow.

Certo, è un modo a basso livello di utilizzare un linguaggio ad alto livello ma ha i vuoi vantaggi. Io l'ho trovato perchè stavo cercando un sistema per far eseguire il codice contenuto nell'OnShow anche quando la form era già visibile. Utilizzando Show o settando visibile a true non funzionava visto che non cambiava lo stato ed era una bella menata.