Russian Qt Forum

Qt => 2D и 3D графика => Тема начата: Khs от Декабрь 21, 2008, 23:13



Название: QGraphicsView/Scene
Отправлено: Khs от Декабрь 21, 2008, 23:13
Привет всем! Вроде просмотрел все топики в этом форуме касающиеся данного вопроса, но нормального ответа не нашел.

1. Чем отличается setBackgroundBrush(...) у QGraphicsView от аналогичной функции setBackgroundBrush(...) у QGraphicsScene, и что является предпочтительнее. Например в моем случае, я делаю карту страны, на которой буду размещать объекты - дороги, города и тп. И я хотел в background поместить пустую карту страны (чтобы на ней уже потом размещать магистрали и тп.).

2. При установке изображения в background QGraphicsView/QGraphicsScene, изображение множество раз повторяется (тоесть как будто матрица изображений получается). А я хотел бы чтобы оно отображалось 1 раз. Как это реализовать?

3. Какой тип карты лучше использовать, векторный или растровый?!

4. В случае растрового изображения, поместить его в background QGraphicsView/QGraphicsScene не проблема вроде. А как быть с векторным изображением?


Название: Re: QGraphicsView/Scene
Отправлено: Rcus от Декабрь 21, 2008, 23:47
1. отличие в том что у сцены может быть несколько отображений. Метод QGraphicsScene::setBackgroundBrush() задает кисть для всей сцены, метод QGraphicsView::setBackgroundBrush() задает кисть для конкретного отображения. В описанном случае я бы вообще использовал ::drawBackground()

2. см. п.1

3. "this depends on.." Ваша задача не нова, можно изучить решения предшественников.

4. если изображение в формате svg, то в Qt есть модуль его поддержки


Название: Re: QGraphicsView/Scene
Отправлено: Khs от Декабрь 21, 2008, 23:52
п.1 Ну а если сцена одна и вид один, есть ли разница где рисовать фон?!

п.3, Я в курсе что вопрос не новый, просто хотел услышать совета от людей которые этим сами занимались.

п.4, Да, я видел классы для svg, но не совсем понял, как мне все это дело потом отобразить в QGraphicsView. Надо будет еще разок посмотреть.
Кто еще что посоветует?!

-

Такс, ну вроде разобрался :)


Название: Re: QGraphicsView/Scene
Отправлено: AAXEE от Декабрь 25, 2008, 17:05
1. Вроде так:
Представь, что сцена квадратная, и она целиком внутри прямоугольной(не квадратной) view.
Так вот фон у сцены будет занимать квадрат, а фон view будет тот, что вне сцены, но внутри view( полоски по краям).


Название: Re: QGraphicsView/Scene
Отправлено: Khs от Декабрь 25, 2008, 22:36
Ага, я уже разобрался, но все равно спасибо ;)

Буду задавать здесь другие вопросы по мере разработки проги :)


Название: QGraphicsView/Scene
Отправлено: Khs от Декабрь 26, 2008, 14:47
Такс, назрел еще один вопрос.
Во всяких геосистемах есть такая функция, как увеличение определенного участка карты (тоесть выделяешь мышкой какой то прямоугольник на карте и экран автоматически центруется на этом участке и он увеличивается..).
Так же хочу сделать и я у себя, я так полагаю, после выделения участка, необходимо чтобы он отцентровался на области, и там уже применить scale(). Или это делается как то проще?!


Название: Re: QGraphicsView/Scene
Отправлено: QCasper от Декабрь 26, 2008, 14:53
QGraphicsView::centerOn() ?


Название: Re: QGraphicsView/Scene
Отправлено: Khs от Декабрь 26, 2008, 15:10
Точно! Спасибо! :)


Название: QGraphicsView/Scene
Отправлено: Khs от Декабрь 27, 2008, 13:50
Имею 3 своих класса, например, classMainWindow (главное окно, на нем размещен тулбар) унаследован от QMainWindow, classGraphicsView (отображается в главном окне) унаследован от QGraphicsView , classGraphicsScene (сцена своего отображения) унаследован от QGraphicsScene.
Так вот, на тулбаре допустим 3 кнопки (b1, b2, b3) - Они являются переключателями режима редактирования для GraphicsView/Scene, тоесть режим поворота, зума и тп.
Так вот, как лучше сделать связь.

