Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Окно - прогресс в отдельном потоке [D7, WinXP]


Страницы:   1 2 3

Дмитрий Белькевич ©   (2008-07-17 19:28:00) 

Нужно реализовать простую прогресс-форму с прогресс баром, небольшой картинкой и кнопкой прерывания процесса.
Нужно, что бы она работала в отдельном потоке.
То, что обычные формы работать в потоке не будут, знаю.
То что рекомендуют выносить код в поток знаю. Но не представляется возможным - прогресс применяется где-то в 25-30 местах и делать 30 разных потоков нецелесообразно.
Переделываю с обычной формы, так как применение processmessages не всегда допустимо, и при долгом процессе часто бывает, что форма прогресса не реагирует на вызов 'hide', оставаясь 'висеть'.
Может быть кто-то видел готовое решение, скорее всего, как я понял, на API. Несколько часов гугленья не принесли результата.


Leonid Troyanovsky   (2008-07-31 08:44:00) [1]


> Дмитрий Белькевич ©   (30.07.08 20:27) [53]

> Интересны новые решения, но не старые догмы.

Решение старо как мир: клиент-сервер.

--
Regards, LVT.


Сергей М.   (2008-07-30 20:38:00) [2]


> При диспетчиризации часть очереди будет потеряна


Чавой-то ?!

Фигню ты сейчас сморозил.

WinAPI-диспетчеризация - это, если угодно и дословно, вызов DispatchMessage.

Функция диспетчеризации требует параметром ужевыбранное из очереди сообщение.
С удалением или без удаления из очереди оно тобой получено для возможной его передачи параметром данной ф-йии - это абсолютно не важно, это не забота данной функции.

Т.е. диспетчеризация не имеет никакого отношения к каким-то там "потерям" - у нее иные задачи.


Дмитрий Белькевич   (2008-07-30 20:27:00) [3]

Извиняюсь, что долго не отвечал - был в отъезде.

>Выбери из очереди и диспетчеризуй только клавиатурные события.

При диспетчиризации часть очереди будет потеряна, а это неприемлемо.

>Если нарисовать кнопку отмены и прогресс-бар в окне доп.потока, то это окошко жить будет, а вот основное окно приложения будет висеть. Т.е. его нельзя будет ни сдвинуть ни свернуть. Оно даже перерисовываться не будет.

Это не проблема.

>Я говорю об обработке в главном потоке, без извращений с формой в другом потоке.

Интересны новые решения, но не старые догмы.

Нашел между делом функцию CreateDialog, буду пробовать с её помощью показать диалог, что бы на api с окнами не возиться.


MultIfleX   (2008-07-21 06:31:00) [4]


> Дмитрий Белькевич ©

Тебе надо чтобы пользователь ждал? Так делай Enable формы False, а в конце твоей "длительной обработки" возвращай True, и используй стандартный ProcessMessages, в прЫнципе это для тебя лучший вариант - ни потоков, ни замутов с апи, все просто и функционально. Если это выходит для тебя накладно по времени, попробуй вызывать реже, или по условию что GetTickCount больше "числа Х".


Сергей М.   (2008-07-19 11:54:00) [5]


> Что бы оживить GUI (прогресс-форму), мне в основном потоке
> приходится постоянно в цикле обработки вызывать ProcessMessages


Да кто ж тебя заставляет для конкретнойцели использовать ProcessMessages ?

Нужно перерисовать прогресс-бар ? Просто вызови его метод Update.

Нужно знать не нажал ли юзер "отмену" ? Выбери из очереди и диспетчеризуй только клавиатурные события.

Всего делов-то)


GrayFace   (2008-07-19 00:47:00) [6]

Почему? Я говорю об обработке в главном потоке, без извращений с формой в другом потоке.


Тын-Дын   (2008-07-19 00:41:00) [7]


> GrayFace ©   (18.07.08 22:34) [48]


Бесполезно. TApplication слишком вросся в TForm.


GrayFace   (2008-07-18 22:34:00) [8]

из [38] возьми вот это и вставь вместо Application.ProcessMessages:
while GetMessage(Msg, FWindow, 0, 0) and not Terminated do begin
  TranslateMessage(Msg);
  DispatchMessage(Msg);
end;

FWindow - SuperPuperProgressForm1.Handle. Думаю, поможет.


ANB   (2008-07-18 16:54:00) [9]


