Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: Гурман от Сентябрь 08, 2015, 22:35



Название: (РЕШЕНО) QMouseEvent перестают поступать в QWidget при смене родителя
Отправлено: Гурман от Сентябрь 08, 2015, 22:35
Есть большой главный QWidget, на котором лежат другие QWidget, для которых большой виджет родитель. У тех, которые лежат, есть обработка события QMouseEvent, по которому эти виджеты перемещаются в координатах родителя. Всё хорошо. Но среди детей, есть такие QWidget, которые сами становятся родителями, если какой-то QWidget въехал в их прямоугольник. Назовем их "рамками". То есть, если виджет перемещался по главному, и въехал в рамку, то рамка становится его родителем. А если перемещался и выехал из неё, то родителем снова становится главный виджет. Соответственно, если в рамке есть другие виджеты, то они перемещаются вместе с рамкой. Это всё работает почти хорошо, кроме одного неприятного эффекта - как только я меняю родителя виджету, в него перестают поступать события QMouseEvent. Кнопка мши при этом не отпущена, то есть, как только виджет въезжает в рамку, он перестаёт двигаться за мшой. Если кнопку отпустить, и снова взять виджет, уже лежащий в рамке и являющийся её потомком, то он начинает получать события мши и двигается. Двигается в рамке, пока он не выйдет за её границы, и у него родитель не сменится на главный большой виджет. Тогда опять события в него перестают поступать и он замирает.

Очевидно, это происходит вот почему:

Цитировать
Note: The widget becomes invisible as part of changing its parent, even if it was previously visible. You must call show() to make the widget visible again.

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


Название: Re: QMouseEvent перестают поступать в QWidget при смене родителя
Отправлено: Old от Сентябрь 09, 2015, 06:56
Так может не стоит менять родителя в процессе перемещения? А делать это только в момент отпускания кнопки мыши.


Название: Re: QMouseEvent перестают поступать в QWidget при смене родителя
Отправлено: Igors от Сентябрь 09, 2015, 09:16
Менять родителя на ходу - затея неудачная. Если же вот "надо - и все тут" - перекрывайте QApplcation::notify (или вешайте фильтр на QApplication) и там уже принудительно посылайте кому надо событие "мши"  :).


Название: Re: QMouseEvent перестают поступать в QWidget при смене родителя
Отправлено: GreatSnake от Сентябрь 09, 2015, 10:51
По-хорошему drag'n'drop-ом должен заниматься родительский виджет, т.е. некий менеджер, а не сам drag-объект.


Название: Re: QMouseEvent перестают поступать в QWidget при смене родителя
Отправлено: Гурман от Сентябрь 09, 2015, 15:33
По-хорошему drag'n'drop-ом должен заниматься родительский виджет, т.е. некий менеджер, а не сам drag-объект.

Сначала так и было сделано, менеджером был "главный" виджет, по которому это всё ездит. Но получилось гораздо более громоздко и очень не удобно. Фишка в том, что каждый из виджетов, как главный, так и рамки, так и обычные виджеты - это всё плагины. Да ещё и динамически загружаемые... ;D  Реализация оказалась значительно проще, когда сам перетаскиваемый объект находит, кто будет его новый родитель. Фишка-2 еще в том, что тут есть иерархия - "рамки" - это тоже виджеты, и они тоже перетаскиваются (код тот же самый, что и для других виджетов), и тоже могут быть детьми других "рамок" с неограниченным уровнем вложенности. Когда всё делается самим перетаскиваемым виджетом, общее решение получается значительно проще и компактнее - главному виджету вообще нет нужды разбираться в иерархии детей-родителей, он про неё ничего не знает. Он даже не знает, что по нему что-то перетаскивают - ему это не нужно.

Если же вот "надо - и все тут" - перекрывайте QApplcation::notify (или вешайте фильтр на QApplication) и там уже принудительно посылайте кому надо событие "мши"  :).

Не... Так можно было бы, если бы это было монолитное приложение. Но так как все эти получатели QMouseEvent находятся в плагинах, количество и состав которых заранее не известны, реализация была бы муторной. Поэтому:

Так может не стоит менять родителя в процессе перемещения? А делать это только в момент отпускания кнопки мыши.

Похоже, придётся переделать на такой вариант. Были некоторые заморочки, из-за которых менять родителя было удобнее при перетаскивании. От некоторых возможностей придётся, увы, отказаться.


Название: Re: QMouseEvent перестают поступать в QWidget при смене родителя
Отправлено: Гурман от Сентябрь 09, 2015, 16:24
Не... не получается по-простому менять родителя при отпускании мыши.

