Merge branch 'crash-fixes' into develop

This commit is contained in:
Luke Campagnola 2014-11-26 21:30:40 -05:00
commit 87453a2e2a
8 changed files with 77 additions and 9 deletions

View File

@ -97,6 +97,8 @@ pyqtgraph-0.9.9 [unreleased]
- Removed a few cyclic references - Removed a few cyclic references
- Fixed Parameter 'readonly' option for bool, color, and text parameter types - Fixed Parameter 'readonly' option for bool, color, and text parameter types
- Fixed alpha on GLScatterPlotItem spots (formerly maxed out at alpha=200) - Fixed alpha on GLScatterPlotItem spots (formerly maxed out at alpha=200)
- Fixed a few bugs causing exit crashes
pyqtgraph-0.9.8 2013-11-24 pyqtgraph-0.9.8 2013-11-24

View File

@ -84,8 +84,8 @@ class GraphicsScene(QtGui.QGraphicsScene):
cls._addressCache[sip.unwrapinstance(sip.cast(obj, QtGui.QGraphicsItem))] = obj cls._addressCache[sip.unwrapinstance(sip.cast(obj, QtGui.QGraphicsItem))] = obj
def __init__(self, clickRadius=2, moveDistance=5): def __init__(self, clickRadius=2, moveDistance=5, parent=None):
QtGui.QGraphicsScene.__init__(self) QtGui.QGraphicsScene.__init__(self, parent)
self.setClickRadius(clickRadius) self.setClickRadius(clickRadius)
self.setMoveDistance(moveDistance) self.setMoveDistance(moveDistance)
self.exportDirectory = None self.exportDirectory = None

View File

@ -270,7 +270,12 @@ from .Qt import isQObjectAlive
## Attempts to work around exit crashes: ## Attempts to work around exit crashes:
import atexit import atexit
_cleanupCalled = False
def cleanup(): def cleanup():
global _cleanupCalled
if _cleanupCalled:
return
if not getConfigOption('exitCleanup'): if not getConfigOption('exitCleanup'):
return return
@ -295,8 +300,22 @@ def cleanup():
s.addItem(o) s.addItem(o)
except RuntimeError: ## occurs if a python wrapper no longer has its underlying C++ object except RuntimeError: ## occurs if a python wrapper no longer has its underlying C++ object
continue continue
_cleanupCalled = True
atexit.register(cleanup) atexit.register(cleanup)
# Call cleanup when QApplication quits. This is necessary because sometimes
# the QApplication will quit before the atexit callbacks are invoked.
# Note: cannot connect this function until QApplication has been created, so
# instead we have GraphicsView.__init__ call this for us.
_cleanupConnected = False
def _connectCleanup():
global _cleanupConnected
if _cleanupConnected:
return
QtGui.QApplication.instance().aboutToQuit.connect(cleanup)
_cleanupConnected = True
## Optional function for exiting immediately (with some manual teardown) ## Optional function for exiting immediately (with some manual teardown)
def exit(): def exit():

View File

@ -49,7 +49,7 @@ class HistogramLUTItem(GraphicsWidget):
self.setLayout(self.layout) self.setLayout(self.layout)
self.layout.setContentsMargins(1,1,1,1) self.layout.setContentsMargins(1,1,1,1)
self.layout.setSpacing(0) self.layout.setSpacing(0)
self.vb = ViewBox() self.vb = ViewBox(parent=self)
self.vb.setMaximumWidth(152) self.vb.setMaximumWidth(152)
self.vb.setMinimumWidth(45) self.vb.setMinimumWidth(45)
self.vb.setMouseEnabled(x=False, y=True) self.vb.setMouseEnabled(x=False, y=True)
@ -59,7 +59,7 @@ class HistogramLUTItem(GraphicsWidget):
self.region = LinearRegionItem([0, 1], LinearRegionItem.Horizontal) self.region = LinearRegionItem([0, 1], LinearRegionItem.Horizontal)
self.region.setZValue(1000) self.region.setZValue(1000)
self.vb.addItem(self.region) self.vb.addItem(self.region)
self.axis = AxisItem('left', linkView=self.vb, maxTickLength=-10) self.axis = AxisItem('left', linkView=self.vb, maxTickLength=-10, parent=self)
self.layout.addItem(self.axis, 0, 0) self.layout.addItem(self.axis, 0, 0)
self.layout.addItem(self.vb, 0, 1) self.layout.addItem(self.vb, 0, 1)
self.layout.addItem(self.gradient, 0, 2) self.layout.addItem(self.gradient, 0, 2)

View File