> Вот. Что бы оживить GUI (прогресс-форму), мне в основном
> потоке приходится постоянно в цикле обработки вызывать ProcessMessages.
>
> Во время вызова, как я пониамаю, обрабатываются все сообщения,
>  накопившиеся в очереди. Так вот - иногда в очередь попадают
> нежелательные сообщения, которые должны будут обработаться
> только после того, как основной поток отработает цикл. И
> потерять, или отфильтровать эти сообщения нельзя.

Если нарисовать кнопку отмены и прогресс-бар в окне доп.потока, то это окошко жить будет, а вот основное окно приложения будет висеть. Т.е. его нельзя будет ни сдвинуть ни свернуть. Оно даже перерисовываться не будет.


Тын-Дын   (2008-07-18 16:38:00) [10]


> Нет, конечно, иначе бы не было бы никаких вопросов. Поэтому
> нужно простое окно Windows, без VCL'а.


program ProgBar;
//Ляпа ©
uses
 Windows,
 Messages,
 commctrl;

Var Tim:Word;
  hProgressBarWnd:HWND;
  MaxPos,MinPos:WORD;

function MyWndProc(Window:HWND;Mess:word;
       WParam:LongInt;LParam:LongInt):LongInt;stdcall;
 begin
   case Mess of
     WM_CREATE:
           begin
             hProgressBarWnd:=CreateWindow(PROGRESS_CLASS,'',
                      WS_CHILD+WS_VISIBLE,
                      10,75,150,20,window,0,HInstance,nil);
           SendMessage(hProgressBarWnd, PBM_SETRANGE,0,MAKELPARAM(MinPos,MaxPos));
           SendMessage(hProgressBarWnd,PBM_SETSTEP,1,0);
           SendMessage(hProgressBarWnd,PBM_SETPOS,0,0);
           Result:=0;
           end;
     WM_DESTROY:
          begin
//Удаляем таймер
             KillTimer( Window, 1 );
             PostQuitMessage(0);
             Result:=0
          end;
     WM_TIMER:
          begin
              SendMessage(hProgressBarWnd, PBM_STEPIT, 0, 0);
            Result:=0
          end;
     else Result:=DefWIndowProc(Window,Mess,WParam,LParam)
 end;
end;

var
wc:TWndClass;
wnd:HWND;
Msg:TMsg;

PollingDelay:integer=500; //500 - интервал 0.5 сек

begin
//Регистрация класса окна
wc.style:=CS_HREDRAW+CS_VREDRAW;
wc.lpfnWndProc:=@MyWndProc;
wc.cbClsExtra:=0;
wc.cbWndExtra:=0;
wc.hInstance:=HInstance;
wc.hIcon:=LoadIcon(THandle(nil),IDI_APPLICATION);
wc.hCursor:=LoadCursor(THandle(nil),IDC_ARROW);
wc.hbrBackground:=COLOR_BTNFACE+1;//COLOR_WINDOW+1;
wc.lpszMenuName:=nil;
wc.lpszClassName:='MyWndClass';

if (RegisterClass(wc)=0) then exit;
//InitCommonControl(CC: Integer): Boolean;  InitCommonControlsEx
InitCommonControls;
   MaxPos:=100;
   MinPos:=0;
//создаем окно
wnd:=CreateWindow('MyWndClass','Таймер',
   WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,0,CW_USEDEFAULT,0,
   0,0,HInstance,nil);
//Устанавливаем таймер
SetTimer( Wnd, 1, PollingDelay, nil );
//показываем окно
ShowWindow(wnd,SW_RESTORE);
UpdateWindow(wnd);
//начинаем тривиальный цикл сообщений
while GetMessage(Msg,0,0,0) do
begin
 TranslateMessage(Msg);
 DispatchMessage(Msg)
end

end.


Дмитрий Белькевич   (2008-07-18 15:54:00) [11]

>когда основной поток сильно грузит компьютер дисковыми операциями.

Когда основной, и он же - единственный... То есть всё - и основная работа и обработка прогресс-форма идёт только в нём. Просто, что бы не подумали, что у меня потоков несколько. Он сейчас один. Собсно, с VCL формой по-другому и не сделать.


Дмитрий Белькевич   (2008-07-18 15:51:00) [12]

>Игорь Шевченко

Большое спасибо, посмотрю.

>Ведь пока осн.поток занят , GUI мертв !

Вот. Что бы оживить GUI (прогресс-форму), мне в основном потоке приходится постоянно в цикле обработки вызывать ProcessMessages.
Во время вызова, как я пониамаю, обрабатываются все сообщения, накопившиеся в очереди. Так вот - иногда в очередь попадают нежелательные сообщения, которые должны будут обработаться только после того, как основной поток отработает цикл. И потерять, или отфильтровать эти сообщения нельзя.

