Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: Igors от Сентябрь 18, 2020, 13:17



Название: Local Event Loop
Отправлено: Igors от Сентябрь 18, 2020, 13:17
Добрый день

Хочу такой ф-ционал (псевдокод)
Код
C++ (Qt)
while (IsMouseDown()) {
 QPoint pt = GetMousePosition();
 if (pt != oldPt)
  DragSomething(pt);
 oldPt = pt;
}
 
Ну конечно цивильно, а не жрать проц в IsMouseDown. Какие есть решения?

Спасибо


Название: Re: Local Event Loop
Отправлено: Racheengel от Сентябрь 18, 2020, 17:54
например добавить

qApp->processEvents();
QThread::msleep(100);


Название: Re: Local Event Loop
Отправлено: Igors от Сентябрь 18, 2020, 20:56
например добавить

qApp->processEvents();
QThread::msleep(100);
"Please be more concrete". Что должно быть в IsMouseDown?


Название: Re: Local Event Loop
Отправлено: RedDog от Сентябрь 18, 2020, 20:59
локальный  QEventLoop нельзя подписать на moseDown ?


Название: Re: Local Event Loop
Отправлено: Igors от Сентябрь 18, 2020, 22:44
локальный  QEventLoop нельзя подписать на moseDown ?
"Прошу исполнить"


Название: Re: Local Event Loop
Отправлено: Racheengel от Сентябрь 20, 2020, 01:11
но вообще на mouseMove надо реагировать.
передвинулась мышь? проверили, зажата ли кнопка. если да - что-то драгаем.


Название: Re: Local Event Loop
Отправлено: Igors от Сентябрь 20, 2020, 10:37
но вообще на mouseMove надо реагировать.
передвинулась мышь? проверили, зажата ли кнопка. если да - что-то драгаем.
Это размазывает код между событиями, что далеко не всегда удобно, особенно при обильных и/или сложных драгах. Гораздо приятнее написать весь драг в одном месте, от начала до конца. Разве это запрещено? Нарушает какие-то прынцыпы? Удивляет неуверенность ответов, ведь гуй - Ваше родное, я же не о матрицах спрашиваю :) Почему не так (псевдокод)

Код
C++ (Qt)
extern bool isDragging = false;
 
bool IsMouseStillDown( void )
{
  if (!isDragging) {
   qApp->installEventFilter(&myFilter);
   isDragging = true;
  }
 
  qApp->processEvents(QEventLoop::WaitForMoreEvents);
 
  if (!isDragging)
   qApp->removeEventFilter(&myFilter);
 
 return isDragging;
}
 
Ну и в myFilter жрать события мыша пока не придет нужное MouseRelease, тогда сбросить флажок isDragging.

Хорошо ли это, какие здесь проблемы?


Название: Re: Local Event Loop
Отправлено: Авварон от Сентябрь 20, 2020, 11:05
Igors придумал корутины


Название: Re: Local Event Loop
Отправлено: Igors от Сентябрь 20, 2020, 11:37
Igors придумал корутины
Ну мыслящий человек обязан что-то придумывать :) Но корутины-то здесь причем? Открыл гугл
Цитировать
Корутины — это новый способ написания асинхронного, неблокирующего кода.
В чем его новизна - хз, просветите. Но никакой асинхронности здесь нет.


Название: Re: Local Event Loop
Отправлено: Racheengel от Сентябрь 21, 2020, 09:46
Хорошо ли это, какие здесь проблемы?

Хорошего ничего не вижу, кроме недостатков.
Пытаясь уйти от проблемы "размазывания кода", Вы создаёте несколько новых проблем.
1. Появляется второй ивент-луп в том же гуй-потоке.
2. Создается дополнительный фильтр событий, которому всё равно нужен отдельный обработчик.
3. Откуда будет зваться IsMouseStillDown() ?
4. Опять же, нужен обработчик логики драга. Что делать, если начали драг с зажатым Ctrl? Или если поведение логики дропа меняется, в зависимости от того, где отпустили мыш?

В общем без дополнительных методов не обойтись, ну а тогда в чём выигрыш данного подхода?

Вообще тема драг-дропа довольно гнилая с точки зрения "прынцыпов", потому что, как правило, приходится в любом случае либо городить огромную state-machine, либо много драг-хендлеров.

Я бы сделал вместо своего ивент лупа базовую обработку типа "нажал (mousePress) - подвинул мыш (mouseMove) - отпустил (mouseRelease)". А уже из mouseMove() вызывать что то типа void onDragging(CMyObject* draggedObject). А уже из данного метода, в зависимости от того, кем является draggedObject (и есть ли он вообще - вдруг юзер решил рабочее поле драгать? например, для выделения или скролла) - вызывать соответсвующие обработчики драгов.

"я так делал, так работает" (с) я


Название: Re: Local Event Loop
Отправлено: Igors от Сентябрь 21, 2020, 11:44
Хорошего ничего не вижу, кроме недостатков.

Пытаясь уйти от проблемы "размазывания кода", Вы создаёте несколько новых проблем.
1. Появляется второй ивент-луп в том же гуй-потоке.
2. Создается дополнительный фильтр событий, которому всё равно нужен отдельный обработчик.
3. Откуда будет зваться IsMouseStillDown() ?
4. Опять же, нужен обработчик логики драга. Что делать, если начали драг с зажатым Ctrl? Или если поведение логики дропа меняется, в зависимости от того, где отпустили мыш?

В общем без дополнительных методов не обойтись, ну а тогда в чём выигрыш данного подхода?
Давайте "говорить об одном и том же" (первый закон логики). Я предложил обсудить проблемы реализации IsMouseStillDown. Да, они непросты, не исключено что именно поэтому желаемый ф-ционал недостижим. Вы же утверждаете что данный ф-ционал вообще неполноценен и не дает выгод. Это не так, выгоды как раз очевидны. Во-первых, IsMouseStillDown пишется один раз на все случаи жизни - уже хорошо. Зовется она при всяком драге, обычно из обработчика MousePressed, тут возможно не понял что Вы имели ввиду. И уж тем более не понял последнего пункта

4. Опять же, нужен обработчик логики драга. Что делать, если начали драг с зажатым Ctrl? Или если поведение логики дропа меняется, в зависимости от того, где отпустили мыш?
Тут как раз все прекрасно. Нужны доп клавиши - просто снял их перед циклом while или или внутри него или передал аргументом от исходного MousePressed. Изменился target под мышей -ну зафиксировали это в локальной объявленной до цикла while (напоминает выгоды лямбды). Наоборот, в событийной схеме все это пусть несложно, но очень хлопотливо.
 
Я совсем не предлагал реализовывать какую-то логику драга в IsMouseStillDown, она просто возвращает false (= мыш отпущен) или true (= мыш зажат, но что-то случилось) - и все 

"я так делал, так работает" (с) я
У меня тоже многое (или почти все) работает, но далеко не всем я могу гордиться  :)


Название: Re: Local Event Loop
Отправлено: Igors от Сентябрь 21, 2020, 11:54
Да, и забыл пожалуй главное  :) "Модальность". Напр драгается нечто по MouseMoved - но это никак не запрещает др событий, глупый юзвеоь может напр закрыть окно и.т.п. Женщины в таких случаях советуют "А зачем Вы это нажимали? Не нужно было этого делать"  :)


Название: Re: Local Event Loop
Отправлено: Racheengel от Сентябрь 21, 2020, 12:46
Да, и забыл пожалуй главное  :) "Модальность". Напр драгается нечто по MouseMoved - но это никак не запрещает др событий, глупый юзвеоь может напр закрыть окно и.т.п. Женщины в таких случаях советуют "А зачем Вы это нажимали? Не нужно было этого делать"  :)

Ну глупый юзверь будет драгом занят, во время драга мыш зажат, он физически на закрытие окна кликнуть не сможет.


Название: Re: Local Event Loop
Отправлено: Old от Сентябрь 21, 2020, 12:51
Зато на Alt-F4 нажать сможет легко. :)


Название: Re: Local Event Loop
Отправлено: Racheengel от Сентябрь 21, 2020, 13:03
Получается следующая петрушка:

если делать на "стандартных" мышесобытиях, то выглядеть будет примерно так:

Main Thread (GUI)
"Main Event Loop":

Код:
::onMousePressed(MouseEvent *e):
   m_clickPosition = e->pos();
   m_objectDragged = findObjectAt(m_clickPosition);
   m_isDrag = true;