Я предполагаю так, на сигнал нажатия кнопки, вызывается соответствующий слот (например slot_rotate(), slot_zoom() и тп).
Рассмотрю на одном слоте.
Допустим
slot_zoom()
{
   // В классе classMainWindow имеются указатели на объект класса classGraphicsView/Scene
   // m_view, m_scene например...

   // здесь меняю курсор у сцены на соответствующий режиму зума через m_scene->setCursor()
   // теперь нужно чтобы при клике на Scene, выполнялось действие соответствующее
   // включенному режиму.
   // Здесь собственно и хотел бы узнать как лучше сделать.

   // Я думаю сделать так, создать какие то флаги - соответствующие режимам, создать в
   // классе classGraphicsScene метод, например setFlag(int flag), и в вызванном слоте, в данном
   // случае slot_zoom() вызывать m_scene->setFlag(zoom_flag). А в переопределенном
   // mousePress в классе classGraphicsScene уже делать switch(flag) и производить необходимые
   // действия в зависимости от режима.
}

Так вот, моя реализация буит правильной? или есть более умные пути к решению данной задачки..?!


Название: Re: QGraphicsView/Scene
Отправлено: BRE от Декабрь 27, 2008, 14:29
Можно так. Вообще можно придумать много вариантов.
Например, можно в classMainWindow добавить сигнал changedState( State ), который подключать ко всем, кому необходимо отслеживать изменение состояния.


Название: Re: QGraphicsView/Scene
Отправлено: BRE от Декабрь 30, 2008, 09:40
Как вариант, можно сделать что-то типа:
Код
C++ (Qt)
class State
{
public:
virtual void mouseMove(...) = 0;
virtual void mousePress(...) = 0;
virtual void keyboarPress(...) = 0;
};
 
// Переопределить методы для режима zoom
class ZoomState : public State
{
public:
virtual void mouseMove(...);
virtual void mousePress(...);
virtual void keyboarPress(...);
};
 
// Переопределить методы для режима rotate
class RotateState : public State
{
public:
virtual void mouseMove(...);
virtual void mousePress(...);
virtual void keyboarPress(...);
};
 

А в соответствующих обработчиках событий classGraphicsScene:
Код
C++ (Qt)
// currentState это указатель на State
 
void classGraphicsScene::mouseMoveEvent(...)
{
currentState->mouseMove(...);
}
 
void classGraphicsScene::mousePressEvent(...)
{
currentState->mousePress(...);
}
 

Смена режима, это изменение указателя currentState на объект соответствующего класса.


Название: QGraphicsView/Scene
Отправлено: Khs от Декабрь 30, 2008, 17:45
Ну да, по сути можно по-разному все это реализовать, просто я когда спрашивал, хотел узнать как будет оптимальней :)


Название: Re: QGraphicsView/Scene
Отправлено: Khs от Январь 13, 2009, 10:17
Итак, возник вопрос из той же темы :)

Структуру классов описывал выше. Так вот. Сделал так.
В главном окне класса classMainWindow, создал тулбар, на котором имеется допустим 5 тулбаттонов (каждая кнопка это какой-то режим, тоесть задействован может только 1 режим). На группу (QButtonGroup *toolsGroup) повесил коннект:
connect(m_toolsGroup, SIGNAL(buttonClicked(int)), SLOT(toolsGroupClicked(int)));
, где:

void classMainWindow::toolsGroupClicked(int)
{
    m_mapScene->setMode(classGraphicsScene::Mode(m_toolsGroup->checkedId()));
}


Тоесть в зависимости от переключателя в классе сцены устанавливается режим (зум, поворот иль т.п.)

В классе сцены соответственно имеется слот:

void classGraphicsScene::setMode(Mode mode)
{
    m_Mode = mode;

    switch(m_Mode)
    {
        case mode_zoomin:
             //
        break;

        case ...
        ...
    }
}


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

void classGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
     switch(m_Mode)
    {
        case mode_zoomin:
             // надо сделать зум например
        break;

        case ...
        ...
    }
}


Так вот, сам вопрос, scale и rotate, они ж принадлежат объекту класса classGraphicsView. Как мне их вызвать из одного из этих case? Я так полагаю, либо я сделал неправильно архитектуру взаимодействия классов, либо как-то нужно сделать слоты и сигналы, тока не оч. пойму как это лучше сделать :\


