О Qt и рисовании прямоугольничков

Мой опыт с Qt достаточно своеобразен: как только я начинал лобзиком выпиливать что-то свое руками, внезапно обнаруживалось, что все украдено до нас и есть уже готовое или почти готовое решение. Из последних открытий в этом месте: QGraphicsItemGroup, подошедший мне на 98% после того, как я изготовил свое частное решение (которое, конечно, выкинул после этого).

Но вот столкнулся с задачкой, которая, вроде бы, не решена:

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

В-принципе, QPainter это все умеет, у него есть CompositionMode, где все подобные преобразования можно задать. Но:

  • Этот самый QPainter доступен из функции рисования (::paint) и более нигде разумного доступа к нему нет.
  • То есть нужно переопределить ::paint для всех используемых типов объектов (круги, квадратики, линии и так далее).
  • При этом, переопределить только для QGraphicsItemGroup - не работает, ейный paint рисует невидимый прямоугольник (кисточкой Qt::NoBrush) вокруг себя и все, а все дети рисуются отдельно и разбираться с ними тоже надо отдельно.

Получается, все используемые типы объектов, которым это надо (штук пять) нужно сабклассить, переопределять в них один метод, добавлять еще парочку (get/setPainterCompositionMode), то бишь практически одинаковый код в пяти местах.

По моему опыту с Qt, это означает, что я что-то не заметил и это место уже должно быть украдено до меня.....

Comments

Состояние паинтера можно менять только из paint. Так что нужно создавать базовый класс который умеет переключать композишн.
Можно попробовать изменять композишн из QGraphicsItemGroup наследника, при этом включив DontSavePainterState
http://doc.qt.nokia.com/latest/qgraphicsview.html#OptimizationFlag-enum
но это тоже может быть чревато...так как QPainter один на всех.
Еще кстати есть QGraphicsEffects http://doc.qt.nokia.com/4.7/qgraphicseffect.html, писать быстрые эффекты на нем сложно, но если речь о десктопе, то должно получится.

Вероятно, QGraphicsEffect должен подойти для решения Вашей задачи.

А есть ли оттуда простой доступ к нижележащим объектам, чтобы XOR сделать?

Не, эффекты растерные, получают pixmap. То есть ксорить надо уже пиксмап.

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

Эффект назначеный группе работает для всех дочерних элементов. Т.е. достаточно написать XOR эффект. А можно более конктренее задачу описать. Все подэлементы группы должны ксорится с тем, поверх чего рисуются.

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

"Это" таскается по изображению и должно быть видно
- углы (точно надо XOR-ить)
- стороны (тоже XOR)
- про сетку (внутри 4-угольника) я пока не уверен, если XOR-ить то что-то неэстетично, возможно нужен другой эффект блендинга.

А все это вместе - это QGraphicsItemsGroup, за исключением кругов по углам, которые самостоятельны (и влияют на перерисовку сетки).

Короче, обычная, не очень простая, *предметная* задача.

Ну я бы все же реализовал это с помощью эффектов. Вот, например, как реализован эффект opacity (все в методе draw(QPainter *painter)):

QPoint offset;
Qt::CoordinateSystem system = sourceIsPixmap() ? Qt::LogicalCoordinates : Qt::DeviceCoordinates;
QPixmap pixmap = sourcePixmap(system, &offset, QGraphicsEffect::NoPad);
if (pixmap.isNull())
return;

painter->save();
painter->setOpacity(d->opacity);

if (system == Qt::DeviceCoordinates)
painter->setWorldTransform(QTransform());

painter->drawPixmap(offset, pixmap);
painter->restore();

Т.е. фактически нужно переопределить один виртуальный метод и все.

С наследником все понятно: сохранили composition mode, поменяли, нарисовали себя (позвали метод базового класса), восстановили composition mode. Четыре строчки.

Но делать наследника для каждого используемого примитива (квадрат, эллипс, линия, полигон, кажется еще что-то) - скучно и противно.

Про хак с отключением кэширования - интересно, может сработать, хотя если объектов много, то секс гарантирован.

Мы отключили painter state saving для всего телефона и ничего, никаких артефактов хотя тоже боялись что как сломается...

Я попробовал, получилась жесть: те объекты, которым этого (XOR) не надо - теперь тоже XOR-ятся.

А если внутри GraphicsItemGroup менять, а потом восстанавливать - то дети не рисуются как надо т.к. дети рисуются не "внутри" рисования Group.

Пойду онанировать пять объектов одинаково сабклассить.

Попробуй эффект написать. Что то типа этого http://qt.gitorious.org/qt/kenji-clone/blobs/bd7f0e3dfb0f509e55b67284f4e...
надо наверное правильный композишн поставить.
Его достаточно на выбранную группу установить.
http://doc.qt.nokia.com/latest/qgraphicsitem.html#setGraphicsEffect

При этом слетит Opacity (которая тоже эффект)?

Главное то - 25 строчек (5 типов объектов по 5) тупого кода (сохранить Mode, поменять, вернуть) - это гарантированно работает. Ну еще в десятке мест имена базовых классов поменять.

А эффект - это скорее всего и кода больше и отлаживать придется (и, кроме того, я ваще не представляю как это писать т.е. надо изучать).

Мое исходное удивление - оно про то, что нельзя флаги паинтера поменять уже сейчас, в существующем Qt. Все предыдущие разы это значило, что я что-то не заметил.

> При этом слетит Opacity (которая тоже эффект)?
Скорее всего да. Эффекты на объекте может быть только один. Если хочется несколько нужно какой нибудь мета эффект сделать.

> Мое исходное удивление - оно про то, что нельзя флаги паинтера поменять уже сейчас, в
> существующем Qt. Все предыдущие разы это значило, что я что-то не заметил.

Паинтер может создаваться на каждый кадр, или каждый объект. Типа абстракция такая. Нет глобального паинтера. Вот и нельзя менять его состояние.

Паинтер в объект прилетает снаружи. Мне удивительно, что стандартно объект не может его слегка модифицировать и мне приходится эту фичу привинчивать. Казалось бы, такое желание, как XOR или luminocity blending или что там еще есть (в Фотошопе :) - это естественное желание.

Ну то есть вот на примере фотошопа: есть слои (по Z-координате), blending mode (с нижележащими) у каждого слоя - своя. А в Qt этого места не оказалось к моему удивлению.

Кагбы для этих целей и прикрутили эффекты. Правда как то через пень колоду.
Сделай свой класик на основе qGraphicsItem. В котором и будут все эти блендинг пропертисы. И отнаследуй прямоугольнички и кружочки из него. Мне кажется троли слажали когда сделали QTextGraphicsItem и QEllipseGraphicsItem, то есть для быстрого старта очень даже полезно, но когда нужно сделать чуть менее тривиальную штуку, народ ругается.

Да, возможно что сдублировать код от эффекта (сложить opacity и нужное мне) - наиболее красивый способ в смысле дублирования кода.

Но пока мы тут переписывались - я уже втупую засабклассил Rect,Line и Polygon, а больше мне и не надо оказалось.