PDA

View Full Version : Drawing on QGraphicsView



JonathanForQT4
27th March 2007, 11:09
Hey guys, I'm trying to draw some stuff within the viewport of QGraphicsView....when I do this within paintEvent it doesn't work for some reason.

Is the QGraphicsScene for the view overwriting what I am drawing (..although my scene's color is transparent)? Any help ideas would be appreciated.

Thanks,
Jonathan

wysota
27th March 2007, 11:12
In general you shouldn't draw on the graphics view directly from within the paint event. Instead use drawBackground() and drawForeground() methods.

JonathanForQT4
27th March 2007, 11:31
I just wrote drawForeground, since I want to draw a scale (for x and y) of the scene I have, I called drawForeground from paintEvent since I need it updated whenever I call update(). The painter I pass it is QPainter mainPainter(this); (from paintEvent) and the Rect I pass it is the rect I defined with setSceneRect....since coordinates are relative to the scene.

The scale would look like your are looking through a rifle scope and would be over the viewport. I got it working when I had defined a widget (everything written in the paintEvent) within a qscrollarea....but now I'm using QGraphicsView and am having trouble with drawing whatsoever.

thanks again,

Jonathan

wysota
27th March 2007, 13:06
But why do you reimplement the paint event in the first place? You should reimplement drawForeground or drawBackground instead.

JonathanForQT4
27th March 2007, 13:14
I did reimplement drawForeground...but it's not painting anything within the viewport....

wysota
27th March 2007, 13:18
How did you reimplement it? Can we see the code? Did you take into consideration that all coordinates are in the scene coordinate system?

JonathanForQT4
28th March 2007, 10:43
void someclass::drawForeground(QPainter *painter, const QRectF &rect){

if(m_measureDistDragging){
QPen tmp;
tmp.setColor(QColor(230, 100, 50, 200));
tmp.setWidth(1);
painter->setPen(tmp);
painter->drawLine(m_measureDistStartPoint, m_currentMousePos);
}

}

when the mouse is clicked m_measureDistStartPoint is set to mapToScene(event->pos()) since when I use it for drawing it needs to be in scene coordinates. The same applies to m_currentMousePos.

When I am debugging I can see that the line is there, but when I am just using the program, I never see the line drawn. Any ideas?

//edit: btw. m_measureDistDragging is true....

JonathanForQT4
29th March 2007, 17:43
anyone have any ideas? I can see the line drawn when I am debugging and see the line right after this code is executed and the window pops up....I'm guessing it's overwritten in paintEvent or something?

wysota
29th March 2007, 17:58
Did you override the paint event?

JonathanForQT4
30th March 2007, 17:04
I tried the reimplementing paintEvent with the same code (the class was just a widget before and it worked then...no it's a QGraphicsView class....but should still work since QGraphicsView inherits QWidget....), but to no avail...then you said, "But why do you reimplement the paint event in the first place? You should reimplement drawForeground or drawBackground instead."
So...I tried foreground....I'm now assuming that paintEvent is drawing over it?

wysota
30th March 2007, 17:33
No. paintEvent calls drawForeground() after calling drawBackground() and drawItems(). It doesn't do anything after drawForeground.

vermarajeev
2nd April 2007, 08:55
What I think is you have to use QGraphicsscene, then set the graphicsscene to QGraphicsView which takes scene as agument. then add scene->addLine( yourLine ) to where you want either mouseMove, mouseRelease or mousePress.

Once done there is no need to define any paintEvent or drawbackground.
I used this idea to draw a line on QGraphicsView.

Hope this helps

JonathanForQT4
3rd April 2007, 15:48
//edit::thanks wysota for explaining the sequence of events :)

thanks vermarajeev, this is a good quick hack I did not think about. I say hack, because there is considerable lag when it is constantly deleting/redrawing every time the mouse gets moved (meaning the user has activated the measure bar).

The reason I wanted to get this working in paintEvent, drawForeground is because in another post I said: "since I want to draw a scale (for x and y) of the scene I have" and "The scale would look like your are looking through a rifle scope and would be over the viewport."

Both the code above and the AxisScale (aka, rifle scope over viewport) code were in the paintEvent of the previousWidget I had made....

Is there something easy that I am missing as to why I can't see this when I'm putting in the reimplemented paintEvent within my reimplemented QGraphicsView class?
Can someone try this code from above in a paintEvent within a QGraphicsView and tell me it's working?

thanks,

Jonathan

wysota
3rd April 2007, 20:49
Could you show us a screenshot of what you want to achieve?

vermarajeev
4th April 2007, 12:05
Do you want something like this


graphicView::graphicView(QGraphicsScene* s)
:QGraphicsView(s), m_canvas(s){
m_edgeItem = new QGraphicsLineItem( 0, m_canvas );
}

void graphicView::mousePressEvent( QMouseEvent* e ){
m_firstPt = e->pos();
m_lastPt = e->pos();
}
void graphicView::mouseMoveEvent( QMouseEvent* e ){
m_lastPt = e->pos();
}
void graphicView::mouseReleaseEvent( QMouseEvent* e ){
m_lastPt = e->pos();
m_edgeItem->setLine( QLineF( m_firstPt, m_lastPt ) );
}

JonathanForQT4
4th April 2007, 13:45
wysota: I have attached the picture of the measuredist line as it was in my program before I started using qgraphics view (my program was a scrollarea, which you can see with a center widget (white square) plus children widgets...which are the various dots and rectangle...)

The line in this picture is drawn within paintEvent of the central widget (white square on which dots, rectangle and the measuredist line are).

As for "the scale would look like your are looking through a rifle scope and would be over the viewport", which I described earlier...I implemented this a little differently in reality....I kept the axis scales along the edges of the viewport as you can see.....as the user zooms in...the white center widget becomes bigger and the axis scale updates realtime...
when the white center widget is bigger, you can also scroll (duh) and as before....the axis scale updates real time.

vermarajeev: I pretty much did the same thing as you, the reason I say this is a hack is because of two things....the scale I use for the QGraphicsView changes when the user zooms in or out....thus the line thickness has to change in proportion to that...which is annoying, second..the there is a lag for me when I have clicked once and move the mouse around (the line is changing size to where the mouse currently is being moved in realtime). I assume this lag is because it has to draw things to the canvas real time....not exactly sure.
The underlying point is....if I figure out how to draw it in the QGraphicsView drawForeground or paintEvent....the drawing of this should be realtime along with the axisscale (see picture above).

I hope if have made things easier to understand and am anxious to see what you have to say.


Thanks,

Jonathan

P.S. I know there are some things were fixed in 4.2.2 to 4.2.3....should I bother updating to 4.2.3?

//edit: vermarajeev: I just implemented your code...lag still happening....:( Although I update the line everytime a mouseMoveEvent occurs....(and the user has asked to measureDistance).

vermarajeev
4th April 2007, 13:59
I have done a sample program at this link
http://www.qtcentre.org/forum/f-qt-programming-2/t-qgraphicsview-drawing-6412.html

I dont know whether it will help you. It is quite similar to what I given the example above but
with one change. There is drawItems which might help you. I too have some issues.
When I draw lines (straight lines in graphicsView the lines coordinates gets changed). I have put it into new thread coz it is some differnent issue.

Hope it might help you coz as I see you want a QPainter to do stuffs and drawItem provides one.

Anyway let me know the status
Thanks

wysota
4th April 2007, 14:16
The line in this picture is drawn within paintEvent of the central widget (white square on which dots, rectangle and the measuredist line are).
What does the line do? Maybe you could implement it as a rubber band? If not, it's a perfect candidate for using a QGraphicsLineItem. It'll be transformed with the view, so it should be easy to handle.


As for "the scale would look like your are looking through a rifle scope and would be over the viewport", which I described earlier...I implemented this a little differently in reality....I kept the axis scales along the edges of the viewport as you can see.....as the user zooms in...the white center widget becomes bigger and the axis scale updates realtime...
when the white center widget is bigger, you can also scroll (duh) and as before....the axis scale updates real time.
Nice. I would put it in drawBackground() (or drawForeground, depending if you want it drawn over or under the items).

JonathanForQT4
4th April 2007, 14:57
vermarajeev: I have tried to help out with your problem...I hope my help is beneficial.

wysota: the line is to measure the distance between two points. if you look at the picture again you'll see in the bottom right hand corner (in the statusbar) that the info of the line is printed there....simply put: the user can measure the distance between two points and see the line between them.

As for the axis scale, thanks for the compliment, I too think it is rather groovy :)
When I put this code in drawForeground, drawBackground etc. it does not work....this is the same with the measureDistance line (that I want to also draw within drawForeground)! I have also been trying to get both of these things to paint, but they simply don't want or do get painted.

So, back to the code I posted from before....any clue as to why this is not working within my reimplemented drawForeground?

JonathanForQT4
4th April 2007, 16:00
after trying lots of stuff out....drawForeground is not realtime drawing....there's always a lag been it being drawn and sometimes the line doesn't display properly and the old lines don't always get erased.

As far as putting it in paintEvent...when I try to reimplement it my entire scene gets painted white and I don't see any objects...I'm completely baffled.

wysota
4th April 2007, 22:16
I would make the line a regular item. I would create it when mouse button is pressed and destroy when it is released. As you'll have the line coordinates in scene coordinate system, calculating the distance regardless of the scale, etc. should be easy.

wysota
4th April 2007, 22:43
Here is an example of what I mean.

Gopala Krishna
5th April 2007, 13:27
Here is an example of what I mean.

Do you think an update() call is needed in each of the mouse events implemented in the reimplemented scene class? I tried commenting them and it worked fine. :)

