Свой цикл обработки событий поверх Application.run

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Свой цикл обработки событий поверх Application.run

Сообщение zub » 17.01.2014 12:15:00

В обычном ГУИ приложении понадобилось событийную модель отлова координат курсора свернуть в процедурную. Чтобы писать процедуры вида
Код: Выделить всё
procedure MySuperProc;
...
coord:=GetMouseCoord;
//тут выполнение продолжится только после того как пользователь укажет точку и нажмет lmb
...

Ничего умнее чем посадить внутрь GetMouseCoord свой цикл обработки событий не придумалось... там сидит конструкция вида
Код: Выделить всё
while _условие_завершения_ do
begin
     Application.HandleMessage;
end;

Все хорошо работает, пока пользователь не начинает запускать подобные выкрутасы рекурсивно - Т.е. нажимает кнопку в обработчике которой сидит MySuperProc, запускается GetMouseCoord со своим циклом, а этот подлец вместо указания точки нажимает кнопку второй раз (запретить повторное нажатие пока не отработало первое не предлагать, т.к. иногда это нужно пользователю).
Попробовал из обработчика кнопки запускать MySuperProc асинхронно, перед этим выставив _условие_завершения_ чтобы уже запущенный дополнительный цикл обработки сообщений успел завершиться - не получилось - Application.HandleMessage успевает запустить асинхронный вызов до выхода из цикла.
Помогает только "двойной" асинхронный запуск, т.е. асинхронно запускаю процедуру, которая асинхронно запускает MySuperProc. Жуткий костыль((

Как временно приостановить обработку асинхронных запусков для HandleMessage?
Как подобные вещи организуются по правильному?
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Свой цикл обработки событий поверх Application.run

Сообщение Mirage » 19.01.2014 11:25:43

Т.е. тебе надо чтобы по нажатию кнопки в GUI (TButton) включался режим "укажи точку", в котором пользователь должен кликнуть где-то мышкой, а координаты этого клика надо передать в YourSuperProc()? Правильно я понял?
Тогда я не понял нафига вышеприведенные огороды нужны. Просто делаешь режим такой и все. Можешь даже курсор поменять, чтобы пользователю понятней было.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Свой цикл обработки событий поверх Application.run

Сообщение zub » 19.01.2014 13:02:19

Да, смысл в этом, но только частично. Все нужные режимы сделаны и в "событийном" режиме всё работает. Но получается очень громоздко - элементарные действия размазываются по куче обработчиков. Основной смысл всетаки в том чтобы выполнение YourSuperProc() тормозилось на этапе указания точки и продолжалось после указания, но без остановки всего интерфейса - чтоб вместо указания пользователь имел возможность ткнуть другую кнопку, отменив YourSuperProc() и запустив YourSuperProc2() или нажать еск.
Например процедура рисования линии будет выглядеть как то так:
Код: Выделить всё
procedure MySuperProc;
var
    coord1;coord2:Coordinstes3D;
begin
if GetMouseCoord(coord1) then //возвращает true если точка указана, false в случае отмены или какойлибо ошибки
  if GetMouseCoord(coord2) then
   AddLineToDrawing(coord1,coord2);
end;

Ифы для обработки ошибок - если пользователь нажал еск или вызвал другую "команду", текущая завершается ничего не выполнив.
Таких "процедур - комманд" будет очень много, надо максимально упростить их написание и желательно иметь возможность их простого выноса в скрипты
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Свой цикл обработки событий поверх Application.run

Сообщение Mirage » 19.01.2014 14:51:28

Ну я бы сделал что-то типа фреймворка для этого (отличная идея для стартапа!). Если в общих чертах:
  • Есть отдельный поток (либо пул потоков), где работают SuperProc'ы.
  • Всякие GetMouseCoord() прерывают выполнение потока и ждут соответствующего события в основном потоке. Для GetMouseCoord() это будет, очевидно клик мышью, хотя я бы назвал более четко - WaitForMouseLeftButtonClick().
  • Есть соглашения, настройки или даже параметры самих функций, определяющие что влияет на эти функции, что заставляет их возвращать False и т.д. Например, если пользователь закрыл форму, то наверное имеет смысл всем функциям False вернуть. Или сериализоваться и продолжить с того же места при последующем запуске.
В скрипты все это вынесется тривиально.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Свой цикл обработки событий поверх Application.run

Сообщение zub » 19.01.2014 20:38:50

Да, можно попробовать с потоками, но чтото мне подсказывает что гемороя и "огородов" будет еще больше (многопоточного опыта не имею).

>>Есть отдельный поток (либо пул потоков), где работают SuperProc'ы.
приложение однопоточное и не расчитано на многопоточность. Наверно SuperProc'ы должны выполняться в основном потоке

>>Всякие GetMouseCoord() прерывают выполнение потока и ждут соответствующего события в основном потоке. Для GetMouseCoord() это будет, очевидно клик мышью, хотя я бы назвал более четко - WaitForMouseLeftButtonClick().
Приложение - векторный графический редактор, по факту это Get3DPoint, т.к. способов ввода координат несколько - как используя мышь, так и клавиатуру. Кроме того у Get3DPoint есть аргументы - адреса процедур и структур данных для наглядного "резинового" рисования вслед за мышкой, чтоб пользователь заранее видел что он получит в итоге.
Для лучшего понимания что мне надо http://forum.lazarus.freepascal.org/ind ... ttach=7469 На анимации правда ввод координат только мышкой

>>Есть соглашения, настройки или даже параметры самих функций, определяющие что влияет на эти функции, что заставляет их возвращать False и т.д. Например, если пользователь закрыл форму, то наверное имеет смысл всем функциям False вернуть. Или сериализоваться и продолжить с того же места при последующем запуске.
Соглашений всяких уже есть без бутылки не вспомнить\разобраться... крайне хочется всё упростить без потери функционала
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Свой цикл обработки событий поверх Application.run

Сообщение Mirage » 19.01.2014 22:29:48

zub писал(а):Да, можно попробовать с потоками, но чтото мне подсказывает что гемороя и "огородов" будет еще больше (многопоточного опыта не имею).
приложение однопоточное и не расчитано на многопоточность. Наверно SuperProc'ы должны выполняться в основном потоке


В одном потоке нынче далеко не уедешь.;)
А данная схема проста и фактически однопоточна, т.к. одновременно работает только один поток. Т.е. синхронизации дополнительной не нужно.
Поток в данном случае создается чисто для того, чтобы иметь возможность писать SuperProc'ы отдельно, как изначально хотелось, не размазывая их.
Более прямого пути выполнить данное требование не вижу. Крутить LCL'овский цикл где попало не стоит, он на это не рассчитан.

Насчет типа приложения - я так и понял.

Соглашений (или настроек, но лучше разумные и интуитивно понятные соглашения) в серьезном проекте будет много. Их имеет смысл документировать.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Свой цикл обработки событий поверх Application.run

Сообщение zub » 20.01.2014 10:07:34

>>В одном потоке нынче далеко не уедешь.
Хз, пока неплохо получалось ехать))

