PDA

View Full Version : How and/or when to subclass QGraphicsView and QGraphicsScene and properly use them



wieniew
13th February 2013, 17:10
Have subclassed QGraphicsScene (GridScene) and QGraphicsView (GridView). In GridScene drawBackGround method draw grid with text lables on the hor and ver axis. Must zoom in ,out using QToolButtons, (no scrollbars) on dlg, and pan left, right, up, down with mouse. Cannot get clear view of how to use QGraphicsScene with QGraphicsView.
1. Do override the mouse events in GridView and GridScene to execute the neccessary actions?
a. If do What do in the GridView and what in the GridScene methods?
b. If not, how do propagate the changes to the QGraphicsScene?
2. When is it necessary to subclass QGraphicsScene, will it be better to draw the grid on the scene in the GridView class?
3. If also override QGraphicsView:: drawBackGround, what needs to be drawn here and what in GridScene:: drawBackGround ?
4. Do I need to override paintevent in either or both GridView and GridScene?



class GridView : public QGraphicsView
{
Q_OBJECT

public:
GridView(QWidget* parent = 0);

private:
...
public slots:
void zoomIn ();
void zoomOut();

private:
GridScene* m_scene;
int m_minScaleFactor;
int m_maxScaleFactor;
int m_curScaleFactor;

};




GridView::GridView(QWidget* parent)
: QGraphicsView(parent)
{
setGeometry(QRect(0, 0, 474, 621));
setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
setDragMode (QGraphicsView::ScrollHandDrag);
m_minScaleFactor = 0;
m_maxScaleFactor = 500;
m_curScaleFactor = 250;
m_curPanX = 0;
m_curPanY = 0;

m_scene = new GridScene;
this->setScene (m_scene);
m_scene->setSceneRect(QRectF(0, 0, 1500, 1500));
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);

// setViewportUpdateMode(QGraphicsView::FullViewPortU pdate);
}

void GridView::zoomIn ()
{
qreal scale = qPow(qreal(2), (m_curScaleFactor - 250) / qreal(50));
QTransform transform;
transform.scale(scale, scale);
m_curScaleFactor = transform.m11();
}

void GridView::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
m_pan = true;
m_panStartX = event->x();
m_panStartY = event->y();
setCursor(Qt::ClosedHandCursor);
event->accept();
return;
}
event->ignore();
}

void GridView::mouseMoveEvent(QMouseEvent *event)
{
if (m_pan)
{
m_curPanX = (event->x() - m_panStartX);
m_curPanY = (event->y() - m_panStartY);
m_panStartX = event->x();
m_panStartY = event->y();
event->accept();
return;
}
event->ignore();
}

void GridView::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
m_pan = false;
setCursor(Qt::ArrowCursor);

m_curPanX = event->x();
m_curPanY = event->y();
setCursor(Qt::OpenHandCursor);
event->accept();
return;
}
event->ignore();
}




#include <QGraphicsScene>

class GridScene : public QGraphicsScene
{

public:
GridScene();

protected:
void drawBackground(QPainter *painter, const QRectF &rect);

private:
...

};



GridScene::GridScene()
{
minX = 0.0;
maxX = 10.0;
numXTicks = 12;

minY = 0.0;
maxY = 10.0;
numYTicks = 12;
}

void GridScene::drawBackground(QPainter *painter, const QRectF &rect)
{
QRect rect1(Margin, Margin,
width() - 2 * Margin, height() - 2 * Margin);
if (!rect1.isValid())
return;

for (int i = 0; i <= numXTicks; ++i) {
int x = rect1.left() + (i * (rect1.width() - 1) / numXTicks);
double label = minX + (i * spanX() / numXTicks);
painter->setPen(QPen(QColor(Qt::darkBlue)));
painter->drawLine(x, rect1.top(), x, rect1.bottom());
painter->setPen(QPen(QColor(Qt::darkBlue)));
painter->drawLine(x, rect1.bottom(), x, rect1.bottom() + 5);
painter->drawText(x - 50, rect1.bottom() + 5, 100, 20,
Qt::AlignHCenter | Qt::AlignTop,
QString::number(label));
}
for (int j = 0; j <= numYTicks; ++j) {
int y = rect1.bottom() - (j * (rect1.height() - 1)/ numYTicks);
double label = minY + (j * spanY()
/ numYTicks);
painter->setPen(QPen(QColor(Qt::darkBlue)));
painter->drawLine(rect1.left(), y, rect1.right(), y);
painter->setPen(QPen(QColor(Qt::darkBlue)));
painter->drawLine(rect1.left() - 5, y, rect1.left(), y);
painter->drawText(rect1.left() - Margin, y - 10, Margin - 5, 20,
Qt::AlignRight | Qt::AlignVCenter,
QString::number(label));
}
painter->drawRect(rect1.adjusted(0, 0, -1, -1));
}

Santosh Reddy
13th February 2013, 19:31
Implement these functions in QGraphicsScene, Grid, Text, X-Axis, Y-Axis, Move items, move text, etc
Implement these functions in QGraphicsView, Scale/Zoom, Rotation, Shifting, panning, etc

wieniew
14th February 2013, 10:13
Thanks. I got the panning to work by first calling the relevant QGraphicsView mouse events first in the overriden functions eg. QGraphicsView::mousePressEvent(event). But zooming still a problem. I can zoom with the mouse wheel (and of course then only up and down)but not by pressing the zoom toolbuttons. In the application zooming with mouse wheel is not allowed but first must get it working properly with the buttons. Do I maybe have to override key pressed events in the view and send a key pressed event from the dialog?

Santosh Reddy
14th February 2013, 10:59
Create slots in the view to zoom in/out and emit signals from the zoom toolbuttons.

wieniew
15th February 2013, 10:55
Thank you!
Got it working, I was trying to be too clever in the zoomIn and -Out methods. A plain call to scale(factor, factor) did it.
For the next step I may have to start over. The labels for the axis must be visible at all times, so in effect only the grid lines must move on zoom and pan. I've thought about subclassing QGraphicsTextItem for the X and Y labels, and QGraphicsItem for the gird. The current GridScene class then not needed anymore as GridScene::drawBackGround method will be done in the subclassed items, using QGraphicsAnchorLayout to keep the three items in their proper places in the GridView. Not sure how to proceed though. Forward mouse, and button events to the item subclasses? Is this the best way to do it?