Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > С/С++: Кроссплатформенное программирование, Qt/Gtk+/wxWidgets > QGraphicsPathItem vs QVector<QGraphicsLineItem>


Автор: Glad 23.4.2014, 12:26
Доброго времени суток!
У меня задача нарисовать дорогу по модели(данные). Я нашёл(случайно) класс QGraphicsPathItem и все связанные с ним(QPainterPath, например). Но взглянув внутрь, как-то смутился.. Черепашья графика ? Зачем нужен этот класс ? И имеет ли он какие-нибудь преимущества над скажем QVector<QGraphicsLineItem> ?
Заранее благодарю.

Автор: Amp 23.4.2014, 13:15
QPainterPath позволяет создавать сложные контуры и кривые, комбинируя их из простых линий, дуг, кривых Безье, тестовых глифов и т.п. Полученный путь (path) можно сохранить и использовать (отрисовать) в будущем. Стандартная сущность для векторной графики (аналог, например, cairo_path_t в cairo). Все это конечно же можно аппроксимировать вручную линиями, сложив их в вектор, но это мартышкин труд с кучей недостатков. Например будут проблемы с заливкой, пересечениями контуров, соединениями между линиями (joins).

Автор: Glad 23.4.2014, 14:28
Вы знаете меня смутило несколько вещей в этом всём деле. Вот несколько из них:
* Каждый элемент Path это не обязательно фигура.. В частности, там возвращается тип объекта moveTo, что как-то странно(хотя в терминах черепашьей графики это нормально).
* Селекшн не всегда работает правильно.. Я нарисовал тестовую ломанную с помощью этих инструментов и ломанная выделялась даже если я кликал мимо нее. Впрочем, определить алгоритм попадания я так и не смог: по началу я подумал что это просто попадание в BoundingBox ломанной, но потом потестировав и "потыкав" по экрану, понял что это не так. 
* Нет возможности выделять и редактировать  элементы ломаной. Если я захочу выделить элемент ломаной и что-то с ним сделать(например, изменить толщину дороги в этом участке или сменить тип дороги с гравейки на асфальт) то этого не сделаешь с помощью этих инструментов(а может быть я их просто не знаю?).

Вот как-то так.. Всё конечно зависит от решаемой задачи.. Если задача вывести буквы/цифры без возможности редактирования, то наверное этот инструмент подошёл как нельзя лучше. 


Автор: Amp 23.4.2014, 15:13
Цитата(Glad @  23.4.2014,  14:28 Найти цитируемый пост)
* Каждый элемент Path это не обязательно фигура.. В частности, там возвращается тип объекта moveTo, что как-то странно(хотя в терминах черепашьей графики это нормально).

Фигуры - это комбинации линий  и кривых. Например path в виде прямоугольника можно представить комбинацией одного moveTo и четырех lineTo вызовов. В итоге будет образован замкнутый контур, который мы сможем назвать фигурой. Методы addRect, addPolygon, addEllipse можно считать высокоуровневыми надстройками, которые в своей реализации все равно в итоге сводятся к последовательности из moveTo/lineTo/arcTo. Ну да, выходит, что бегает черепашка и оставляет за собой линию. В определенные моменты она может обравать линию и начинать рисовать новую (получаются sub path-ы в рамках одного path).

Цитата(Glad @  23.4.2014,  14:28 Найти цитируемый пост)
* Нет возможности выделять и редактировать  элементы ломаной. Если я захочу выделить элемент ломаной и что-то с ним сделать(например, изменить толщину дороги в этом участке или сменить тип дороги с гравейки на асфальт) то этого не сделаешь с помощью этих инструментов(а может быть я их просто не знаю?).

Это все надо писать самому. К тому же нельзя изменить цвет или толщину какого-то отдельного учатка внутри path. Для Qt это один целый элемент, к которому применяется текущие настройки кистей и цвета.


Цитата(Glad @  23.4.2014,  14:28 Найти цитируемый пост)
* Селекшн не всегда работает правильно

Не знаю в чем тут дело.

Автор: math64 24.4.2014, 07:38
Это не черепашья графика. Черепашья графика - это когда изображение непросредственно выводится на монитор, что медленно и происходит мерцание экрана. В Qt обязательная двойная буферизация, как что мерцания не будет.
moveTo и lineTo - естественный способ задания кривой. В итоге это сводится к аналогичным вызовам Microsoft API.
QPainterPath задает одну область, заливаемую одной кистью (хотя возможно, градиентной или пустой) и обводимой одним пером (хотя возможно, градиентным или пустым). Если нужно что-то выделить другим цветом, нужен второй QPainterPath.
QPainterPath следует рассматривать как черный ящик. Хотя можно получить точки moveTo и lineTo, лучше их хранить где-то в своём классе.

Выделение действительно, иногда ведёт себя не так, как хочется. В этом случае нужно писать своё выделение.

Автор: Glad 24.4.2014, 12:50
Цитата(Amp @  23.4.2014,  15:13 Найти цитируемый пост)
Это все надо писать самому. К тому же нельзя изменить цвет или толщину какого-то отдельного учатка внутри path. Для Qt это один целый элемент, к которому применяется текущие настройки кистей и цвета.

А как это сделать ? Я просто не очень понимаю алгоритм действий. Т.е. как задать свой собственный алгоритм определения попадает ли точка в фигуру ? Я переопределил метод contains но при клике он даже не был вызван.. Странно

Автор: math64 24.4.2014, 15:01
Обычно в своем GraphicsItem нужно переопредить одновременно следующие методы:
Код

    QRectF boundingRect() const;
    QPainterPath shape() const;
    bool contains(const QPointF &point) const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);

и они должны работать согласованно
paint() рисует внутри shape(),
и соблюдаются соотнношения:
boundingRect() = shape().boundingRect()
contains() =  shape().contains()

т.о. для определения принадлежности точки может быть вызвано не contains(), а shape().contains()


Автор: Glad 24.4.2014, 23:45
Цитата(math64 @  24.4.2014,  15:01 Найти цитируемый пост)
Обычно в своем GraphicsItem нужно переопредить одновременно следующие методы:
Выделить всёкод C++
1:
2:
3:
4:
    
    QRectF boundingRect() const;
    QPainterPath shape() const;
    bool contains(const QPointF &point) const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);

и они должны работать согласованно
paint() рисует внутри shape(),
и соблюдаются соотнношения:
boundingRect() = shape().boundingRect()
contains() =  shape().contains()

т.о. для определения принадлежности точки может быть вызвано не contains(), а shape().contains()


Спасибо! Ясно примерно. Хотя я так посмотрел.. У QGraphicsItem есть метод shape. Получается что так или иначе возращается QPainterPath(кстати по значению, что тоже смутило учитывая частоту вызова данной функции). Т.е. с помощью shape() мы можем построить свой контур и Qt сожет определить попали ли мы в этот контур автоматически, я правильно понимаю ? 
Пока конечно не вдаюсь в детали, но с первого взгляда это кажется сложным.. Не проще было бы дать юзеру определять с помощью того же метода contains содержится ли точка в фигуре или нет ? smile 

Автор: math64 25.4.2014, 07:24
contains() для точки частная задача. по умочанию она вызывает shape().contains()
Если можно выполнить contains() быстрее, можно предложить другую реализацию.
shape() нужен для других задач, например фигуры выделение контуром, которое с помощью contains() не сделаешь.
shape() возвращает QPainterPath по значению, а не по ссылке, т.к. путь нельзя модиффицировать (если он хранится постоянно), или он может создаваться каждый раз при вызове, чтобы не тратить много памяти на его хранение.
Как бы то ни было, конструктор копирования выполняется быстро, т.к. данные хранятся в QPainterPathPrivate, а копия использует те же данные, что и оригинал (в QPainterPathPrivate есть счетчик использования) - так устроены многие классы Qt.

Автор: Glad 25.4.2014, 18:53
А прикол хотите ? smile Я определил shape для своей дороги.. И вроде выделяется всё правильно.. Но.. Если из переопределенного мной  метода paint убрать рисование базовым методом QGraphicsPathItem::paint объект перестанет выделяться.. Хотя у меня подозрение, что он всё-таки выделяется, просто не рисует выделение.

Автор: math64 25.4.2014, 22:33
Цитата(Glad @  25.4.2014,  18:53 Найти цитируемый пост)
Хотя у меня подозрение, что он всё-таки выделяется, просто не рисует выделение

Именно так. при прорисовке нужно смотреть на параметр option

Автор: Glad 26.4.2014, 14:00
math64, 
Спасибо за ответы!

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)