::onMouseReleased(MouseEvent *e):
   hangleRelease(e->pos(), m_objectDragged);
   m_isDrag = false;

::onMouseMoved(MouseEvent *e):
   if (m_isDrag)
     handleDrag(e->pos(), m_objectDragged)

"Свои имплементации":
hangleDrag() - тут тянем объект (меняем координаты whatever)
hangleRelease() - тут дропнули объект



А если на "локальном эвентлупе":

Main Thread (GUI)
"Main Event Loop":

Код:
::onMousePressed(MouseEvent *e)
{
   m_clickPosition = e->pos();
   m_objectDragged = findObjectAt(m_clickPosition);
   m_isDrag = true;

  m_oldPosition = e->pos();

  installFilter(this);

   while (m_isDrag)
   {
      if (m_oldPosition != m_lastEvent ->pos())
      {
         m_oldPosition = m_lastEvent ->pos();
         hangleDrag(m_lastEvent , m_oldPosition);
      }

      qApp->processEvents();
      msleep(...);
   }

  uninstallFilter(this);

  hangleRelease(m_lastEvent ->pos(), m_objectDragged);
}


::onMouseReleased(MouseEvent *e)
{
  m_lastEvent = e;
  m_isDrag = false;
}


::onMouseMoved(MouseEvent *e)
{
   if (m_isDrag)
     m_lastEvent = e;
}

То есть стало больше кода, появился фильтр эвентов, промежуточные переменные, и всё равно надо имплементировать hangleDrag() и hangleRelease()...
Не понятно, где мы выигрываем ???


Название: Re: Local Event Loop
Отправлено: Racheengel от Сентябрь 21, 2020, 13:06
Зато на Alt-F4 нажать сможет легко. :)

Похоже, QGraphicsScene имеет "защиту" от этого во время драга. По крайней мере, у меня ничего не происходит, окно не закрывается.


Название: Re: Local Event Loop
Отправлено: Igors от Сентябрь 21, 2020, 13:39
То есть стало больше кода, появился фильтр эвентов, промежуточные переменные, и всё равно надо имплементировать hangleDrag() и hangleRelease()...
Не понятно, где мы выигрываем ???
А зачем Вы нарисовали onMouseReleased, onMouseMoved,  im_isDrag, m_lastEvent и.т.п. ? Вся эта ботва становится ненужной если у нас есть козырная IsMouseStillDown, и мы спокойно сосредоточим весь ф-ционал в одном hangleDrag, хотя бы так
Код
C++ (Qt)
void MyWindow::MouseListDown( QMouseEvent * e )
{
  if (!HandleListItemDrag(e))
   DoListSelection(e);
}
 


Название: Re: Local Event Loop
Отправлено: Racheengel от Сентябрь 21, 2020, 15:52
А как реализовать драг, не принимая ивенты от мыши?
Никак.
И это надо делать где то, хоть во внутреннем ивент-лупе, хоть во внешнем.
Кода меньше не станет.
Поэтому я не понимаю смысл "огород городить".


Название: Re: Local Event Loop
Отправлено: Igors от Сентябрь 21, 2020, 17:09
А как реализовать драг, не принимая ивенты от мыши?
Никак.
Как сказано в стартовом посте. GetMousePosition - не проблема, годится хотя бы QCursor::pos. Ну а можно и "развитОй социализм", напр
Код
C++ (Qt)
while (IsMouseStillDown()) {
 while (GetLocalEvent(e)) {
   switch (e->type()) {
     case QEvent::KeyPressed:
       if (CheckCancel(e))     // drag aborted ?
        return false;
   }
 }
...
}
 
И это надо делать где то, хоть во внутреннем ивент-лупе, хоть во внешнем.
Кода меньше не станет.
Ну вот первая попавшаяся m_clickPosition. Одно дело когда это локальная переменная в методе - о ней можно просто забыть. И совсем другое когда это член класса за которым нужно следить в неск методах.

Поэтому я не понимаю смысл "огород городить".
Да все Вы поняли, по глазам вижу  :)

Вот правда реализовать IsMouseStillDown совсем не просто.