Название: Re: QGraphicsView/Scene
Отправлено: Rcus от Январь 13, 2009, 10:40
Я бы это реализовал как перехватчик событий представления, который имеет слот для смены состояния. А внутри класса делегировать полномочия по обработке событий через функциональные указатели/композицию объектов


Название: Re: QGraphicsView/Scene
Отправлено: Rcus от Январь 13, 2009, 14:11
/*hdr*/
Код
C++ (Qt)
#ifndef RVIEWWATCHER_H
#define RVIEWWATCHER_H
 
#include <QObject>
 
class QGraphicsView;
class QEvent;
 
class RViewWatcher : public QObject
{
   Q_OBJECT
public:
   RViewWatcher(QGraphicsView *view, QObject *parent);
   int state() const;
public slots:
   void setState(int id);
protected:
   typedef bool (*Delegate)(QGraphicsView *, QEvent *);
   Delegate _handler;
   int _state;
   bool eventFilter(QObject *watched, QEvent *event);
};
 
#endif // RVIEWWATCHER_H
 
/*src*/
Код
C++ (Qt)
#include "rviewwatcher.h"
#include <QGraphicsView>
#include <QEvent>
#include <QMouseEvent>
 
bool zoomer(QGraphicsView *view, QEvent *event)
{
   static const qreal ZOOM_FACTOR = 1.05;
   if (event->type() != QEvent::MouseButtonPress)
       return false;
   QMouseEvent *e = static_cast<QMouseEvent *>(event);
   switch (e->button()) {
   case Qt::LeftButton:
       view->setMatrix(QMatrix(ZOOM_FACTOR, 0, 0, ZOOM_FACTOR, 0, 0), true);
       break;
   case Qt::RightButton:
       view->setMatrix(QMatrix(1/ZOOM_FACTOR, 0, 0, 1/ZOOM_FACTOR, 0, 0), true);
       break;
   case Qt::MidButton:
       view->resetMatrix();
       break;
   default:
       return false;
   }
   return true;
}
 
bool doNothing(QGraphicsView *view, QEvent *event)
{
   return false;
}
 
RViewWatcher::RViewWatcher(QGraphicsView *view, QObject *parent) : QObject(parent)
{
   view->installEventFilter(this);
   setState(1);
}
 
int RViewWatcher::state() const
{
   return _state;
}
 
void RViewWatcher::setState(int id)
{
   switch (id) {
   case 0:
       _handler = zoomer;
       break;
   case 1:
       _handler = doNothing;
       break;
   default:
       Q_ASSERT(false);
   }
}
 
bool RViewWatcher::eventFilter(QObject *watched, QEvent *event)
{
   if (!qobject_cast<QGraphicsView *>(watched))
       return false;
   return _handler(static_cast<QGraphicsView *>(watched), event);
}
 

Через функциональные указатели конечно просто, но для чего-то серьезного непригодно, а идея композиции уже была указана в сообщении BRE.


Название: Re: QGraphicsView/Scene
Отправлено: pethead от Октябрь 29, 2010, 06:23
не подскажете код как прорисовать в QGraphicsView\Scene фоновую картинку карты? читал читал но че то не все понял. сделал отрисовку в методе QGraphicsView::drawBackground, карта рисуется и масштабируется, но при сдвижке по скроллам карта портится (пошла полосами)


Код
C++ (Qt)
QPixmap map;
 
//так же делаю
view->setDragMode(QGraphicsView::ScrollHandDrag);
view->centerOn(0,0);// карта рисутется от левого верхнего угла сцены - все ок.
//но если подвигать скроллы карта рисуется все время угла видимой области, а не от угла 0,0 сцены.
//(делал и Scene::drawBackground)
 
NetView::NetView(QGraphicsScene *scene, QWidget *parent)
   : QGraphicsView(scene, parent)
{
QString name = "C:/MAP1.bmp";
map.load(name);
}
 
void NetView::drawBackground(QPainter *painter, const QRectF &rect)
{
painter->drawPixmap(rect.left(),rect.top(),map);
}
 

я делал карту через браш:
view->setBackgroundBrush(QPixmap("C:/RUSMAP1.bmp"));
в принципе устраивает, и работает прекрасно, правда если карта маленькая то она размножается как tile :)
так бы сделать только через метод drawBackground но не получается что то ...

**решено:
сделал так:

Код
C++ (Qt)
void cene::drawBackground(QPainter *painter, const QRectF &rect)
{
int w=img.width();
int h=img.height();
QRectF r;
r.setRect(0,0,w,h);
 
painter->drawImage(r,img);
}


