Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Зависание GUI / 25 сообщений из 53, страница 1 из 3
25.01.2018, 06:14
    #39590547
Зависание GUI
Не понятно почему, но при запуске программы через компилятор всё работает нормально, а при запуске exe'шника во время обработки данных (в цикле запросы через ADOQuery к Access и Progressbar.Position:=i;) частенько GUI "зависает" на 1/10 Progressbar'a. Программа "Не отвечает", хотя обработка выполняется.
Неужели, единственное решение, это отдельный поток для обработки? Мне не нужно во время выполнения кода взаимодействовать с GUI, только обновлять Progressbar.
...
Рейтинг: 0 / 0
25.01.2018, 06:30
    #39590552
JayDi
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
Progress-бар не отрисуется, пока код не выполнится до конца (основной поток занят выполнением). Чтобы отрисовать, надо в процессе долгой операции вызвать Application.ProcessMessages (̶г̶у̶с̶а̶р̶ы̶ любители потоков, молчать:-) ), что позволит "высвободить GUI" и обработать ожидающие команды типа отрисовки нового значения прогресс бара или нажатия на кнопки (так что с последним надо быть аккуратнее и отключать перед выполнением).
...
Рейтинг: 0 / 0
25.01.2018, 07:15
    #39590556
makhaon
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
Михаил Евгеньевич,

Вас пугает многопоточность и вы хотите об этом поговорить? Многопоточность делается сейчас несколькими строками кода или компонентами вообще без строчки кода (штуки 3 навскидку назову). Пробуйте, там всё просто. Не элементарно, но просто.
...
Рейтинг: 0 / 0
25.01.2018, 07:27
    #39590560
Зависание GUI
makhaon,
Меня не пугает многопоточность. Просто спросил нормально ли это и почему это происходит? И можно ли обойтись без многопоточности. Действительно ли нельзя обновлять Progressbar внутри основного потока?
Нажимаем кнопку, запускается цикл
Код: pascal
1.
2.
for i:0 to Progressbar.Max do
  Progressbar.Position:=i;


Это нормально.

а
Код: pascal
1.
2.
3.
4.
5.
for i:0 to Progressbar.Max do
  begin
    do_something;
    Progressbar.Position:=i;
  end;


уже приводит к зависанию GUI.
...
Рейтинг: 0 / 0
25.01.2018, 07:31
    #39590566
Zelius
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
Михаил Евгеньевич,

Код: pascal
1.
2.
3.
4.
5.
6.
for i:0 to Progressbar.Max do
  begin
    do_something;
    Progressbar.Position:=i;
    Application.ProcessMessages; <== Про ProcessMessages уже сказали
  end;



Ну и самое главное - это узнать, на чем же подвисает главный поток, что бы дальше разбираться
...
Рейтинг: 0 / 0
25.01.2018, 08:19
    #39590586
Док
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
Михаил Евгеньевич
Код: pascal
1.
2.
3.
4.
5.
for i:0 to Progressbar.Max do
  begin
    do_something; {1}
    Progressbar.Position:=i;{2}
  end;



уже приводит к зависанию GUI.
Сначала выполнится {1}, а затем {2}. Если {1} довольно длителен по времени, то задержка отрисовки {2} уже заметна на глаз.
...
Рейтинг: 0 / 0
25.01.2018, 08:22
    #39590587
Док
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
Михаил Евгеньевич,

попробуй такой код
Код: pascal
1.
2.
3.
4.
5.
for i:0 to Progressbar.Max do
  begin
    sleep(1000);//do_something;
    Progressbar.Position:=i;
  end;


затем поиграйся с аргументом функции sleep. И поймешь, в чем дело
...
Рейтинг: 0 / 0
25.01.2018, 10:01
    #39590635
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
Михаил Евгеньевича
Код: pascal
1.
2.
3.
4.
5.
for i:0 to Progressbar.Max do
  begin
    do_something;
    Progressbar.Position:=i;
  end;



