Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: Stefano - Luglio 06, 2012, 10:04:41 pm

Titolo: risposta ad evento TAction
Inserito da: Stefano - Luglio 06, 2012, 10:04:41 pm
da un form Menu chiamo un altro form GestioneContabileAffGiu (non modale),
va tutto bene, eccetto che vorei evitare la dichiarazione globale di frm_GestioneContabileAffGiu: Tfrm_GestioneContabileAffGiu;
ed averla solo nella procedure.

dovrei riuscire a passare l'istanza frm_GestioneContabileAffGiu a LiberaGestioneContabileAffGiu
qualcosa di simile

frm_GestioneContabileAffGiu.act_Uscita.OnExecute := @LiberaGestioneContabileAffGiu(frm_GestioneContabileAffGiu);

es: se modifico cosi' procedure Tfrm_MenuAree.LiberaGestioneContabileAffGiu(Sender: TObject; var Rifer: TObject);

frm_GestioneContabileAffGiu.act_Uscita.OnExecute := @LiberaGestioneContabileAffGiu(Sender, frm_GestioneContabileAffGiu);

in compilazione
Error: Incompatible type for arg no. 1: Got "<procedure variable type of procedure(TObject;var TCloseAction) of object;Register>", expected "<procedure variable type of procedure(TObject) of object;Register>"

spero in qualche suggerimento.

Codice: [Seleziona]
//Actcion act_Uscita seve ad intercettare la pressione di un tasto cui e' associata la TAction act_Uscita nel form Tfrm_GestioneContabileAffGiu
//
procedure Tfrm_MenuAree.GestioneContabileAffGiu;
var frm_GestioneContabileAffGiu: Tfrm_GestioneContabileAffGiu;
begin
  frm_GestioneContabileAffGiu := Tfrm_GestioneContabileAffGiu.Create(Self);
  frm_GestioneContabileAffGiu.Parent := Self;
  frm_GestioneContabileAffGiu.Top := 0;
  frm_GestioneContabileAffGiu.Left := 0;
  frm_GestioneContabileAffGiu.Width := Self.ClientWidth;
  frm_GestioneContabileAffGiu.Height := Self.ClientHeight;
  //
  //se uso la dichiarazione locale frm_GestioneContabileAffGiu: Tfrm_GestioneContabileAffGiu;
  //quando va ad eseguire LiberaGestioneContabileAffGiu  frm_GestioneContabileAffGiu e' = nil
  //
  frm_GestioneContabileAffGiu.act_Uscita.OnExecute := @LiberaGestioneContabileAffGiu;
  frm_GestioneContabileAffGiu.Show;
end;

il metodo close

Codice: [Seleziona]
procedure Tfrm_MenuAree.LiberaGestioneContabileAffGiu(Sender: TObject);
begin
  //mi serve ricevere il riferimento frm_GestioneContabileAffGiu
  //
  frm_GestioneContabileAffGiu.Close;
  //FreeAndNil(frm_GestioneContabileAffGiu);
end;           
Titolo: Re:risposta ad evento TAction
Inserito da: Maverich - Luglio 07, 2012, 12:02:09 pm
direi che non serve dichiarare un'istanza frm_GestioneContabileAffGiu,
se come penso si tratta di una scelta del tipo:

Form1 -> form base; Form2 -> formchild

dopo la Interface, nelle uses basta aggiungere la unit relativa a <frm_GestioneContabileAffGiu>

unit <MenuAree>;

{$mode objfpc}{$H+}

interface

uses
 Classes, SysUtils ....., <unit relativa a frm_GestioneContabileAffGiu> ...

type
  { Tfrm_MenuAree }

  Tfrm_MenuAree = class(TForm)

l'istanza frm_GestioneContabileAffGiu e' già dichiarata nella
unit tramite la var  frm_GestioneContabileAffGiu: Tfrm_GestioneContabileAffGiu;
tipicamente in modo simile
Codice: [Seleziona]
  Tfrm_GestioneContabileAffGiu = class(TForm)
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  frm_GestioneContabileAffGiu: Tfrm_GestioneContabile

implementation

{$R *.lfm}

end.

di conseguenza non serve dichiarare altro, naturalmente il form non deve essere presente
nella creazione automatica dei Form
verifca Opzioni Progetto->Form  e se presente sposta tramitye la freccia => sul pannello Form Disponibili;
in caso contrario avrai un doppione quello creato all'avvio e quello creato a runtime

