Russian Qt Forum

Qt => 2D и 3D графика => Тема начата: iroln от Октябрь 07, 2011, 22:21



Название: QGraphicsScene/QGraphicsView. Неверно определяется активная область элемента
Отправлено: iroln от Октябрь 07, 2011, 22:21
И снова здравствуйте, уважаемые форумчане и просто хорошие люди!

У меня снова проблема, но похоже, в этот раз проблема не у меня, а у Qt.
На графической сцене неверно определяется активная область элемента (в моём случае QGraphicsRectItem). То есть при клике мышкой вне элемента срабатывает select item со всеми вытекающими.
Есть вот такая тестовая программа:
Код
C++ (Qt)
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsItem>
#include <QDebug>
 
int main(int argc, char** argv) {
   QApplication app(argc, argv);
   QGraphicsScene scene;
   QGraphicsView  view;
 
   scene.setSceneRect(0, 0, 10, 10);
   scene.addRect(2, 2, 6, 6, QPen(Qt::black, 0, Qt::DotLine));
 
   QGraphicsItem* item = scene.addRect(3, 3, 5, 5, QPen(Qt::red));
   item->setFlag(QGraphicsItem::ItemIsSelectable, 1);
 
   qDebug() << item->boundingRect();
   qDebug() << item->shape();
 
   view.setScene(&scene);
 
   view.resize(900, 900);
   view.show();
   view.scale(80, 80);
 
   return app.exec();
}
 

Тут создаётся сцена размером 10x10 единиц. Создаётся прямоугольник размером 6x6 (красный). Событие select item срабатывает на одну единицу влево и вверх от элемента (показано на сцене серой пунктирной рамкой). При этом boundingRect и shape возвращают корректные значения. Возможно, проблема в QGraphicsView.

(http://lostpic.net/thumbs/a29d90ca9159c51622debc6159a94a1a.png) (http://lostpic.net/?photo=401454)

Я написал багрепорт в PySide, там сказали, что проблема в Qt скорее всего при округлении (так как координаты сцены дробные), баг закрыли как несуществующий.

Это жутко мешает жить. У меня в приложении есть много прямоугольников, которые расположены впритык друг к другу и имеют размеры 1x1. Из-за этого бага я не могу их выбирать мышкой (выбираются не те) :).

В общем, может есть идеи, как обойти эту дурацкую засаду? Программу надо доделывать, а я упёрся в эту ерунду, которая всё портит.


Название: Re: QGraphicsScene/QGraphicsView. Неверно определяется активная область элемента
Отправлено: iroln от Октябрь 08, 2011, 09:34
Написал вот такой проверочный код на питоне:
Код
Python
from PySide.QtGui import *
from PySide.QtCore import *
 
class TestView(QGraphicsView):
 
   def mousePressEvent(self, event):
       pos = self.mapToScene(event.pos())
       print pos
 
       items = self.scene().items(pos, Qt.ContainsItemShape, Qt.AscendingOrder)
       print items
 
       #super(TestView, self).mousePressEvent(event)
       pass
 

Мои подозрения на счёт округления подтверждаются. Функция mapToScene возвращает дробные координаты сцены (всё честно). Но вот метод сцены items, видимо, внутри себя округляет переданные дробные координаты точки в большую сторону, поэтому select и срабатывает. Например X-координата точки = 2.3, X элемента = 3. При округлении X-координаты точки получается 3, срабатывает select. Короче, должно округляться в меньшую сторону если точка (курсор) левее и выше элемента и в большую сторону если курсор правее и ниже элемента. Это баг Qt, без сомнения. Пока придётся самому реализовывать проверку и выделение элементов по клику мышки, а это кривизна. :(
Даже если округлять в меньшую сторону, всё равно срабатывает. То есть проблема может и с округлением, а может и нет, но точно внутри QGraphicsScene::items.
Блин, я глючу. :) С QGraphicsScene::items всё в полном порядке. видимо проблема в стандартной реализации QGraphicsView:mousePressEvent и именно там происходит кривое округление координат.



Название: Re: QGraphicsScene/QGraphicsView. Неверно определяется активная область элемента
Отправлено: iroln от Октябрь 08, 2011, 11:46
Залез я в исходники Qt и посмотрел. Похоже, нашёл где ошибка.
Ошибка в файле qgraphicsscene.cpp