>>А данная схема проста и фактически однопоточна, т.к. одновременно работает только один поток. Т.е. синхронизации дополнительной не нужно.
Вот это и смущает, по факту многопоточности какбы ненадо.

>>Крутить LCL'овский цикл где попало не стоит, он на это не рассчитан.
Тоже верно, хотя проверил, всё работает в win, qt, gtk. Проблема обработкой асинхронных вызовов в месте с текущим событием легко решается если вызывать не Application.QueueAsyncCall а добавлять асинхронные вызовы в свою очередь и перекидывать в Application.AsyncCall после Application.HandleMessage.

Записал переделку SuperProc'ов на потоки в "долгий" фичлист, пока оставлю так как есть
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Свой цикл обработки событий поверх Application.run

Сообщение stanilar » 20.01.2014 17:25:33

Перечитал два раза (не сильно вникая), так и не понял что Вам надо, особенно про синхронность/асинхронность. Без потоков как-то так:

Чтобы запретить повторный вызав, пишем:
Код: Выделить всё
var InWork : boolean
procedure MySuperProc;
begin
  if InWork then Exit;
  InWork := True;
....
  InWork := False;
end;


использовать стэк если пользователь жмет кнопку два раза, и их надо обработать:
Код: Выделить всё
var Stack : integer;
procedure MySuperProc;
begin
  Stack := Stack + 1;
  if Stack > 1 then Exit;
....
  Stack := Stack - 1;
  if Stack > 0 then
  begin
    Stack := Stack - 1;
    MySuperProc;
  end;
end;


На коде со стэком сделать и ввод коодинат(введем две координаты):
Код: Выделить всё
var Stack : integer;
function StartEnter2Point : TPoint;
begin
  Stack := 2;
  try
    while Stack <> 0 do
    case Stack of
      2 : Result.X := MySuperProc;
      1 : Result.Y := MySuperProc;
    end;
    Result := True;
  except
    Result := False;
  end;
end;

