Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2011.11.13;
Скачать: CL | DM;

Вниз

Как правильно показывать длительный процесс?   Найти похожие ветки 

 
Дмитрий С ©   (2011-07-12 14:36) [0]

Есть форма, на ней несколько кнопок, едитов и прочих контролов. При нажатии на кнопку начинается некий длительный процесс (в главном потоке), который разбавляется вызовами Application.ProcessMessages и обновлением, к примеру, прогрессбара. Процесс должен быть останавливаемым, т.е. при нажатии на некую кнопку Стоп - должен корректно прекратиться (иными словами приложение не должно повиснуть).
Так вот вопрос заключается вот в чем: каким образом правильно блокировать нажатие остальных кнопок, редактирование едитов, вызовов Action-ов, пунктов меню и прочего?
Дисаблить их вручную?
Или [псевдо]модальную форму прогресса отображать?
Или еще какие варианты есть? Как правильно вобщем?


 
SQLEXPRESS   (2011-07-12 14:47) [1]

components[i].enabled = false
ButtonStop.enabled = true

repeat
 Work;
until Condition1 or Condition2;

onButtonStopClick
Condition2 := true;


 
TUser ©   (2011-07-12 14:58) [2]

Надо "процесс" запустить в отдельном потоке.


 
Дмитрий С ©   (2011-07-12 15:04) [3]


> TUser ©   (12.07.11 14:58) [2]
> Надо "процесс" запустить в отдельном потоке.

А это не важно на самом деле. Контролы всеравно надо блокировать.


> components[i].enabled = false
> ButtonStop.enabled = true
>
> repeat
>  Work;
> until Condition1 or Condition2;
>
> onButtonStopClick
> Condition2 := true;

Надо еще учитывать те контролы, которые были до этого задисаблены, а также те которые не нужно дисаблить. Вобщем совсем не универсальное решение:(
Хотя со стопом то что надо


 
SQLEXPRESS   (2011-07-12 15:10) [4]


> Надо еще учитывать те контролы, которые были до этого задисаблены,
>  а также те которые не нужно дисаблить.

Так и хочется сказать "Надо" :)

TComponent(те контролы, которые были до этого задисаблены).tag := 11;
TComponent(те которые не нужно дисаблить).tag := 1;
if components[i].Tag = 0 then
components[i].enabled = false


 
Inovet ©   (2011-07-12 16:04) [5]

А чем плохо по простому, в обработчике формы с прггрессбаром FormDeactivate что-нибудь такое:

if Visible then
begin
 SetFocus;
 MessageBeep(MB_ICONEXCLAMATION);
end


и + кнопка "Отмена" с выставление флага.


 
DVM ©   (2011-07-12 16:07) [6]


> Дмитрий С ©   (12.07.11 15:04) [3]

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


 
Inovet ©   (2011-07-12 16:31) [7]

> [6] DVM ©   (12.07.11 16:07)
> И ничего ты не нажмешь на главной пока она не закроется.

А если Process Message вызывать в цикле.


 
DVM ©   (2011-07-12 16:35) [8]


> Inovet ©   (12.07.11 16:31) [7]


> А если Process Message вызывать в цикле.

нажать все равно не выйдет


 
Inovet ©   (2011-07-12 17:26) [9]

> [8] DVM ©   (12.07.11 16:35)
> нажать все равно не выйдет

А где тогда будет рабочий цикл для которого прогресс нужено отображать?


 
DVM ©   (2011-07-12 17:58) [10]


> Inovet ©   (12.07.11 17:26) [9]


> А где тогда будет рабочий цикл для которого прогресс нужено
> отображать?

Можно так:
1) В главном окне нажимают кнопку начала длительной операции.
2) Появляется модальная дочерняя форма.
3) На дочерней форме есть таймер и кнопка Стоп.
4) Таймер выставлен на время, необходимое, чтобы дочерняя форма успела появиться, перерисоваться и т.д., запускается при ее появлении.
5) По срабатыванию таймера (один раз, потом он сам себя выключает) начинает длительная операция, в цикле которой (если цикл есть конечно) вызывается ProcessMessages, чтобы в случае необходимости можно было обработать нажатие кнопки Стоп.
6) При нажатии кнопки Стоп выставляется флаг завершения, который проверяется в цикле и по которому цикл завершается.

Если цикла нет, то метод не подходит, лучше все в отдельном потоке делать.


 
MonoLife ©   (2011-07-12 18:04) [11]


> Inovet ©   (12.07.11 16:31) [7]
> ..
> А если Process Message вызывать в цикле.

дык, вроде,

>  вопрос заключается вот в чем: каким образом правильно блокировать
> нажатие остальных кнопок,


имхо тогда:
> DVM ©   (12.07.11 16:07) [6]
> ...
> а что модальное окно не устраивает? в нем и прогресс. И
> ничего ты не нажмешь на главной пока она не закроется.