Фишка в том, что виджет надо не только затаскивать внутрь рамки, но и вытаскивать из неё. А если родителем является рамка, то виджет перестает отображаться, когда выезжает из рамки. Чтобы этого не было, надо при начале перетаскивания менять родителя снова с рамки на главный виджет. Но при этом... виджет закрывается и перестаёт получать события. Замкнутый круг.

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



Название: Re: QMouseEvent перестают поступать в QWidget при смене родителя
Отправлено: GreatSnake от Сентябрь 09, 2015, 16:34
Чтобы избежать всех этих заморочек задействуй QDrag.


Название: Re: QMouseEvent перестают поступать в QWidget при смене родителя
Отправлено: Гурман от Сентябрь 09, 2015, 16:50
Чтобы избежать всех этих заморочек задействуй QDrag.

Не, это ещё более громоздко и совершенно не нужно. Там маймить надо, размаймливать, с иконками возиться... В виджетах-рамках придётся делать ловушку, на главном виджете придётся делать ловушку. Куча не нужного кода. QDrag имеет смысл, когда надо тащить драг куда-то наружу, а тут перетаскивание только в границах главного виджета. Даже ограничитель сделан, чтобы за границы нельзя было вытащить

Решение очень близко - экспериментирую с grabMouse(), один раз получилось, но это было какое-то совпадение сигналов, повторить пока не удаётся.


Название: Re: (РЕШЕНО) QMouseEvent перестают поступать в QWidget при смене родителя
Отправлено: Гурман от Сентябрь 09, 2015, 16:55
А вообще-то с grabMouse() после show() при смене родителя работает, надо только releaseMouse() делать в обработчике mouseReleaseEvent(QMouseEvent *event). Правда иногда почему-то при grabMouse() перепрыгивает фокус на другие приложения. Не могу уловить закономерность, это довольно редко. Возможно это дырка виндозы.


Название: Re: QMouseEvent перестают поступать в QWidget при смене родителя
Отправлено: Igors от Сентябрь 10, 2015, 08:36
Не...
Не...
Неконструктивный подход, Вы немедленно начинаете искать изъяны в предложенном решении. А поскольку Вы под впечатлением своих мыслей - найти очень легко. Поэтому дело кончается упрямым "не" :) Попробуйте найти какое-то рациональное зерно. Ну например

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

Да, и если Вы все еще на Qt4, то не факт что Ваша проблема воспроизведется в пятерке. Где кстати возможен удобный перехватчик на уровне окна


Название: Re: QMouseEvent перестают поступать в QWidget при смене родителя
Отправлено: Гурман от Сентябрь 10, 2015, 12:28
Вы немедленно начинаете искать изъяны в предложенном решении.

А вы нет? Тогда я бы вас на работу не взял... Это обязательное действие, как часть конструктивного подхода. В системном программировании вообще второй обязательный шаг - поиск изъянов в предполагаемом решении, не важно, откуда оно взялось, извне заимствовано или самостоятельно придумано. Лучше найти их раньше, чем биться головой и переделывать потом. Это не всегда получается сделать, но это делать всегда надо. Я и от подчинённых всегда этого требую.

А Вас интересует только виджет захвативший "мшу" :)

По отношению к обработке мши - да. Если имелась в виду групповая операция над несколькими виджетами, то к обработке событий мши она никакого отношения не имеет, сделана совершенно отдельно и отлично работает. С поддержкой undo и redo.

если Вы все еще на Qt4, то не факт что Ваша проблема воспроизведется в пятерке

Не факт, но в данном конкретном случае это совершенно не важно. Окно виджета также точно будет терять ввод, поскольку и в Qt 5.5 виджет также становится невидим при вызове setParent() - это описано в документации (http://doc.qt.io/qt-5/qwidget.html#setParent). Возможно только, что после show() виджет станет получать события мши без необходимости вызова grabMouse(). Но grabMouse() просто ничего не изменит (в Qt 5.5 он тоже есть). Или если изменит, его можно будет удалить. Но скорее всего, всё это будет работать точно также, как в 4.х.

А вообще вопрос исчерпан - всё уже работает, как требуется.


Название: Re: QMouseEvent перестают поступать в QWidget при смене родителя
Отправлено: GreatSnake от Сентябрь 10, 2015, 12:32
А вообще вопрос исчерпан - всё уже работает, как требуется.
Ну да, до тех пор пока qt-ишники не вспомнят, что забыли вызвать releaseMouse() в hide()  ;D


Название: Re: QMouseEvent перестают поступать в QWidget при смене родителя
Отправлено: Гурман от Сентябрь 10, 2015, 12:39
А вообще вопрос исчерпан - всё уже работает, как требуется.
Ну да, до тех пор пока qt-ишники не вспомнят, что забыли вызвать releaseMouse() в hide()  ;D

Даже если вспомнят - это ничего не изменит. Ну также точно будет виджет мшу перехватывать, и также точно будет работать.