Codice: [Seleziona]
//
procedure Tfrm_MenuAree.GestioneContabileAffGiu;
begin
  frm_GestioneContabileAffGiu := Tfrm_GestioneContabileAffGiu.Create(Self);
  frm_GestioneContabileAffGiu.Parent := Self;
  frm_GestioneContabileAffGiu.Top := 0;
  frm_GestioneContabileAffGiu.Left := 0;
  frm_GestioneContabileAffGiu.Width := Self.ClientWidth;
  frm_GestioneContabileAffGiu.Height := Self.ClientHeight;
  frm_GestioneContabileAffGiu.act_Uscita.OnExecute :=@LiberaGestioneContabileAffGiu;
  frm_GestioneContabileAffGiu.Show;
end;

quando eseguira' questa parte, l'instanza sara' ancora visibile
Codice: [Seleziona]
procedure Tfrm_MenuAree.LiberaGestioneContabileAffGiu(Sender: TObject);
begin
  frm_GestioneContabileAffGiu.Close;
  FreeAndNil(frm_GestioneContabileAffGiu);
end;
Titolo: Re:risposta ad evento TAction
Inserito da: Stilgar - Luglio 08, 2012, 11:15:39 pm
Ciao a tutti.
Scusate se mi intrometto.
Per me basta solo gestire correttamente gli eventi della form e giocare con la struttura della LCL/VCL.
Allora, CloseAction permette di imposatare il comportamento alla richiesta di chiusura.
E' utile intercettarlo per sapere cosa succede, ma non indispensabile.

Allora Stefano, dichiari la form come child di un'altra form.
Vuoi intercettare correttamente la chiusura, per poter annullare il riferimento esterno alla form secondaria. (Ricapitolo solo per vedere se ho capito il problema).

Se sì, scatta la proposta oscena.
1) Dichiari una variabile nella form primcipale per la form secondaria come TForm.
2) Registri per l'eliminazione del componente il controllore (la form principale) con FreeNotification.
3) Sovrascrivi il metodo notification.
Occhio che questo metodo ha una marea di chiamate. Devi filtare per gli eventi di tipi "eliminazione" e mirare alla componente che ha indirizzo in memoria come la tua form.
Un if banalissimi, spiegato in termini astrusi ;)
Codice: [Seleziona]
if (Operation=...) and (Component = fFormSecondaria) then fFormSecondaria = null;
In questo modo lasci liberi i callback (eventi) della form (il sistema pemette di avere un solo callback nativo, il broad cast degli eventi è sempre fai-da-te-sperando-non-faccia-casini).
Questo è anche un modo molto comodo per fare il broad cast della creazione ed eliminazione dei componenti.
Quando accedi alla fFormSecondaria devi castare il riferimento al tipo della tua form.
Codice: [Seleziona]
  
  Tfrm_GestioneContabileAffGiu(fFormSecondaria).xxxxx
O ti fai una funzioncina che ti semplifici il codice. Ma è esteticamente una pessima idea ;)
Stedano, se hai capito è un miracolo. Penso di essermi espesso malissssssssimo.
Titolo: Re:risposta ad evento TAction
Inserito da: Stefano - Luglio 10, 2012, 08:07:46 am
Altroche intromissione, e' proprio avere idee diverse, che aiuta.
Sicuramente per colpa mia, ma non ho capito molto, magari due righe di codice per chiarire meglio il concetto.

Citazione
tu scrivi  Quando accedi alla fFormSecondaria devi castare il riferimento al tipo della tua form.
Tfrm_GestioneContabileAffGiu(fFormSecondaria).xxxxx

ma Tfrm_GestioneContabileAffGiu e' la form secondaria, chiamata dalla form Tfrm_ManuAree (form primaria)
che io chiamo cosi'

procedure Tfrm_MenuAree.GestioneContabileAffGiu;
begin
  frm_GestioneContabileAffGiu := Tfrm_GestioneContabileAffGiu.Create(Self);
  frm_GestioneContabileAffGiu.Parent := Self;
  frm_GestioneContabileAffGiu.act_Uscita.OnExecute := @LiberaGestioneContabileAffGiu;
  frm_GestioneContabileAffGiu.Show;
end;

//vado a naso non ho il codice sottomano
dovrei fare:

Tfrm_MenuAree(frm_GestioneContabileAffGiu).Create   .... o Create(Self)

if (Operation = CloseAction) and (Component = frm_GestioneContabileAffGiu) then frm_GestioneContabileAffGiu = null

altra cosa, esiste un modo per verificare se ci sono eventi sospesi (in seguito a chiamate non gestite) , oppure se la memoria ' stata liberata.
Titolo: Re:risposta ad evento TAction
Inserito da: nomorelogic - Luglio 10, 2012, 09:57:11 am
altra cosa, esiste un modo per verificare se ci sono eventi sospesi (in seguito a chiamate non gestite) , oppure se la memoria ' stata liberata.