wysota
5th April 2007, 13:40
It doesn't slow anything down, so why not? :) But you're right, it's not required.

vermarajeev
7th April 2007, 08:26
Thanks to wysota for his quick solution.
I made a mistake not to override QGraphicsScene. Anyway I'll try getting the solution overriding QGraphicsView and without overriding QGraphicsScene and see how it works.

JonathanForQT4-->
It was nice to see your quick solution. Thanks!!! wysota made my work easy.

Njoy and have fun as I'm with qt4.

//And it works... Anything is possible with qt4 :D

JonathanForQT4
10th April 2007, 11:31
wysota: thanks for your code, it is pretty much the same thing I had already implemented after vermarajeev suggested this:)
The reason I keep posting is because I wanted to find out how to get it to work within drawForeground so that I could get the axis scale painting within there too....Or would you also suggest making the axis scale some items -- I think things would get too sluggish.

I was also thinking that you could add a widget to the scene....and the widget could be the axis scale....any ideas if this is doable?

I was reading this:

http://labs.trolltech.com/blogs/2007/03/09/qgraphicsview-widgets-on-the-canvas/

and this:

http://labs.trolltech.com/blogs/2007/04/02/graphics-view-with-layouts-and-widgets/

and that's why I ask if I should put my axis scale in a widget's paintEvent (like it was before in the picture I attached) and then add the widget overtop of the viewPort from QGraphicsView.