Название: Re: QGraphicsView/Scene
Отправлено: GreatSnake от Октябрь 29, 2010, 12:31
Цитировать
я делал карту через браш:
view->setBackgroundBrush(QPixmap("C:/RUSMAP1.bmp"));
Ни в коем случае не доверяйте это делать Qt. Особенно если используете трансформации.

Вот вариант. Работает с очень большими изображениями с любыми трансформациями:
Код
C++ (Qt)
void MyScene::drawBackground( QPainter* p, const QRectF& clip )
{
if( !bg_image_ )
{
QGraphicsScene::drawBackground( p, clip );
return;
}
 
p->eraseRect( clip );
 
QMatrix wm( p->worldMatrix() );
 
if( wm.m11() != 1 &&
clip.intersects( bg_image_->rect() ) )
{
QImage img;
QRectF c( clip );
if( wm.m11() > 1 )
c.adjust( -1, -1, 1, 1 );
 
qreal cx = c.x();
qreal cy = c.y();
qreal cw = qMax( 16., c.width() );
qreal ch = qMax( 16., c.height() );
 
if( cx < 0 )
{
cw += cx;
cx = 0;
}
if( cy < 0 )
{
ch += cy;
cy = 0;
}
cw = qMin( cw, bg_image_->width() - cx );
ch = qMin( ch, bg_image_->height() - cy );
img = bg_image_->copy( QRectF( cx, cy, cw, ch ).toRect() );
cw = qRound( cw * wm.m11() );
ch = qRound( ch * wm.m11() );
img = img.scaled( (int) cw, (int) ch, Qt::IgnoreAspectRatio,
/*wm.m11() > .8 ? Qt::SmoothTransformation :*/ Qt::FastTransformation );
p->setWorldMatrix( QMatrix( 1, 0, 0, 1, wm.dx(), wm.dy() ) );
p->drawImage( QPointF( cx * wm.m11(), cy * wm.m11() ), img );
p->setWorldMatrix( wm );
} else
{
p->drawImage(
QPointF( clip.x(), clip.y() ),
*bg_image_,
QRectF( clip.x(), clip.y(),
qMin( clip.width(), qreal( bg_image_->width() ) ),
qMin( clip.height(), qreal( bg_image_->height() ) ) ) );
}
}
 
QImage   *bg_image_; - предварительно загруженная картинка

Цитировать
void cene::drawBackground(QPainter *painter, const QRectF &rect)
{
   int w=img.width();
   int h=img.height();
   QRectF r;
   r.setRect(0,0,w,h);
 
   painter->drawImage(r,img);      
}
Очень неоптимальный вариант.
При любом изменении вьюпорта будет перерисовываться вся картинка.
А если захотите использовать масштабирование, то Qt каждый раз будет масштабировать ВСЮ(!) картинку.


Название: Re: QGraphicsView/Scene
Отправлено: pethead от Октябрь 29, 2010, 12:46
что такое "изменение вьюпорта"?
так а мне и надо чтоб картинка (карта) тоже масштабировалась.

попробую ваш код - посмотрю что он делает....
посмотрел... при изменении масштаба меняются пропрции карты, а при масштабе 100% она не возвращается в 1:1.
какие то странности., меня устраивает мой неоптимальный код. :)
спасибо за ответы.


Название: Re: QGraphicsView/Scene
Отправлено: GreatSnake от Октябрь 29, 2010, 13:18
Цитировать
посмотрел... при изменении масштаба меняются пропрции карты, а при масштабе 100% она не возвращается в 1:1.
Значит как-то не так масштабируете

Цитировать
какие то странности., меня устраивает мой неоптимальный код. Улыбающийся
спасибо за ответы.
Ну-ну)


Название: Re: QGraphicsView/Scene
Отправлено: pethead от Октябрь 29, 2010, 13:29
как не так?

Код:
    double newScale = scale.left(scale.indexOf(tr("%"))).toDouble() / 100.0;
    QMatrix oldMatrix = view->matrix();
    view->resetMatrix();
    view->translate(oldMatrix.dx(), oldMatrix.dy());
    view->scale(newScale, newScale);


вот приложил скрины. w* это мой код делает, с* это ваш (на 150% он вообще ипортил карту), а у меня все в соответствии с масштабом.
http://narod.ru/disk/26716931000/PIC.ZIP.html