nel senso: in seguito a chiamate *non ancora* gestite a causa del fatto che il main-thread è occupato?
Credo sia necessario andare a vedere cosa fa Application.ProcessMessages.
E comunque anche la chiusura di un form avviene come risposta ad un evento, probabilmente l'esaurimento della coda avviene naturalmente :)
Titolo: Re:risposta ad evento TAction
Inserito da: Stefano - Luglio 10, 2012, 11:36:22 am
si' un qualcosa che mi permetta di verificare cosa sta succedendo dietro il codice

In Delphi XE2 attivando Finestra Debug - Local Variable dove e' possibile seguire ogni operazione di assegnazione.

In Lazarus inestre Debub -> Variabili Locali, non produce alcun risultato
vedo sempre il messaggio Self <error reading variable>, qualunque sia l'istruziuone in esecuzione in un dato momento.

Cosi' se per ipotesi frm_GestioneContabileAffGiu dovesse continuare ad essere presente, anche dopo le operazioni di chiusura
sarebbe facile capire che e' stato commesso qualche errore.

Magari l'equivalente esiste anche in Lazarus, ma al momento non  l'ho ancora trovata.
Titolo: Re:risposta ad evento TAction
Inserito da: nomorelogic - Luglio 10, 2012, 01:21:02 pm
ops, pensavo intendessi a runtime in generale invece si trattava di debug :P

in opzioni progetto -> opzioni compilatore -> linking
c'è la spunta su "genera informazioni di debug per GDB"?
Titolo: Re:risposta ad evento TAction
Inserito da: Stefano - Luglio 10, 2012, 03:46:09 pm
opzione gia' attivata;
Choose type of debug info -> Automatico (-g)

l'unica Finestra che somiglia a quella citata di Delphi e' : Stack Chiamata
Titolo: Re:risposta ad evento TAction
Inserito da: Stilgar - Luglio 10, 2012, 10:51:32 pm
Ciao Stefano.
Ho allegato un esempio.
Come vedrai è di una banalità disarmante.
Diciamo che questo è un buon sistema per tenere il riferimento alle istanza di altri componenti.

PS:
Se hai dei thread che lavorano in back ground, è bene che crei una sorta di monitor.
Anche una cosa semplice semplice del tipo "E' vivo/è defunto" nulla di complesso.
Per quanto riguarda le chiamate in Single Thread ... in genere non sono rientranti, ma bloccanti. Quindi se nella chiusura della Form Secondria gli spari un loop infinito ... blocchi l'applicazione.
Non fatevi confondere dal nome "eventi"... :D Non sono affatto eventi, sono call back. 
Riciao
Titolo: Re:risposta ad evento TAction
Inserito da: Stefano - Luglio 11, 2012, 10:39:12 am
Ho scaricato l'esempio, e risulta piu' semplice del mio sistema.

Partendo dal tuo esempio, provo a creare una routine generica (CreaForm), passando il Form da creare,
pero' non accetta aForm
Error: Incompatible type for arg no. 1: Got "TForm", expected "TComponentClass"

per creare chiamo CreaForm , invece di Application.CreateForm(TForm2, FChild)
Codice: [Seleziona]
procedure TForm1.Button3Click(Sender: TObject);
begin
  CreaForm(Form2);
  //passo Form2 che e' un TForm
end; 
ciclo generale di creazione
Codice: [Seleziona]
procedure CreaForm(sForm: TForm);
begin
  //Error: Incompatible type for arg no. 1: Got "TForm", expected "TComponentClass"
  Application.CreateForm(aForm, FChild);  //errore in compilazione, nel parametro aForm

  //se invece
  Application.CreateForm(TForm2, FChild);  //va bene
end;
Titolo: Re:risposta ad evento TAction
Inserito da: Stefano - Luglio 11, 2012, 11:09:21 am
ho modificato cosi':
Codice: [Seleziona]
procedure CreaForm(aForm: TComponentClass);
begin
  Application.CreateForm(aForm, FChild);
  FChild.Parent := Self;
  FChild.Top := 0;
  FChild.Left := 0;
  FChild.Show;
  FChild.FreeNotification(Self);
end;

e chiamo con  CreaForm(<form da gestire>);

es: CreaForm(TForm2); 
Titolo: Re:risposta ad evento TAction
Inserito da: Stilgar - Luglio 11, 2012, 11:22:37 am
Puoi dichiarare un tipo :
Codice: [Seleziona]
  TFormClass = class of TForm;
e usare questo come tipo del parametro.
Altrimenti la tua routine accetterebbe anche un ... TConnection. (Ad esempio)
EDIT:
Quello che non andava bene nella prima versione è che stavi dicendo al compilatore che deve attendersi una chiamata con passaggio d'istanza.
Codice: [Seleziona]
procedure CreaForm(aForm: TComponentClass); begin   Application.CreateForm(aForm, FChild);
Qui, invece, stai dicendo che vuoi passare una classe, un tipo.
Titolo: Re:risposta ad evento TAction
Inserito da: Stefano - Luglio 11, 2012, 07:39:50 pm
ho provato la nuova versione, e tutto bene, eccetto il controllo dei componenti interni al Form

  type TFormClass = class of TForm;  //dichiarazione

