PDA

View Full Version : How to tell when QGraphicsView has changed?



swbluto
30th November 2009, 03:20
Hello, I'm trying to automatically advance() the scene whenever the QGraphicsView changes position. However, I don't know how to tell when the QGraphicsView changes. The scrollHandDrag is enabled, so the user can change the view by scrolling the scroll bars or by dragging the mouse on the scene. I found out that whenever the mouse is being dragged, the "auto mouser dragger" automatically intercepts the mouseMoveEvent so I can't use that when the thing is moved by hand (I had hopes maybe I could disable the scroll bars later so I'd only have to deal with that).

Anyone know?

My long unadulterated, uber-unhelpfully-commented code
is in the next post because this thing has size limits. I'm putting it here just in case it might be helpful to someone.


I tried subclassing the QGraphicsView class in mapView, but apparently the QGraphicsView constructor passes in a scene and it complained when I tried to pass a scene inside the mapView constructor(which defaults to the QGraphicsView constructor) about "private member" or some such, so I abandoned that approach, although I have a feeling I'll have to do that eventually.

wysota
30th November 2009, 08:19
I think the easiest way is to connect to the scrollbars' valueChanged() signal.

scascio
30th November 2009, 10:41
I had same needs and have no scroll bar.

The solution I have found is to listen to the mouse release event after a drag.
Since I manges distincts tools, I know when it is a drag event or not because the tool is activated previously. Then I can get the viewed rect of the scene by asking it to the map.

But this solution does not catch resize event of the view so I need to treat this event too.

If you set scrollbars, then you need to listen to scrollbars changes too.

Maybe this will help you but I am interested if you find a better way to do it, since I find painful to catch 3 events for same operation.

wysota
30th November 2009, 11:06
I had same needs and have no scroll bar.
Sure you have, it's just invisible.

scascio
30th November 2009, 12:48
Do you mean that valueChanged signal is emitted in all cases, even with Qt::ScrollBarAlwaysOff?
Great! I run and try it!

wysota
30th November 2009, 13:43
Do you mean that valueChanged signal is emitted in all cases, even with Qt::ScrollBarAlwaysOff?

The value changes, doesn't it? The fact if you see it on the screen or not is irrelevant.

scascio
30th November 2009, 14:01
Yes. It works very well. For resize events just listen to rangeChanged signal.

So with only 4 lines I listen to view changes :

// somewhere in a derived QGraphicsView method...
connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(changeViewedRect()));
connect(verticalScrollBar() , SIGNAL(valueChanged(int)), this, SLOT(changeViewedRect()));
connect(horizontalScrollBar(), SIGNAL(rangeChanged(int, int)), this, SLOT(changeViewedRect()));
connect(verticalScrollBar() , SIGNAL(rangeChanged(int, int)), this, SLOT(changeViewedRect()));

How great! Thanks

swbluto
1st December 2009, 04:57
Thanks scascio! I reimplemented it in the form below to call the "advance()" function for the scene so that the graphics items can be updated as needed.


connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), scene, SLOT(advance()));
connect(verticalScrollBar() , SIGNAL(valueChanged(int)), scene, SLOT(advance()));
connect(horizontalScrollBar(), SIGNAL(rangeChanged(int, int)), scene, SLOT(advance()));
connect(verticalScrollBar() , SIGNAL(rangeChanged(int, int)), scene, SLOT(advance()));

Problem is, advance() gets called 12 times each time the arrow is clicked on the scroll bar. It gets called hundreds of times when the mouse is used to drag the view. This ends up producing very slow program response.

Ok, so I had an idea. I only care about the view if the actual view has changed. I think I'll check the view's origin mapped to the scene to see if it's changed - if it's actually changed, then *really* advance.

scascio
1st December 2009, 08:29
...
Problem is, advance() gets called 12 times each time the arrow is clicked on the scroll bar. It gets called hundreds of times when the mouse is used to drag the view. ...

How sad. I dont use scrolbars so I didnt notice it, sorry. And I didnt integrated it in my application yet. For dragging event, I though it is normal to to be called for each mouseMove, isn't it?

I think you're right to filter change states of the view in the slot, then call advance. But you will need to check the bottom left corner of the view, won't you? Why only the origin?

And another question that may seems stupid since I dont know your design and since I dont use advance method : why are you connecting this slot to changes of the view? advance is usually used for animation, eg to change state and position of items? I dont understand why items need to advance when you scroll the view of the scene.

wysota
1st December 2009, 10:38
Problem is, advance() gets called 12 times each time the arrow is clicked on the scroll bar. It gets called hundreds of times when the mouse is used to drag the view. This ends up producing very slow program response.

I think you should be calling advance() using a timer and not when the view on the scene changes. You can't expect to control how the user uses your program but you can expect to control what happens in the application itself.