PDA

View Full Version : QGraphicsView: determining the current scale factor



chezifresh
23rd April 2009, 02:37
I have a QGraphicsView where I'm allowing the user to change the zoom using CTRL-MouseWheel. The way QGraphicsView::scale(float, float) is setup it requires you to store the previous scale.

Ex:


def wheelEvent(self, evt):
mods = evt.modifiers()
if mods == Qt.ControlModifier and evt.orientation() == Qt.Vertical:
# Most mouse types work in steps of 15 degrees
# in which case the delta value is a multiple of 120;
# i.e., 120 units * 1/8 = 15 degrees
numDegrees = evt.delta() / 8
numSteps = numDegrees / 15

newScale = self._scale + ((5*numSteps)/100.0)
if newScale > 0.0 and newScale < 5.0:
self.resetMatrix()
self.scale(newScale, newScale)
self._scale = newScale
return
QtGui.QGraphicsView.wheelEvent(self, evt)


First off... is there a better way of doing this?

This all falls apart because it relies on the fact that "self._scale" represents the current scale factor applied to the matrix. As well as a myriad of other issues that are caused by calling resetMatrix().

So I was wondering if there is a way to derive the current scale factor. I assumed there isn't since its not part of the public API... so I'm hoping someone has come up with a smarter way to zoom in/out.

By the way this works fine if I just want to zoom in/out but I'm also doing a fitToSelection() call which messes up everything.

Full code is available here in case anyone is interested:
http://www.qtcentre.org/forum/f-qt-programming-2/t-how-to-browse-images-in-thumbnail-view-15739.html

robertson1
26th April 2009, 20:55
You can calculate it from the view rect by comparing the rect in view and scene coordinates

chezifresh
27th April 2009, 18:33
is it really that easy? Seems like they should have an accessor for this. Either way, thanks for the tip

wysota
27th April 2009, 18:59
You should save the (relative) scale factor as a separate local variable in the item. Trying to recalculate it from the view or from the matrix itself will work only in some of the cases.

chezifresh
27th April 2009, 21:19
Thanks for the reply, but does that help when calling the fitInView (http://doc.trolltech.com/4.5/qgraphicsview.html#fitInView-3) method?

I have a fitSelectedItems method that will fit the view to the currently selected items which alters the transformation matrix out of my control. I could override the method and put my own logic in place but that kind of defeats the purpose of using the builtin method.

In my case I'm not doing any shearing or rotating so it might be sufficient to do what 'robertson1' suggested (unless you can think of a reason why thats not true).

wysota
27th April 2009, 22:47
I'm not sure what you mean. FitInView can take a QGraphicsItem pointer so you can make sure the whole item is visible without knowing its transform. And if you need to know the rectangle occupied by any item, ask it for its QGraphicsItem::sceneBoundingRect() (or for a united bounding rect for a bunch of items you find interesting). Neither of these has much to do with the "scale" of the item. If the item has a "50%" scale but its parent has a "50%" scale as well, then the sceneBoundingRect() size will be 25% of the boundingRect() size although the scale of the item is 50%. So what you want is either a so called "level of detail" which you can transform by dividing the rectangle in the view occupied by the item by the size of its bounding rect (25% in our example) or a relative scale of the item (50%) which you should keep in the item itself. Also remember that rotating an item influences its sceneBoundingRect() but not its relative scale.

chezifresh
27th April 2009, 23:35
Thats a good point the painter does have a level of detail property that could be used for this.

My situation is rather simple since I'm just scaling then entire view, not individual items.

So when I'm using fitToView it scales the view to the sceneBoundingRect() of the item. The kicker is then if I want to zoom in or out from that point I have to start with a relative scale factor. Thats where the level of detail comes in play.

I also just took a peek at the QGraphicsView source and this is peppered throughout the code:


// Find the ideal x / y scaling ratio to fit \a source into \a target.
qreal xratio = targetRect.width() / sourceRect.width();
qreal yratio = targetRect.height() / sourceRect.height();

// Scale according to the aspect ratio mode.
switch (aspectRatioMode) {
case Qt::KeepAspectRatio:
xratio = yratio = qMin(xratio, yratio);
break;
case Qt::KeepAspectRatioByExpanding:
xratio = yratio = qMax(xratio, yratio);
break;
case Qt::IgnoreAspectRatio:
break;
...
painterMatrix *= QTransform()
.translate(targetRect.left(), targetRect.top())
.scale(xratio, yratio)
.translate(-sourceRect.left(), -sourceRect.top());
}

wysota
28th April 2009, 07:14
The kicker is then if I want to zoom in or out from that point I have to start with a relative scale factor. Thats where the level of detail comes in play.

This one you can keep yourself in the view and update it every time you zoom the view. You don't need to know the transforms of individual items.