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:
Matt Liberty 2019-06-01 16:28:23 -04:00
parent eb90616ae2
commit c52382c3b9
10 changed files with 33 additions and 33 deletions

View File

@ -67,11 +67,11 @@ class SignalProxy(QtCore.QObject):
"""If there is a signal queued up, send it now."""
if self.args is None or self.block:
return False
#self.emit(self.signal, *self.args)
self.sigDelayed.emit(self.args)
self.args = None
args, self.args = self.args, None
self.timer.stop()
self.lastFlushTime = time()
#self.emit(self.signal, *self.args)
self.sigDelayed.emit(args)
return True
def disconnect(self):

View File

@ -346,9 +346,9 @@ class DockLabel(VerticalLabel):
ev.accept()
def mouseReleaseEvent(self, ev):
ev.accept()
if not self.startedDrag:
self.sigClicked.emit(self, ev)
ev.accept()
def mouseDoubleClickEvent(self, ev):
if ev.button() == QtCore.Qt.LeftButton:

View File

@ -503,8 +503,8 @@ class Flowchart(Node):
finally:
self.blockSignals(False)
self.sigChartLoaded.emit()
self.outputChanged()
self.sigChartLoaded.emit()
self.sigStateChanged.emit()
def loadFile(self, fileName=None, startDir=None):

View File

@ -205,8 +205,8 @@ class HistogramLUTItem(GraphicsWidget):
def regionChanging(self):
if self.imageItem() is not None:
self.imageItem().setLevels(self.getLevels())
self.sigLevelsChanged.emit(self)
self.update()
self.sigLevelsChanged.emit(self)
def imageChanged(self, autoLevel=False, autoRange=False):
if self.imageItem() is None:

View File

@ -711,10 +711,10 @@ class ROI(GraphicsObject):
if hover:
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.RightButton)
ev.acceptClicks(QtCore.Qt.MidButton)
self.sigHoverEvent.emit(self)
else:
self.setMouseHover(False)

View File

@ -834,8 +834,8 @@ class ScatterPlotItem(GraphicsObject):
pts = self.pointsAt(ev.pos())
if len(pts) > 0:
self.ptsClicked = pts
self.sigClicked.emit(self, self.ptsClicked)
ev.accept()
self.sigClicked.emit(self, self.ptsClicked)
else:
#print "no spots"
ev.ignore()

View File

@ -427,8 +427,8 @@ class ViewBox(GraphicsWidget):
self.updateAutoRange()
self.updateViewRange()
self._matrixNeedsUpdate = True
self.sigStateChanged.emit(self)
self.background.setRect(self.rect())
self.sigStateChanged.emit(self)
self.sigResized.emit(self)
def viewRange(self):
@ -561,8 +561,6 @@ class ViewBox(GraphicsWidget):
# If nothing has changed, we are done.
if any(changed):
self.sigStateChanged.emit(self)
# Update target rect for debugging
if self.target.isVisible():
self.target.setRect(self.mapRectFromItem(self.childGroup, self.targetRect()))
@ -574,6 +572,8 @@ class ViewBox(GraphicsWidget):
elif changed[1] and self.state['autoVisibleOnly'][0] and (self.state['autoRange'][1] is not False):
self._autoRangeNeedsUpdate = True
self.sigStateChanged.emit(self)
def setYRange(self, min, max, padding=None, update=True):
"""
Set the visible Y range of the view to [*min*, *max*].
@ -1156,8 +1156,8 @@ class ViewBox(GraphicsWidget):
self._resetTarget()
self.scaleBy(s, center)
self.sigRangeChangedManually.emit(mask)
ev.accept()
self.sigRangeChangedManually.emit(mask)
def mouseClickEvent(self, ev):
if ev.button() == QtCore.Qt.RightButton and self.menuEnabled():
@ -1498,12 +1498,6 @@ class ViewBox(GraphicsWidget):
if any(changed):
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()
# Inform linked views that the range has changed
@ -1514,6 +1508,13 @@ class ViewBox(GraphicsWidget):
if link is not None:
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):
if not self._matrixNeedsUpdate:
return
@ -1541,9 +1542,9 @@ class ViewBox(GraphicsWidget):
m.translate(-st[0], -st[1])
self.childGroup.setTransform(m)
self._matrixNeedsUpdate = False
self.sigTransformChanged.emit(self) ## segfaults here: 1
self._matrixNeedsUpdate = False
def paint(self, p, opt, widget):
self.checkSceneChange()

View File

@ -559,8 +559,8 @@ class Parameter(QtCore.QObject):
self.childs.insert(pos, child)
child.parentChanged(self)
self.sigChildAdded.emit(self, child, pos)
child.sigTreeStateChanged.connect(self.treeStateChanged)
self.sigChildAdded.emit(self, child, pos)
return child
def removeChild(self, child):
@ -571,11 +571,11 @@ class Parameter(QtCore.QObject):
del self.names[name]
self.childs.pop(self.childs.index(child))
child.parentChanged(None)
self.sigChildRemoved.emit(self, child)
try:
child.sigTreeStateChanged.disconnect(self.treeStateChanged)
except (TypeError, RuntimeError): ## already disconnected
pass
self.sigChildRemoved.emit(self, child)
def clearChildren(self):
"""Remove all child parameters."""

View File

@ -50,11 +50,11 @@ class ColorButton(QtGui.QPushButton):
def setColor(self, color, finished=True):
"""Sets the button's color and emits both sigColorChanged and sigColorChanging."""
self._color = functions.mkColor(color)
self.update()
if finished:
self.sigColorChanged.emit(self)
else:
self.sigColorChanging.emit(self)
self.update()
def selectColor(self):
self.origColor = self.color()

View File

@ -227,13 +227,13 @@ class GraphicsView(QtGui.QGraphicsView):
else:
self.fitInView(self.range, QtCore.Qt.IgnoreAspectRatio)
self.sigDeviceRangeChanged.emit(self, self.range)
self.sigDeviceTransformChanged.emit(self)
if propagate:
for v in self.lockedViewports:
v.setXRange(self.range, padding=0)
self.sigDeviceRangeChanged.emit(self, self.range)
self.sigDeviceTransformChanged.emit(self)
def viewRect(self):
"""Return the boundaries of the view in scene coordinates"""
## easier to just return self.range ?
@ -262,7 +262,6 @@ class GraphicsView(QtGui.QGraphicsView):
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.updateMatrix()
self.sigScaleChanged.emit(self)