2010-03-26 9 views
6

Ich brauche ein Popup-Fenster, bevor eine Abfrage Ausführung zu zeigen, zeigen die verstrichene Zeit, während die SQL-Abfrage ausgeführt wird, und das Fenster schließen, wenn die Abfrage beendet.Wie wird die verstrichene Zeit angezeigt, während eine lange SQL-Abfrage ausgeführt wird?

Eigentlich mache ich so etwas wie diese

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; 

Wie kann das Etikett aktualisiert werden, um die verstrichene Zeit zu zeigen, während die Abfrage ausgeführt wird? Verwenden eines Timers? Oder ein Thread?

+0

Welche Datenbank? Mindestens eine, die ich kenne (DBISAM/ElevateDB) bietet ein sehr praktisches OnProgress-Ereignis für ihre Abfragekomponente. Sie können in ihrem Code verstrichene Zeit, Prozent abgeschlossen usw. –

+2

kleine Anregung zu berichten: Ich würde legt den 'try' vor dem' frm.Init' – mjn

+2

Beachten Sie, dass Aufruf 'frm.Close' in Speicherlecks, es sei denn die Form führt hat einen OnClose-Handler, der 'caFree' setzt. Mit 'frm.Release' wäre das viel sicherer. – mghie

Antwort

4

Sie müssen die Abfrage asynchron ausführen, damit Sie das Formular in der Zwischenzeit aktualisieren können.
Der einfachste Weg, es zu tun, ohne sich die Hände schmutzig mit den Fäden zu bekommen ist AsynCalls library Andreas Hausladen zu bedienen ist.
Sie können auch einen Blick auf die OmniThread Library von Primoz Gabrijelcic geben.

+0

+1 für mich auf fast die gleiche Antwort zu schlagen. (Ich würde Omnithread zuerst vorschlagen, und asyncalls zweiten) :) – skamradt

+0

Einer von diesen ist vermutlich besser als die Verwendung eines "TThread" Nachkommens. Das Ausführen einer Abfrage in einem anderen Thread kann jedoch problematisch sein. Es ist natürlich notwendig, jede Arbeit mit den Verbindungsobjekten im Haupt-Thread parallel zu blockieren, es sei denn, die Verbindungsobjekte erlauben explizit parallelen Zugriff von verschiedenen Threads (höchstwahrscheinlich nicht der Fall bei VCL-Klassen). Es kann zusätzliche Einschränkungen geben, wie zum Beispiel die Ausführung aller Abfragen in demselben Thread, in dem die Verbindung hergestellt wurde. Siehe http://17slon.com/blogs/gabr/2009/02/building-connection-pool.html zum Beispiel. – mghie

+0

+1 Vielen Dank für Ihren Rat, die AsynCalls Bibliothek funktioniert perfekt. – Salvador

0

Diese Frage ist viel komplexer als ursprünglich geplant; Das macht es zu einer guten Frage. Zunächst

Ich dachte Application.ProcessMessages der richtige Weg war, aber es ist ein potenzielles Minenfeld, wenn Sie vorsichtig sind (dank @skamradt für den Hinweis). Es hilft auch nicht mit einem einzigen blockierenden Anruf.

Ein Hintergrund-Thread wird wie folgt benötigt: (Danke @mghie für das Aufzeigen der Fehler sind jetzt gelöst). Es kann immer noch Probleme mit dem Datenbankobjekt geben, das in verschiedenen Threads aufgerufen wird. Daher muss der Hintergrundthread möglicherweise eine eigene Datenbankverbindung für diese Operation haben (falls möglich).

Im Beispiel unten ich nicht speziell den Code für die Erstellung und Zerstörung des Fortschrittsfenster gezeigt haben, wie sie den Code noch länger machen wird, und seine leicht zu tun.

Also brauchen wir zwei Objekte, dies zu tun:

Zunächst wird der Hintergrund-Thread die Abfrage zu verarbeiten.

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.   

Das Formular, das die Abfrage ruft:

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

Seien Sie sehr vorsichtig bei der Verwendung von Application.ProcessMessages, da es leicht zu anderen Problemen führen kann, wenn Sie nicht vorsichtig sind, und sollte im Allgemeinen als das letzte, was Sie tun möchten, behandelt werden, nicht das erste. Suchen Sie zuerst nach einer anderen Lösung, z. B. einem Hintergrundthread. – skamradt

+1

Sie haben Recht - ich erinnere mich jetzt, dass Sie in schreckliche Reentrancy-Probleme kommen können, wenn Sie nicht vorsichtig sind. Der richtige Ansatz ist also nur ein Thread und ein Timer, wobei der Hintergrundthread für das Update beibehalten wird. –

+0

-1 zum Aufrufen von VCL-Code aus einem Hintergrundthread und zum Aufrufen von Methoden von Komponenten, die möglicherweise bereits freigegeben wurden. – mghie

1

Sie müssen ausführen, um die Abfrage in einem Hintergrund-Thread, wenn Sie die Benutzeroberfläche zu reagieren während der Ausführung der Abfrage werden sollen. Wenn die Abfrage die Stornierung unterstützt, können Sie auch eine Schaltfläche zum Abbrechen hinzufügen. Ich glaube, dass nur ADO-Abfragen dies unterstützen.

Verwandte Themen