или, на худой конец, components[i].enabled=components[i].Tag<>0;


 
Inovet ©   (2011-07-12 18:26) [12]

> [10] DVM ©   (12.07.11 17:58)
> По срабатыванию таймера (один раз, потом он сам себя выключает)
> начинает длительная операция, в цикле которой (если цикл
> есть конечно) вызывается ProcessMessages,

Ещё адрес долгоработающей функции надо передавать, мы же этой формой с прогрессбаром будем в разных местах пользоваться, только с таймером не очень хорошо. Разве нет события по которому можно вызвать функцию, как-то обсуждали здесь это.

Так чем плохо с просто Show, а чем-то оно плохо, может есть какой другой способ не терять фокус. Будет навроде модального.

> [5] Inovet ©   (12.07.11 16:04)

С отдельным потокм - понятно, что правильно.


 
Inovet ©   (2011-07-12 18:27) [13]

> [11] MonoLife ©   (12.07.11 18:04)

С модальным понятно как - DVM показал, только сложнее получается.


 
_Юрий   (2011-07-12 19:09) [14]


> 4) Таймер выставлен на время, необходимое, чтобы дочерняя
> форма успела появиться, перерисоваться и т.д., запускается
> при ее появлении.
> 5) По срабатыванию таймера (один раз, потом он сам себя
> выключает) начинает длительная операция, в цикле которой
> (если цикл есть конечно) вызывается ProcessMessages, чтобы
> в случае необходимости можно было обработать нажатие кнопки
> Стоп.
>


Кошмар какой.  
Чтобы выполнить некоторое действие после того, как форма закончит свое появление и перерисовку, достаточно добавить в конец ее очереди сообщений свое  сообщение (PostMessage), и всех делов.


 
DVM ©   (2011-07-12 19:45) [15]


> _Юрий


> Кошмар какой.  

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


 
_Юрий   (2011-07-12 20:15) [16]


> DVM ©   (12.07.11 19:45) [15]
>
>


Почему?


 
DVM ©   (2011-07-12 20:51) [17]


> _Юрий   (12.07.11 20:15) [16]


> Почему?

На самом деле и мой и твой вариант оба плохи. Каждый по своему.

Недостатки твоего:

1) Использование сообщений, что накладывает определенные ограничения на программу в плане переносимости на другие платформы (с некоторых пор я что-то стал уделять этому больше внимания). Формы могут быть созданы и под Linux, и даже таймеры, но очереди сообщений там у потоков не будет. Ну это может быть несущественно, если программа исключительно под Win.

2) Не очень понятно, в какой момент слать сообщение, чтоб оно гарантированно легло в конец очереди сообщений и не опередило никакое из сообщений, необходимых для корректного создания/отображения окна. Вроде бы OnShow подходит для посылки сообщения, но сдается мне, что и в этом случае можем словить проблемы, особенно, если кто-то или что-то еще решит точно как мы с помощью сообщений провести какую то инициализацию в данном окне.

Недостатки моего:

1) Сам по себе таймер неплох (и по сути посылает такое же сообщение), но его интервал - это костыль, так как он выбирается (точнее подбирается) экспериментально, что тоже в определенных обстоятельствах может создать проблемы.


 
_Юрий   (2011-07-12 23:00) [18]


> DVM ©   (12.07.11 20:51) [17]


> 1)Использование сообщений, что накладывает определенные ограничения на программу в плане переносимости на другие платформы


полагаю, что кроссплатформенность для Delphi-программ - это таки утопия.

> 2) Не очень понятно, в какой момент слать сообщение,


на OnShow. А поймав сообщение, начинает исполнять заранее присвоенный call-back. Если его перезатерли, или забыли присвоить - ошибка сразу всплывет, проблем не будет.


> 1) Сам по себе таймер неплох (и по сути посылает такое же
> сообщение),


The WM_TIMER message is a low-priority message. The GetMessage and PeekMessage functions post this message only when no other higher-priority messages are in the thread"s message queue.

так что это по сути тоже самое, и интервал особо не важен. Только сам таймер - это лишняя сущность


 
DVM ©   (2011-07-12 23:32) [19]


> _Юрий   (12.07.11 23:00) [18]


> полагаю, что кроссплатформенность для Delphi-программ -
> это таки утопия.

Поживем увидим, в исходниках VCL от Delphi XE уже частенько можно встретить не то что IFDEF LINUX/UNIX, но даже IFDEF MACOS.
Но я вообще подразумевал Lazarus. Вполне уже зрелый продукт. Не так сложно писать код работающий и там и сям. Главное сразу об этом заботиться.


> Только сам таймер - это лишняя сущность

им проще пользоваться зато


 
Amoeba_   (2011-07-13 01:36) [20]


