Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
if ( Application.Terminated ) // выполняется завершение работы приложения
or ( Application.ModalLevel > 0 ) // открыты модальные окна
or ( Self.WindowState = wsMinimized ) // окно программы свернуто
or ( { другие условия, при которых не нужно продолжать бесконечный цикл отрисовки } ) then
А правильно делать так:
Рисовать сцену только в OnPaint, а в OnIdle делать только InvalidateRect.
Тогда вы решите проблему со свернутыми окнами, заблокированными экраном по Win+L, да и вообще во всех потенциально возможных случаях + исчезнет проблема с мелькающей дефолтной отрисовкой на обработку WM_PAINT.
procedure TFormMain.OnApplicationIdle(Sender: TObject; var Done: Boolean);
repeat // main loop
Application.ProcessMessages;
if CheckBox1.Checked then ObjMove;
DoRender;
Label1.Caption:=format('NF %d ',[NFrame]);
inc(NFrame);
// добавленная строка
BClose := ( BClose ) or ( Application.Terminated );
until BClose;
Вы мою демку не проверяли в работе?Я смотрю на код, который вы привели. Я знаю как работает Application.ProcessMessages и знаю как работает Application.Run. Этого достаточно.
1. WM_QUIT в моём примере обрабатывается корректно, это проверяемо — сообщение WM_QUIT проходит при закрытии окна «крестиком», выключении PC; также сообщение можно послать из другой программы; все 3 случая — 100% правильно отрабатываются, вообще-то это следует из текста примера;
2. Application.OnIdle происходит своим чередом. В своём примере я его не использую (а зачем?) и пишу о том, что это OnIdle «главного цикла» VCL, а у нашей графики свой «главный цикл», который впрочем не мешает VCL, что и требуется по условиям задачи автора. Могу на OnIdle повесить что нибудь, например, проверку, не изменились ли текстовые файлы с шейдерами. Это будет работать.Это не будет работать. Киньте на форму ApplicationEvents и убедитесь самостоятельно.
3. У автора шла речь об одном потоке и простом примере, я тоже не стала преумножать число потоков приложения. Мой способ сводится к тому, как в одном потоке приложения сделать 2 или более «независимых циклов», конечно эти циклы не есть отдельные потоки, но они могут работать параллельно без семафоров. Синхронизацию через TThread.Synchronize мой пример никак не нарушает, проверено — я использую такой способ в своём многопоточном сервере, все ок.Любой сторонний компонент/библиотека, использующий TThread зависнет при вызове Synchronize. Например компоненты Indy использующие серверные сокеты. Вы можете даже не знать, что эти компоненты создают потоки. Просто компоненты перестанут работать, втихую. А все благодаря вашему «циклу».
Ничего мой пример не ломает. Это просто цикл который крутится сам по себе, у него нет магических свойств. Это обычный типовой приём из области практического программирования, не я его придумала.Вы просто не знаете устройство VCL. VCL под капотом выполняет далеко не
Application.ProcessMessages использовать можно, об этом гласит Help и офф. примеры.Само собой, если функция в паблике — то использовать можно, но не нужно, потому что это очень плохой тон. Даже так: ОЧЕНЬ плохой тон. Вы пока еще новичек в мире программирования, поэтому просто поверьте моему опыту, хорошо?
Постарайтесь быть доброжелательнее, во имя Хабра. Хотя бы потому, что Вам будет крайне сложно доказать неработоспособность рабочего примера. В примере-то всего 5 строчек и все работают правильно. Я бы никогда не взялась такое оспаривать.Я с вами не воевал. Я просто против, когда учат плохому. Вы можете убедится в том, что ваши 5 строчек ломают чужой код. Как это сделать — я написал. Между прочим автор текущей статьи сделал все как раз таки правильно, и ваш рендер стоило бы переделать именно так.
Мир?
Кинула на форму кнопку, вызвала в её обработчике OnClick функцию Winapi.Window.PostQuitMessage(0), по нажатию кнопки приложение закрывается.
procedure TForm1.Button1Click(Sender: TObject);
begin
PostQuitMessage(0);
end;
Приложение не завершается. Но это не удивительно. PostQuitMessage отправляет только WM_QUIT. TApplication.ProcessMessage конечно же обрабатывает WM_QUIT, но он только выставляет флаг FTerminate := True;, который нужен для выхода из главного оконного цикла Application.Run, который вы привели. А кто выставит ваш флаг BClose, чтобы код вышел из вашего цикла? Никто. Это прекрасно видно по коду, который вы привели.procedure TApplication.ProcessMessages;
var
Msg: TMsg;
begin
while ProcessMessage(Msg) do
{ loop } ;
end;
Кто вызывает Idle? Никто. Итак у нас поломаный Idle. Я на всякий случай лично проверил это вот этим обработчиком добавив его в вашем примере:procedure TForm1.ApplicationEvents1Idle(Sender: TObject;
var Done: Boolean);
begin
AllocConsole;
WriteLn(IntToStr(Random(1000)));
Done := False;
end;
procedure TApplication.Idle(const Msg: TMsg);
var
Control: TControl;
Done: Boolean;
begin
Control := DoMouseIdle;
if FShowHint and (FMouseControl = nil) then
CancelHint;
Application.Hint := GetLongHint(GetHint(Control));
Done := True;
try
if Assigned(FOnIdle) then FOnIdle(Self, Done);
if Done then
if FActionUpdateDelay <= 0 then
DoActionIdle
else
if IdleTimerHandle = 0 then
begin
// Constantly assigning to the IdleTimerDelegate causes a
// memory allocation, and alot of TFNTimerProc's appear in Gen0 because of this.
// Only assign the delgate once; that is all that is needed.
if not Assigned(IdleTimerDelegate) then
IdleTimerDelegate := @IdleTimerProc;
IdleTimerHandle := SetTimer(0, 0, FActionUpdateDelay, IdleTimerDelegate);
if IdleTimerHandle = 0 then
DoActionIdle
end;
except
HandleException(Self);
end;
if (GetCurrentThreadID = MainThreadID) and CheckSynchronize then
Done := False;
if Done then WaitMessage;
end;
У нас дополнительно отваливается Application.Hint, но это ерунда. Главное, что теперь у нас CheckSynchronize не вызывается. А CheckSynchronize — это обработчик всех TThread.Synchronize.Выводы: при использовании графики (DX или OpenGL) в VCL приложениях не обязательно привязывать рендер к событиям VCL-компонентов, вы можете по прежнему использовать свой «главный цикл», также как в WinAPI программах. Только не забывайте «дать подышать» VCL при помощи Application.ProcessMessages, он все таки занимается обработкой сообщений.Выводы: автор комментария выше ничего не проверял, или проверял не так. Код по прежнему поломан, что хорошо видно из исходников VCL, которые были приведены, а так же легко проверяется в примере автора.
Вариант 3 (мой+Ваш)Это не мой вариант. Это только ваш вариант, который ломает код. Мой вариант как в статье выше, и это правильный вариант.
Для дальнейших успехов, в тот момент когда вы посылаете сообщение WM_QUIT вы должны дополнительно: либо вызвать Form1.Close, либо напрямую сбросить мой флаг.Гениально. А если это библиотечный код, и он не знает ничего о Form1.BClose?
Хм, я всегда в paintBox рисовал, и ничего не надо городить с очередями сообщений. Чем плох этот подход?
Использование Direct3D с высокоуровневыми библиотеками компонентов VCL/LCL