Освобождение и блокировка событий мыши между несколькими виджетами

Прямо в точку

У меня есть несколько виджетов QLabel внутри QVBoxLayout.

Когда я нажимаю левую кнопку мыши и начинаю перетаскивать (на самом деле я не перетаскиваю никаких виджетов, я перемещаю курсор мыши только при нажатии левой кнопки мыши), один из QLabels получает mousePressEvent, и все последующие mouseMoveEvents отправляются в него. QLabel, получивший mousePressEvent, даже если курсор мыши вышел за пределы ограничивающей рамки этого QLabel.

Это не работает для меня. Я хочу, чтобы исходный QLabel, который первым получил mousePressEvent, перестал получать события мыши, если событие произошло за пределами ограничивающего прямоугольника QLabel. Если это произойдет, я хочу, чтобы другой QLabel (туда, куда переместился курсор) начал обрабатывать события мыши.

Установка для отслеживания мыши значения true не помогла, «блокировка» все равно происходит.

Есть предложения? Спасибо <3


Немного предыстории

На самом деле я реализую механизм выбора текста для своего приложения для просмотра документов.

Растровые изображения получаются со страниц документа и устанавливаются как растровые изображения QLabels. Когда происходит событие mousePressEvent, выбор начинается и продолжается в следующих событиях onMouseMove. Но когда я хочу выбрать текст на нескольких страницах и перемещаю курсор мыши на другую QLabel, события по-прежнему отправляются на эту исходную QLabel. В этом проблема. У меня прекрасно функционирующий механизм выбора для одной страницы. Проблема возникла только сейчас, когда я понял, что события мыши работают в Qt таким образом.


УПД

Я записал короткое 1-минутное видео с описанием проблемы - https://thewikihow.com/video_m00dV4s1Am8

Вам придется нелегко, потому что поведение, которое вы видите, соответствует ожиданиям людей. Когда пользователь нажимает указатель мыши на виджет-кнопку (или другой подобный виджет), он или она явно передает все последующие события мыши этому виджету до тех пор, пока кнопка мыши не будет отпущена. Это базовый UX. Но похоже, что вы маскируете виджет кнопки под другой тип элемента управления. Не могли бы вы рассказать подробнее о том, чего вы пытаетесь достичь визуально?

Dúthomhas 19.08.2024 23:59

Если вам нужно, чтобы события мыши обрабатывались высшим органом власти (т. е. виджетом, которому принадлежат объекты QLabel), одним из подходов может быть информирование об этом вашего подкласса QLabel. Вместо того, чтобы напрямую обрабатывать события мыши, заставьте ее реагировать на них, посылая сигналы. Они подключаются к слотам виджета-владельца, который затем может управлять логикой мыши для документа в целом.

paddy 19.08.2024 23:59

Другой подход может заключаться в использовании фильтрации событий и, следовательно, в отслеживании момента нажатия мыши (сохранение ссылки на объект и положение мыши относительно этого объекта) и управлении любым движением мыши до тех пор, пока кнопка не будет отпущена. Тем не менее, как отмечалось выше, необходимо использовать «высшие полномочия»: поскольку выбор может охватывать более чем один объект, вы не можете позволить этому объекту управлять им единолично. Это означает, что вышеуказанный фильтр событий и управление должны быть установлены на «менеджере выбора» (который, вероятно, также является «менеджером документов»).

musicamante 20.08.2024 00:18

Честно говоря, сделать то, что вы пытались, не невозможно. Но поскольку, как уже было сказано, принятое нажатие мыши сделает объект захватом мыши, это будет означать, что вам потребуется реализовать дополнительный механизм для запроса других «братьев и сестер» или, возможно, попытки отправить им переназначенное событие: ненужные сложности это будет еще более подвержено ошибкам, если вы не будете осторожны с реализацией, которая может стать уродливой и сложной в обслуживании или отладке. Рассмотрите правильную иерархию объектов: реальный контекст любого выделения — это не его метки, поэтому им необходимо управлять на уровне родителя/документа.

musicamante 20.08.2024 00:27

@Dúthomдобавил ссылку на видео, показывающее, о чем я говорю. Я согласен, это ожидаемое поведение, и для меня это вполне логично, я не говорю, что это недостатки Qt.

