PDA

Ver la Versión Completa : Para momentaneamente el programa


Kaesbu
29-09-2003, 00:20:39
Buenas. Me gustaria saber si se puede hacer de alguna forma o no lo siguiente: Resulta tengo mi programa haciendo un bucle y cuando clickeo un boton el programa no me hace caso por que esta haciendo las ordenes del bucle. Se puede para momentaneamente el bucle para que atienda la orden del boton y luego pueda seguir haciendo el bucle???? Muchas gracias.

Al González
29-09-2003, 02:25:48
:) ¡Buen día!

Kaesbu:

Puedes utilizar el método ProcessMessages del objeto Application, llamándolo al final (y dentro) del ciclo. Por ejemplo:


For I := 1 To 1000000 Do
Begin
...(sentencias)...
Application.ProcessMessages;
End;


ProcessMessages hace que la aplicación procese todos los eventos pendientes que le han llegado (como teclazos, clicks de ratón, avisos del sistema operativo, etc.). Extrae uno a uno los mensajes de su cola de mensajes pendientes. Cuando ya no hay ninguno que procesar, entonces vuelve y continúa con la rutina donde fue llamado.

Esto permite llevar a cabo ciclos de procesamiento que pueden ser algo lentos, sin que la aplicación deje de responder a las acciones del usuario.

No obstante, debe uno tener cuidado al utilizar el método ProcessMessages, ya que éste puede desencadenar de nuevo el ciclo que actualmente se está ejecutando, resultando en una doble ejecución simultánea del mismo proceso.

Ésto último se podría dar, por ejemplo, al ejecutar el ciclo en el evento OnClick de un botón, y luego durante el mismo, el usuario vuelve a presionar el mismo botón, el cuál es nuevamente accionado gracias a la llamada a Application.ProcessMessages.

Para evitar este problema, existen varias soluciones (como sucede siempre en la programación ;)). Una de ellas es inhabilitar el botón mientras ejecuta su proceso:


TForm1.Button1Click (Sender :TObject);
Begin
Button1.Enabled := False;

Try
For I := 1 To 1000000 Do
Begin
...(sentencias)...
Application.ProcessMessages;
End;
Finally
{ Se uitiliza Try..Finally para asegurar que el botón vuelva a
habilitarse, aunque se eleve una excepción en el ciclo }
Button1.Enabled := True;
End;
End;


Al utilizar ProcessMessages, debe uno considerar las posibles acciones que el usuario pueda realizar, o mejor dicho, los eventos que el programa pueda procesar, durante la ejecución del ciclo, e inhabilitar o impedir aquellos procesos que pudieran alterar la ejecución normal de la aplicación.

Espero esto sea de utilidad. Seguimos en contacto.

Al González :).

roman
29-09-2003, 04:26:02
Completando un poco lo que te indica Al González, para lograr el efecto de pausar el ciclo y luego
continuar podrías intentar algo como esto:


var
I: Integer;

begin
Pausa := false;

for I := 1 to 50000 do
begin
while Pausa do
Application.ProcessMessages;

{ Aquí el proceso que realices }

Application.ProcessMessages;
end;
end;


Pausa sería una variable en tu formulario que inicializas a false al comienzo del ciclo for de manera
que el ciclo while no se ejecuta.

El segundo ProcessMessages permite procesar otros eventos. Uno de ellos podría ser el click de un botón
"Pausa" en donde pones la variable Pausa a true. Esto hará que la condición del ciclo while sea cierta y,
como verás, es un ciclo que no terminará mientras Pausa sea true de manera que el ciclo for queda
"suspendido".

El ProcessMessages dentro del ciclo while permitirá que se sigan procesando los mensajes, por ejemplo el
click de un botón "Continuar" en donde pondrías la variable Pausa a false haciendo que el ciclo while
termine y continúe el ciclo for.


Por otra parte, pienso que una solución "más seria" y sólida sería utilizar threads para ejecutar lo que
haces dentro del ciclo for.