>  DVM ©   (12.07.11 17:58) [10]
>
>
> > Inovet ©   (12.07.11 17:26) [9]
>
>
> > А где тогда будет рабочий цикл для которого прогресс нужено
> > отображать?
>
> Можно так:
> 1) В главном окне нажимают кнопку начала длительной операции.
>
> 2) Появляется модальная дочерняя форма.
> 3) На дочерней форме есть таймер и кнопка Стоп.
> 4) Таймер выставлен на время, необходимое, чтобы дочерняя
> форма успела появиться, перерисоваться и т.д., запускается
> при ее появлении.
> 5) По срабатыванию таймера (один раз, потом он сам себя
> выключает) начинает длительная операция, в цикле которой
> (если цикл есть конечно) вызывается ProcessMessages, чтобы
> в случае необходимости можно было обработать нажатие кнопки
> Стоп.
> 6) При нажатии кнопки Стоп выставляется флаг завершения,
>  который проверяется в цикле и по которому цикл завершается.
>

Я когда-то именно так делал на D1.


 
Дмитрий С ©   (2011-07-13 06:16) [21]

Еще вариант с [псевдо]модальным окном. Я так делал, но не знаю правильно ли, выкладываю на суд (код небольшой, поэтому комментарии вставлять не буду):


unit uBusyForm;

interface

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

type

 TBusyForm = class(TForm)
   StaticText1: TStaticText;
 private
   FBusyCounter: Integer;
   FShowTime: Cardinal;
   TW: Pointer;
   Win: THandle;
   procedure ShowBusy;
   procedure HideBusy;
 public
   procedure BusyMethod;
   procedure BeginBusy;
   procedure EndBusy;
   procedure ForceShow;
 end;

implementation

{$R *.dfm}

{ TBusyForm }

procedure TBusyForm.BeginBusy;
begin
 if FBusyCounter = 0 then ShowBusy;
 Inc(FBusyCounter);
end;

procedure TBusyForm.BusyMethod;
begin
 if (not Visible) and (GetTickCount - FShowTime > 500) then ForceShow;
 Application.ProcessMessages;
 //Application.HandleMessage;
end;

procedure TBusyForm.EndBusy;
begin
 Dec(FBusyCounter);
 if FBusyCounter = 0 then HideBusy;
end;

procedure TBusyForm.ForceShow;
begin
 if FBusyCounter > 0 then
 begin
   if not Application.MainForm.Visible then
     Position := poScreenCenter;
   Show;
 end;
end;

procedure TBusyForm.HideBusy;
begin
 if Visible then Hide;
 EnableTaskWindows(TW);
 Application.ModalFinished;
 SetActiveWindow(Win);
end;

procedure TBusyForm.ShowBusy;
begin
 Win := Application.ActiveFormHandle;
 FShowTime := GetTickCount;
 Application.ModalStarted;
 Include(FFormState, fsModal);
 TW := DisableTaskWindows(0);
end;

end.


Работает примерно так:

BusyForm.BeginBusy;
try
...Долгий процесс...Основная форма будет заблокирована, а форма с прогрессом появится, если процесс затянется более чем на полсекунды.
finally
 BusyForm.EndBusy;
end;

Единственно, не реализовано прерывание процесса, но это несложно.


 
Дмитрий С ©   (2011-07-13 06:23) [22]


> ..Долгий процесс...Основная форма будет заблокирована, а
> форма с прогрессом появится, если процесс затянется более
> чем на полсекунды.

А и плюс надо вместо Application.ProcessMessages  вызывать BusyForm.BusyMethod


 
Dennis I. Komarov ©   (2011-07-13 08:51) [23]


> Как правильно вобщем?

ИМХО
1. Показали модальную форму с прогрессом и btnCancel. Запустили поток для "процесса".
2. Никаких форм. Запускаем поток для "процесса". Перед запуском он проверяет выполняется ли "процесс" если нет, то далее. Результаты выводятся по выполнению.


 
DiamondShark ©   (2011-07-13 11:50) [24]


> 1) Сам по себе таймер неплох (и по сути посылает такое же
> сообщение), но его интервал - это костыль, так как он выбирается
> (точнее подбирается) экспериментально, что тоже в определенных
> обстоятельствах может создать проблемы.

Зачем его подбирать? Выставить в 1 и всё.



Страницы: 1 вся ветка

Текущий архив: 2011.11.13;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.008 c
2-1311508935
prodex
2011-07-24 16:02
2011.11.13
Как построить запрос в Oracle, если...


15-1310467018
Дмитрий С
2011-07-12 14:36
2011.11.13
Как правильно показывать длительный процесс?


2-1311230686
alexdn
2011-07-21 10:44
2011.11.13
Изменение Label


1-1272500252
SPeller
2010-04-29 04:17
2011.11.13
Можно ли проверить указатель на корректность?


2-1311149076
Scott Storch
2011-07-20 12:04
2011.11.13
ошибка при работе с параметрами запроса