Название: Re: QGraphicsView/Scene
Отправлено: GreatSnake от Октябрь 29, 2010, 15:00
да вроде так.
Вообщем как хочешь, но имей в виду, что Qt в твоём варианте будет постоянно скейлить твою картинку. И чем больше она будет, тем больше будет нагрузка.

Судя по твоим скринам и скроллбарам на них, у тебя какие-то проблемы с геометрией сцены.


Название: Re: QGraphicsView/Scene
Отправлено: ufna от Октябрь 29, 2010, 15:15
Идея Великого Змея нормальна - скейлить только видимую часть - это убыстряет отрисовку когда речь идет о скейле в большую сторону - в минус то Кутэ скейлить очень быстро. А вот реализация судя по скринам немного хромает.


Название: Re: QGraphicsView/Scene
Отправлено: pethead от Октябрь 29, 2010, 15:23
ну не знаю, но с вашим кодом картинка вообще кривая. скриншоты выложил. может ваш код у вас работает и скейлит не постоянно, а по нужде, но у меня вот он так как то заработал (судя по скринам).
на моем варианте загрузил карту, поскалировал, подвигал загрузка процессора не превышает 25% в моменты скалинга, передвижки, а так вообще не занимает проц.


Название: Re: QGraphicsView/Scene
Отправлено: GreatSnake от Октябрь 29, 2010, 15:35
2 ufna
Идея давным-давно внедрена и замечательно работает  :)
Единственный момент, если идёт движение элементов, то могут быть нестыковки на границах в пределах 1pix из-за погрешности вычислений геометрии exposed области. Единственное как удалось побороть - выставить у вью setCacheMode( CacheBackground );

2 pethead
Цитировать
в смысле "будет скейлить"? так мне и надо чтобы он ее скейлил. :) или что не так то? по вашему он должен ее скуйлить, но не постоянно? а когда?
Чтобы отрисовать дырку допустим 4х4 Qt будет масштабировать всю твою картинку

Цитировать
загрузил карту, поскалировал, подвигал загрузка процессора не первышает 25% в моменты скалинга, передвижки, а так вообще не занимает проц.
Вот когда подложка будет не 800х600, а 5000x5000 ты это сразу заметишь ;)

Цитировать
ну не знаю, но с вашим кодом картинка вообще кривая
А drawBackground() переопределяется в наследнике QGraphicsView или QGraphicsScene?
И почему на скринах такие странные скроллбары?


Название: Re: QGraphicsView/Scene
Отправлено: ufna от Октябрь 29, 2010, 15:48
Цитировать
Идея давным-давно внедрена и замечательно работает 
Единственный момент, если идёт движение элементов, то могут быть нестыковки на границах в пределах 1pix из-за погрешности вычислений геометрии exposed области. Единственное как удалось побороть - выставить у вью setCacheMode( CacheBackground );

а, ну вполне может быть, я судил по скринам выше, не более чем. Суть то правильная, хотя на мой взгляд можно еще оптимизировать :)

1 пиксель тут это да, знаю, понимаю )


Название: Re: QGraphicsView/Scene
Отправлено: GreatSnake от Октябрь 29, 2010, 15:51
Цитировать
Суть то правильная, хотя на мой взгляд можно еще оптимизировать
Ну дык, делись идеями ;)


Название: Re: QGraphicsView/Scene
Отправлено: pethead от Октябрь 30, 2010, 13:30
Цитировать
А drawBackground() переопределяется в наследнике QGraphicsView или QGraphicsScene?
И почему на скринах такие странные скроллбары?
QGraphicsView.
что значит странные? подвинутые.


Название: Re: QGraphicsView/Scene
Отправлено: mal от Ноябрь 07, 2010, 14:50
Возможно продублирую ответ Rcus'а.
Сделай две переменные под масштаб и угол разворота.
В соответствующих слотах/эвентах мышки меняй значения этим параметрам и зови функцию устанавливающую вьхе матрицу откуда захочешь.
Типа
Код:
qreal scale;
int angle;
...
slot_scale()
{
   scale = ...
setupMatrix();
}
slot_rotate()
{
angle = ...
setupMatrix();
}

setupMatrix()
{
   QMatrix matrix;
   matrix.rotate(angle);
   matrix.scale(scale, scale);
   view->setMatrix(matrix);
}