PDA

View Full Version : qgraphicslineitem displaying full width or height of view



mcarter
21st March 2013, 00:53
I have a QGraphicsView that is showing some data. The user wants to be able to lay down some "markers" on this view that stretch the full length of the viewport, either full width or full height. The user also wants to be able to move these markers.

I thought of starting with the QGraphicsLineItem, but I am not sure how to set the width or height based on the viewport since the line item is held in scene coordinates. I need this to work on any scaling of the scene. Also, I believe I will need a small buffer around the line to make it easier to grab, but again this is in scene coordinates . . . would just like to add a couple of pixels on either side of line.

So, is QGraphicsLineItem the way to go or do I implement something within the view itself to control the markers and their drawing?

d_stranz
21st March 2013, 01:24
I faced a similar problem where I needed to overlay labels (of fixed size, independent of the scene scaling) onto the viewport in such a way that collisions among labels were eliminated. Which labels are displayed of course depends on the region of the scene being displayed in the view, so I couldn't use collision detection at the scene level.

The way I solved this was to use two scenes: one that could be shared among multiple views, and a view-specific scene dedicated and owned by each view. I derived a custom QGraphicsView class, and created the overlay scene in its constructor. I then reimplemented the QGraphicsView::drawForeground() method, and in that, I render the dedicated scene.

With this method, you can use QGraphicsItem types in the dedicated scene with all the benefits of scaling, rotation, etc. The major tricky bit is you need to synchronize the world coordinates of the dedicated scene to match the portion of the world displayed in the viewport.

Thanks to Wysota for suggesting this idea in this thread (http://www.qtcentre.org/threads/53274-More-on-selective-QGraphicsItem-visibility), following up on a question first posed in this thread (http://www.qtcentre.org/threads/53103-QGraphicsView-QGraphicsItem-selective-visibility-question).

mcarter
21st March 2013, 18:03
The way I solved this was to use two scenes: one that could be shared among multiple views, and a view-specific scene dedicated and owned by each view. I derived a custom QGraphicsView class, and created the overlay scene in its constructor. I then reimplemented the QGraphicsView::drawForeground() method, and in that, I render the dedicated scene.
hmm, that is an interesting concept. I asume you used some form of the draw items section of the QGraphicsView paintEvent method to draw the dedicated scene . . . and adjusting the transform based on multi-view scene and calling drawItems of scene.


With this method, you can use QGraphicsItem types in the dedicated scene with all the benefits of scaling, rotation, etc.
But if I do not need any of the benefits of the scene transform (all I need is position), is storing them in a dedicated scene the right place?

I also have a need to be able to move the items in the view. If these items are rendered from the dedicated scene, do I have access to grab and move them? Do I need to pass the mouse events to the dedicated scene first or will I need to implement the mouse controls in the view?

d_stranz
21st March 2013, 23:23
I asume you used some form of the draw items section of the QGraphicsView paintEvent

No, as I said, I implemented QGraphicsView::drawForeground() in my derived class, and call QGraphicsScene::render() on the captive scene.


But if I do not need any of the benefits of the scene transform (all I need is position), is storing them in a dedicated scene the right place?

It makes life easier, because these are view-specific items and can't be part of the generic scene. If you never intend to share the same scene with multiple views, then you can simply put them in the main scene and scale them appropriately. But if you will be displaying the same scene in several places, with potentially different viewports and scales, then you need to use a different mechanism. I found this easier to implement. The guts of it are dead simple:



void CustomView::drawForeground( QPainter * pPainter, const QRectF & rect )
{
QGraphicsView::drawForeground( pPainter, rect );
mForegroundScene.render( pPainter, rect, QRectF(), Qt::IgnoreAspectRatio );
}

void CustomView::onSceneRectChanged( const QRectF & rect )
{
setSceneRect( rect );
mForegroundScene.setSceneRect( rect );
}

void CustomView::onZoomed( const QRectF & zoomRect )
{
QRectF canvasRect = rect();

double xScale = ( canvasRect.width() / zoomRect.width() );
double yScale = ( canvasRect.height() / zoomRect.height() );

resetTransform();
centerOn( zoomRect.center() );
scale( xScale, yScale );

mForegroundScene.setSceneRect( zoomRect.normalized() );
}


where CustomView is derived from QGraphicsView and has a QGraphicsScene mForeGroundScene member variable. The onSceneRectChanged is connected to the shared scene's sceneRectChanged() signal, onZoomed() is a slot connected to a zoomed() signal emitted by the QWidget-based class that holds the CustomView instance in a layout.


Do I need to pass the mouse events to the dedicated scene first or will I need to implement the mouse controls in the view?

Good question, and I will eventually have to solve it because I will have the same need in my CustomView.

I am not sure how you would implement mouse interaction, except by doing it in the view itself. The foreground scene doesn't really have any views associated with it (including the one that owns it), so it won't receive any mouse events. I assume you could forward the view's events to the foreground scene or perhaps simply query the foreground scene for any items it contains at the given mouse position.

Perhaps Wysota has already solved this one too ;)

P. S. There's actually more to this custom view than the above - I also implemented the drawBackground() method and added the optional ability to display a QPixmap as an underlay to data graphics and markers drawn above it in the main scene and the overlay scene. The purpose of this whole exercise was to implement a Qwt-style data plotting widget that uses a QGraphicsView as its central canvas. The background pixmap could represent a biological image, like a micrograph of a cell. I might want to overlay that with labels for different parts of the cell, line plots to show the concentration of certain chemicals, and so forth. The same scene data might be viewed in multiple windows, at different magnification scales or locations, so there are things that are scene-specific and other things that are view-specific. The mechanism suggested by Wysota seemed like a flexible way to support all those layers.