From 2c679dfbcc910b9ad3664ef97454fd4d2db18fca Mon Sep 17 00:00:00 2001 From: Luke Campagnola <> Date: Wed, 31 Oct 2012 01:57:00 -0400 Subject: [PATCH] ViewBox will now correctly auto-range when an item's position or transform changes. --- graphicsItems/GraphicsItem.py | 16 +++- graphicsItems/GraphicsObject.py | 3 + graphicsItems/ViewBox/ViewBox.py | 143 +++++++++++++++++-------------- 3 files changed, 96 insertions(+), 66 deletions(-) diff --git a/graphicsItems/GraphicsItem.py b/graphicsItems/GraphicsItem.py index 347d3886..7eee89f3 100644 --- a/graphicsItems/GraphicsItem.py +++ b/graphicsItems/GraphicsItem.py @@ -433,4 +433,18 @@ class GraphicsItem(object): """ Called whenever the transformation matrix of the view has changed. """ - pass \ No newline at end of file + pass + + #def prepareGeometryChange(self): + #self._qtBaseClass.prepareGeometryChange(self) + #self.informViewBoundsChanged() + + def informViewBoundsChanged(self): + """ + Inform this item's container ViewBox that the bounds of this item have changed. + This is used by ViewBox to react if auto-range is enabled. + """ + view = self.getViewBox() + if view is not None and hasattr(view, 'implements') and view.implements('ViewBox'): + view.itemBoundsChanged(self) ## inform view so it can update its range if it wants + \ No newline at end of file diff --git a/graphicsItems/GraphicsObject.py b/graphicsItems/GraphicsObject.py index f893d8dc..4361d1e6 100644 --- a/graphicsItems/GraphicsObject.py +++ b/graphicsItems/GraphicsObject.py @@ -11,10 +11,13 @@ class GraphicsObject(GraphicsItem, QtGui.QGraphicsObject): _qtBaseClass = QtGui.QGraphicsObject def __init__(self, *args): QtGui.QGraphicsObject.__init__(self, *args) + self.setFlag(self.ItemSendsGeometryChanges) GraphicsItem.__init__(self) def itemChange(self, change, value): ret = QtGui.QGraphicsObject.itemChange(self, change, value) if change in [self.ItemParentHasChanged, self.ItemSceneHasChanged]: self._updateView() + if change in [self.ItemPositionHasChanged, self.ItemTransformHasChanged]: + self.informViewBoundsChanged() return ret diff --git a/graphicsItems/ViewBox/ViewBox.py b/graphicsItems/ViewBox/ViewBox.py index d403291b..89fa1487 100644 --- a/graphicsItems/ViewBox/ViewBox.py +++ b/graphicsItems/ViewBox/ViewBox.py @@ -109,6 +109,7 @@ class ViewBox(GraphicsWidget): 'background': None, } + self._updatingRange = False ## Used to break recursive loops. See updateAutoRange. self.setFlag(self.ItemClipsChildrenToShape) @@ -340,6 +341,7 @@ class ViewBox(GraphicsWidget): ============= ===================================================================== """ + changes = {} if rect is not None: @@ -471,6 +473,7 @@ class ViewBox(GraphicsWidget): #if not enable: #import traceback #traceback.print_stack() + if enable is True: enable = 1.0 @@ -520,74 +523,84 @@ class ViewBox(GraphicsWidget): self.updateAutoRange() def updateAutoRange(self): - targetRect = self.viewRange() - if not any(self.state['autoRange']): + ## Break recursive loops when auto-ranging. + ## This is needed because some items change their size in response + ## to a view change. + if self._updatingRange: return - - fractionVisible = self.state['autoRange'][:] - for i in [0,1]: - if type(fractionVisible[i]) is bool: - fractionVisible[i] = 1.0 - - childRange = None - - order = [0,1] - if self.state['autoVisibleOnly'][0] is True: - order = [1,0] - - args = {} - for ax in order: - if self.state['autoRange'][ax] is False: - continue - if self.state['autoVisibleOnly'][ax]: - oRange = [None, None] - oRange[ax] = targetRect[1-ax] - childRange = self.childrenBounds(frac=fractionVisible, orthoRange=oRange) + + self._updatingRange = True + try: + targetRect = self.viewRange() + if not any(self.state['autoRange']): + return - else: - if childRange is None: - childRange = self.childrenBounds(frac=fractionVisible) - - ## Make corrections to range - xr = childRange[ax] - if xr is not None: - if self.state['autoPan'][ax]: - x = sum(xr) * 0.5 - #x = childRect.center().x() - w2 = (targetRect[ax][1]-targetRect[ax][0]) / 2. - #childRect.setLeft(x-w2) - #childRect.setRight(x+w2) - childRange[ax] = [x-w2, x+w2] - else: - #wp = childRect.width() * 0.02 - wp = (xr[1] - xr[0]) * 0.02 - #childRect = childRect.adjusted(-wp, 0, wp, 0) - childRange[ax][0] -= wp - childRange[ax][1] += wp - #targetRect[ax][0] = childRect.left() - #targetRect[ax][1] = childRect.right() - targetRect[ax] = childRange[ax] - args['xRange' if ax == 0 else 'yRange'] = targetRect[ax] - #else: - ### Make corrections to Y range - #if self.state['autoPan'][1]: - #y = childRect.center().y() - #h2 = (targetRect[1][1]-targetRect[1][0]) / 2. - #childRect.setTop(y-h2) - #childRect.setBottom(y+h2) - #else: - #hp = childRect.height() * 0.02 - #childRect = childRect.adjusted(0, -hp, 0, hp) + fractionVisible = self.state['autoRange'][:] + for i in [0,1]: + if type(fractionVisible[i]) is bool: + fractionVisible[i] = 1.0 + + childRange = None + + order = [0,1] + if self.state['autoVisibleOnly'][0] is True: + order = [1,0] + + args = {} + for ax in order: + if self.state['autoRange'][ax] is False: + continue + if self.state['autoVisibleOnly'][ax]: + oRange = [None, None] + oRange[ax] = targetRect[1-ax] + childRange = self.childrenBounds(frac=fractionVisible, orthoRange=oRange) - #targetRect[1][0] = childRect.top() - #targetRect[1][1] = childRect.bottom() - #args['yRange'] = targetRect[1] - if len(args) == 0: - return - args['padding'] = 0 - args['disableAutoRange'] = False - #self.setRange(xRange=targetRect[0], yRange=targetRect[1], padding=0, disableAutoRange=False) - self.setRange(**args) + else: + if childRange is None: + childRange = self.childrenBounds(frac=fractionVisible) + + ## Make corrections to range + xr = childRange[ax] + if xr is not None: + if self.state['autoPan'][ax]: + x = sum(xr) * 0.5 + #x = childRect.center().x() + w2 = (targetRect[ax][1]-targetRect[ax][0]) / 2. + #childRect.setLeft(x-w2) + #childRect.setRight(x+w2) + childRange[ax] = [x-w2, x+w2] + else: + #wp = childRect.width() * 0.02 + wp = (xr[1] - xr[0]) * 0.02 + #childRect = childRect.adjusted(-wp, 0, wp, 0) + childRange[ax][0] -= wp + childRange[ax][1] += wp + #targetRect[ax][0] = childRect.left() + #targetRect[ax][1] = childRect.right() + targetRect[ax] = childRange[ax] + args['xRange' if ax == 0 else 'yRange'] = targetRect[ax] + #else: + ### Make corrections to Y range + #if self.state['autoPan'][1]: + #y = childRect.center().y() + #h2 = (targetRect[1][1]-targetRect[1][0]) / 2. + #childRect.setTop(y-h2) + #childRect.setBottom(y+h2) + #else: + #hp = childRect.height() * 0.02 + #childRect = childRect.adjusted(0, -hp, 0, hp) + + #targetRect[1][0] = childRect.top() + #targetRect[1][1] = childRect.bottom() + #args['yRange'] = targetRect[1] + if len(args) == 0: + return + args['padding'] = 0 + args['disableAutoRange'] = False + #self.setRange(xRange=targetRect[0], yRange=targetRect[1], padding=0, disableAutoRange=False) + self.setRange(**args) + finally: + self._updatingRange = False def setXLink(self, view): """Link this view's X axis to another view. (see LinkView)"""