![]() |
|
![]() ![]() ![]() |
|
wowka19 |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 285 Регистрация: 30.11.2011 Репутация: нет Всего: нет |
Задача:
Необходимо реализовать циджет-поле, на котором будут располагаться объекты-картинки и просто прямые отрезки. Эти объекты-картинки и линии должны двигаться мышкой: навел на объект (линию?!), зажал клик, переместил, опустил. Также нужно, чтобы поле можно было двигать относительно вьюпорта и менять масштаб отображения. Предполагаемое решение: поле - QGraphicsScene вьюпорт - QGraphicsView Вопросы: Правильно ли выбраны: QGraphicsScene и QGraphicsView? может есть что-то лучше для моей задачи (просто впервые с сталкиваюсь с такой проблемой)? Из чего делать объекты и линии? - QGraphicsItem или QGraphicsPixmapItem или еще из чего-нибудь (этих QBla-bla-bla-Item'ов просто тьма). Еще такой момент: одним из объектов должна быть прямоугольная рамка, каждую сторону которой можно будет двигать мышкой (тем самым сужая/расширяя включенную в нее область). Как грамотно ее реализовать??? да и как перемещать тонкие объекты? - их же фиг зацепишь указателем мыши (линии и та же прямоугольная рамка к примеру), может можно как-то расшить область наведения курсором? |
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 23 Всего: 72 |
Большинство нужных функций уже есть в QGraphicsScene, с Qt идут примеры, в т. ч. масштабирование.
Для возможности выбора QGraphicsItem установите флаг ItemIsSelectable, для перемещения - ItemIsMovable. Для линий есть QGraphicsLineItem. При наведении мыши на объект можно создать временные кружочкиQGraphicsEllipseItem по углам (серединам сторон, в центре и возможно еще один в строне для вращения) для изменения размеров. Перемещение этих объектов нужно отслеживать в вместе с ним перемещать основной объект. Для точного перемещения можно использовать стрелки клавиатуры. |
|||
|
||||
wowka19 |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 285 Регистрация: 30.11.2011 Репутация: нет Всего: нет |
А как это реализовать грамотней? Может есть какой-то метод готовый? И как реализовать прямоугольную рамку с изменяемыми мышью шириной/высотой как прямоугольники в MS Visio? У меня есть идеи но все они громоздкие, да и не хочется изобретать велосипед, ведь что-то подсказывает, что редактируемые прямоугольники (как MS Paint и MS Visio) если и не присутствуют в Qt, то хотя бы есть очень простые способы их реализации. Немного уточню что я имею в виду: прямоугольная рамка, при наведении на некоторую окрестность одной из сторон пользователь двигает ее, тем самым изменяя ширину или высоту рамки, а при наведении на углы - высоту и ширину. И как сделать так, чтобы при QGraphicsView::scale() линии рамки (или какого либо другого объекта - не суть) не меняли толщину, а всегда были к примеру в 5px? Это сообщение отредактировал(а) wowka19 - 5.4.2014, 02:33 |
|||
|
||||
vinter |
|
|||
![]() Explorer ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2735 Регистрация: 1.4.2006 Где: Н.Новгород Репутация: 3 Всего: 56 |
||||
|
||||
wowka19 |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 285 Регистрация: 30.11.2011 Репутация: нет Всего: нет |
Ок. Моя идея реализации рамки:
1. Унаследовать QGraphicsRectItem 2. Переопределить boundingRect() так, чтобы он возвращал область немногим больше размера прямоугольника - вот только здесь нихрена не получается:
проверяю при помощи: setCursor(Qt::CrossCursor); но курсор меняется только при наведении на сам объект, а не +-5! Короче полный затык. Проблема осталась. Причем ВСЕ проблемы: 1 - Цитата(math64 @ 3.4.2014, 07:41 Найти цитируемый пост) Для точного перемещения можно использовать стрелки клавиатуры. А как это реализовать грамотней? Может есть какой-то метод готовый? 2 - И как сделать так, чтобы при QGraphicsView::scale() линии рамки (или какого либо другого объекта - не суть) не меняли толщину, а всегда были к примеру в 5px? ------------------ Пожалуй поясню чего я добиваюсь: Мне нужно чтобы при наведении курсора на одну из сторон прямоугольной рамки курсор менялся на Qt::SizeVerCursor или Qt::SizeHorCursor или Qt::SizeBDiagCursor или Qt::SizeFDiagCursor в зависимости от ситуации. Но, понятное дело, нужно что бы эти область была немного больше прямоугольника, чтобы пользователь не задрачивался снайперски попадать точно на линию. Это сообщение отредактировал(а) wowka19 - 6.4.2014, 14:44 |
|||
|
||||
math64 |
|
||||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 23 Всего: 72 |
Для перемещения стрелками клавиатуры нужно переопределить метод keyPressEvent в QGraphicsView
Толщина линий при масшабировании в QGraphicsView будет меняться - тут ничего не сделаешь. Но можно переоредить методы drawBackground() и drawForeground(). Курсор меняется в hoverMoveEvent():
При переопределении методов boundingRect(), shape() и contains() они должны работать согласованно, а paint() должен рисовать только внутри области выдаваемой shape(). Это сообщение отредактировал(а) math64 - 7.4.2014, 07:46 |
||||
|
|||||
wowka19 |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 285 Регистрация: 30.11.2011 Репутация: нет Всего: нет |
Спасибо за ответ, но не понятно как мне помогут drawBackground() и drawForeground() - это же отрисовка фона.
У меня на сцене будет всего один объект (все та же пресловутая рамка), толщина линий которого не должна меняться при QGraphicsView::scale(). Решено сделать так - переопределить метод отрисовки этого объекта следующим образом:
Может есть куда более элегантное решение, чем описанное выше извращение? P.S: borderWidth_ по понятным причинам участвует в формировании boundingRect() и при изменении borderWidth_ меняется и boundingRect(). Документация настоятельно рекомендует вызывать перед изменениями boundingRect() метод prepareGeometryChange(). Кто-нибудь объясните что это ибо я не понимаю, также почему этот метод вызывается до изменений а не после (так же логичней). Да и вообще правильно ли я вызываю этот prepareGeometryChange() в приведенном выше коде? Просто вроде и без этого все работает... Это сообщение отредактировал(а) wowka19 - 7.4.2014, 20:49 |
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 23 Всего: 72 |
Твое извращение лучше делать в MyGraphicsView, примерно так:
prepareGeometryChange() предназначен для другого - он вызывается перед изменением геометрии айтема и ничего не делает, если не установлен флаг ItemSendsGeometryChanges и не переопределен метод itemChange(), который должен реагировать на изменения геометрии. Внутри отрисовки айтема менять его геометрию нельзя - это приведет к рекурсивой перерисовке. Это сообщение отредактировал(а) math64 - 8.4.2014, 08:45 |
|||
|
||||
wowka19 |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 285 Регистрация: 30.11.2011 Репутация: нет Всего: нет |
Объясни пожалуйста что черт возьми делает этот drawForeground() - совершенно не понимаю. Этот метод если я не ошибаюсь вызывается после отрисовки итемов, тогда что мне делать в paint() моего итема? И почему ты расширяешь ( rect.adjust(-borderWidth,-borderWidth,borderWidth,borderWidth); ) на одну толщину пера с каждой стороны - разве это нужно делать не на ПОЛ толщины?
И почему будет рекурсия paint -> paint ? Я проверил через qDebug() << "TEST" в paint() - в итоге никакого зацикливания. Также я не понял твоего объяснения по поводу prepareGeometryChange(). В доках Qt говорится, что его нужно вызывать перед изменением QRectF, возвращаемого методом boundingRect(). Неужели дока врет? Это сообщение отредактировал(а) wowka19 - 9.4.2014, 01:44 |
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 23 Всего: 72 |
Да, rect.adjust() нужно делать на половину ширины линии.
от самого вызова prepareGeometryChange() никакой рекурсии не будет - он при наличии флага ItemSendsGeometryChanges вызывает itemChange(), т.е. как правило ничего не делает. При отрисовке QGraphicsView сначала рисуется фон drawBackground() - обычно выводится сетка, потом рисуются QGraphicsItem в порядке координаты z, при одинаковых z - в порядке добавления, a в конце drawForeground(), где можно выводить элементы редактирования (причем можно использовать прозрачный цвет). Если рамку редактированя выводить в drawForeground() сцену можно использовать несколько раз (например, в Visio уменьшеная сцена выводится в правом нижнем углу) В этом случае дополнительные QGraphicsItem для рамки не нужны. Но можешь выводить рамку традиционным способом - при помощи (наследника) QGraphicsRectItem. Толщина линии задаетя в setPen(), при этом меняется геометрия, поэтому setPen() нужно делать при изменении масштаба - в обработчике колеса мыши и/или выборе масштаба в QComboBox на панели инструментов. |
|||
|
||||
wowka19 |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 285 Регистрация: 30.11.2011 Репутация: нет Всего: нет |
Спасибо. Еще пара вопросов:
1. Зачем в MyGraphicsView::drawForeground(QPainter *painter, const QRectF &) нужные painter->save() и painter->restore() ??? Каков жизненный путь этого *painter (где/когда рождается и где/когда умирает)? 2.
- не понял причинно-следственную связь: это рекомендация или обязательное предписание, диктуемое особенностями Qt? Просто толщина линии высчитывается автоматически в зависимости от текущего масштаба, я просто реализовал это в paint()'е итема. Так нельзя? 3. И самое главное: ты рекомендуешь реализовать рамку в view. А как в этом случае я буду с ней взаимодействовать? К примеру, используя QGraphicsItem, я могу менять вид курсора при наведении на него (и даже на определенные его части, что важно) и т.д. Это сообщение отредактировал(а) wowka19 - 9.4.2014, 09:50 |
|||
|
||||
Glad |
|
|||
Новичок Профиль Группа: Участник Сообщений: 27 Регистрация: 16.8.2005 Репутация: нет Всего: нет |
Привет!
У меня тоже возник вопрос по, так сказать, около-теме. Почему в QPixmapItem boundingRect не пересчитывается при изменении траснформа ? Т.е.:
В общем rectBefore == rectAfter. Это сообщение отредактировал(а) Glad - 9.4.2014, 11:08 |
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 23 Всего: 72 |
painter->save() и painter->restore() нужны для сохранения состояния painter. В моем примере - старого пера. Возможно, можно обойтись и без этого.
Размеры айтемов должны быть рассчитаны ДО отрисовки В зависимости от сложности проекта можно рисовать рамки в GraphicsView::drawForeground() или в своих GraphicsItem. Выбирай сам. GraphicsView все равно нужно переопределять - для обработки (некотрых) из следующих событий:
Если выделяется только один item - обработку его перемещений можно делать как в специальном классе рамки, так и в GraphicsView (при выыделении запоминать указатель на выделенный айтем в поле класса) |
|||
|
||||
wowka19 |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 285 Регистрация: 30.11.2011 Репутация: нет Всего: нет |
попутно пара вопросов:
1. Почему при вызове setCursor(Qt::SizeFDiagCursor) или даже setCursor(QCursor(Qt::SizeFDiagCursor)) в конструкторе наследника QGraphicsRectItem вылетает ошибка:
пока я не допишу в .cpp (где пишу реализацию конструктора) #include <QCursor>? Ведь класс QCursor очевидно определен в этом участке кода, так как подключен <QGraphicsRectItem>. Не понимаю. 2. Почему при перемещении QGraphicsRectItem по сцене мышкой меняется его pos(), НО не меняется его rect() ? а именно его QGraphicsRectItem::rect().topLeft() ? Для чего тогда QGraphicsRectItem держит в себе объект QRectF с ненужной информацией о координате QRectF (x and y), хранил бы тогда QSize для ширины и высоты. 2GladТакое поведение указано в документации по QGraphicsItem. Хотя я не понимаю логики разработчиков, ведь задача boundingRect() описать объект прямоугольной областью (чтоб коллизии правильно посчитать и т.д.), а он не следит за его изменениями. И для справки: QMatrix - устаревший класс, лучше используй QTransform. Это сообщение отредактировал(а) wowka19 - 10.4.2014, 15:21 |
|||
|
||||
math64 |
|
||||||||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 23 Всего: 72 |
1. Делай так:
Qt::SizeFDiagCursor не курсор, а enum. Компилтор не может сам разобраться, как его преобразовать в QCursor. 2. Движением item'a по сцене занимется класс QGraphicsView. он не знает конктреный тип item'а и поэтому изменяет только pos. Если нужно друое поведение, переопредели методы
Добавлено через 4 минуты и 19 секунд PS:
нужен, потому, что в #include <QGraphicsItem> есть только предварительное объавление QCursor, так что компилятор знает, что есть класс QCursor, но не знает его методы. |
||||||||
|
|||||||||
![]() ![]() ![]() |
Правила форума "С/С++: Кроссплатформенное программирование, QT/Gtk+/wxWidgets" | |
|
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, Любитель. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | С/С++: Кроссплатформенное программирование, Qt/Gtk+/wxWidgets | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |