Подтвердить что ты не робот

Получить окно для обновления (и т.д.) без вызова Application.ProcessMessages?

У меня есть унаследованное приложение, в котором есть несколько "длительных" циклов, которые срабатывают в результате взаимодействия с пользователем. Временной код периодически обновляет что-то на экране с информацией о ходе работы (обычно это метка), а затем, по-видимому, убеждает, что визуальное обновление происходит там, а затем код вызывает Application.ProcessMessages(argh!).

Теперь мы все знаем, какие проблемы могут возникнуть в приложении GUI (будучи благотворительным, это было более невинное время назад), и мы находим, что это точно, как яйца, время от времени мы получаем пользователей, невозможно с программой, потому что они нажимают на элементы управления, пока программа "занята".

Какой лучший способ периодически обновлять визуальные формы формы без принятия других событий/сообщений и т.д.?

Мои мысли были либо;
- отключите все элементы управления, прежде чем делать что-нибудь много времени, и оставляя вызовы "... ProcessMessages", чтобы "принудительно" обновить, или
- найти другой способ периодического обновления элемента управления

Я могу сделать первое, но это оставило меня в недоумении - есть ли лучшее решение?

пример кода из наследия;

i:=0;
while FJobToBeDone do
begin
  DoStepOfLoop;
  inc(i);
  if i mod 100 = 0 then
  begin
    UpdateLabelsEtc;
    Application.ProcessMessages;
  end;
end;

Я уже слышу, как ты обманываешься, сзади.: -)

4b9b3361

Ответ 1

Если вы измените параметры Update() на элементах управления после изменения свойств, вы заставите их перерисовывать. Другой способ - переименовать Repaint() вместо Refresh(), что подразумевает вызов Update().

Вам может потребоваться также вызвать Update() для родительских элементов управления или фреймов, но это может полностью исключить вызов ProcessMessages().

Ответ 2

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

У этого есть некоторые другие, серьезные недостатки. Прежде всего, пока другой поток активен, вам придется отключить несколько элементов управления, потому что они могут снова перезапустить поток. Второй недостаток заключается в том, что ваш код должен стать потокобезопасным. Иногда это может быть реальной проблемой. Если вы работаете с устаревшим кодом, очень вероятно, что ваш код не будет потокобезопасным. Наконец, многопоточный код сложнее отлаживать и должен выполняться опытными разработчиками.

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

Я использовал эту технику себе несколько раз, потому что считаю это намного лучше. (Но также сложнее.)

Ответ 3

Не вызывайте Application.Processmessages, это медленно и может генерировать неограниченную рекурсию. Например, чтобы обновить все в Panel1 без щелчка, мы можем использовать этот метод:

procedure TForm1.ForceRepaint;
var
  Cache: TBitmap;
  DC: HDC;
begin
  Cache := TBitmap.Create;
  Cache.SetSize(Panel1.Width, Panel1.Height);
  Cache.Canvas.Lock;
  DC := GetDC(Panel1.Handle);
  try
    Panel1.PaintTo(Cache.Canvas.Handle, 0, 0);
    BitBlt(DC, 0, 0, Panel1.Width, Panel1.Height, Cache.Canvas.Handle, 0, 0, SRCCOPY);
  finally
    ReleaseDC(Panel1.Handle, DC);
    Cache.Canvas.Unlock;
    Cache.Free;
  end;
end;

Для лучшей производительности растровое изображение кеша должно быть создано сначала и бесплатно, когда процесс завершился

Ответ 4

Техника, которую вы ищете, называется threading. Это сложный метод программирования. Код следует обрабатывать с осторожностью, потому что его отлаживать сложнее. Независимо от того, используете ли вы поток или нет, вы должны обязательно отключить элементы управления, с которыми пользователи должны не работать с (я имею в виду элементы управления, которые могут повлиять на текущий процесс). Вы должны использовать действия, чтобы включить/отключить кнопки и т.д.

Ответ 5

Вместо того, чтобы отключать элементы управления, у нас есть boolean var в форме FBusy, а затем просто проверьте это, когда пользователь нажимает кнопку, мы ввели ее по тем причинам, о которых вы говорите, пользователи, которые нажимают кнопки, пока они ждут долгого кода для запуска (насколько страшно, насколько хорошо знаком ваш образец кода).

Итак, у вас получилось что-то вроде

procedure OnClick(Sender:TObejct);
begin
    if (FBusy) then
    begin
        ShowMessage('Wait for it!!');
        Exit;
    end
    else FBusy := True;
    try
        //long running code
    finally
        FBusy := False;
    end;
end;

Его бессильно помнить о том, чтобы рэп долгого кода в блоке try-finally в случае выходов или исключений, так как у вас получится форма, которая не будет работать.

Как мы уже говорили, мы используем потоки, если для кода, который не будет влиять на данные, скажем, запустите отчет или анализ данных, но некоторые вещи это не варианты, скажем, если мы обновляем 20 000 записей о продукции, я хочу, чтобы кто-то пытался продать или иным мудрым образом изменил средние полеты, поэтому нам нужно заблокировать приложение, пока оно не будет выполнено.