уже приводит к зависанию GUI.
В 1-м ответе объяснили, почему это приведет к "зависанию". Потому, что во всём этом цикле никто не собирается обрабатывать WM_PAINT.
Думаю, с минимальными изменениями если, добавь в цикл
Код: pascal
1.
Progressbar.Update;

если такое есть или сразу (точно есть)
Код: pascal
1.
UpdateWindow( Progressbar.Handle );



Это частично поможет.
...
Рейтинг: 0 / 0
25.01.2018, 10:06
    #39590640
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
ДокМихаил Евгеньевич,

попробуй такой код
Код: pascal
1.
2.
3.
4.
5.
for i:0 to Progressbar.Max do
  begin
    sleep(1000);//do_something;
    Progressbar.Position:=i;
  end;



затем поиграйся с аргументом функции sleep. И поймешь, в чем дело
Код не пробовал с телефона пишу.
По идее этот код должен так же "зависать", как и у ТС.

Док, возможно, если ты это на Лазаре написал, там могли впихнуть и Update, а не только Invalidate, внутрь "Position:=".
...
Рейтинг: 0 / 0
25.01.2018, 10:10
    #39590645
Зависание GUI
YuRock,

Спасибо!
Код: pascal
1.
Progressbar.Update;
...
Рейтинг: 0 / 0
25.01.2018, 10:19
    #39590652
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
Zelius
Код: pascal
1.
Application.ProcessMessages; <== Про ProcessMessages уже сказали


Это так себе способ перерисовать конкретное окно. Недостатки:
1. Обработаются все накопившиеся сообщения, что долго и в данном случае не нужно;
2. Самое главное. После InvalidateRect (которая будет вызвана при изменении прогрессбара) далеко не всегда делается посылка WM_PAINT:

The system sends a WM_PAINT message to a window whenever its update region is not empty and there are no other messages in the application queue for that window.

Поэтому это может просто не помочь.
Для этого случая существует специальная функция - UpdateWindow.
...
Рейтинг: 0 / 0
25.01.2018, 10:34
    #39590667
Зависание GUI
YuRock,

Код: pascal
1.
2.
3.
4.
5.
6.
7.
for i:0 to Progressbar.Max do
  begin
    Progressbar.Position:=i;
    Progressbar.Update;
    do_something;
    
  end;


Подвисает, но почти в конце цикла.


Код: pascal
1.
2.
3.
4.
5.
6.
7.
for i:0 to Progressbar.Max do
  begin
    Progressbar.Position:=i;
    Application.ProcessMessages;
    do_something;
 
  end;



Не подвисает.
...
Рейтинг: 0 / 0
25.01.2018, 10:38
    #39590673
Зависание GUI
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
begin
  progressbar1.Max:=1000;
  for i := 0 to 1000 do
    begin
      Progressbar1.Position:=i;
      //Progressbar1.Update;
      Application.ProcessMessages;
      Sleep(100);
    end;

end;


Для экспериментов.
Код: pascal
1.
Progressbar1.Update;


Зависает.
...
Рейтинг: 0 / 0
25.01.2018, 11:07
    #39590701
white_nigger
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
Михаил Евгеньевич
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
begin
  progressbar1.Max:=1000;
  for i := 0 to 1000 do
    begin
      Progressbar1.Position:=i;
      //Progressbar1.Update;
      Application.ProcessMessages;
      Sleep(100);
    end;

end;



Для экспериментов.
Код: pascal
1.
Progressbar1.Update;



Зависает.Всё именно так и должно работать
...
Рейтинг: 0 / 0
25.01.2018, 11:54
    #39590770
makhaon
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
Михаил Евгеньевич,

