Moved emits after all method state updates since PySide2 immediately executes signals.
Pull request #907 addressed a specific case where a signal was emitted before a state update. If an application's slot then calls back into the instance, the instance was in an inconsistent state. This commit audits and fixes similar issues throughout the pyqtgraph library. This commit fixes several latent issues: * SignalProxy: flush -> sigDelayed -> signalReceived would have incorrectly resulted in timer.stop(). * ViewBox: resizeEvent -> sigStateChange -> background state * ViewBox: setRange -> sigStateChange -> autoranging not updated correctly * ViewBox: updateMatrix -> sigTransformChanged -> any _matrixNeedsUpdate = True -> ignored * Parameter: Child may have missed state tree messages on insert or received extra on remove * GraphicsView: updateMatrix -> sigDeviceRangeChanged/sigDeviceTransformChange -> before propagated to locked viewports.
This commit is contained in:
parent
eb90616ae2
commit
c52382c3b9
@ -67,11 +67,11 @@ class SignalProxy(QtCore.QObject):
|
|||||||
"""If there is a signal queued up, send it now."""
|
"""If there is a signal queued up, send it now."""
|
||||||
if self.args is None or self.block:
|
if self.args is None or self.block:
|
||||||
return False
|
return False
|
||||||
#self.emit(self.signal, *self.args)
|
args, self.args = self.args, None
|
||||||
self.sigDelayed.emit(self.args)
|
|
||||||
self.args = None
|
|
||||||
self.timer.stop()
|
self.timer.stop()
|
||||||
self.lastFlushTime = time()
|
self.lastFlushTime = time()
|
||||||
|
#self.emit(self.signal, *self.args)
|
||||||
|
self.sigDelayed.emit(args)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
|
@ -346,9 +346,9 @@ class DockLabel(VerticalLabel):
|
|||||||
ev.accept()
|
ev.accept()
|
||||||
|
|
||||||
def mouseReleaseEvent(self, ev):
|
def mouseReleaseEvent(self, ev):
|
||||||
|
ev.accept()
|
||||||
if not self.startedDrag:
|
if not self.startedDrag:
|
||||||
self.sigClicked.emit(self, ev)
|
self.sigClicked.emit(self, ev)
|
||||||
ev.accept()
|
|
||||||
|
|
||||||
def mouseDoubleClickEvent(self, ev):
|
def mouseDoubleClickEvent(self, ev):
|
||||||
if ev.button() == QtCore.Qt.LeftButton:
|
if ev.button() == QtCore.Qt.LeftButton:
|
||||||
|
@ -503,8 +503,8 @@ class Flowchart(Node):
|
|||||||
finally:
|
finally:
|
||||||
self.blockSignals(False)
|
self.blockSignals(False)
|
||||||
|
|
||||||
self.sigChartLoaded.emit()
|
|
||||||
self.outputChanged()
|
self.outputChanged()
|
||||||
|
self.sigChartLoaded.emit()
|
||||||
self.sigStateChanged.emit()
|
self.sigStateChanged.emit()
|
||||||
|
|
||||||
def loadFile(self, fileName=None, startDir=None):
|
def loadFile(self, fileName=None, startDir=None):
|
||||||
|
@ -205,8 +205,8 @@ class HistogramLUTItem(GraphicsWidget):
|
|||||||
def regionChanging(self):
|
def regionChanging(self):
|
||||||
if self.imageItem() is not None:
|
if self.imageItem() is not None:
|
||||||
self.imageItem().setLevels(self.getLevels())
|
self.imageItem().setLevels(self.getLevels())
|
||||||
self.sigLevelsChanged.emit(self)
|
|
||||||
self.update()
|
self.update()
|
||||||
|
self.sigLevelsChanged.emit(self)
|
||||||
|
|
||||||
def imageChanged(self, autoLevel=False, autoRange=False):
|
def imageChanged(self, autoLevel=False, autoRange=False):
|
||||||
if self.imageItem() is None:
|
if self.imageItem() is None:
|
||||||
|
@ -711,10 +711,10 @@ class ROI(GraphicsObject):
|
|||||||
|
|
||||||
if hover:
|
if hover:
|
||||||
self.setMouseHover(True)
|
self.setMouseHover(True)
|
||||||
self.sigHoverEvent.emit(self)
|
|
||||||
ev.acceptClicks(QtCore.Qt.LeftButton) ## If the ROI is hilighted, we should accept all clicks to avoid confusion.
|
ev.acceptClicks(QtCore.Qt.LeftButton) ## If the ROI is hilighted, we should accept all clicks to avoid confusion.
|
||||||
ev.acceptClicks(QtCore.Qt.RightButton)
|
ev.acceptClicks(QtCore.Qt.RightButton)
|
||||||
ev.acceptClicks(QtCore.Qt.MidButton)
|
ev.acceptClicks(QtCore.Qt.MidButton)
|
||||||
|
self.sigHoverEvent.emit(self)
|
||||||
else:
|
else:
|
||||||
self.setMouseHover(False)
|
self.setMouseHover(False)
|
||||||
|
|
||||||
|
@ -834,8 +834,8 @@ class ScatterPlotItem(GraphicsObject):
|
|||||||
pts = self.pointsAt(ev.pos())
|
pts = self.pointsAt(ev.pos())
|
||||||
if len(pts) > 0:
|
if len(pts) > 0:
|
||||||
self.ptsClicked = pts
|
self.ptsClicked = pts
|
||||||
self.sigClicked.emit(self, self.ptsClicked)
|
|
||||||
ev.accept()
|
ev.accept()
|
||||||
|
self.sigClicked.emit(self, self.ptsClicked)
|
||||||
else:
|
else:
|
||||||
#print "no spots"
|
#print "no spots"
|
||||||
ev.ignore()
|
ev.ignore()
|
||||||
|
@ -427,8 +427,8 @@ class ViewBox(GraphicsWidget):
|
|||||||
self.updateAutoRange()
|
self.updateAutoRange()
|
||||||
self.updateViewRange()
|
self.updateViewRange()
|
||||||
self._matrixNeedsUpdate = True
|
self._matrixNeedsUpdate = True
|
||||||
self.sigStateChanged.emit(self)
|
|
||||||
self.background.setRect(self.rect())
|
self.background.setRect(self.rect())
|
||||||
|
self.sigStateChanged.emit(self)
|
||||||
self.sigResized.emit(self)
|
self.sigResized.emit(self)
|
||||||
|
|
||||||
def viewRange(self):
|
def viewRange(self):
|
||||||
@ -561,18 +561,18 @@ class ViewBox(GraphicsWidget):
|
|||||||
|
|
||||||
# If nothing has changed, we are done.
|
# If nothing has changed, we are done.
|
||||||
if any(changed):
|
if any(changed):
|
||||||
self.sigStateChanged.emit(self)
|
|
||||||
|
|
||||||
# Update target rect for debugging
|
# Update target rect for debugging
|
||||||
if self.target.isVisible():
|
if self.target.isVisible():
|
||||||
self.target.setRect(self.mapRectFromItem(self.childGroup, self.targetRect()))
|
self.target.setRect(self.mapRectFromItem(self.childGroup, self.targetRect()))
|
||||||
|
|
||||||
# If ortho axes have auto-visible-only, update them now
|
# If ortho axes have auto-visible-only, update them now
|
||||||
# Note that aspect ratio constraints and auto-visible probably do not work together..
|
# Note that aspect ratio constraints and auto-visible probably do not work together..
|
||||||
if changed[0] and self.state['autoVisibleOnly'][1] and (self.state['autoRange'][0] is not False):
|
if changed[0] and self.state['autoVisibleOnly'][1] and (self.state['autoRange'][0] is not False):
|
||||||
self._autoRangeNeedsUpdate = True
|
self._autoRangeNeedsUpdate = True
|
||||||
elif changed[1] and self.state['autoVisibleOnly'][0] and (self.state['autoRange'][1] is not False):
|
elif changed[1] and self.state['autoVisibleOnly'][0] and (self.state['autoRange'][1] is not False):
|
||||||
self._autoRangeNeedsUpdate = True
|
self._autoRangeNeedsUpdate = True
|
||||||
|
|
||||||
|
self.sigStateChanged.emit(self)
|
||||||
|
|
||||||
def setYRange(self, min, max, padding=None, update=True):
|
def setYRange(self, min, max, padding=None, update=True):
|
||||||
"""
|
"""
|
||||||
@ -1156,8 +1156,8 @@ class ViewBox(GraphicsWidget):
|
|||||||
|
|
||||||
self._resetTarget()
|
self._resetTarget()
|
||||||
self.scaleBy(s, center)
|
self.scaleBy(s, center)
|
||||||
self.sigRangeChangedManually.emit(mask)
|
|
||||||
ev.accept()
|
ev.accept()
|
||||||
|
self.sigRangeChangedManually.emit(mask)
|
||||||
|
|
||||||
def mouseClickEvent(self, ev):
|
def mouseClickEvent(self, ev):
|
||||||
if ev.button() == QtCore.Qt.RightButton and self.menuEnabled():
|
if ev.button() == QtCore.Qt.RightButton and self.menuEnabled():
|
||||||
@ -1498,14 +1498,8 @@ class ViewBox(GraphicsWidget):
|
|||||||
|
|
||||||
if any(changed):
|
if any(changed):
|
||||||
self._matrixNeedsUpdate = True
|
self._matrixNeedsUpdate = True
|
||||||
# emit range change signals
|
|
||||||
if changed[0]:
|
|
||||||
self.sigXRangeChanged.emit(self, tuple(self.state['viewRange'][0]))
|
|
||||||
if changed[1]:
|
|
||||||
self.sigYRangeChanged.emit(self, tuple(self.state['viewRange'][1]))
|
|
||||||
self.sigRangeChanged.emit(self, self.state['viewRange'])
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
# Inform linked views that the range has changed
|
# Inform linked views that the range has changed
|
||||||
for ax in [0, 1]:
|
for ax in [0, 1]:
|
||||||
if not changed[ax]:
|
if not changed[ax]:
|
||||||
@ -1514,6 +1508,13 @@ class ViewBox(GraphicsWidget):
|
|||||||
if link is not None:
|
if link is not None:
|
||||||
link.linkedViewChanged(self, ax)
|
link.linkedViewChanged(self, ax)
|
||||||
|
|
||||||
|
# emit range change signals
|
||||||
|
if changed[0]:
|
||||||
|
self.sigXRangeChanged.emit(self, tuple(self.state['viewRange'][0]))
|
||||||
|
if changed[1]:
|
||||||
|
self.sigYRangeChanged.emit(self, tuple(self.state['viewRange'][1]))
|
||||||
|
self.sigRangeChanged.emit(self, self.state['viewRange'])
|
||||||
|
|
||||||
def updateMatrix(self, changed=None):
|
def updateMatrix(self, changed=None):
|
||||||
if not self._matrixNeedsUpdate:
|
if not self._matrixNeedsUpdate:
|
||||||
return
|
return
|
||||||
@ -1541,9 +1542,9 @@ class ViewBox(GraphicsWidget):
|
|||||||
m.translate(-st[0], -st[1])
|
m.translate(-st[0], -st[1])
|
||||||
|
|
||||||
self.childGroup.setTransform(m)
|
self.childGroup.setTransform(m)
|
||||||
|
self._matrixNeedsUpdate = False
|
||||||
|
|
||||||
self.sigTransformChanged.emit(self) ## segfaults here: 1
|
self.sigTransformChanged.emit(self) ## segfaults here: 1
|
||||||
self._matrixNeedsUpdate = False
|
|
||||||
|
|
||||||
def paint(self, p, opt, widget):
|
def paint(self, p, opt, widget):
|
||||||
self.checkSceneChange()
|
self.checkSceneChange()
|
||||||
|
@ -559,8 +559,8 @@ class Parameter(QtCore.QObject):
|
|||||||
self.childs.insert(pos, child)
|
self.childs.insert(pos, child)
|
||||||
|
|
||||||
child.parentChanged(self)
|
child.parentChanged(self)
|
||||||
self.sigChildAdded.emit(self, child, pos)
|
|
||||||
child.sigTreeStateChanged.connect(self.treeStateChanged)
|
child.sigTreeStateChanged.connect(self.treeStateChanged)
|
||||||
|
self.sigChildAdded.emit(self, child, pos)
|
||||||
return child
|
return child
|
||||||
|
|
||||||
def removeChild(self, child):
|
def removeChild(self, child):
|
||||||
@ -571,11 +571,11 @@ class Parameter(QtCore.QObject):
|
|||||||
del self.names[name]
|
del self.names[name]
|
||||||
self.childs.pop(self.childs.index(child))
|
self.childs.pop(self.childs.index(child))
|
||||||
child.parentChanged(None)
|
child.parentChanged(None)
|
||||||
self.sigChildRemoved.emit(self, child)
|
|
||||||
try:
|
try:
|
||||||
child.sigTreeStateChanged.disconnect(self.treeStateChanged)
|
child.sigTreeStateChanged.disconnect(self.treeStateChanged)
|
||||||
except (TypeError, RuntimeError): ## already disconnected
|
except (TypeError, RuntimeError): ## already disconnected
|
||||||
pass
|
pass
|
||||||
|
self.sigChildRemoved.emit(self, child)
|
||||||
|
|
||||||
def clearChildren(self):
|
def clearChildren(self):
|
||||||
"""Remove all child parameters."""
|
"""Remove all child parameters."""
|
||||||
|
@ -50,11 +50,11 @@ class ColorButton(QtGui.QPushButton):
|
|||||||
def setColor(self, color, finished=True):
|
def setColor(self, color, finished=True):
|
||||||
"""Sets the button's color and emits both sigColorChanged and sigColorChanging."""
|
"""Sets the button's color and emits both sigColorChanged and sigColorChanging."""
|
||||||
self._color = functions.mkColor(color)
|
self._color = functions.mkColor(color)
|
||||||
|
self.update()
|
||||||
if finished:
|
if finished:
|
||||||
self.sigColorChanged.emit(self)
|
self.sigColorChanged.emit(self)
|
||||||
else:
|
else:
|
||||||
self.sigColorChanging.emit(self)
|
self.sigColorChanging.emit(self)
|
||||||
self.update()
|
|
||||||
|
|
||||||
def selectColor(self):
|
def selectColor(self):
|
||||||
self.origColor = self.color()
|
self.origColor = self.color()
|
||||||
|
@ -227,12 +227,12 @@ class GraphicsView(QtGui.QGraphicsView):
|
|||||||
else:
|
else:
|
||||||
self.fitInView(self.range, QtCore.Qt.IgnoreAspectRatio)
|
self.fitInView(self.range, QtCore.Qt.IgnoreAspectRatio)
|
||||||
|
|
||||||
self.sigDeviceRangeChanged.emit(self, self.range)
|
|
||||||
self.sigDeviceTransformChanged.emit(self)
|
|
||||||
|
|
||||||
if propagate:
|
if propagate:
|
||||||
for v in self.lockedViewports:
|
for v in self.lockedViewports:
|
||||||
v.setXRange(self.range, padding=0)
|
v.setXRange(self.range, padding=0)
|
||||||
|
|
||||||
|
self.sigDeviceRangeChanged.emit(self, self.range)
|
||||||
|
self.sigDeviceTransformChanged.emit(self)
|
||||||
|
|
||||||
def viewRect(self):
|
def viewRect(self):
|
||||||
"""Return the boundaries of the view in scene coordinates"""
|
"""Return the boundaries of the view in scene coordinates"""
|
||||||
@ -262,7 +262,6 @@ class GraphicsView(QtGui.QGraphicsView):
|
|||||||
h = self.range.height() / scale[1]
|
h = self.range.height() / scale[1]
|
||||||
self.range = QtCore.QRectF(center.x() - (center.x()-self.range.left()) / scale[0], center.y() - (center.y()-self.range.top()) /scale[1], w, h)
|
self.range = QtCore.QRectF(center.x() - (center.x()-self.range.left()) / scale[0], center.y() - (center.y()-self.range.top()) /scale[1], w, h)
|
||||||
|
|
||||||
|
|
||||||
self.updateMatrix()
|
self.updateMatrix()
|
||||||
self.sigScaleChanged.emit(self)
|
self.sigScaleChanged.emit(self)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user