>в ряде случаев отнюдь не лишнее. Пользователю необязательно видеть изменения прогресса при КАЖДОМ его изменении, иногда достаточно обновления раз в некоторый интервал, в секунду, в две...

Да, знаю такое, но немного по-другому делал. Сейчас есть два метода у формы - Step и Step100, первый обновляет прогрессбар после каждого вызова, второй ведёт внутренний счетчик и обновляет только каждый сотый.

В этом случае, да - таймер тоже хорош.

>Любой, позволяющий получать и диспетчеризовать только нужные (а не все подряд) сообщения - Get/PeekMessage + DispatchMessage

Тут проблема. Сообщения нужны все, но после цикла.
Да и проблема больше не с ними, это я уже научился обрабатывать. Но просто хочется заодно все эти заглушки поубирать. А пробелема с тем, что форма почему-то перестаёт реагировать на вызов Hide. Замечено, что проявляется, когда основной поток сильно грузит компьютер дисковыми операциями.

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

Нет, конечно, иначе бы не было бы никаких вопросов. Поэтому нужно простое окно Windows, без VCL'а.


Тын-Дын   (2008-07-18 15:26:00) [13]


> Дмитрий Белькевич ©   (18.07.08 11:03) [23]
>
> Согласитесь,
>  что сделать одно окно проще


Заметь, ты сам написал - " окно"

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


Сергей М.   (2008-07-18 14:46:00) [14]


> спросил - какой способ лучше?


Любой, позволяющий получать и диспетчеризовать только нужные (а не все подряд) сообщения - Get/PeekMessage + DispatchMessage


> как в отдельном потоке - конечно, Application.ProcessMessages
> не нужен, от него и избавляемся.
>


Да не то что не нужен - не допустим !

Но я, вообще-то, об основном, в котором Application.ProcessMessages допустим по логике VCL, но при этом якобы недопустим по твоей прикладной логике. Вот про возможные его альтернативы я и повел речь.


Игорь Шевченко   (2008-07-18 14:45:00) [15]


> Таймер - лишнее, конечно. PostThreadMessage.


в ряде случаев отнюдь не лишнее. Пользователю необязательно видеть изменения прогресса при КАЖДОМ его изменении, иногда достаточно обновления раз в некоторый интервал, в секунду, в две...


Сергей М.   (2008-07-18 14:40:00) [16]

Я вообще не понимаю, зачемпри данных условиях нужен доп.поток и почему осн.поток не должен "отвлекаться" .. Ведь пока осн.поток занят , GUI мертв ! Ни форму двинуть, ни приложение терминировать, ни картинку перерисовать  .."Живое" окно доп.потока с прогресс-баром и кнопкой "Отмена" в расчет не берем - это, конечно, тоже GUI, но совершенно бестолковый, "корове седло")


Дмитрий Белькевич   (2008-07-18 14:37:00) [17]

>А форма с прогрессом будет, наверно, по таймеру опрашивать эти переменные)

Таймер - лишнее, конечно. PostThreadMessage.

>А причем здесь Application.ProcessMessages ?
Использование оного по твоему же условию невозможно - окно с прогресс-баром должно быть создано в контексте дополнительного потока...

Я думал, что от это вот было сказано по поводу работы с VCL, без доролнительных потоков:

>Вызов этого метода - не единственный и не лучший способ реакции на сообщения.

И спросил - какой способ лучше?

Так как в отдельном потоке - конечно, Application.ProcessMessages не нужен, от него и избавляемся.


Игорь Шевченко   (2008-07-18 14:30:00) [18]

Я делал где-то так:

основная форма:

unit main;

interface

uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 ExtCtrls, StdCtrls, ProgressThread;

type
 TfMain = class(TForm)
   Button: TButton;
   Timer: TTimer;
   procedure ButtonClick(Sender: TObject);
   procedure TimerTimer(Sender: TObject);
 private
   FProgressThread: TProgressThread;
   procedure LongProcessStarted;
   procedure LongProcessCompleted;
 end;

var
 fMain: TfMain;

implementation

{$R *.DFM}

procedure TfMain.ButtonClick(Sender: TObject);
begin
 LongProcessStarted;
 Timer.Enabled := true;
end;

procedure TfMain.LongProcessCompleted;
begin
 FProgressThread.Terminate;
 FProgressThread.WaitFor;
 FProgressThread.Free;
