2010-03-26 11 views
6

Necesito mostrar una ventana emergente antes de la ejecución de una consulta, mostrar el tiempo transcurrido mientras se está ejecutando la consulta sql, y cerrar esa ventana cuando finaliza la consulta.¿Cómo se muestra el tiempo transcurrido mientras se ejecuta una larga consulta SQL?

hecho, me hacer algo como esto

var 
    frm : tFrmPopupElapsed; 
    // this form has a TTimer and a TLabel to show the elapsed time 
    // but the label is not updated, I tried using update and refresh 
    // but nothing happens 
begin 
    frm := tFrmPopupElapsed.Create(nil); 
    try 
    frm.Init; // this procedure enables the timer 
    frm.Show(); 
    ExecuteMyVeryLongQuery(); 
    finally 
    frm.Close; 
    end; 
end; 

¿Cómo puede la etiqueta se actualizará para mostrar el tiempo transcurrido mientras se ejecuta la consulta? ¿Usando un temporizador? O un hilo?

+0

¿Qué base de datos? Al menos uno que conozco (DBISAM/ElevateDB) ofrece un evento OnProgress muy útil en su componente de consulta. Puede codificar en su informe el tiempo transcurrido, porcentaje completado, etc. –

+2

sugerencia pequeña: pondría el 'try' antes de' frm.Init' – mjn

+2

Tenga en cuenta que al llamar 'frm.Close' dará como resultado pérdidas de memoria a menos que el formulario tiene un controlador 'OnClose' que establece' caFree'. Usar 'frm.Release' sería mucho más seguro. – mghie

Respuesta

4

Debe ejecutar la consulta asincrónicamente, lo que le permite actualizar el formulario mientras tanto.
La forma más sencilla de hacerlo sin ensuciarse las manos con los hilos es usar el AsynCalls library de Andreas Hausladen.
También puede echar un vistazo a OmniThread Library de Primoz Gabrijelcic.

+0

+1 por darme casi la misma respuesta. (Iba a sugerir Omnithread primero, y asyncalls segundo) :) – skamradt

+0

Cualquiera de estos es probablemente mejor que usar un descendiente 'TThread'. Pero ejecutar una consulta en un hilo diferente puede ser problemático. Por supuesto, es necesario bloquear cualquier trabajo con los objetos de conexión en paralelo en el hilo principal, a menos que los objetos de conexión permitan explícitamente el acceso paralelo desde diferentes hilos (lo más probable es que no sea el caso con las clases VCL). Puede haber restricciones adicionales, como tener que ejecutar todas las consultas en el mismo hilo en el que se estableció la conexión. Ver http://17slon.com/blogs/gabr/2009/02/building-connection-pool.html por ejemplo. – mghie

+0

+1 Muchas gracias por su consejo, la biblioteca AsynCalls funciona perfectamente. – Salvador

0

Esta pregunta es mucho más compleja de lo que inicialmente se pensaba; que lo hace una buena pregunta.

Inicialmente pensé que Application.processmessages era el camino correcto, sin embargo es un campo de minas potencial a menos que sea cuidadoso (gracias @skamradt por señalar esto). Tampoco ayuda con una sola llamada de bloqueo.

Se necesita un hilo de fondo de la siguiente manera: (gracias @mghie por señalar los errores que ahora se resuelven ). Todavía puede haber problemas con el objeto de la base de datos llamado en diferentes hilos, por lo que el hilo de fondo puede necesitar tener su propia conexión de base de datos para esta operación (si es posible).

En el siguiente ejemplo, no he mostrado específicamente el código para crear y destruir la ventana de progreso, ya que hará que el código sea aún más largo, y es fácil de hacer.

Así que tenemos dos objetos de hacer esto:

En primer lugar el subproceso de fondo para procesar la consulta.

unit BackgroundProcess; 

{$mode objfpc}{$H+} 

interface 

uses 
    Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, windows; 

const 
    WM_MY_BACKGROUNDNOTIFY = WM_USER + 555; { change this } 
    NOTIFY_BEGIN = 22; 
    NOTIFY_END = 33; 

type 
    TBackgroundQueryThread = class(TThread) 
    private 
    hwndnotify : HWND; 
    protected 
    procedure Execute; override; 
    public 
    constructor Create(owner: TForm); 
    end; 


implementation 

constructor TBackgroundQueryThread.Create(owner: TForm) ; 
begin 
    inherited Create(False); 
    hwndnotify := owner.Handle; 
    FreeOnTerminate := true; 
    resume; 
end; 

procedure TBackgroundQueryThread.Execute; 
begin 
    PostMessage(hwndnotify, WM_MY_BACKGROUNDNOTIFY, NOTIFY_BEGIN, 0); 
    Sleep(2000); (* Query goes here. *) 
    PostMessage(hwndnotify, WM_MY_BACKGROUNDNOTIFY, NOTIFY_END, 0); 
end; 

end.   

La forma que invoca la consulta:

unit mainform; 

interface 

uses 
    Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, 
    StdCtrls, ExtCtrls, windows, BackgroundProcess; 

type 
    TForm1 = class(TForm) 
    private 
    frm : tFrmPopupElapsed; 
    { private declarations } 
    procedure OnMyBackgrounNotify(var Msg: TMessage); message WM_MY_BACKGROUNDNOTIFY; 
    public 
    { public declarations } 
    end; 

var 
    Form1: TForm1; 

implementation 


procedure TForm1.OnMyBackgrounNotify(var Msg: TMessage); 
begin 
    if (msg.WParam = NOTIFY_BEGIN) THEN 
    BEGIN 
    if (frm = nil) THEN 
    BEGIN 
     frm := tFrmPopupElapsed.Create(nil); 
     frm.Init; // this procedure enables the timer 
     frm.Show(); 
    END; 
    END; 

    if (msg.WParam = NOTIFY_END) THEN 
    BEGIN 
    if (frm <> nil) THEN 
    BEGIN 
     frm.Close; 
    END; 
    END; 

end; 

end. 
+2

Tenga mucho cuidado al usar Application.ProcessMessages, ya que puede causar otros problemas fácilmente si no tiene cuidado, y generalmente debe tratarse como la última cosa que le gustaría hacer, no la primera. Busque otra solución, como un hilo de fondo, primero. – skamradt

+1

Tienes razón, recuerdo que ahora puedes tener terribles problemas de reentrada a menos que tengas cuidado. Entonces, el enfoque correcto es solo un hilo y un temporizador, manteniendo el hilo de fondo para la actualización. –

+0

-1 para llamar al código VCL desde una cadena de fondo y para llamar a métodos de componentes que ya pueden liberarse. – mghie

1

Usted tendrá que ejecutar la consulta en un subproceso de fondo si desea que la interfaz de usuario de responder durante la ejecución de la consulta. Si la consulta admite la cancelación, también puede agregar un botón de cancelar. Sin embargo, creo que solo las consultas de ADO respaldan esto.

Cuestiones relacionadas