Название: Re: Local Event Loop
Отправлено: Igors от Сентябрь 22, 2020, 14:34
И притих.. :) Собсно реакция абсолютно нормальная и 100% естественная
Цитировать
А почему так никто не делает (я не видел)? Выходит все дураки а он один такой умный?
И сразу пошли аргументы "в одну калитку", мол, да тут ничего хорошего и нет и все такое.. :) А между тем вторичный цикл событий является (по меньшей мере) стандартным и хорошо известным приемом, почему я не могу его юзать в Qt ???

Вернемся к теме, как же реализовать IsMouseStillDown? Казалось бы, что тут хитрого - да жрать в фильтре все события мыша и клавы (возвращать true) пока не увидим MouseReleased. К сожалению, так не выходит. Беда в том что processEvents обрабатывает "пачку" событий, и MouseReleased может быть совсем не последним в этой пачке,  напр

- UpdateEvent
- MouseReleased
- MouseDoubleClick  // резвый юзер успел!

И что делать с последним MouseDoubleClick ? Сожрать нельзя - тогда double-click на айтем не будет открывать окно (причем подленько, "иногда"). И пропустить низзя - ведь тогда обработка будет вызвана в теле драга (пусть там ничего пока не драгнулось, но все равно из него надо выйти).

И шо делать  ???


Название: Re: Local Event Loop
Отправлено: Racheengel от Сентябрь 22, 2020, 15:42
А между тем вторичный цикл событий является (по меньшей мере) стандартным и хорошо известным приемом, почему я не могу его юзать в Qt ???

Кому известным? Где является?

Цитировать
И шо делать  ???

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


Название: Re: Local Event Loop
Отправлено: Igors от Сентябрь 23, 2020, 12:43
Кому известным? Где является?
Мне известным со времен Вындоуз 3.0, Вам - не знаю. Используется везде, в том числе и в ядре Qt

А именно, использовать стандартный кутишный подход. Да, он не прозрачен.
А почему Вы думаете что я его не использовал? Но мне надоело с ним мучиться. Я хочу нормальные методы, по одному на драг, или даже глобальные ф-ции и вызывать их из разных окон. А не дурацкую "систему рычагов" с кучей переменных класса и бесконечными проверками state. Для этого по сути нужно написать одну ф-цию

Но "покажите мне другие работающие".
Так я и показал в стартовом посте :)

Перестать беспокоиться и начать жить  :)
Все-таки хочется быть молодым :)


Название: Re: Local Event Loop
Отправлено: Racheengel от Сентябрь 23, 2020, 14:24
И что делать с последним MouseDoubleClick ?

Но как раз DoubleClick не может прийти во время драга (если, конечно, драг не через правую кнопку мыши реализуется, что тоже может иметь смысл).
Это гарантированно реализовано на уровне самого фреймворка.

Я, кстати, не так давно намучился с кейсом, когда юзер во время драга ухитрялся нажимать правую кнопку.
Тогда вся чудо-система событий начинала сходить с ума, Qt пыталась одновременно и контексное меню показать, и драг продолжить.
Единственное разумное решение в этом случае - игнорировать ВСЕ события от кнопок мыши, кроме левой. Драг должен быть эксклюзивно блокирующей процедурой с т.з. действий юзера.



Название: Re: Local Event Loop
Отправлено: Igors от Сентябрь 23, 2020, 17:38
Но как раз DoubleClick не может прийти во время драга (если, конечно, драг не через правую кнопку мыши реализуется, что тоже может иметь смысл).
Это гарантированно реализовано на уровне самого фреймворка.
Приходит DoubleClick, сценарий такой:

- случился MousePress, приложение готовит драг (что может занять некоторое время) и вызывает processEvents

- но юзер ничего драгать не хотел, он отпустил мыша и снова его нажал, т.е. "двойной щелчок мыши" (боже как идиотски это звучит). Прочем это случилось ДО ТОГО как приложение вызвало processEvents

- теперь в processEvents сначала придут MouseReleased (напр для окна, потом для виджета) - и их надо пропускать, иначе будут неприятности (залипшие кнопари и.т.п.)

- но упр-е еще не вернулось из processEvents, и следующим придет DoubleClick


Название: Re: Local Event Loop
Отправлено: Racheengel от Сентябрь 24, 2020, 12:05
- случился MousePress, приложение готовит драг (что может занять некоторое время) и вызывает processEvents

Вот в этом и проблема. Драг надо начинать не по правому нажатию, а по первому движению после правого нажатия.


