Russian Qt Forum
Апрель 24, 2024, 10:56 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: QGraphicsScene/QGraphicsView. Неверно определяется активная область элемента  (Прочитано 8943 раз)
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.



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

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

В общем, может есть идеи, как обойти эту дурацкую засаду? Программу надо доделывать, а я упёрся в эту ерунду, которая всё портит.
« Последнее редактирование: Октябрь 07, 2011, 22:24 от iroln » Записан
iroln
Гость
« Ответ #1 : Октябрь 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 и именно там происходит кривое округление координат.

« Последнее редактирование: Октябрь 08, 2011, 10:03 от iroln » Записан
iroln
Гость
« Ответ #2 : Октябрь 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. Естественно, что если курсор слева или сверху от элемента в пределах одной единицы, то этот прямоугольник будет пересекаться с элементом. Просто ужасно глупый баг и не понятно, зачем вообще тут нужен этот прямоугольник, если можно проверять вхождение точки в элемент.
Записан
_OLEGator_
Гость
« Ответ #3 : Октябрь 08, 2011, 23:55 »

Да, такой костыль помоему я видел и в другом месте.
Осмелюсь предположить, что это делается для проверки пересечения 2х QPainterPath, а QPainterPath из одной точки не валиден, равно как прямоугольник с нулевым размером, поэтому нарисовали такое безобразие...
Записан
iroln
Гость
« Ответ #4 : Октябрь 10, 2011, 14:09 »

_OLEGator_, ну тут вроде как нет 2-х QPainterPath. В общем очень спорное решение. Почему прямоугольник размером 1x1, а не 0.00001x0.00001 или 1000x1000? Риторический вопрос. Улыбающийся

Вот, привожу скриншот-рисунок с комментариями к чему приводит вот такое "классное решение":

Записан
_OLEGator_
Гость
« Ответ #5 : Октябрь 10, 2011, 14:44 »

Я имел в виде QPainterPath для функции, которая, если я не ошибаюсь, и используется для поиска пересечений и попаданий:
Код
C++ (Qt)
bool QGraphicsItem::collidesWithPath ( const QPainterPath & path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape ) const [virtual]
Соответственно, если ее переопределить для точного поиска, то можно эту ситуацию с прямоугольником обработать.
Записан
iroln
Гость
« Ответ #6 : Октябрь 10, 2011, 14:51 »

Насколько я понимаю всю эту кухню, collidesWithPath используется для обработки столкновений элементов между собой и при интерактивном управлении элементами на сцене (выделение, перемещение и т.д.) не участвует.

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

Offline Offline

Сообщений: 5


Просмотр профиля
« Ответ #7 : Май 25, 2018, 09:46 »

Как решили данную проблему?
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.636 секунд. Запросов: 22.