Класс QGraphicsScenePrivate, метод itemsAtPosition. Метод возвращает список элементов для текущей позиции курсора или пальца (если тач). Он везде используется, а значит ошибка будет всегда.

Код
C++ (Qt)
/*!
   Returns all items for the screen position in \a event.
*/

QList<QGraphicsItem *> QGraphicsScenePrivate::itemsAtPosition(const QPoint &/*screenPos*/,
                                                             const QPointF &scenePos,
                                                             QWidget *widget) const
{
   Q_Q(const QGraphicsScene);
   QGraphicsView *view = widget ? qobject_cast<QGraphicsView *>(widget->parentWidget()) : 0;
   if (!view)
       return q->items(scenePos, Qt::IntersectsItemShape, Qt::DescendingOrder, QTransform());
 
   const QRectF pointRect(scenePos, QSizeF(1, 1));
   if (!view->isTransformed())
       return q->items(pointRect, Qt::IntersectsItemShape, Qt::DescendingOrder);
 
   const QTransform viewTransform = view->viewportTransform();
   return q->items(pointRect, Qt::IntersectsItemShape,
                   Qt::DescendingOrder, viewTransform);
}
 

Проблема вот тут:
Код
C++ (Qt)
const QRectF pointRect(scenePos, QSizeF(1, 1));
 

Ищется пересечение с прямоугольником размером 1x1. Естественно, что если курсор слева или сверху от элемента в пределах одной единицы, то этот прямоугольник будет пересекаться с элементом. Просто ужасно глупый баг и не понятно, зачем вообще тут нужен этот прямоугольник, если можно проверять вхождение точки в элемент.


Название: Re: QGraphicsScene/QGraphicsView. Неверно определяется активная область элемента
Отправлено: _OLEGator_ от Октябрь 08, 2011, 23:55
Да, такой костыль помоему я видел и в другом месте.
Осмелюсь предположить, что это делается для проверки пересечения 2х QPainterPath, а QPainterPath из одной точки не валиден, равно как прямоугольник с нулевым размером, поэтому нарисовали такое безобразие...


Название: Re: QGraphicsScene/QGraphicsView. Неверно определяется активная область элемента
Отправлено: iroln от Октябрь 10, 2011, 14:09
_OLEGator_, ну тут вроде как нет 2-х QPainterPath. В общем очень спорное решение. Почему прямоугольник размером 1x1, а не 0.00001x0.00001 или 1000x1000? Риторический вопрос. :)

Вот, привожу скриншот-рисунок с комментариями к чему приводит вот такое "классное решение":
(http://lostpic.net/thumbs/4be88822c0027046d018427211c034f5.png) (http://lostpic.net/?photo=407978)


Название: Re: QGraphicsScene/QGraphicsView. Неверно определяется активная область элемента
Отправлено: _OLEGator_ от Октябрь 10, 2011, 14:44
Я имел в виде QPainterPath для функции, которая, если я не ошибаюсь, и используется для поиска пересечений и попаданий:
Код
C++ (Qt)
bool QGraphicsItem::collidesWithPath ( const QPainterPath & path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape ) const [virtual]
Соответственно, если ее переопределить для точного поиска, то можно эту ситуацию с прямоугольником обработать.


Название: Re: QGraphicsScene/QGraphicsView. Неверно определяется активная область элемента
Отправлено: iroln от Октябрь 10, 2011, 14:51
Насколько я понимаю всю эту кухню, collidesWithPath используется для обработки столкновений элементов между собой и при интерактивном управлении элементами на сцене (выделение, перемещение и т.д.) не участвует.

Я ещё, конечно, поковыряю, но сдаётся мне, что всё равно придётся полностью переписывать обработчики событий мышки для QGraphicsScene, а это кривизна страшная, там куча подводных камней. Ещё и тормозить всё это будет скорее всего, особенно в Python.


Название: Re: QGraphicsScene/QGraphicsView. Неверно определяется активная область элемента
Отправлено: Dunkan от Май 25, 2018, 09:46
Как решили данную проблему?