QGraphicsItem: Пользовательский компонент для выбора области на сцене

QGraphicsItem

Пусть в приложении требуется функция увеличения произвольной области изображения, как под лупой. В качестве примера решения этой задачи скомпонуем простую Qt-программу.

В левой части отображается оригинал. Для него мы используем QGraphicsView и QGraphicsScene. Увеличенная копия выделенной части картинки выводится в правой верхней области окна с помощью QLabel.

Область увеличения изображения задается в виде прямоугольника, который можно перемещать при помощи мыши по принципу Drag&Drop. Размер области увеличения может быть изменен путем манипуляций с углами прямоугольника, соответствующего границам области, по аналогии с тем, к чему нас приучили всевозможные графические редакторы.

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

Реализация

В качестве нашей основной рабочей поверхности воспользуемся QGraphicsView и QGraphicsScene. Определим виджет главного окна для приложения:

И добавим базовую реализацию:

Чтобы не усложнять пример, я поместил картинку в ресурсы приложения, и загружаю ее в QPixmap оттуда. Размер Сцены задается исходя из размеров изображения, поскольку именно изображение наш основной объект экспериментов. Сама картинка добавляется на Сцену с помощью setPixmap().

Важно: Не забудьте связать Сцену QGraphicsScene с Представлением QGraphicsView:

Чтобы изображение всегда вписывалось в Представление, переопределим функцию-обработчик события изменения размеров виджета resizeEvent():

Также не забудем про заготовку слота, реагирующего на изменение выбранной области:

В качестве компонента для выделения области на Сцене мы могли бы взять за основу QGraphicsRectItem с установленным флагом ItemIsMovable. Но это решение недостаточно гибкое. Придется идти другим путем, и реализовать собственный компонент, унаследовав его от QGraphicsItem:

Нам приходится контролировать каждый аспект поведения компонента вручную, но это дает полный контроль над ситуацией:

Кода получилось довольно много, но его суть определяется в трех пунктах:

  1. Рабочая область компонента возвращается через функцию-член boundingRect(). Не забудьте учесть, что помимо самой области выделения мы берем небольшой запас на маленькие квадраты, указывающие на зоны растяжения, которые появляются по углам прямоугольника при наведении курсора мыши;
  2. Отрисовка компонента происходит в функции paint(). В ней мы рисуем текущую зону выделения и зоны растяжения (маленькие квадраты в углах). Последние отображаются только тогда, когда курсор мыши попадает в boundingRect();
  3. События мыши мы контролируем с помощью функций hoverEnterEvent(), hoverLeaveEvent(), mousePressEvent(), mouseMoveEvent() и mouseReleaseEvent(). Об этом пункте поговорим поподробнее.

Обратите внимание, что для работы hoverEnterEvent(), hoverLeaveEvent() требуется явно включить поддержку с помощью setAcceptHoverEvents( true ). В обработчиках этих событий мы определяем, что курсор мыши входит или выходит из области нашего компонента: меняем значение m_hovered, а затем обеспечиваем перерисовку (неявный вызов paint()) с помощью update().

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

В mouseReleaseEvent() мы сбрасываем информацию об активной зоне, в которую пришелся щелчок левой кнопкой мыши, поскольку перемещать или менять размер компонента больше не требуется.

Вся «магия» заключена в функции mouseMoveEvent(). В ней мы контролируем все видоизменения компонента. Основная сложность здесь кроется в необходимости контроля границ. Мы учитываем, что область выделения компонента не должна выходить за границы Сцены (т.е. изображения), а также не должна становиться слишком маленькой. Для реализации всего этого нам достаточно простых наборов условий и знаний геометрии школьного уровня.

Осталось только подключить созданный компонент к нашей Сцене:

Выводы

Мы реализовали собственный компонент для Сцены QGraphicsScene, который позволяет выделять прямоугольную область с помощью мыши. Мы использовали этот компонент в качестве лупы, но идею можно обобщить и для решения более сложных задач.

Понравилась статья? Поделиться с друзьями:
Добавить комментарий