Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: petrusic - Luglio 15, 2024, 10:51:45 pm

Titolo: Come richiamare una procedure/function interna ad altra Form
Inserito da: petrusic - Luglio 15, 2024, 10:51:45 pm
Mi trovo di fronte alla necessità di richiamare dalla Form9 una procedura che è dichiarata all'interno diella Form8.

Ho letto questo (https://www.lazaruspascal.it/index.php?topic=573.msg4482#msg4482) ed ho provato a riportare il conecetto nel mio programma, ma non riesco ma completare la compilazione per via di un ERRORE incontrato dal compilartore.

Riporto le porzioni di codice inserite:

Nella Fom8 (dove si trova la procedura da richiamare):
Codice: [Seleziona]
unit Frm8;

{$mode ObjFPC}{$H+}
interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, Grids,
  StdCtrls,
  UModuloDatiDB, utilmie, Types;
type

  { TForm8 }

  TForm8 = class(TForm)
    BPulsNoAmmort: TButton;
    BPulsAmmortOk: TButton;
    GridPreAmmort: TStringGrid;
    procedure BPulsNoAmmortClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure GridPreAmmortDrawCell(Sender: TObject; aCol, aRow: Integer;
      aRect: TRect; aState: TGridDrawState);
    procedure GridPreAmmortEnter(Sender: TObject);

public
  procedure ScriMovGridPreAmmort(voceCesp, coVoCesp, dtMovim, impMovim, descrMovim, voceSotCtoCas, coVoceSotCtoCas: String);

var
  striAmmortMan: AnsiString;

private

var
// bla bla
end;

var
  Form8: TForm8;

implementation
uses
  Frmmain, Frm9;
{$R *.lfm}

{ TForm8 }       
// bla bla                                                                                                               

Nella Fom9 (dove richiamo la procedura della Form8):
Codice: [Seleziona]
unit Frm9;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils, DB, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ExtCtrls, Grids, LCLType, Math, memds, LR_Class, LR_DBSet, lr_e_pdf, Types,
  Frm8, Frm12, UModuloDatiDB, utilmie;

type
  { TForm9 }

  TForm9 = class(TForm)
    BPulsAvanti: TButton;
// bla bla

  private
    FAltraForm: TForm8;
    procedure SetAltraForm(AValue: TForm8);
  public
    property AltraForm: TForm8 read FAltraForm write SetAltraForm;

var
  dataFin, segnoIniFinAn: String;

end;

var
  Form9: TForm9;

// bla bla
implementation

// bla bla
        Form8.AltraForm.scriMovGridPreAmmort(voCespite, coCespite, dtMov, impMov, descrMov, voSotCtoCas, coSotCtoCas);      //<--- ERRORE in compilazione

L'ERRORE segnalato dal compilatore è:  frm9.pas(2305,15) Error: identifier idents no member "AltraForm"
 ???
Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: DragoRosso - Luglio 16, 2024, 09:31:44 am
Codice: [Seleziona]
        Form8.AltraForm.scriMovGridPreAmmort(voCespite, coCespite, dtMov, impMov, descrMov, voSotCtoCas, coSotCtoCas);      //<--- ERRORE in compilazione
L'ERRORE segnalato dal compilatore è:  frm9.pas(2305,15) Error: identifier idents no member "AltraForm"
 ???

"AltraForm" è definita in TForm9, non in TForm8. Quindi togli il Form8 davanti alla chiamata di "AltraForm":

Codice: [Seleziona]
        AltraForm.scriMovGridPreAmmort(voCespite, coCespite, dtMov, impMov, descrMov, voSotCtoCas, coSotCtoCas);
Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: DragoRosso - Luglio 16, 2024, 10:45:15 am
Aggiungo un commento a quanto detto sino a qua.

La chiamata di procedure o funzioni tra Form diverse in genere sarebbe da evitare.
La Form è un contenitore visuale e in genere viene usato per gestire tutti gli aspetti visuali e procedurali relativi alla Form stessa (magari con l'eccezione della Form Principale ...).

Il fatto che da una Form si chiami una procedura di un'altra Form indica molte volte che il codice non è stato sviluppato bene. Ci sono ovviamente delle eccezioni pienamente giustificate su ciò, ad esempio se devo richiamare un dato dopo che la Form è stata nascosta (come per le Form modali), etc ....

Se c'è necessità di elaborare qualcosa da più parti, è meglio creare una classe / record non visuale e usare questa per effettuare le operazioni (ad esempio una Form può alimentare i dati della classe e un'altra Form li può estrarre elaborati e visualizzare quando serve).

Istanziando la classe / record come globale avremo la possibilità di chiamare le varie funzioni non solo da una qualsiasi Form, ma anche da eventuali  Thread.

Ciao
Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: petrusic - Luglio 16, 2024, 03:58:50 pm
Aggiungo un commento a quanto detto sino a qua.

La chiamata di procedure o funzioni tra Form diverse in genere sarebbe da evitare.
La Form è un contenitore visuale e in genere viene usato per gestire tutti gli aspetti visuali e procedurali relativi alla Form stessa (magari con l'eccezione della Form Principale ...).

Ho capito. A tal proposito devo dirti che, pur non avendone coscienza, ho sempre fatto così, passando al momento della chiamata della Form, la stringa coi dati da mostrare, ma questa volta pensavo di sfruttare l'idea per risparmiare i tempi di costruzione della stringa (nella Form9) e di estrazione dalla stessa (nella Form8). Ciò, però, mi porterebbe naturalmente ad usare un ciclo iterativo di chiamate alla procedura della Form8 per riempire le celle della StringGrid da mostrare, alla fine di tale ciclo, con la solita chiamata .ShowModal.

Citazione
(author=DragoRosso)
Se c'è necessità di elaborare qualcosa da più parti, è meglio creare una classe / record non visuale e usare questa per effettuare le operazioni (ad esempio una Form può alimentare i dati della classe e un'altra Form li può estrarre elaborati e visualizzare quando serve).

Istanziando la classe / record come globale avremo la possibilità di chiamare le varie funzioni non solo da una qualsiasi Form, ma anche da eventuali  Thread.

Questo tuo suggerimento potrebbe un'opportunità per conoscere meglio Lazarus.   :) ::)
Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: petrusic - Luglio 16, 2024, 05:05:30 pm
@ DragoRosso

Ritorno alla tua precedente risposta perchè, riflettendo sulla Classe intermedia che hai chiamato  classe / record, ho individuato la classe TCollection (una classe base per raccolte (non ordinate) di TCollectionItems)

Nel wiki di Lazarus è riportato il seguente
esempio (https://wiki.lazarus.freepascal.org/TCollection)
Contiene le funzioni .GetItems e .SetItems.

Potrebbe fare al caso mio, se l'Item potesse essere un array bidimensionale.

Ci ragiono su.  :)
Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: DragoRosso - Luglio 17, 2024, 12:30:10 pm
Riporto per chi è curioso un esempio (l'ho fatto in dieci minuti, non è perfetto ovviamente) di isolamento di oggetti.

In questo caso si tratta di due FORM.

Da notare che nessuna delle due unita delle FORM (unit1 e unit2) è inserita in alcuna uses ,,,

E' un esempio basilare scheletrico  su come si può effettuare una "astrazione" in modo da isolare completamente  (o quasi) due oggetti complessi come le FORM.

Ciao

P.S.: FPC ha un baco, riporta che una variabile non è usata, quando invece lo è ed è il fulcro di tutto. In Delphi non viene segnalato ciò. Il codice è esattamente identico. Ciò è solo a livello di segnalazione, non inficia il funzionamento.
Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: petrusic - Luglio 17, 2024, 05:25:50 pm
Riporto per chi è curioso un esempio (l'ho fatto in dieci minuti, non è perfetto ovviamente) di isolamento di oggetti.

Non riesco a capire come funziona l'esempio che hai pubblicato nell'ultimo tuo post. Mi sa che alla fine mi convenga affidarmi ai miei metodi di passare dati da una Form ad un'altra. Forse allungano un pò il processo operativo, ma mi risultano comprensibili
Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: DragoRosso - Luglio 17, 2024, 06:58:55 pm
Non preoccuparti, scegli la strada che ti dà più sicurezza. Il codice lo devi mantenere te, quindi è meglio che scegli il modo che ritieni, come anche tu riporti, più comprensibile.

Ciao
Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: petrusic - Luglio 19, 2024, 04:31:33 pm
Ritorno sull'argomento perchè in questo momento utilizzo molto gli array bidimensionali e mi attira l'idea di passare alla 2.a Form un array già pronto all'uso, piuttosto che costruire una String del tipo Riga1Col1+chr(9)+Riga1Col2+chr(9)+...+Riga1Col1n+chr(13)+Riga2Col1+chr(9)+...+RiganColn,
per poi, nella Form di destinazione, ritornare a comporre l'array da scorrere per la successiva operatività,

Se è chiaro quello che voglio dire, ciò mi permetterebbe di costruire, col Focus dentro 1.a Form, l'array occorrente direttamente nella classe, diciamo, intermedia (per es. daticomunji).
Poi,
Nel momento in cui la 2.a Form prende il Focus, essa potrebbe leggere direttamente i dati, pronti all'uso, nella Classe daticomuni e procedere, quindi, più rapidamente alla compilazione, per es., della StringGrid finale.

Se potessi fare un lavoro del genere, avrei comnpiuto un gran passo avanti nel linguaggio.  ;)

Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: DragoRosso - Luglio 19, 2024, 06:42:46 pm
Vedi l'allegato ....
Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: DragoRosso - Luglio 20, 2024, 11:21:47 am
Ho lasciato volontariamente incompleta la funzionalità del record "TDatiCoumni" ... tra l'altro ad esempio manca la funzione di "CLEAR" dello stesso in modo che l'array interno si svuoti di eventuali contenuti.

Come suggerimento posso dire che per cancellare l'array interno si può usare una modalità semplicissima senza scomodarsi a ciclare per l'intero array ...
Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: petrusic - Luglio 20, 2024, 03:46:16 pm
Vedi l'allegato DatiComuni.zip

Ti ringrazio per la pazienza ed il tempo che mi stai dedicando.
Con questo secondo esempio, mi sembra tutto molto più chiaro e comincio a capire il meccanismo.

Ora posso provare a metterlo in campo.  :D
Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: petrusic - Luglio 20, 2024, 10:35:29 pm
@ DragoRosso

Ho cominciato a familiarizzare con la tecnica di passaggio dati fra due Form facendoli transitare da una Classe di variabili.
Dall'ultimo esempio che mi hai girato, ho capito che la Unit1 è la Form cedente dati, mentre la Unit2 è la Form ricevente. La Unit3 è la Classe "daticomuni".

Quest'ultima viene inizializzata col comando "DatiComuni.Init(5, 2)" , inserito all'interno della Unit1.
La Unit2 con la sua Form2, contenente la StringGRid da riempire coi dati provenienti dalla Unit1, contiene una sola procedura
Codice: [Seleziona]
procedure TForm2.FormShow(Sender: TObject);
var row, col: integer;
begin
  //Legge il record e lo inserisce nella griglia
  for row := 0 to DatiComuni.NumRow-1 do
    for col := 0 to DatiComuni.NumCol-1 do
      StringGrid1.Cells[col+1, row+1] :=  DatiComuni.Dati[row, col];
end;                           
che si occupa del riempimento delle celle della StringGrid1.

Non ho trovato la
Citazione
procedure FormCreate
così come non ho trovato la
Citazione
Application.CreateForm(TForm2, Form2);
nella Unit1.

Ho provato ad eseguire il programmino di esempio che mi hai girato e funziona perfettamente.

Ma come può funzionare se manca il Create della Form2 ?
Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: DragoRosso - Luglio 21, 2024, 09:39:48 am
La Unit3 è la Classe "daticomuni".
Non è proprio una classe, bensì un record. I record "avanzati" possono agire similmente alle classi, ma sono più .... snelli .... ad esempio non bisogna istanziarli. Puoi leggere qui: https://www.lazaruspascal.it/index.php?page=145 (https://www.lazaruspascal.it/index.php?page=145)
Gli advanced record implementano delle funzionalità che le classi non hanno: ad esempio si possono sostituire gli operatori ... cioè posso ad esempio eseguire una somma tra due record o una moltiplicazione, o altro (operator overloading).

Non ho trovato la
Citazione
procedure FormCreate
così come non ho trovato la
Citazione
Application.CreateForm(TForm2, Form2);
nella Unit1.
In Lazarus, per impostazione predefinita, le FORM e i DATAMODULE (e solo questi due oggetti) vengono "autoistanziati" all'avvio dell'applicazione.
E' possibile evitare ciò andando nelle opzioni di progetto / FORM (vedi allegato).
Quando si modifica quella opzione (quindi si inserisce una FORM in AUTOCREAZIONE o meno), viene cambiato automaticamente il file di progetto .lpr (in questo caso Project2.lpr) inserendo o togliendo la riga Application.CreateForm corrispondente.

Per quello che riguarda l'evento "FormCreate", esso non è obbligatorio e serve solo se hai delle attività da svolgere immediatamente dopo la creazione della Form stessa.
Nel caso della Form1 (in unit1.pas), viene eseguita l'inizializzazione di DatiComuni (ossia l'allocazione dello spazio per righe e colonne volute) ... è solo per quello che nella Form1 esiste quell'evento.

Ciao

P.S.: se vuoi inserire la procedura Clear questo è il suggerimento (devi inserire la dichiarazione anche nella definizione del record):
Codice: [Seleziona]
procedure TDatiCoumni.Clear;
var tempRow, tempCol: integer;
begin
  tempRow := Length(fDati);
  tempCol := Length(fDati[0]);
  //Questo azzera l'array
  SetLength(fDati, 0, 0);
  //Questo risetta l'array ponendo a default (per le stringhe vale "") il contenuto
  SetLength(fDati, tempCol, tempRow);
end;
Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: petrusic - Luglio 23, 2024, 04:05:10 pm
Mi sembra sempre molto complicato.

Ho risolto più semplicemente a modo mio.
Ti prego di scusarmi per il tempo che hai dovuto perdere  per cercare di aiutarmi. Mi dispiace tanto.
 :-[
Comunque, Grazie di tutto.
Titolo: Re:Come richiamare una procedure/function interna ad altra Form
Inserito da: DragoRosso - Luglio 23, 2024, 06:32:13 pm
Non c'è problema. Segui la strada che ti è più congeniale.

Alla prossima.