У меня есть несколько виджетов 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
Если вам нужно, чтобы события мыши обрабатывались высшим органом власти (т. е. виджетом, которому принадлежат объекты QLabel), одним из подходов может быть информирование об этом вашего подкласса QLabel. Вместо того, чтобы напрямую обрабатывать события мыши, заставьте ее реагировать на них, посылая сигналы. Они подключаются к слотам виджета-владельца, который затем может управлять логикой мыши для документа в целом.
Другой подход может заключаться в использовании фильтрации событий и, следовательно, в отслеживании момента нажатия мыши (сохранение ссылки на объект и положение мыши относительно этого объекта) и управлении любым движением мыши до тех пор, пока кнопка не будет отпущена. Тем не менее, как отмечалось выше, необходимо использовать «высшие полномочия»: поскольку выбор может охватывать более чем один объект, вы не можете позволить этому объекту управлять им единолично. Это означает, что вышеуказанный фильтр событий и управление должны быть установлены на «менеджере выбора» (который, вероятно, также является «менеджером документов»).
Честно говоря, сделать то, что вы пытались, не невозможно. Но поскольку, как уже было сказано, принятое нажатие мыши сделает объект захватом мыши, это будет означать, что вам потребуется реализовать дополнительный механизм для запроса других «братьев и сестер» или, возможно, попытки отправить им переназначенное событие: ненужные сложности это будет еще более подвержено ошибкам, если вы не будете осторожны с реализацией, которая может стать уродливой и сложной в обслуживании или отладке. Рассмотрите правильную иерархию объектов: реальный контекст любого выделения — это не его метки, поэтому им необходимо управлять на уровне родителя/документа.
@Dúthomдобавил ссылку на видео, показывающее, о чем я говорю. Я согласен, это ожидаемое поведение, и для меня это вполне логично, я не говорю, что это недостатки Qt.
@musicamante Моей первой мыслью было использование родительского элемента всех QLabels, однако я обнаружил, что это очень неэффективно, поскольку в этом случае мне придется перебирать все виджеты (QLabels) в макете и определять относительные координаты между виджетами, чтобы определить куда переместился курсор QLabel. Но это все еще вариант.
@paddy мой предыдущий ответ связан с твоим комментарием
@VladPonomarev При всем уважении, я вынужден не согласиться, поскольку это не «вариант»: вероятно, это вариант. Работа со сложными объектными структурами почти всегда увеличивает сложность и эффективность вычислений. Если реализация будет выполнена надлежащим образом, это может решить некоторые проблемы эффективности. Например, вам не обязательно постоянно перебирать все виджеты: если вы не учитываете «перетаскивание выделений» (выборы, которые могут прокручивать содержимое), вполне достаточно знать относительную геометрию виджетов, тогда вам просто нужно проверить если позиция находится в пределах их прямоугольника.
@VladPonomarev относительно «Мне придется перебирать все виджеты...» — почему? Вы уже получаете события мыши, а оконная система уже более интеллектуальна, чем просто линейный поиск во всем. Таким образом, если ваш виджет излучает сигнал, потому что на него наведена мышь, владелец уже знает, происходит ли какое-то перетаскивание (или что-то еще), и, следовательно, нужно ли что-то с этим делать. Если вы не хотите рассылать спам-сигналы, вместо этого вы можете использовать события входа/выхода мыши.
@VladPonomarev У вас относительно сложная структура объектов, стремление к абсолютной эффективности — это химера. Вам необходимо сбалансировать эффективность возможных вычислений ЦП и реализации кода. Первая цель — найти правильный способ, который концептуально и логически имеет смысл, учитывает иерархию объектов и позволяет читать код, который можно соответствующим образом отладить. С этого момента вы можете в конечном итоге провести дальнейшую оптимизацию, если обнаружите, что реализация недостаточно эффективна и эти оптимизации ничего не нарушают.
Ах, хорошее видео. Да, единственный способ решить эту проблему — создать невидимый виджет верхнего уровня, который будет посредником между вашим пользователем и отображаемыми виджетами — эффективно перехватывая взаимодействия мыши и выборочно связывая их с соответствующим объектом ниже. Прошло некоторое время с тех пор, как я играл с Qt, поэтому у меня нет немедленного предложения о том, как это лучше всего реализовать, но слоты и система сигналов Qt очень полны, и у IIRC есть механизм, с помощью которого это можно сделать.
Я достиг того, что мне нужно, выполнив следующие шаги:
Вот видео, демонстрирующее решение: https://thewikihow.com/video_zHebZRYnF5Y?si=1zKBQs7JuEd2sjpp
Примечание. Все события mouseMoveEvent, которые происходят за пределами самого первого ограничивающего прямоугольника виджета (который захватывает mousePressEvent), по-прежнему будут пересылаться через его обработчик mouseMoveEvent. И каждый раз он будет это игнорировать, и каждый раз родителю придется выполнять свою работу. Что мне не нравится, так это то, что при каждом таком событии придется проходить через обработчики событий нескольких виджетов, а также необходимость создавать новое событие и отправлять его в найденный виджет. Это решение не идеально, но оно хорошо справляется со своей задачей.
Рад, что у вас все получилось!
Вам придется нелегко, потому что поведение, которое вы видите, соответствует ожиданиям людей. Когда пользователь нажимает указатель мыши на виджет-кнопку (или другой подобный виджет), он или она явно передает все последующие события мыши этому виджету до тех пор, пока кнопка мыши не будет отпущена. Это базовый UX. Но похоже, что вы маскируете виджет кнопки под другой тип элемента управления. Не могли бы вы рассказать подробнее о том, чего вы пытаетесь достичь визуально?