function MySuperProc: integer;
begin
  While not Mouse.Down do
  begin
    Application.ProcessMessages;
    if Abort then raise Exception.Create;
  end;
Result := Mouse.Position;
Stack := Stack - 1;
end;


Про синхронность/асинхронность даже не понял для чего Вы хотите ее применить.
stanilar
постоялец
 
Сообщения: 289
Зарегистрирован: 09.03.2010 19:09:02

Re: Свой цикл обработки событий поверх Application.run

Сообщение zub » 20.01.2014 20:03:42

>>Перечитал два раза (не сильно вникая), так и не понял что Вам надо
))Как понятней объяснить незнаю. хочется от классов, методов и событий уйти к обычным процедурам.
Есть графическое приложение, надо наполнять его различными инструментами типа рисования различных примитивов... хочется прийти к некоему простому минимальному стандарту в написании этих инструментов. Грубо говоря 1 инструмент - 1 "плоская" процедура, а не класс с методами. Я выше привел "инструмент" рисования линии - проще помоему уже не придумать и привел анимированную гифку как это всё выглядит в итоге

>> особенно про синхронность/асинхронность
Про синхронность не скажу, а Application.QueueAsyncCall пытался использовать для запуска процедуры после окончания моего "дополнительного" цикла обработки сообщений.

>>Чтобы запретить повторный вызав, пишем:
>>использовать стэк если пользователь жмет кнопку два раза, и их надо обработать:
>>На коде со стэком сделать и ввод коодинат(введем две координаты):
Нет, это не про то. Требуется ОСТАНОВКА выполнения процедуры на время некоторых GUIшных взаимодействий, после получения от гуя необходимого результата, требуется ПРОДОЛЖЕНИЕ выполнения процедуры
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Свой цикл обработки событий поверх Application.run

Сообщение stanilar » 21.01.2014 09:09:23

Отсановка выполнения процедуры произойдет сразу как только вы войдете в цикл
Код: Выделить всё
while true do Application.ProcessMessages.


Application.QueueAsyncCall - а он не остановит цикл обработки всего приложения?
stanilar
постоялец
 
Сообщения: 289
Зарегистрирован: 09.03.2010 19:09:02

Re: Свой цикл обработки событий поверх Application.run

Сообщение zub » 21.01.2014 13:02:48

>>Отсановка выполнения процедуры произойдет сразу как только вы войдете в цикл
да, с этим всё ясно. выполнение остонавливается\продолжается на конструкции вида
Код: Выделить всё
procedure MyApplicationRun
...
while _условиевыхода_ do Application.(Handle|Process)Messages;

HandleMessages и ProcessMessages по сути делают одно и тоже, только в случае ProcessMessages будет стопроцентная загрузка проца, поэтому использую HandleMessages
Проблемы начинаются в случае рекурсивного запуска, т.е. "SuperProc1" уже работает и крутится мой MyApplicationRun, но пользователю понадобилось остановить его выполнение и запустить SuperProc2.
Т.е. нужно выставить соответствующее _условиевыхода_, подождать пока MyApplicationRun завершится и обработка вернется в обычный Application.Run и только потом запускать SuperProc2. Вот тут я и думал что поможет асинхронный запуск SuperProc2, но нифига, помогает только "двойной" асинхронный))
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Свой цикл обработки событий поверх Application.run

Сообщение zub » 23.01.2014 14:31:33

Mirage
>>А данная схема проста и фактически однопоточна, т.к. одновременно работает только один поток. Т.е. синхронизации дополнительной не нужно.
Почитал про потоки - трудности с синхронизацией всетаки будут, т.к. фактически будет двухпоточная схема
1 основной поток программы, ГУЯ и системы управления вторым потоком
2 поток для SuperProc`ов
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Свой цикл обработки событий поверх Application.run

Сообщение Alex2013 » 29.01.2014 21:55:32

Посмотри что я понаписал по проекту MidAsm (Там векторный редактор как утилита нужен) возможно на что-то найдешь для своего проекта ..)


viewtopic.php?p=76024
Alex2013
долгожитель
 
Сообщения: 3145
Зарегистрирован: 03.04.2013 11:59:44

Re: Свой цикл обработки событий поверх Application.run

Сообщение zub » 29.01.2014 22:13:08

>>Посмотри что я понаписал по проекту MidAsm (Там векторный редактор как утилита нужен) возможно на что-то найдешь для своего проекта ..)
Рыться в понаписаном, еще и незная что искать - спасибо.
Лучше просто в 2х словах скажи как решал\планировал решать подобные дела.
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26


Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: Google [Bot] и гости: 231

Рейтинг@Mail.ru