Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Qt4.8 - Консультация по QGraphicsScene и др... сцена, вьюпорт, кастом-объекты 
:(
    Опции темы
wowka19
Дата 2.4.2014, 22:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 285
Регистрация: 30.11.2011

Репутация: нет
Всего: нет



Задача:
Необходимо реализовать циджет-поле, на котором будут располагаться объекты-картинки и просто прямые отрезки.
Эти объекты-картинки и линии должны двигаться мышкой: навел на объект (линию?!), зажал клик, переместил, опустил.
Также нужно, чтобы поле можно было двигать относительно вьюпорта и менять масштаб отображения.

Предполагаемое решение:
поле - QGraphicsScene
вьюпорт - QGraphicsView

Вопросы:
Правильно ли выбраны: QGraphicsScene и QGraphicsView? может есть что-то лучше для моей задачи (просто впервые с сталкиваюсь с такой проблемой)?
Из чего делать объекты и линии? - QGraphicsItem или QGraphicsPixmapItem или еще из чего-нибудь (этих QBla-bla-bla-Item'ов просто тьма).
Еще такой момент: одним из объектов должна быть прямоугольная рамка, каждую сторону которой можно будет двигать мышкой (тем самым сужая/расширяя включенную в нее область). Как грамотно ее реализовать???
да и как перемещать тонкие объекты? - их же фиг зацепишь указателем мыши (линии и та же прямоугольная рамка к примеру), может можно как-то расшить область наведения курсором?

PM MAIL   Вверх
math64
Дата 3.4.2014, 07:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 23
Всего: 72



Большинство нужных функций уже есть в QGraphicsScene, с Qt идут примеры, в т. ч. масштабирование.
Для возможности выбора QGraphicsItem установите флаг ItemIsSelectable, для перемещения - ItemIsMovable.
Для линий есть QGraphicsLineItem.
При наведении мыши на объект можно создать временные кружочкиQGraphicsEllipseItem по углам (серединам сторон, в центре и возможно еще один в строне для вращения) для изменения размеров.
Перемещение этих объектов нужно отслеживать в вместе с ним перемещать основной объект.
Для точного перемещения можно использовать стрелки клавиатуры.
PM   Вверх
wowka19
Дата 5.4.2014, 01:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 285
Регистрация: 30.11.2011

Репутация: нет
Всего: нет



Цитата(math64 @  3.4.2014,  07:41 Найти цитируемый пост)
Для точного перемещения можно использовать стрелки клавиатуры.

А как это реализовать грамотней? Может есть какой-то метод готовый?


И как реализовать прямоугольную рамку с изменяемыми мышью шириной/высотой как прямоугольники в MS Visio? У меня есть идеи но все они громоздкие, да и не хочется изобретать велосипед, ведь что-то подсказывает, что редактируемые прямоугольники (как MS Paint и MS Visio) если и не присутствуют в Qt, то хотя бы есть очень простые способы их реализации. Немного уточню что я имею в виду: прямоугольная рамка, при наведении на некоторую окрестность одной из сторон пользователь двигает ее, тем самым изменяя ширину или высоту рамки, а при наведении на углы - высоту и ширину.

И как сделать так, чтобы при QGraphicsView::scale() линии рамки (или какого либо другого объекта - не суть) не меняли толщину, а всегда были к примеру в 5px?

Это сообщение отредактировал(а) wowka19 - 5.4.2014, 02:33
PM MAIL   Вверх
vinter
Дата 5.4.2014, 08:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Explorer
****


Профиль
Группа: Завсегдатай
Сообщений: 2735
Регистрация: 1.4.2006
Где: Н.Новгород

Репутация: 3
Всего: 56



Цитата(wowka19 @  5.4.2014,  02:39 Найти цитируемый пост)
У меня есть идеи но все они громоздкие, да и не хочется изобретать велосипед, ведь что-то подсказывает, что редактируемые прямоугольники 

Расскажи свои идеи, реализация будет действительно громоздкой. Никаких готовых решений в Qt нет. Всё нужно ручками писать.


--------------------
Мой блог
PM MAIL WWW   Вверх
wowka19
Дата 6.4.2014, 14:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 285
Регистрация: 30.11.2011

Репутация: нет
Всего: нет



Ок. Моя идея реализации рамки:
1. Унаследовать QGraphicsRectItem
2. Переопределить boundingRect() так, чтобы он возвращал область немногим больше размера прямоугольника
  - вот только здесь нихрена не получается:
Код

QRectF MMGraphicsFrameItem::boundingRect()
{
    return QRectF(rect().x()-5, rect().y()-5, rect().width()+5, rect().height()+5);
}

проверяю при помощи: 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
PM MAIL   Вверх
math64
Дата 7.4.2014, 07:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 23
Всего: 72



Для перемещения стрелками клавиатуры нужно переопределить метод keyPressEvent в QGraphicsView
Код

void GraphicsView::keyPressEvent(QKeyEvent *event)
{
       switch(event->key())
       {
       case Qt::Key_Left:
       QGraphicsItem* item = scene()->selectedItems().first();
       QPointF pos = item->pos();
       item->setPos(pos.x()-1, pos.y());
       break;
       ...
       }
       event->accept();
}

Толщина линий при масшабировании в QGraphicsView будет меняться - тут ничего не сделаешь.
Но можно переоредить методы drawBackground() и drawForeground().
Курсор меняется в hoverMoveEvent():
Код

MyGraphicsItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
       if ( ... ) {
            ...
            setCursor(QCursor(Qt::PointingHandCursor) );
       }
       QGraphicsItem::hoverMoveEvent(event);
}

При переопределении методов boundingRect(), shape() и contains() они должны работать согласованно,
а paint() должен рисовать только внутри области выдаваемой shape().

Это сообщение отредактировал(а) math64 - 7.4.2014, 07:46
PM   Вверх
wowka19
  Дата 7.4.2014, 19:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 285
Регистрация: 30.11.2011

Репутация: нет
Всего: нет



Спасибо за ответ, но не понятно как мне помогут drawBackground() и drawForeground() - это же отрисовка фона.

У меня на сцене будет всего один объект (все та же пресловутая рамка), толщина линий которого не должна меняться при QGraphicsView::scale(). Решено сделать так - переопределить метод отрисовки этого объекта следующим образом:

Код

void paint(Qbla-bla-bla...)
{
    QPen pen(Qt::black);
    prepareGeometryChange();
    borderWidth_ = 1.0 / scene()->views()[0]->transform().m11();
    pen.setWidthF(borderWidth_);
    painter->setPen(pen);
    painter->drawRect(QRectF(0,0, width_,height_));
}



Может есть куда более элегантное решение, чем описанное выше извращение?

P.S: borderWidth_ по понятным причинам участвует в формировании boundingRect() и при изменении borderWidth_ меняется и boundingRect(). Документация настоятельно рекомендует вызывать перед изменениями boundingRect() метод prepareGeometryChange(). Кто-нибудь объясните что это ибо я не понимаю, также почему этот метод вызывается до изменений а не после (так же логичней). Да и вообще правильно ли я вызываю этот prepareGeometryChange() в приведенном выше коде? Просто вроде и без этого все работает...

Это сообщение отредактировал(а) wowka19 - 7.4.2014, 20:49
PM MAIL   Вверх
math64
Дата 8.4.2014, 08:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 23
Всего: 72



Твое извращение лучше делать в MyGraphicsView, примерно так:
Код

MyGraphicsView::drawForeground(QPainter *painter, const QRectF &)
{
    painter->save();
    QGraphicsItem* item = scene()->selectedItems().first();
    QRectF rect = item->boundingRect();
    QPen pen(Qt::black);
    double borderWidth = 1.0 / transform().m11();
    rect.adjust(-borderWidth,-borderWidth,borderWidth,borderWidth);
    pen.setWidthF(borderWidth);
    painter->setPen(pen);
    painter->drawRect(rect);
    painter->restore();
}

prepareGeometryChange() предназначен для другого - он вызывается перед изменением геометрии айтема и ничего не делает, если не установлен флаг ItemSendsGeometryChanges и не переопределен метод itemChange(), который должен реагировать на изменения геометрии.
Внутри отрисовки айтема менять его геометрию нельзя - это приведет к рекурсивой перерисовке.

Это сообщение отредактировал(а) math64 - 8.4.2014, 08:45
PM   Вверх
wowka19
Дата 9.4.2014, 01:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 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
PM MAIL   Вверх
math64
Дата 9.4.2014, 07:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 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 на панели инструментов.


PM   Вверх
wowka19
Дата 9.4.2014, 09:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 285
Регистрация: 30.11.2011

Репутация: нет
Всего: нет



Спасибо. Еще пара вопросов:

1. Зачем в MyGraphicsView::drawForeground(QPainter *painter, const QRectF &) нужные painter->save() и painter->restore() ??? Каков жизненный путь этого *painter (где/когда рождается и где/когда умирает)?

2. 
Цитата(math64)

Толщина линии задается в setPen(), при этом меняется геометрия, ПОЭТОМУ setPen() нужно делать при изменении масштаба - в обработчике колеса мыши и/или выборе масштаба в QComboBox на панели инструментов.

 - не понял причинно-следственную связь: это рекомендация или обязательное предписание, диктуемое особенностями Qt? Просто толщина линии высчитывается автоматически в зависимости от текущего масштаба, я просто реализовал это в paint()'е итема. Так нельзя?

3. И самое главное: ты рекомендуешь реализовать рамку в view. А как в этом случае я буду с ней взаимодействовать? К примеру, используя QGraphicsItem, я могу менять вид курсора при наведении на него (и даже на определенные его части, что важно) и т.д.

Это сообщение отредактировал(а) wowka19 - 9.4.2014, 09:50
PM MAIL   Вверх
Glad
Дата 9.4.2014, 11:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 16.8.2005

Репутация: нет
Всего: нет



Привет!
У меня тоже возник вопрос по, так сказать, около-теме. Почему в QPixmapItem boundingRect не пересчитывается при изменении траснформа ? Т.е.:
Код

QMatrix mat;
mat.scale(fScale, fScale);
QRectF rectBefore = m_pPixmapItem->boundingRect();
m_pPixmapItem->setTtasform(QTransform(mat), true);
QRectF rectAfter= m_pPixmapItem->boundingRect();


В общем rectBefore == rectAfter.



Это сообщение отредактировал(а) Glad - 9.4.2014, 11:08
PM MAIL   Вверх
math64
Дата 9.4.2014, 11:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 23
Всего: 72



painter->save() и painter->restore() нужны для сохранения состояния painter. В моем примере - старого пера. Возможно, можно обойтись и без этого.
Размеры айтемов должны быть рассчитаны ДО отрисовки

В зависимости от сложности проекта можно рисовать рамки в GraphicsView::drawForeground() или в своих GraphicsItem.
Выбирай сам.
GraphicsView все равно нужно переопределять - для обработки (некотрых) из следующих событий: 
Код

class MyGraphicsView : public QGraphicsView
{
      ...
protected:
         void dragEnterEvent(QDragEnterEvent *event); 
         void dragMoveEvent(QDragMoveEvent *event);
         void dropEvent(QDropEvent *event);
         void keyPressEvent(QKeyEvent *event);

         virtual void mouseDoubleClickEvent(QMouseEvent *event);
         virtual void mouseMoveEvent(QMouseEvent *event);
         virtual void mousePressEvent(QMouseEvent *event);
         virtual void mouseReleaseEvent(QMouseEvent *event);
         virtual void wheelEvent(QWheelEvent *event);
         virtual void drawBackground(QPainter *painter, const QRectF &rect);
         virtual void drawForeground(QPainter *painter, const QRectF &rect);
}

Если выделяется только один item - обработку его перемещений можно делать как в специальном классе рамки, так и в GraphicsView (при выыделении запоминать указатель на выделенный айтем в поле класса)

PM   Вверх
wowka19
Дата 10.4.2014, 14:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 285
Регистрация: 30.11.2011

Репутация: нет
Всего: нет



попутно пара вопросов:
1. Почему при вызове setCursor(Qt::SizeFDiagCursor) или даже setCursor(QCursor(Qt::SizeFDiagCursor)) в конструкторе наследника QGraphicsRectItem вылетает ошибка:
Цитата(wowka19)
QGraphicsItem::setCursor: невозможно преобразовать параметр 1 из "Qt::CursorShape" в "const QCursor &"
Причина: невозможно преобразовать "Qt::CursorShape" в "const QCursor"

пока я не допишу в .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
PM MAIL   Вверх
math64
Дата 11.4.2014, 07:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 23
Всего: 72



1. Делай так:
Код

setCursor(QCursor(Qt::SizeFDiagCursor) );

Qt::SizeFDiagCursor не курсор, а enum. Компилтор не может сам разобраться, как его преобразовать в QCursor.

2. Движением item'a по сцене занимется класс QGraphicsView. он не знает конктреный тип item'а и поэтому изменяет только pos. Если нужно друое поведение, переопредели методы
Цитата(math64 @  9.4.2014,  11:29 Найти цитируемый пост)

Код

         virtual void mouseMoveEvent(QMouseEvent *event);
         virtual void mousePressEvent(QMouseEvent *event);
         virtual void mouseReleaseEvent(QMouseEvent *event);



Добавлено через 4 минуты и 19 секунд
PS:
Код

#include <QCursor>

нужен, потому, что в #include <QGraphicsItem> есть только предварительное объавление QCursor, так что компилятор знает, что есть класс QCursor, но не знает его методы.
PM   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С/С++: Кроссплатформенное программирование, QT/Gtk+/wxWidgets"
JackYF
Любитель
  • В заголовке темы в квадратных скобках обозначьте используемую вами библиотеку, например: [QT],[GTK],[wx].
  • Если вопрос актуален только для некоторой версии библиотеки, либо, если вы пользуетесь не самой последней версией, укажите это. Например: [QT4], [GTK2].
  • Все начинающие изучать Qt - не забудьте зайти сюда.
  • Проставьте несколько ключевых слов темы, чтобы её можно было легче найти.
  • В вопросе укажите полную версию версию библиотеки, а также все дополнительные используемые программные пакеты.
  • Не забывайте пользоваться кнопкой "Код".
  • Телепатов на форуме нет! Задавайте чёткий, конкретный и полный вопрос. Указывайте полностью ошибки компилятора и компоновщика.
  • Новое сообщение должно иметь прямое отношение к тематике этого раздела. Флуд, флейм, оффтопик запрещены.
  • Категорически запрещается обсуждение вареза, "кряков", взлома программ и т.д.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, Любитель.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | С/С++: Кроссплатформенное программирование, Qt/Gtk+/wxWidgets | Следующая тема »


 




[ Время генерации скрипта: 0.1291 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.