Название: Re: Local Event Loop
Отправлено: Igors от Сентябрь 24, 2020, 12:48
Вот в этом и проблема. Драг надо начинать не по правому нажатию, а по первому движению после правого нажатия.
У меня обычно по левой кнопке, правая contextual, но это не принципиально. Да, точнее/культурнее сначала дождаться "first motion"
Код
C++ (Qt)
if (!IsMouseMoved()) return false
while (IsMoiseStillDown()) {
...
}
Разница между этими двумя ф-циями лишь в ожидаемом событии (MouseMoved и MoiseReleased). Но от проблемы DoubleClick это не спасает, уже по MousePressed я должен что-то подрисовать (показывая "драг возможен") и сколько времени это займет - хз

Есть такая фишка, казалось бы "то что надо"
Цитировать
void QAbstractEventDispatcher::interrupt()
Interrupts event dispatching. The event dispatcher will return from processEvents() as soon as possible.
Но увы, она совсем не гарантирует "по одной не ошибешься", DoubleClick приходит в том же вызове processEvents


Название: Re: Local Event Loop
Отправлено: Racheengel от Сентябрь 24, 2020, 14:03
Если пришел дабл клик - надо игнорировать начало драга, т.к. левая кнопь не зажата уже.


Название: Re: Local Event Loop
Отправлено: Igors от Сентябрь 25, 2020, 14:42
Если пришел дабл клик - надо игнорировать начало драга, т.к. левая кнопь не зажата уже.
Не очень ясно как "игнорировать начало драга", ведь он уже "в процессе". Да и вообще обсуждение как-то выдохлось  :'(

А помните давеча Вы тему создали, что-то про технологии. Как молодые люди блистали своими обширными познаниями! А тут победить несчастный дабл клик - и их всех как ветром сдуло, остался один собеседник, вероятно моего возраста. Вот уж неделю жду, ну кто же предложит очевидное решение что я реализовал. А узнать что-то (для себя) новое - уж и не помышляю :'(


Название: Re: Local Event Loop
Отправлено: tux от Сентябрь 25, 2020, 15:21
Если пришел дабл клик - надо игнорировать начало драга, т.к. левая кнопь не зажата уже.
Ежели он пришёл, то значить кнопка ужо отпушшена. А, ежели так, то уже пришёл дроп. :)


Название: Re: Local Event Loop
Отправлено: Racheengel от Сентябрь 25, 2020, 19:07
А тут победить несчастный дабл клик - и их всех как ветром сдуло, остался один собеседник, вероятно моего возраста.

Ну за Ваш возраст не знаю, а мне еще лет 25 и на пенсию :)

А дабл клик - "дело тонкое". Много геморроя с такими решениями, ибо много состояний. Тут либо "юзать что фреймворк дал", либо полностью свою логику писать. Только не миксить одно с другим, боже упаси...

Вот уж неделю жду, ну кто же предложит очевидное решение что я реализовал. А узнать что-то (для себя) новое - уж и не помышляю :'(

Не знаю, для меня "очевидным" было решение на мышиных эвентах. "Работает же" :)


Название: Re: Local Event Loop
Отправлено: Igors от Сентябрь 26, 2020, 12:59
Ежели он пришёл, то значить кнопка ужо отпушшена. А, ежели так, то уже пришёл дроп. :)
Пришло (уже) MouseReleased, но в предлагаемой схеме (стартовый пост) дроп не выполнялся, мы все еще сидим в processEvents

Тут либо "юзать что фреймворк дал", либо полностью свою логику писать. Только не миксить одно с другим, боже упаси...
А что собсно "фреймворк дал" ??? Разве есть какие-то указания (прямые или хотя бы косвенные) что драг надо делать на базовых событиях мыша? Мне ни о чем таком не известно. Очевидно что на базовых любой драг сделать можно, но столь же очевидно что этот путь самый примитивный и трудоемкий

А дабл клик - "дело тонкое". Много геморроя с такими решениями, ибо много состояний.
"Состояния" (если мы говорим об одном и том же) плодятся именно вследствие разбросанности ф-ционала драга по 3-4 мышиным событиям.

Не знаю, для меня "очевидным" было решение на мышиных эвентах. "Работает же"
А переписывать имеющийся рабочий код никто не заставляет. Предложенную схему можно юзать для новых драгов.