PDA

Ver la Versión Completa : ¿Cómo evitar que programa se congele?


pgranados
19-09-2023, 02:58:56
Buen día compañeros, tengo una aplicación donde paso la información de un XML a base de datos, el proceso tardara dependiendo la cantidad de xml, lo que busco es como evitar que el usuario se desespere y le empiece a dar clic y por ende el programa se congele (No responde)
Ya puse un contador así que busco alguna opción como deshabilitar la forma o que el hacerle clics no le afecte, ¿Cómo puedo hacer esto?

Neftali [Germán.Estévez]
19-09-2023, 08:45:16
Es una pregunta demasiado genérica (o a la forma de explicar el proceso le faltan detalles).
Dependiendo de cómo esté programado el proceso de pasar los datos del XML a la Base de Datos, las soluciones pueden ser diferentes.

Por ejemplo, si la importación es por pasos, (donde puedas interferir durante el proceso) puedes usar ProcessMessages y una barra de estado o mensajes.
Si el proceso es "compacto" y no puedes intervenir puedes evaluar usar threads, para mostrar también una barra de progreso (sin fin) o una animación.

Puede haber más y mejores que estas 2, pero sin más detalles es difícil saber qué opción es la que te conviene o la que te funcionará.

duilioisola
19-09-2023, 09:47:23
A nivel práctico, lo que comenta Neftalí sería:


OnCreate()
// Un label en donde irás mostrando el progreso de proceso
// Invisible por defecto
LabelProgeso.Visible := False;


procedure ProcesaXML();
var
Registro : integer;
begin
// Visibilizo e inicializo el Label que muestra el progreso.
LabelProgreso.Visible := True;
LabelProgreso.Caption := '';
Registro := 1;

// Todo entre try..except para evitar que quede el label visible si algo falla
try
while XML_tenga_datos do
begin
// Muestro la información para que el usuario no se ponga nervioso
LabelProgreso.Caption := 'Procesando Registro Nro. ' + IntToStr(Registro);
// Fuerzo a que se procesen los eventos de mouse, teclado y refresco de pantalla
Application.ProcessMessages;

// Procesar Registro XML
...

Next_Dato_XML;
Inc(Registro);
end;
finally
// Inivisibilizo Label ya que ha terminado el proceso
LabelProgreso.Visible := False;
end;
end;



Yo suelo poner un panel con labels, memos para logs y barras de progreso que hago visible tapando o invisibilizando los otros paneles de la aplicación.

pgranados
19-09-2023, 16:50:58
Gracias por sus comentarios, explicare mejor la situación:

Pasar un XML a la base de datos me demora 0.1 - 0.2 segundos c/u

el problema viene cuando son por ejemplo 7k XML, pues tardaremos como 10 minutos aprox, entonces necesito "congelar" o hacer algo para que el usuario no este dándole clic al sistema y que este cuelgue

Tengo un ciclo mas o menos así:


var UUID:string;
begin
for I := 0 to FileListBox.Items.Count-1 do
begin
FormaMensaje.label.caption:= 'XML '+IntToStr(i)+' de '+ IntToStr(FileListBox.Items.Count-1);
FormaMensaje.label.refresh;
XMLDocLocal.LoadFromFile(NomArchivoXML);
UUID:= ExtraerDatosParaNombreArchivo(XMLDocLocal); // Obtengo el identificador del XML UUID
Datamodule.FDQuery.Open('SELECT UUID FROM TABLA WHERE UUID = '+#39+UUID+#39');
if Datamodule.FDQuery.isEmpty then // No existe en la base de datos, debo de grabarlo
XMLaTabla(XMLDocLocal);
// Mas cosas por acá............................
end;
end;

duilioisola
19-09-2023, 17:56:55
Entiendo que FormaMesaje es un Form que contiene un label en el que muestras el progreso.
Solo debes cambiar esta línea.


var UUID:string;
begin
for I := 0 to FileListBox.Items.Count-1 do
begin
FormaMensaje.label.caption:= 'XML '+IntToStr(i)+' de '+ IntToStr(FileListBox.Items.Count-1);

Application.ProcessMessages; // FormaMensaje.label.refresh;

XMLDocLocal.LoadFromFile(NomArchivoXML);
UUID:= ExtraerDatosParaNombreArchivo(XMLDocLocal); // Obtengo el identificador del XML UUID
Datamodule.FDQuery.Open('SELECT UUID FROM TABLA WHERE UUID = '+#39+UUID+#39');
if Datamodule.FDQuery.isEmpty then // No existe en la base de datos, debo de grabarlo
XMLaTabla(XMLDocLocal);
// Mas cosas por acá............................
end;
end;


A veces, el procesar los mensajes de Windows incorpora una demora grande por lo que quizás quieras realizar este proceso cada n pasadas.

var UUID:string;
begin
for I := 0 to FileListBox.Items.Count-1 do
begin
FormaMensaje.label.caption:= 'XML '+IntToStr(i)+' de '+ IntToStr(FileListBox.Items.Count-1);

// Solo refresca 1 de cada 10 veces.
if (i mod 10 = 0) then
Application.ProcessMessages; // FormaMensaje.label.refresh;

XMLDocLocal.LoadFromFile(NomArchivoXML);
UUID:= ExtraerDatosParaNombreArchivo(XMLDocLocal); // Obtengo el identificador del XML UUID
Datamodule.FDQuery.Open('SELECT UUID FROM TABLA WHERE UUID = '+#39+UUID+#39');
if Datamodule.FDQuery.isEmpty then // No existe en la base de datos, debo de grabarlo
XMLaTabla(XMLDocLocal);
// Mas cosas por acá............................
end;
end;

Casimiro Notevi
19-09-2023, 18:01:59
Justo lo que te han contestado :)

De toda formas, también puedes deshabilitar el botón cuando lo pulsen, así no podrán darle más veces.

procedure TForm1.Button1Click(Sender: TObject);
begin
Button1.Enabled:=False;
//
...
end;

Neftali [Germán.Estévez]
20-09-2023, 08:39:56
Además de lo dicho y fijándome en este código:




XMLaTabla(XMLDocLocal);
// Mas cosas por acá............................




Yo añadiría lo siguiente:
Datamodule.FDQuery.DisableControls;
try
for I := 0 to FileListBox.Items.Count-1 do begin ...
XMLaTabla(XMLDocLocal);
// Mas cosas por acá..........................
...
finally
Datamodule.FDQuery.EnableControls;
end;

Puede mejorar dependiendo de los componentes visuales que tengas asociados al Dataset (si los tienes). Lo mismo con otros que tengas asociados a componentes visuales (Grids,...)