так и должно быть. если у тебя do_something делается 100 миллисекунд и больше, готовься к тормозам. ну или делай сразу нормально, то есть с потоками.
...
Рейтинг: 0 / 0
25.01.2018, 12:36
    #39590825
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
Михаил Евгеньевич
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
begin
  progressbar1.Max:=1000;
  for i := 0 to 1000 do
    begin
      Progressbar1.Position:=i;
      //Progressbar1.Update;
      Application.ProcessMessages;
      Sleep(100);
    end;

end;



Для экспериментов.
Код: pascal
1.
Progressbar1.Update;



Зависает.
Зависать может что-то другое, но не прорисовка прогрессбара при Progressbar1.Update. В отличие от прорисовки путем выкрутки сообщений (которых может еще и не быть).
...
Рейтинг: 0 / 0
25.01.2018, 12:37
    #39590826
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
Михаил Евгеньевич,

т.ч. НЕ ВЕРЮ (с)
...
Рейтинг: 0 / 0
25.01.2018, 12:56
    #39590851
Зависание GUI
YuRock,

"Зависает" всё приложение. Вместе с прогрессбаром. В диспетчере задач "Не отвечает". Прогресс бар не движется. Код выполняется.
...
Рейтинг: 0 / 0
25.01.2018, 13:11
    #39590871
Zelius
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
Михаил Евгеньевич,

если в программе ADO, то попробуйте асинхронное соединение и выполнение
...
Рейтинг: 0 / 0
25.01.2018, 13:16
    #39590876
krapotkin
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
progressbar.update решит вопрос Прогресс не движется
но если окно перекрыть другим окном, то останется только один прогрессбар
если написать форме update то все будет перерисовываться но все равно "висеть". ибо "висение" суть "нереакция на клаву/мышь"
если написать Application.ProcessMessage, то все начнет двигаться, но заодно будут вызваться и другие обработчики, что может привести даже и к новому входу и в этот же обработчик и другим спецэффектам...
т.е. побочка при лечении может быть и страшнее самой болезни.

так что нормальный вариант это именно вынос долгих кусков в отдельный поток
...
Рейтинг: 0 / 0
25.01.2018, 13:23
    #39590886
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
Михаил ЕвгеньевичYuRock,

"Зависает" всё приложение. Вместе с прогрессбаром. В диспетчере задач "Не отвечает". Прогресс бар не движется. Код выполняется.
А, понятно, это в XP и в 7-ке такой баг был, когда винда запоминала картинку окна и не перерисовывала её даже при корректной перерисовке, пока не обработаешь какое-то там сообщение. В 10-ке пофиксили наконец-то вроде - у меня такое не повторяется.
...
Рейтинг: 0 / 0
25.01.2018, 13:41
    #39590909
энди
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
Посмотрите AsyncCalls + TaskEx. Можно прямо по тексту программы практически ничего не меняя убрать часть кода в отдельный тред, а в основном просто сделаете свой прогресс. Я так сделал, правда у меня в основном потоке форма с анимационных гифом показывается, но прицип тот же.
...
Рейтинг: 0 / 0
25.01.2018, 13:52
    #39590925
rgreat
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
кстати AsyncCalls на x64 работает?
...
Рейтинг: 0 / 0
25.01.2018, 13:59
    #39590934
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
krapotkinтак что нормальный вариант это именно вынос долгих кусков в отдельный поток
Ну это понятно, но ТС не хочет
Михаил ЕвгеньевичНеужели, единственное решение, это отдельный поток для обработки? Мне не нужно во время выполнения кода взаимодействовать с GUI
Ведь это же чудесно, что не нужно. Значит никаких проблем при вынесении логики в отдельный поток вообще не будет.
Михаил Евгеньевичтолько обновлять Progressbar
1-й вариант:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
unit Unit1;

interface

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

type
  TTestThread = class( TThread )
  private
    FProgressBarHandle: HWND;
    FMax: Integer;
    FWaitEvent: TSimpleEvent;
  protected
    procedure Execute; override;
  public
    constructor Create( AProgressBarHandle: HWND; AMax: Integer );
    destructor Destroy; override;
  end;

  TForm1 = class(TForm)
    btnStart: TButton;
    ProgressBar1: TProgressBar;
    procedure btnStartClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FThread: TTestThread;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

