Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2011.11.13;
Скачать: [xml.tar.bz2];

Вниз

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

 
Дмитрий С ©   (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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.53 MB
Время: 0.005 c
2-1311114807
alexdn
2011-07-20 02:33
2011.11.13
Снять координаты курсора


15-1311014236
NailMan
2011-07-18 22:37
2011.11.13
Шутим тут, плюшками балуемся...


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


4-1251798761
Bordo
2009-09-01 13:52
2011.11.13
Подключение плагина на C++ к программе на Delphi


15-1296927057
xayam
2011-02-05 20:30
2011.11.13
Вариации шахмат. Какие не перечислены?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский