Russian Qt Forum

Qt => 2D и 3D графика => Тема начата: Гурман от Февраль 25, 2015, 16:50



Название: (РЕШЕНО) При первом рисовании сцены не работает mapToItem
Отправлено: Гурман от Февраль 25, 2015, 16:50
Есть графический айтем, который привязан к другому графическому айтему, как к родителю, то есть, указатель на родителя выдаётся методом parentItem(). Обнаружил странный глюк - при первом рисовании сцены, если я вызываю
Код:
    qDebug() << mapToItem( parentItem(), QPointF(tipX,bodyY) ); // tipX,bodyY - координаты точки в системе суб-айтема
то получаю в консоли
Цитировать
QPointF(2.5, 4.33)
при этом tipX == 2.5 ,bodyY == 4.33, то есть, трансляция точки в координаты айтема не происходит

Но если очистить сцену, и нарисовать на ней всё тоже самое повторно, то
Цитировать
QPointF(-40, 20)
что и должно быть. Пытался до первого рисования очистить сцену, потом рисовать на ней - без разницы.

Полез в потроха Qt отладчиком, и обнаружил, что при выполнении mapToItem внутри производится проверка флага QTransform::m_dirty (очевидно, флаг наличия трансформации). Так вот при первом рисовании он 0, а при повторном 1. Существенно - необходимая трансформация выполняется всегда ПОСЛЕ того, как на схеме уже что-то нарисовано. На самом деле, сначала рисуются объекты схемы, потом соединения между объектами. При рисовании соединений возникает этот глюк. Разницы между первым и вторым рисованием в моём коде нет никакой. Объект класса QTransform - это тот самый, что возвращает метод QGraphicsItem::itemTransform(). Трансформация производится вызовом его QPointF QTransform::map(QPointF). И вот внутри этого метода в первый раз оказывается, что... никакой трансформации нет, и я вместо отображения получаю локальные координаты точки в суб-айтеме.

Совершенно не понятно, почему при первом вызове нет признака трансформации, а при втором - уже есть. И где этот признак взводится. Причем та же фигня при попытке сделать mapToScene(QPointF(tipX,bodyY)), что может быть объяснимо вызовом mapToItem(parentItem()...) внутри mapToScene() /*не лазил туда*/.

Между рисованием объектов сцены, и рисованием соединителей производится восстановление сохраненных в сцене масштабов отображения и позиции окна. Но они по идее не должны влиять, поскольку совершенно одинаково выполняются как при первом рисовании, так и при втором. ЗЫ: Как и ожидалось, отключение этих восстановлений ничего не даёт.

В общем, еще один глюк Qt, похожий на баг. В Инете ничего найти не удалось.


Название: Re: При первом рисовании сцены не работает mapToItem
Отправлено: Гурман от Февраль 25, 2015, 18:59
Посмотрел, как работает mapToScene в этом случае (результат точно такой же, как у mapToItem - при первом вызове ). Там афинные преобразования, которые при первом рисовании не учитывают нужные смещения, а при втором уже учитывают.

Креатор из комплекта Qt 4.7, у него нет останова на изменении переменных, поймать где эти преобразования внутри Qt меняются не могу.


Название: Re: При первом рисовании сцены не работает mapToItem
Отправлено: Igors от Февраль 25, 2015, 19:13
В общем, еще один глюк Qt, похожий на баг.
Никогда не работал с QGraphicsScene, но все же: почему бы не компильнуть примерчик (один из идущих с Qt) и вставить туда строку печати? Если воспроизведется - ну кто ставит dirty я постараюсь найти (недавно было нечто подобное). Конечно подготовка измененного примера Ваша, даже не пытайтесь это кому-то "поручить"  :)


Название: Re: При первом рисовании сцены не работает mapToItem
Отправлено: Гурман от Февраль 25, 2015, 20:48
Да изменить то не проблема, проблема найти пример. Я как-то не особенно ориентируюсь в примерах Qt, последний раз их ковырял лет 5 назад. Это мне еще разбираться надо, какой пример подходящий.

Если недавно было нечто подобное - может вспомните хотя бы контекст, в котором это было? Ключевые слова, которые тогда упоминались?


Название: Re: При первом рисовании сцены не работает mapToItem
Отправлено: Гурман от Февраль 25, 2015, 23:10
Перенес первое рисование из конструктора главного окна в метод, который вызывается по таймеру (http://www.prog.org.ru/index.php?topic=28477.msg208398#msg208398) через 1 мс после завершения этого конструктора. mapToScene в этом месте стал работать правильно, но потом при последующих рисованиях лезут артефакты. Не отрисовываются кусками айтемы, линии. Такого вообще ни разу не было. Пока ничего не помогло, ни update(), ни сбрасывание кэша QGraphicsView, ни invalidate() сцены. В какой-то такой глюкоген попал, что пока не ясно куда копать.

ЗЫ: от артефактов я избавился, установив gw->setViewportUpdateMode( QGraphicsView::FullViewportUpdate ); но... при этом опять перестал работать mapToScene.


Название: Re: При первом рисовании сцены не работает mapToItem
Отправлено: Igors от Февраль 26, 2015, 11:27
Да изменить то не проблема, проблема найти пример. Я как-то не особенно ориентируюсь в примерах Qt, последний раз их ковырял лет 5 назад. Это мне еще разбираться надо, какой пример подходящий.
Открываем фолдер Examples. Ищем файлы содержащие QGraphicsScene. Ага, они в фолдере graphicsview. Выбираем пример - ну вот diagramscene (наугад). Смотрим есть ли там QGraphicsItem - есть. Ну а дальше Вам и карты в руки.

Важно проверить является ли найденный Вами баг общим или же связан с конкретным проектом. Дальше могут появиться мысли. Это стоит небольшой возни с примером.

Если недавно было нечто подобное - может вспомните хотя бы контекст, в котором это было? Ключевые слова, которые тогда упоминались?
Я имею ввиду недавний случай с "ложной перерисовкой" (найти что ее вызывает)


Название: Re: При первом рисовании сцены не работает mapToItem
Отправлено: Гурман от Февраль 26, 2015, 14:15
Это не толковый вариант действий. Там сто пудов проекты не совпадают с моим, может быть куча нюансов. Поэтому я сделал по-другому - создал с 0 "лабораторный" проект, который в проблемной части по структуре совпадает с моим. Только меньше его, примерно как детский шалаш меньше башни Россия в Москва-сити, всего 150 строк.

И сразу получил глюк - первый же вызов mapToItem внутри айтема-наследника, при попытке пересчитать его локальные координаты в координаты айтема-родителя вернул мне... увы... те же локальные координаты наследника.  :-[

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

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

Лабораторный проект могу дать для проверки в более новых версиях Qt. Может оказаться, что там баг исправлен. Сам их ставить и проверять не буду - не до того. Из-за этого бага застряла реализация сложной и необходимой функциональности - ортогональных соединителей между айтемами.


Название: Re: При первом рисовании сцены не работает mapToItem
Отправлено: Гурман от Февраль 26, 2015, 22:30
Всё, проблема найдена. Я не в том месте, где надо, устанавливал позицию субайтема. В результате получалось, что рисовало всё правильно, и даже правильные координаты потом возвращало, но относительные координаты считало не правильно. Вызов setPos() для установки координат субайтема в системе координат родителя должен быть обязательно до того, как Qt первый раз вызовет boundingRect() или paint() при привязке айтема к сцене.