Vlad Ponomarev 20.08.2024 03:17

@musicamante Моей первой мыслью было использование родительского элемента всех QLabels, однако я обнаружил, что это очень неэффективно, поскольку в этом случае мне придется перебирать все виджеты (QLabels) в макете и определять относительные координаты между виджетами, чтобы определить куда переместился курсор QLabel. Но это все еще вариант.

Vlad Ponomarev 20.08.2024 03:38

@paddy мой предыдущий ответ связан с твоим комментарием

Vlad Ponomarev 20.08.2024 03:40

@VladPonomarev При всем уважении, я вынужден не согласиться, поскольку это не «вариант»: вероятно, это вариант. Работа со сложными объектными структурами почти всегда увеличивает сложность и эффективность вычислений. Если реализация будет выполнена надлежащим образом, это может решить некоторые проблемы эффективности. Например, вам не обязательно постоянно перебирать все виджеты: если вы не учитываете «перетаскивание выделений» (выборы, которые могут прокручивать содержимое), вполне достаточно знать относительную геометрию виджетов, тогда вам просто нужно проверить если позиция находится в пределах их прямоугольника.

musicamante 20.08.2024 03:46

@VladPonomarev относительно «Мне придется перебирать все виджеты...» — почему? Вы уже получаете события мыши, а оконная система уже более интеллектуальна, чем просто линейный поиск во всем. Таким образом, если ваш виджет излучает сигнал, потому что на него наведена мышь, владелец уже знает, происходит ли какое-то перетаскивание (или что-то еще), и, следовательно, нужно ли что-то с этим делать. Если вы не хотите рассылать спам-сигналы, вместо этого вы можете использовать события входа/выхода мыши.

paddy 20.08.2024 03:52

@VladPonomarev У вас относительно сложная структура объектов, стремление к абсолютной эффективности — это химера. Вам необходимо сбалансировать эффективность возможных вычислений ЦП и реализации кода. Первая цель — найти правильный способ, который концептуально и логически имеет смысл, учитывает иерархию объектов и позволяет читать код, который можно соответствующим образом отладить. С этого момента вы можете в конечном итоге провести дальнейшую оптимизацию, если обнаружите, что реализация недостаточно эффективна и эти оптимизации ничего не нарушают.

musicamante 20.08.2024 03:52

Ах, хорошее видео. Да, единственный способ решить эту проблему — создать невидимый виджет верхнего уровня, который будет посредником между вашим пользователем и отображаемыми виджетами — эффективно перехватывая взаимодействия мыши и выборочно связывая их с соответствующим объектом ниже. Прошло некоторое время с тех пор, как я играл с Qt, поэтому у меня нет немедленного предложения о том, как это лучше всего реализовать, но слоты и система сигналов Qt очень полны, и у IIRC есть механизм, с помощью которого это можно сделать.

Dúthomhas 20.08.2024 03:58
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
11
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я достиг того, что мне нужно, выполнив следующие шаги:

  • Виджет (в моем случае QLabel со страницей pdf) получает mousePressEvent и обрабатывает последующие mouseMoveEvents.
  • Он принимает событие mouseMoveEvent только в том случае, если оно произошло внутри ограничивающей рамки виджета.
  • Если это произошло за пределами ограничивающей рамки виджета, оно игнорируется, и событие отправляется родительскому виджету, который содержит остальные виджеты (PagesContainer в моем случае является родительским, он содержит все QLabels со страницами PDF).
  • Затем родительский виджет определяет относительные координаты, используя MapFromGlobal для получения координат относительно своей системы координат.
  • Затем он пытается найти виджет в его макете по указанным координатам, используя childAt.
  • Если виджет был найден, он создает новый mouseMoveEvent со всеми параметрами, скопированными из исходного mouseMoveEvent, кроме координат - они преобразуются еще раз, но на этот раз в систему координат найденного виджета, и публикует это событие.

Вот видео, демонстрирующее решение: https://thewikihow.com/video_zHebZRYnF5Y?si=1zKBQs7JuEd2sjpp

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

Рад, что у вас все получилось!

Dúthomhas 21.08.2024 15:41

Другие вопросы по теме