Scusa, per curiosità: a me capita spesso di usare "ProcessMessages" quando sto lanciando un'operazione che può durare parecchio. In pratica, ogni "tot", emetto dei messaggi, e poi un bel "ProcessMessages" per evitare che il programma sembri bloccato.
Ora, supponiamo che io abbia un programma "fatto male" con 2 bottoni, di cui il primo bottone è quello che lancia il processo lungo che dicevo prima.
Mentre questo processo sta ancora girando, io clicco sul secondo bottone.
Cosa succede?
Io ho sempre pensato che partano i lavori del secondo bottone, interrompendo quelli del primo. Quando finiscono le operazioni del secondo bottone, ripartono quelle del primo.
E' corretto?
Grazie, Mario
Funziona che porta a termine prima il codice del primo bottone che viene premuto.
Per questo motivo è consigliabile pilotare le proprietà .enabled di tutti i componenti della schermata che possono rompere i maroni.
Esempio se ho button1 e button2, se premo su button1 la prima cosa che deve fare è disabilitare l'operatività di button2 e riattivarla solo alla fine del processo.
Così si evitano comportamenti anomali
ProcessMessages, di fondo, forza l'elaborazione dei messaggi in coda
per fare questo, interrompe il main thread e, se lo si fa in un loop, può allungare di molto la durata del loop stesso
L'uso di ProcessMessages genera ben altro, oltre a quanto descritto. Di fatto il suo uso altera la sequenza di esecuzione del MAIN THREAD e può avere effetti difficili da diagnosticare.
Windows si basa sui "messaggi" e molto di ciò che lo riguarda comporta la gestione dei messaggi: un tasto premuto genera un messaggio (anzi più di uno), altri eventi di sistema (ad esempio la chiusura di una form) generano messaggi.
Potrebbe non bastare la disattivazione dei componenti attivi grafici: provate a pensare cosa succede se premo CTRL F4 (dovrebbe chiudere una dialog box aperta) o altri "tasti di sistema" mentre sono a gestire un evento nel thread principale e uso ProcessMessages in quella dialog box ...
Non usate ProcessMessages se non ne avete l'assoluta necessità e ormai tale funzione potrebbe essere tranquillamente "eliminata". Cercate invece di usare le tecniche che ci sono e ormai tali tecniche non hanno segreti o parti oscure ... l'uso dei thread o di altri sistemi per l'esecuzione asincrona di codice particolarmente pesante o lungo risolve tranquillamente il problema del "congelamento" della parte grafica.
Così come si dovrebbe evitare loop "infiniti" (infiniti per modo di dire ovviamente) all'interno del codice: gli eventi risolvono molto spesso benissimo tali casistiche (così come anche i semafori).
Il SO poi farà effettivamente dei loop, ma insieme a mille altre cose non sprecando energie per magari testare in loop una variabile boolena finchè non cambia stato.
var
test: boolean;
test := true;
//Il codice gira "a manetta" finchè la variabile non diventa false, se fatto in un evento delle LCL il programma si congelerà (oltre che vedere schizzare l'uso della CPU alle stelle).
while test do
;
//Peggio che mai fare
while test do
Application.ProcessMessages;
.....
.....
Ciao ciao
Usa il metodo Refresh o Update del componente al posto dell' Application.ProcessMessages.
Inoltre consiglio di cambiare il cursore con il cursore "animato", io lo chiamo "il calamaro che gira" .... ;D
Esempio:
//Questo evento congela l'applicazione e non fà vedere il conteggio in corso
procedure TForm1.Button1Click(Sender: TObject);
var i: integer;
begin
for i:=0 to 100 do
begin
Label1.Caption:=i.ToString;
sleep(10);
end;
end;
//Con questo evento invece l'applicazione è congelata ma fà correttamente vedere il conteggio.
procedure TForm1.Button2Click(Sender: TObject);
var i: integer;
begin
for i:=0 to 100 do
begin
Label1.Caption:=i.ToString;
sleep(10);
Label1.Refresh;
end;
end;
Ciao
Per chi volesse avere un bel cursore, lascio in allegato lo stesso.
Qui l'esempio.
Create una nuova applicazione in Lazarus, inserite un bottone nella Form e create due eventi (FORMCREATE e BUTTON1CLICK) con il doppio click sulla Form e sul bottone. Sovrascrivete con quanto qui postato nella sezione IMPLEMENTATION.
IL CURSORE DOVETE INSERIRLO NELLE RISORSE DI LAZARUS (ci sono altri modi ma questo è quello usato per default) dal menu "Opzioni progetto" / "Risorse" e dovete chiamarlo PROVA come nel codice.
var
test: boolean;
procedure TForm1.FormCreate(Sender: TObject);
var newcur: TCursorImage;
begin
newcur := TCursorImage.Create;
newcur.LoadFromResourceName(hinstance, 'PROVA');
Screen.Cursors[1] := newcur.ReleaseHandle;
Form1.Cursor := 1;
newcur.Free;
test := true;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
test := not test;
if test then
Form1.Cursor := 1
else
Form1.Cursor := crDefault;
end;
Ciao
P.S.: bel cursore si fà per dire ;D
Puoi disabilitare i componenti per tipologia aggiungendo eventuali eccezioni per ciò che deve rimanere sempre abilitato:
for i := 0 to ComponentCount -1 do
begin
if Components[i] is TBitBtn then
TBitBtn(Components[i]).Enabled := not TBitBtn(Components[i]).Enabled;
if Components[i] is TMenuItem then
TMenuItem(Components[i]).Enabled := not TMenuItem(Components[i]).Enabled;
end;
BitBtn1.Enabled := True;
oppure memorizzare lo stato corrente in una variabile
fLoading := True; // quando inizio a caricare i dati,
begin
// caricamento dati ...
end;
fLoading := False; // quando finisco di caricare dati
e poi verificare tale variabile prima di eseguire il codice legato ad un eventuale evento if not fLoading then
begin
// ... codice da eseguire
end;
Prego! :)
PS: Quasi dimenticavo ... ricorda anche di gestire l'eventuale chiusura accidentale del programma:
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
CanClose := not fLoading;
end;
Ciao