Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: bonmario - Dicembre 16, 2021, 08:48:52 am

Titolo: Impostare chiamata ad un evento
Inserito da: bonmario - Dicembre 16, 2021, 08:48:52 am
Ciao a tutti,
avendo studiato a scuola Turbo Pascal, ed avendo "imparato" ad usare fpc/Lazarus da autodidatta, ci sono alcune cose che ho imparato meccanicamente, senza ben capire i meccanismi che ci sono dietro.

Ora, sto avendo un problema, che non riesco a risolvere, a causa di una di queste cose che non capisco.

Provo a spiegare il problema ...

Ho un oggetto dichiarato così:
Codice: [Seleziona]
PopupNotifier1: TPopupNotifier;

Dovendo far eseguire delle operazioni quando un utente chiude la finestrella del popup, ho scritto questo codice:
Codice: [Seleziona]
PopupNotifier1.OnClose:=@ChiudiPopup;

La procedura "ChiudiPopup", è stata dichiarata così:
Codice: [Seleziona]
procedure TForm1.ChiudiPopup(Sender: TObject; var CloseAction: TCloseAction);

Quello che non capisco è questo: quando assegno la "OnClose", scrivo solo il nome della procedura, senza dichiarare i parametri da passargli ... da dove li ricava ?????

Provo a rifare la domanda in un'altra maniera: visto che "CloseAction" è dichiarata come "var", se in un certo punto del programma dovessi testare il valore di quella variabile, come faccio ?

Grazie, Mario
Titolo: Re:Impostare chiamata ad un evento
Inserito da: DragoRosso - Dicembre 16, 2021, 09:13:16 am
Quello che viene effettuata è una assegnazione di "puntatore", ossia assegni alla funzione OnClose (che è di fatto un puntatore e per default è nil) un nuovo puntatore a funzionne @ChiudiPopup.

In linea prettamente teorica, l'assegnazione potrebbe violare tutte le regole e il puntatore ad esempio non essere un puntatore a funzione ma ad una variabile o altro (e in questo caso a RUNTIME avresti un bel AV).

Però il compilatore PASCAL (tutti i compilatori PASCAL) DOVREBBE ESEGUIRE una verifica con la firma dei metodi, ossia con i parametri che le funziomi o le procedure dichiarano. Attenzione che non tutti i compilatori eseguono la verifica con la definizione del risultatoo (ad esempio integer o boolen) in caso di funzioni.

La definizione di OnClose la trovi nell'unità dove è definito TPopUpMenu:

Codice: [Seleziona]
  TPopupMenu = class(TMenu)
  private
    FAlignment: TPopupAlignment;
     ....................
  published
     ...............
    property OnClose: TNotifyEvent read FOnClose write FOnClose;
  end;

OnCLose è un TNotifyEvent, quindi la tua assegnazione è ERRATA .....

Codice: [Seleziona]
  TNotifyEvent = procedure(Sender: TObject) of object;

Ha un parametro in più, CloseAction non ci stà ....
Quindi se ti viene compilato il codice, desumo che il compilatore freePascal non esegue il controllo ma fà solo l'assegnazione della procedura .....

Ciao
Titolo: Re:Impostare chiamata ad un evento
Inserito da: DragoRosso - Dicembre 16, 2021, 09:29:44 am
Provo a rifare la domanda in un'altra maniera: visto che "CloseAction" è dichiarata come "var", se in un certo punto del programma dovessi testare il valore di quella variabile, come faccio ?

Intanto qui la definizione è errata, comunque in generale quella sarebbe una variabile di tipo TCloseAction.

Quindi può assumere uno dei valori seguenti.
Codice: [Seleziona]
TCloseAction = (caNone, caHide, caFree, caMinimize);

Per default avrà un valore (presumo caNone)  e la puoi testare con un IF ad esempio oppure la puoi assegnare ad uno dei valori consentiti.

Ciao
Titolo: Re:Impostare chiamata ad un evento
Inserito da: bonmario - Dicembre 16, 2021, 10:15:18 am
Scusami, ma credo che stiamo parlando di cose diverse: tu stai parlando di "TPopupMenu", io di "TPopupNotifier".

Probabilmente è questo il motivo per cui mi scrivi che la dichiarazione che ho fatto è errata.

