Форум: "Прочее";
Текущий архив: 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