end;

procedure TfMain.LongProcessStarted;
begin
 FProgressThread := TProgressThread.Create (Self);
end;

procedure TfMain.TimerTimer(Sender: TObject);
begin
 LongProcessCompleted;
end;

end.


поток с окном (дочерним по отношению к основной форме)

unit ProgressThread;

interface

uses
 Windows, Forms, Classes;

type
 TProgressThread = class(TThread)
 private
   FTop, FLeft: Integer;
   FParentWidth, FParentHeight: Integer;
   FWindow: HWND;
   FParentWindow: HWND;
   function InitProgressThread: Boolean;
 protected
   procedure Execute; override;
 public
   constructor Create (ParentForm: TForm);
   destructor Destroy; override;
 end;

implementation
uses
 SysUtils, Messages;

function ThreadWndProc (Wnd: HWND; Message: UINT; wParam: WPARAM;
 lParam: LPARAM): LRESULT; stdcall;
var
 ps: PAINTSTRUCT;
 rctext: TRect;
begin
 case Message of
 WM_TIMER:
   begin
     if wParam = 1000 then begin
       InvalidateRect (Wnd, nil, false);
       Result := 0;
     end else
       Result := DefWindowProc (Wnd, Message, wParam, lParam);
   end;
 WM_DESTROY:
   begin
     KillTimer(Wnd, 1000);
     PostQuitMessage(0);
     Result := 0;
   end;
 WM_PAINT:
   begin
     BeginPaint(Wnd, ps);
     SetTextColor(ps.hdc, 0);
     GetClientRect(Wnd, rctext);
     DrawText (ps.hdc, PChar(Format('running %d', [GetTickCount])), -1,
       rctext, DT_CENTER or DT_VCENTER);
     EndPaint(Wnd, ps);
     Result := 0;
   end;
 else
   Result := DefWindowProc (Wnd, Message, wParam, lParam);
 end;
end;

function TProgressThread.InitProgressThread: Boolean;
const
 Height = 100;
 Width = 200;
var
 wc: WNDCLASS;
begin
 FillChar (wc, SizeOf(wc), 0);
 wc.lpfnWndProc := @ThreadWndProc;
 wc.lpszClassName := 'HSProgressWindowClass';
 wc.hInstance := HInstance;
 wc.hbrBackground := GetStockObject(WHITE_BRUSH);
 Result := Windows.RegisterClass(wc) <> 0;
 if not Result then
   Exit;
 FWindow := CreateWindowEx (0, 'HSProgressWindowClass', 'ProgressWindow',
   WS_CHILD or WS_VISIBLE or WS_BORDER, (FParentWidth - Width) div 2,
     (FParentHeight - Height) div 2, Width, Height, FParentWindow, 1,
     HInstance, Self);
 Result := IsWindow(FWindow);
 if not Result then
   Exit;
 SetTimer(FWindow, 1000, 500, nil);
 UpdateWindow(FWindow);
end;

{ TProgressThread }

constructor TProgressThread.Create(ParentForm: TForm);
begin
 FreeOnTerminate := false;
 FLeft := ParentForm.Left;
 FTop := ParentForm.Top;
 FParentWidth := ParentForm.Width;
 FParentHeight := ParentForm.Height;
 FParentWindow := ParentForm.Handle;
 inherited Create (false);
end;

procedure TProgressThread.Execute;
var
 Msg: TMsg;
begin
 if not InitProgressThread then
   Exit;
 while GetMessage(Msg, FWindow, 0, 0) and not Terminated do begin
   TranslateMessage(Msg);
   DispatchMessage(Msg);
 end;
 if Terminated then
   DestroyWindow(FWindow);
end;

destructor TProgressThread.Destroy;
begin
 Windows.UnregisterClass('HSProgressWindowClass', HInstance);
 inherited;
end;

end.


Сергей М.   (2008-07-18 14:24:00) [19]


> будет оповещать поток с формой отображения каким-то образом
> о собственно прогрессе


Поди тоже через переменные, раз ему "отвлекаться" нельзя)

А форма с прогрессом будет, наверно, по таймеру опрашивать эти переменные)

Жесть !)


Сергей М.   (2008-07-18 14:22:00) [20]


> как основной поток узнает, что ему надо остановить длинный
> режим ?


Он же говорит - осн.поток будет опрашивать флаг.


Страницы:   1 2 3

Наверх

Титульная страница Поиск, карта сайта Написать письмо