Detto questo, questa è parte del codice che non mi compila:
Codice: [Seleziona]
      PopupNotifier1:=TPopupNotifier.Create(nil);
      try
        PopupNotifier1.Icon.Assign(Form1.Icon);
        PopupNotifier1.Title:=Titolo;
        PopupNotifier1.Text:=MsgDaEme;
        PopupNotifier1.TextFont.Color:=clBlack;
        PopupNotifier1.OnClose:=@ChiudiPopup;
     
        qui c'è un ciclo while in cui ogni secondo vorrei capire se l'utente ha chiuso o meno la finestrella di notifica, al cui interno
        ho l'istruzione qui sotto:
        if (CloseAction <> caFree) then begin

Il compilatore mi dice che quella variabile non esiste ...
Titolo: Re:Impostare chiamata ad un evento
Inserito da: DragoRosso - Dicembre 16, 2021, 03:16:46 pm
Pardon, stamane ero di fretta e mi era sfuggito il dettaglio.

Comunque la definizione proviene parai pari da:

Codice: [Seleziona]
  TCloseEvent = procedure(Sender: TObject; var CloseAction: TCloseAction) of object;

CloseAction non è una variabile appartenente alla classe, ed è usabile SOLO all'interno dell'evento ossia nella tua procedura "ChiudiPopUP".

OVVIAMENTE LA TUA PROCEDURA che hai abbinato a OnClose deve avere lo stessa definizione:

Codice: [Seleziona]
procedure TFormxxx.ChiudiPopUP(Sender: TObject;  var CloseAction: TCloseAction);
begin
  //qui puoi usare CloseAction
end; 

CloseAction non riporta cosa l'utente ha deciso di fare, ma cosa il tuo codice vuole fare quando avviene l'evento. Sinceramente non conosco quella classe, e non riesco ad abbinare questo dato logicamente (es. caMinimize cosa fà su un popup).