// Saludos

roman
29-09-2003, 04:30:42
Al González

Si puedes por favor edita tu mensaje para
cortar la línea de comentario dentro del código
que escribiste ya que causa que el texto de tu
comentario y los demás no quepan en la pantalla.

// Gracias


Nota posterior

Gracias Al, ahora ha quedado muy bien

Kaesbu
29-09-2003, 18:04:44
Muchas gracias a los dos por contestar y hacerme las aclaraciones
sobre el tema. Poco a poco voy aprendiendo mas cosas sobre Delphi. Muchas gracias y un saludo.

Kaesbu
29-09-2003, 19:43:20
Uffffff, lo he probado y se me han empeorado las cosas, ya que
el bucle lo que hace es bajar un archivo. El problema es que cuando llega al application.processmessages el buffer se vacia y el programa sale del bucle. Alguna idea? Gracias.

roman
29-09-2003, 21:55:43
Kaesbu escribió

El problema es que cuando llega al application.processmessages el buffer se vacia y el programa sale del bucle. Alguna idea? Gracias.


Habría que ver cómo estás bajando el archivo. ¿Qué es lo que haces en cada paso del bucle?

Aquí te va una idea de cómo usar threads. El ejemplo es para copiar un archivo mostrando el avance en un ProgressBar pero puede servirte como punto de partida ya que la situación es similar: copiar bloques de archivos.

Primero debes declarar un descendiente de TThread que se ocupará del copiado:


type
TCopyFileThread = class
private
ProgressBar: TProgressBar; // Barra de progreso
procedure UpdateProgress;

protected
procedure Execute; override;

public
CopyFrom: String; // Archivo origen
CopyTo: String; // Archivo destino
BufferSize: LongInt; // Tamaño de cada bloque

constructor Create(AProgressBar: TProgressBar);
end;


El método Execute se llama cuando se inicia el hilo y es ahí donde debes poner el código de copiado.

UpdateProgress se encargará de actualizar el ProgressBar.

La implementación es así:


constructor TCopyFile.Create(AProgressBar: TProgressBar);
begin
// Crear el hilo pero no iniciarlo
inherited Create(true);

ProgressBar := AProgressBar;
BufferSize := 255;
end;

procedure TCopyFile.UpdateProgress;
begin
ProgressBar.StepBy(BufferSize);
end;

procedure TCopyFile.Execute;
var
Source, Dest: TFileStream;
Buffer: Pointer;
BytesRead: LongInt;

begin
Source := TFileStream.Create(CopyFrom, fmOpenRead);
Dest := TFileStream.Create(CopyTo, fmCreate);
GetMem(Buffer, BufferSize);
ProgressBar.Max := Source.Size;

try
/*
En este ciclo se hace el copiado

Terminated es una variable del hilo que es false
hasta que éste termine
*/
while not Terminated do
begin
BytesRead := Source.Read(Buffer^, BufferSize);
Dest.Write(Buffer^, BytesRead);

/*
Es necesario llamar a UpdateProgress mediante
Synchronize ya que UpdateProgress usda objetos
del VCL
*/
Synchronize(UpdateProgress);

if BytesRead < BufferSize then Terminate;
end;
finally
Source.Free;
Dest.Free;
FreeMem(Buffer);
end;
end;


Eso es todo. En tu formulario declaras una variable CopyFile de tipo TCopyFileThread. Para iniciar el copiado haces:


CopyFile := TCopyFileThread.Create(ProgressBar1);
CopyFile.CopyFrom := archivo origen;
CopyFile.CopyTo := archivo destino;
CopyFile.Resume;


donde ProgressBar1 es un ProgressBar en tu formulario.

// Saludos

En un botones adecuados de tu formulario usas los métodos

CopyFile.Suspend para pausar el copiado
CopyFile.Resume para continuar
CopyFile.Terminate para cancelar el copiado