[Qt-interest] Advice on QGraphicsScene, View, ...

Christian Gagneraud cgagneraud at techworks.ie
Mon Jul 12 12:45:18 CEST 2010


Hi all,

I'm currently working on a kind of diagram editor. For now I'm 
focusing only on the basics: background, grid and zoom.

What I would like to do is to add a "mouse position marker" which will 
snap on the grid, typically it should be made of one vertical line, 
spreading all the height of the display area and one horizontal line 
that spread all the width of the display area. But I don't want to 
replace the mouse cursor, which will still be used to reflect the 
current operating mode (selection, move, ...).
My first attempt was to create a specialized QGraphicsItem which seems 
to work OK, but I'm not really sure I'm doing it the right way (event 
problems, and line size problems).

The grid and the zoom is manage in the view, and the position marker 
is managed in the scene (for now).


/////////////////////////////////////////////////////////
//// The marker
/////////////////////////////////////////////////////////

class MousePositionMarker : public QGraphicsItem
{
public:
     explicit MousePositionMarker(QGraphicsScene *parent = 0);

     void move(QPointF);

     virtual QRectF boundingRect() const;
     virtual void paint(QPainter *painter, const 
QStyleOptionGraphicsItem *option, QWidget *widget = 0);

private:
     QGraphicsLineItem *m_line_x;
     QGraphicsLineItem *m_line_y;
};

MousePositionMarker::MousePositionMarker(QGraphicsScene *scene)
{
     setPos(0, 0);
     m_line_x = new QGraphicsLineItem(this);
     m_line_x->setPos(0, 0);
     m_line_y = new QGraphicsLineItem(this);
     m_line_y->setPos(0, 0);
     scene->addItem(this);
}

QRectF MousePositionMarker::boundingRect() const
{
     return m_line_x->boundingRect().united(m_line_y->boundingRect());
}

void MousePositionMarker::paint(QPainter *painter, const 
QStyleOptionGraphicsItem *option, QWidget *widget)
{
     m_line_x->paint(painter, option, widget);
     m_line_y->paint(painter, option, widget);
}

void MousePositionMarker::move(QPointF point)
{
     m_line_x->setPen(QPen(QBrush(Qt::black), 1));
     m_line_x->setPos(m_line_x->x(), point.y());
     m_line_x->setLine(-100, 0, scene()->width()+100, 0);
     m_line_y->setPen(QPen(QBrush(Qt::black), 1));
     m_line_y->setPos(point.x(), m_line_y->y());
     m_line_y->setLine(0, -100, 0, scene()->height()+100);
}

/////////////////////////////////////////////////////////
// The view:
/////////////////////////////////////////////////////////

View::View( QGraphicsScene * scene, QWidget * parent) :
	QGraphicsView(scene, parent), m_drawGrid(true)
{
}

void View::setGrid(Grid *grid)
{
	m_grid = grid;
}

QRectF View::displayedSceneRect() const {
	QSize viewport_size = viewport()->size();
	QTransform view_to_scene   = viewportTransform().inverted();
	QPointF scene_left_top     = view_to_scene.map(QPointF(0.0, 0.0));
	QPointF scene_right_bottom = 
view_to_scene.map(QPointF(viewport_size.width(), viewport_size.height()));
	return(QRectF(scene_left_top, scene_right_bottom));
}

void View::drawBackground(QPainter *p, const QRectF &r)
{
	// White background
	p->save();
	p->setRenderHint(QPainter::Antialiasing, false);
	p->setRenderHint(QPainter::SmoothPixmapTransform, false);
	p->setPen(Qt::NoPen);
	p->setBrush(Qt::white);
	p->drawRect(r);
	p->restore();

	// Draw grid
	if (m_drawGrid)
		m_grid->draw(p, r, this);
}

void View::zoomFitAll()
{
     fitInView(scene()->sceneRect(),
               Qt::KeepAspectRatio);
     emit zoomChanged(transform().m11());
}

void View::zoomFitSelection()
{
}

void View::zoomIn()
{
     if (zoomFactor() < zoomFactorMax) {
         scale(1.2, 1.2);
         emit zoomChanged(transform().m11());
     }
}

void View::zoomOut()
{
     if (zoomFactor() > zoomFactorMin) {
         scale(1/1.2, 1/1.2);
         emit zoomChanged(transform().m11());
     }
}

qreal View::zoomFactor() const
{
     return transform().m11();
}

void View::wheelEvent(QWheelEvent* event)
{
	if (event->modifiers() & Qt::ControlModifier) {
		if (event->delta()>0)
			zoomIn();
		else
			zoomOut();
		event->accept();
	}
	else
		QGraphicsView::wheelEvent(event);
}

void View::mouseMoveEvent(QMouseEvent* event)
{
	QGraphicsView::mouseMoveEvent(event);
}

/////////////////////////////////////////////////////////
//// The scene
/////////////////////////////////////////////////////////
Scene::Scene(QObject *parent) :
     QGraphicsScene(parent)
{
     // A4-ish landscape, 0.1mm pitch
     setSceneRect(0, 0, 3000, 2100);
     // Borders
     addRect(sceneRect(),QPen(Qt::gray,3));

     //
     m_mousePositionMarker = new MousePositionMarker(this);
}

bool Scene::event(QEvent * event )
{
     //
     // This doesn't work, the cast always fail
     //
     QGraphicsSceneHoverEvent *hev = 
dynamic_cast<QGraphicsSceneHoverEvent *>(event);
     if ( hev && m_mousePositionMarker)
         m_mousePositionMarker->move(hev->scenePos());
     }
     return QGraphicsScene::event(event);
}

void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
     //
     // This works, but only when a button is pressed
     //
     if(m_mousePositionMarker)
         m_mousePositionMarker->move(event->scenePos());
     QGraphicsView::mouseMoveEvent(event);
}


I have several problems:

1 - The thickness of the marker lines is one pixel wide (QPen width is 
set to 1 within MousePositionMarker), but this is not one screen 
pixel, so when I zoom in and in (so that 1 pixel in the scene is more 
than one pixel on the screen), the lines became more than one pixel 
wide. I would like to have a 1 pixel line whatever the zoom level.

2 - The position marker moves only when the mouse move *and* a mouse 
button is pressed. What I would like to do is make the marker follow 
the mouse pointer, all the time, but I didn't find any MouseHover event.

3 - I didn't implement the snap to grid thing yet, but I have the 
feeling that I should move the MousePositionMarker into the view, not 
the scene.


Can anyone around enlighten me on these, point me to some examples or 
documentation? Any idea to share?

Thanks,
Chris


-- 
Christian Gagneraud,
Electronics and software engineer

TechWorks Marine Ltd
4a, Park Lane
Dun Laoghaire, Co Dublin
Ireland

Tel: + 353 1 2365990
Fax: + 353 1 2365992
Web: www.techworks.ie





More information about the Qt-interest-old mailing list