The whole thing seems stupid to me though because QGraphicsView is a inherits QWidget and I should be able to paint over the viewPort, arggggg!

vermarajeev: I have now added thanks for this first suggestion of making the measureDist line an QGraphicsItem

hb
21st May 2007, 17:17
I am also having the task of drawing an axis scale on a QGraphicsView, that is supposed to be at a fixed widget position, with fixed linewidth etc. I think I finally found a solution. Basically, you can just override drawForeground, turn off the world coordinate system mapping and draw directly in view coordinates. The following code would for example draw the axes as two plain lines:



void myGraphicsView::drawForeground(QPainter *painter, const QRectF &rect)
{
int axesOffset = 10;
int bottom = height() - 2*axesOffset;
int right = width() - 2*axesOffset;

painter->setWorldMatrixEnabled(false);
painter->setPen(Qt::white);
painter->drawLine(axesOffset,axesOffset,axesOffset,bottom);
painter->drawLine(axesOffset,bottom,right,bottom);
}

Bitto
21st May 2007, 21:55
No drawing is "real-time" drawing in Qt. Not on X11, not Windows, not OpenGL. There's always an asynchronous mechanism; anything else is bound to cause flicker, or be very inefficient at the very least.

If you want to understand how QGraphicsView::drawBackground and ::drawForeground work, you should create yourself a very small simple example with a 100x100 scene in a plain view, and see what happens if you draw shapes of various colors at different places. Start by drawing the scene's scene rect, filling it with a solid color, and see what happens when you resize the view, and move the scrollbars.



void CustomView::drawBackground(QPainter *painter, const QRectF &exposed)
{
painter->fillRect(sceneRect(), Qt::blue);
}

void CustomView::drawForeground(QPainter *painter, const QRectF &exposed)
{
// note: semi-transparent color, otherwise the items and
// background won't shine through the foreground
painter->fillRect(sceneRect(), QColor(64, 128, 192, 128));
}


Don't expect drawing to be "instantaneous"; that's not how Qt works. :-) Still, you can easily get frame rates beyond 100fps if you do things right.

Btw, I also recommend http://doc.trolltech.com/4.2/graphicsview.html#the-graphics-view-architecture as bed time reading, and if you look at the examples (elastic-nodes uses drawBackground, for example), you'll probably get the picture quite soon :-). Notice especially the part that explains the coordinate systems; since drawForeground and drawBackground render in scene coordinates, but QWidget::paintEvent() renders in device coordinates.

JonathanForQT4
8th June 2007, 16:43
thanks hb I was able to use the painter->setWorldMatrixEnabled(false); trick and can now re use my code from my old implementation of this problem :)

Cheers,
Jonathan