@ -145,7 +145,7 @@ class PlotItem(GraphicsWidget):
self.layout.setVerticalSpacing(0) self.layout.setVerticalSpacing(0)
if viewBox is None: if viewBox is None:
viewBox = ViewBox() viewBox = ViewBox(parent=self)
self.vb = viewBox self.vb = viewBox
self.vb.sigStateChanged.connect(self.viewStateChanged) self.vb.sigStateChanged.connect(self.viewStateChanged)
self.setMenuEnabled(enableMenu, enableMenu) ## en/disable plotitem and viewbox menus self.setMenuEnabled(enableMenu, enableMenu) ## en/disable plotitem and viewbox menus
@ -168,14 +168,14 @@ class PlotItem(GraphicsWidget):
axisItems = {} axisItems = {}
self.axes = {} self.axes = {}
for k, pos in (('top', (1,1)), ('bottom', (3,1)), ('left', (2,0)), ('right', (2,2))): for k, pos in (('top', (1,1)), ('bottom', (3,1)), ('left', (2,0)), ('right', (2,2))):
axis = axisItems.get(k, AxisItem(orientation=k)) axis = axisItems.get(k, AxisItem(orientation=k, parent=self))
axis.linkToView(self.vb) axis.linkToView(self.vb)
self.axes[k] = {'item': axis, 'pos': pos} self.axes[k] = {'item': axis, 'pos': pos}
self.layout.addItem(axis, *pos) self.layout.addItem(axis, *pos)
axis.setZValue(-1000) axis.setZValue(-1000)
axis.setFlag(axis.ItemNegativeZStacksBehindParent) axis.setFlag(axis.ItemNegativeZStacksBehindParent)
self.titleLabel = LabelItem('', size='11pt') self.titleLabel = LabelItem('', size='11pt', parent=self)
self.layout.addItem(self.titleLabel, 0, 1) self.layout.addItem(self.titleLabel, 0, 1)
self.setTitle(None) ## hide self.setTitle(None) ## hide

View File

@ -1696,6 +1696,8 @@ class ViewBox(GraphicsWidget):
def forgetView(vid, name): def forgetView(vid, name):
if ViewBox is None: ## can happen as python is shutting down if ViewBox is None: ## can happen as python is shutting down
return return
if QtGui.QApplication.instance() is None:
return
## Called with ID and name of view (the view itself is no longer available) ## Called with ID and name of view (the view itself is no longer available)
for v in list(ViewBox.AllViews.keys()): for v in list(ViewBox.AllViews.keys()):
if id(v) == vid: if id(v) == vid:

View File

@ -0,0 +1,38 @@
import os, sys, subprocess, tempfile
import pyqtgraph as pg
code = """
import sys
sys.path.insert(0, '{path}')
import pyqtgraph as pg
app = pg.mkQApp()
w = pg.{classname}({args})
"""
def test_exit_crash():
# For each Widget subclass, run a simple python script that creates an
# instance and then shuts down. The intent is to check for segmentation
# faults when each script exits.
tmp = tempfile.mktemp(".py")
path = os.path.dirname(pg.__file__)
initArgs = {
'CheckTable': "[]",
'ProgressDialog': '"msg"',
'VerticalLabel': '"msg"',
}
for name in dir(pg):
obj = getattr(pg, name)
if not isinstance(obj, type) or not issubclass(obj, pg.QtGui.QWidget):
continue
print name
argstr = initArgs.get(name, "")
open(tmp, 'w').write(code.format(path=path, classname=name, args=argstr))
proc = subprocess.Popen([sys.executable, tmp])
assert proc.wait() == 0
os.remove(tmp)

View File

@ -71,6 +71,13 @@ class GraphicsView(QtGui.QGraphicsView):
QtGui.QGraphicsView.__init__(self, parent) QtGui.QGraphicsView.__init__(self, parent)
# This connects a cleanup function to QApplication.aboutToQuit. It is
# called from here because we have no good way to react when the
# QApplication is created by the user.
# See pyqtgraph.__init__.py
from .. import _connectCleanup
_connectCleanup()
if useOpenGL is None: if useOpenGL is None:
useOpenGL = getConfigOption('useOpenGL') useOpenGL = getConfigOption('useOpenGL')
@ -102,7 +109,8 @@ class GraphicsView(QtGui.QGraphicsView):
self.currentItem = None self.currentItem = None
self.clearMouse() self.clearMouse()
self.updateMatrix() self.updateMatrix()
self.sceneObj = GraphicsScene() # GraphicsScene must have parent or expect crashes!
self.sceneObj = GraphicsScene(parent=self)
self.setScene(self.sceneObj) self.setScene(self.sceneObj)
## Workaround for PySide crash ## Workaround for PySide crash
@ -143,7 +151,6 @@ class GraphicsView(QtGui.QGraphicsView):
def paintEvent(self, ev): def paintEvent(self, ev):
self.scene().prepareForPaint() self.scene().prepareForPaint()
#print "GV: paint", ev.rect()
return QtGui.QGraphicsView.paintEvent(self, ev) return QtGui.QGraphicsView.paintEvent(self, ev)
def render(self, *args, **kwds): def render(self, *args, **kwds):