constructor TTestThread.Create( AProgressBarHandle: HWND; AMax: Integer );
begin
  FProgressBarHandle := AProgressBarHandle;
  FMax := AMax;
  FWaitEvent := TSimpleEvent.Create;
  inherited Create( False );
end;

destructor TTestThread.Destroy;
begin
  Terminate;
  FWaitEvent.SetEvent;
  inherited;
  FWaitEvent.Free;
end;

procedure TTestThread.Execute;
var
  i: Integer;
begin
  SendMessage( FProgressBarHandle, PBM_SETRANGE, 0, MakeLong( 0, FMax ) );
  for i := 0 to FMax do begin
    SendMessage( FProgressBarHandle, PBM_SETPOS, i, 0 );
    FWaitEvent.WaitFor( 100 );
    if Terminated then
      Exit;
  end;
end;

procedure TForm1.btnStartClick(Sender: TObject);
begin
  FThread.Free;
  FThread := TTestThread.Create( ProgressBar1.Handle, 1000 );
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil( FThread );
end;

end.


2-й вариант:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
unit Unit1;

interface

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

const
  WM_SET_PROGRESS_STEP = WM_USER + 1;

type
  TTestThread = class( TThread )
  private
    FFormHandle: HWND;
    FMax: Integer;
    FWaitEvent: TSimpleEvent;
  protected
    procedure Execute; override;
  public
    constructor Create( AFormHandle: HWND; AMax: Integer );
    destructor Destroy; override;
  end;

  TForm1 = class(TForm)
    btnStart: TButton;
    ProgressBar1: TProgressBar;
    procedure btnStartClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FThread: TTestThread;
    procedure OnNeedStep( var Msg: TMessage ); message WM_SET_PROGRESS_STEP;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

constructor TTestThread.Create( AFormHandle: HWND; AMax: Integer );
begin
  FFormHandle := AFormHandle;
  FMax := AMax;
  FWaitEvent := TSimpleEvent.Create;
  inherited Create( False );
end;

destructor TTestThread.Destroy;
begin
  Terminate;
  FWaitEvent.SetEvent;
  inherited;
  FWaitEvent.Free;
end;

procedure TTestThread.Execute;
var
  i: Integer;
begin
  for i := 0 to FMax do begin
    SendMessage( FFormHandle, WM_SET_PROGRESS_STEP, i, 0 );
    FWaitEvent.WaitFor( 100 );
    if Terminated then
      Exit;
  end;
end;

procedure TForm1.btnStartClick(Sender: TObject);
begin
  FThread.Free;
  ProgressBar1.Max := 1000;
  FThread := TTestThread.Create( Handle, ProgressBar1.Max );
end;

procedure TForm1.OnNeedStep( var Msg: TMessage );
begin
  ProgressBar1.Position := Msg.WParam;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil( FThread );
end;

end.


В обоих вариантах можно SendMessage заменить на PostMessage, если на самом деле нет необходимости в мгновенной синхронной перерисовке.

И еще нюанс. Если во время выполнения потока изменится Handle формы (это может произойти, например, при изменении BorderStyle) - то, конечно, все поламается (сообщения перестанут отправляться и интерфейс обновляться соответственно тоже). Но это лучше, чем отгрести полный крах при использовании в потоке Form1.Handle в этот момент.
Т.ч. если надумаешь "сильно" менять интерфейс - останови вначале этот поток.
...
Рейтинг: 0 / 0
25.01.2018, 15:22
    #39591019
энди
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Зависание GUI
rgreat,

Сам лично не проверял, но написано
Supported compilers: Delphi 5 to 10.1
Supported platforms: Win32, Win64
...
Рейтинг: 0 / 0
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Зависание GUI / 25 сообщений из 53, страница 1 из 3
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]