se dentro un Form ho un TPageControl con varie Pagine o un TPanel, non posso accedervi

  CreaForm(TFrm_Prova);   //creo il Form, ma non ho istanze per Tfrm_Prova
  .... come accedo a TPanel o altro ?
  <istanza>.pnlComunicazione.Visble := False;  //per <istanza> non ho riferimenti

con il metodo "tradizionale" ho un'istanza frm_Prova

Codice: [Seleziona]
  frm_Prova := Tfrm_Prova.Create(Self);
  frm_Prova.pnlComunicazioni.Visible := False;  //nasconde TPanel

  //visualizzo le pagine che interessano
   for i := 0 to frm_Prova.PageControl1.PageCount -1 do
       begin
         frm_Prova.PageControl1.Pages[i].TabVisible := True;
         if i > 3 then frm_Prova.PageControl1.Pages[i].TabVisible := False;
       end;
nuova versione
Codice: [Seleziona]
Tfrm_MenuAree = class(TForm)
  procedure CreaForm(aForm: TFormClass);
  procedure Notification(AComponent: TComponent; Operation: TOperation); override;

procedure Tfrm_MenuAree.CreaForm(aForm: TFormClass);
begin
  Application.CreateForm(aForm, FChild);
  FChild.Parent := Self;
  FChild.Top := 0;
  FChild.Left := 0;
  FChild.Show;
  FChild.FreeNotification(Self);
end;
Titolo: Re:risposta ad evento TAction
Inserito da: Stilgar - Luglio 11, 2012, 10:13:36 pm
O usi un'interfaccia.
In questo modo la form secondaria l'implementa, ma le cose si complicano un attimino...
Titolo: Re:risposta ad evento TAction
Inserito da: Stefano - Luglio 12, 2012, 08:18:45 am
un'interfaccia di per se espone dei metodi;
anche implementando l'interfaccia, senza un'istanza specifica ad un Form non sara' possibile
controllare gli elementi interni (forse).

prova a dare un'occhiata al codice

Codice: [Seleziona]
unit unt_InterfaceTest;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms;

  type TFormClass = class of TForm;

  type IEventHandler = Interface
    ['{3D1B55A3-F2BD-422C-869B-BD63AF8FBDEF}']
    procedure CreaForm(aForm: TFormClass);
  end;
quindi implemento una classe che usi l'interface
Codice: [Seleziona]
  type

    { TGenerale }

      TGenerale = class(TInterfacedObject, IEventHandler)
    private
      FChild: TForm;

      procedure CreaForm(aForm: TFormClass);

    public
  end;

type
  { Tfrm_MenuAree }

  Tfrm_MenuAree = class(TForm)
....
  private
    { private declarations }
      clGenerale: TGenerale;  //istanza alla classe TGenerale

...
procedure Tfrm_MenuAree.FormCreate(Sender: TObject);
begin
  TFormClass.Create(Self);

  try
    clGenerale := TGenerale.Create;
end;         
ora resta da sostituire Self con un riferimento diretto
Codice: [Seleziona]
procedure TGenerale.CreaForm(aForm: TFormClass);
begin
  Application.CreateForm(aForm, FChild);

  //TGenerale non e' la classe che gestisce i Form, non conosce Self
  {FChild.Parent := Self; }

  FChild.Parent := frm_MenuAree.PanelAppoggio;
  FChild.Top := 0;
  FChild.Left := 0;
  FChild.Show;
  {FChild.FreeNotification(Self);} //non ancora implementato nell'Interface
end;
vado a richiamare ilo metodo
Codice: [Seleziona]
clGenerale.CreaForm(TForm2);
Titolo: Re:risposta ad evento TAction
Inserito da: Stilgar - Luglio 12, 2012, 09:42:02 am
Quello che intendevo è nella gestione dell'interfaccia ;)
Per fare un esempio concreto ... modifico l'esempio di prima.
EDIT:
Per commentare il codice un attimino.
Ho creato un'interfaccia che permetta di puntare ai metodi della form secondaria.
Ho cambiato il modo di dichiarare il campo della form principale.
Ho cambiato il modo di costruire la form secondaria.
L'importante è non usare un attimo la definizione dell'interfaccia e un altro la form.
Se usi le interfaccie devi creare una sorta di zona militarizzata, dove non entri direttamente. Altrimenti scardini il meccanismo del ref counting. ;)
Tieni conto che in questo modo otterresti un codice molto più modulare e "leggibile".