Как показать истекшее время выполнения длинного SQL-запроса? - PullRequest
6 голосов
/ 27 марта 2010

Мне нужно показать всплывающее окно перед выполнением запроса, показать время, прошедшее с момента выполнения запроса sql , и закрыть это окно, когда запрос завершится.

На самом деле я делаю что-то вроде этого

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;

Как можно обновить метку, чтобы показать истекшее время, пока выполняется запрос? Используете таймер? Или нить?

Ответы [ 3 ]

4 голосов
/ 27 марта 2010

Вам необходимо выполнить запрос в асинхронном режиме , что позволит вам обновить форму между тем.
Самый простой способ сделать это, не пачкая рук с помощью потоков , - это использовать библиотеку Андреаса Хаусладена AsynCalls .
Вы также можете взглянуть на OmniThread Library от Примоза Габриелчича.

1 голос
/ 27 марта 2010

Вам нужно будет выполнить запрос в фоновом потоке, если вы хотите, чтобы пользовательский интерфейс реагировал во время выполнения запроса. Если запрос поддерживает отмену, вы также можете добавить кнопку отмены. Однако я считаю, что только запросы ADO поддерживают это.

0 голосов
/ 27 марта 2010

Этот вопрос намного сложнее, чем предполагалось изначально; что делает это хорошим вопросом.

Изначально я думал, что Application.processmessages - правильный путь, однако это потенциальное минное поле, если вы не будете осторожны (спасибо @skamradt за это). Это также не помогает с одним блокирующим вызовом.

Необходим фоновый поток следующим образом: (спасибо @mghie за указание на ошибки, которые сейчас решены). Могут быть проблемы с вызовом объекта базы данных в разных потоках, поэтому фоновому потоку может потребоваться собственное подключение к базе данных для этой операции (если это практически возможно).

В приведенном ниже примере я специально не показывал код для создания и уничтожения окна прогресса, так как он сделает код еще длиннее и его легко выполнить.

Итак, для этого нам нужны два объекта:

Во-первых, фоновый поток для обработки запроса.

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.           

Форма, которая вызывает запрос:

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.
...