Non ha quindi senso testarla con l'IF se il tuo codice non l'ha a sua volta già impostata (per default dovrebbe essere sempre caNone all'ingresso della procedura).

Ciao
Titolo: Re:Impostare chiamata ad un evento
Inserito da: nomorelogic - Dicembre 16, 2021, 03:21:08 pm
In pratica le proprietà che iniziano con "On" sono dei puntatori a metodo (cioè a procedura/funzione di una istanza).
Quando le assegni, assegni l'indirizzo che la procedura deve andare in esecuzione quando si verifica quell'evento.

Come dice DragoRosso è all'interno di quella procedura che puoi usare le variabili che trovi nella definizione dell'evento.
Titolo: Re:Impostare chiamata ad un evento
Inserito da: DragoRosso - Dicembre 16, 2021, 04:08:30 pm
Oggi era una giornata buona per rimanere a dormire.

Forse ho compreso cosa intendeva chiedere @bonmario.

La visualizzazione di un PopUP non è bloccante, ossia il codice non viene bloccato dopo la sua visualizzazione.

Quando l'utente deciderà di chiudere il PopUP, viene generato l'evento OnClose, evento che tu hai fatto puntare alla tua procedura e che quindi verrà eseguita.

Se vuoi nel tuo codice (ESTERNO ALL'EVENTO OnClose, quindi esterno alla tua procedura ChiudiPopUP) sapere quando questo è avvenuta, semplicemente usa una varibiale boolean definita globalalmente o nella tua classe Form per settarla nell'evento. Poi la puoi testare con tutta calma.

Esempio:
Codice: [Seleziona]
var PopUPChiuso: boolean;
//Da qualche parte lo inizializzi a false
// PopUPChiuso := false;

///Da qualche parte lo testi e lo rimetti a false
// If PopUPChiuso then
//   begin
//      ..... fai qualcosa
//      PopUPChiuso := false;
//   end;

procedure TForm1.ChiudiPopUP(Sender: TObject;  var CloseAction: TCloseAction);
begin
   PopUPChiuso := True;
end; 

EDIT: Nota che il ".......fai qualcosa" lo potresti fare anche nell'evento ChiudiPopUP, compatibilmente con la tua logica e il tuo codice ....

Ciao
Titolo: Re:Impostare chiamata ad un evento
Inserito da: bonmario - Dicembre 16, 2021, 04:57:42 pm
Grazie @DragoRosso, ho fatto come dici tu, usando "PopUPChiuso" e va bene, ma c'è una cosa che continuo a non capire ...

Se la dichiarazione è questa:
Codice: [Seleziona]
procedure TForm1.ChiudiPopUP(Sender: TObject;  var CloseAction: TCloseAction);

la variabile "CloseAction", nonostante sia stata dichiarata con la clausola "var", perderà il suo valore all'uscita della procedura "ChiudiPopUP" ?

Se così fosse, che senso ha dichiararla come "var" ??

Grazie, Mario
Titolo: Re:Impostare chiamata ad un evento
Inserito da: DragoRosso - Dicembre 16, 2021, 05:24:04 pm
Grazie @DragoRosso, ho fatto come dici tu, usando "PopUPChiuso" e va bene, ma c'è una cosa che continuo a non capire ...

Se la dichiarazione è questa:
Codice: [Seleziona]
procedure TForm1.ChiudiPopUP(Sender: TObject;  var CloseAction: TCloseAction);

la variabile "CloseAction", nonostante sia stata dichiarata con la clausola "var", perderà il suo valore all'uscita della procedura "ChiudiPopUP" ?

Se così fosse, che senso ha dichiararla come "var" ??

Grazie, Mario

Perchè è una variabile che nell'evento può essere settato. Il suo valore definisce come si comporterà il componente che riceve il messaggio WM_CLOSE di Windows.

EDIT: Ovviamente CloseAction verrà usato dal chiamante per fare qualcosa.

Con il PopUP non comprendo come possa essere di utilità, ma in una Form, se assegni l'evento OnClose vedrai che viene create la stessa identica procedura come evento.

In quel caso se assegni ad esempio a CloseAction il valore ca_Hide, la Form si "nasconderà" cioè diventerà invisibile. Se usi caFree, la Form verrà distrutta.

La definizione di VAR indica che il parametro viene passato come riferimento (puntatore in memoria) e non come valore (nello stack o nei registri CPU).

E' normale definire un parametro con "var" quando si hanno a che fare con metodi legati ad eventi o altro (callback ad esempio).

Ciao
Titolo: Re:Impostare chiamata ad un evento
Inserito da: bonmario - Dicembre 16, 2021, 06:10:23 pm
EDIT: Ovviamente CloseAction verrà usato dal chiamante per fare qualcosa.

Era quello che mi interessava !!! Se fossi riuscito a capirlo, avrei usato direttamente qual valore, senza usare una variabile mia.

Con il PopUP non comprendo come possa essere di utilità, ma in una Form, se assegni l'evento OnClose vedrai che viene create la stessa identica procedura come evento.

L'oggetto che uso, TPopupNotifier, permette di emettere dei messaggi, è molto simile ai messaggi che sembrano un fumetto di Windows 10.
Ci sono alcune cose che mi devo ricordare durante la giornata, e per ricordarmele faccio uscire il messaggio tramite quell'oggetto.

In determinati casi, mi serve capire quando l'utente chiude il messaggio, in modo da poter fare altre operazioni.

Grazie, Mario
Titolo: Re:Impostare chiamata ad un evento
Inserito da: DragoRosso - Dicembre 16, 2021, 06:56:26 pm
EDIT: Ovviamente CloseAction verrà usato dal chiamante per fare qualcosa.

Era quello che mi interessava !!! Se fossi riuscito a capirlo, avrei usato direttamente qual valore, senza usare una variabile mia.

Quella variabile ha "vita" esclusivamente in quella procedura, valore di default all'inizio e valore eventualmente settato da te entro la fine della procedura.

Non la si può "chiamare" da nessun altra parte, o meglio anche se definisci una varibaile globale con lo stesso nome, NON SARA' QUELLA COMUNQUE.

E' appunto una "var" perchè il chiamante (il thread principale del tuo programma, che genericamente ti è nascosto) deve poterla usare, ma la usa solo lui al di fuori della tua procedura.

EDIT: il thread principale usa il valore ritornato dalla variabile, ovviamente non la variabile ....

Ciao.
Titolo: Re:Impostare chiamata ad un evento
Inserito da: bonmario - Dicembre 17, 2021, 08:03:24 am
Ok, forse ora ho capito.

Grazie, Mario