Make sure we don't leave view boxes alive by doing a 'bridge' for the on the plot items. Also added warnings if proper cleanup wasn't done.
This commit is contained in:
parent
1edf1375ed
commit
6433795e78
@ -32,6 +32,9 @@ else:
|
||||
if USE_PYSIDE:
|
||||
from PySide import QtGui, QtCore, QtOpenGL, QtSvg
|
||||
import PySide
|
||||
from PySide import shiboken
|
||||
isQObjectAlive = shiboken.isValid
|
||||
|
||||
VERSION_INFO = 'PySide ' + PySide.__version__
|
||||
|
||||
# Make a loadUiType function like PyQt has
|
||||
@ -78,6 +81,8 @@ else:
|
||||
pass
|
||||
|
||||
|
||||
import sip
|
||||
isQObjectAlive = sip.isdeleted
|
||||
loadUiType = uic.loadUiType
|
||||
|
||||
QtCore.Signal = QtCore.pyqtSignal
|
||||
|
@ -256,6 +256,7 @@ from .graphicsWindows import *
|
||||
from .SignalProxy import *
|
||||
from .colormap import *
|
||||
from .ptime import time
|
||||
from pyqtgraph.Qt import isQObjectAlive
|
||||
|
||||
|
||||
##############################################################
|
||||
@ -284,7 +285,10 @@ def cleanup():
|
||||
s = QtGui.QGraphicsScene()
|
||||
for o in gc.get_objects():
|
||||
try:
|
||||
if isinstance(o, QtGui.QGraphicsItem) and o.scene() is None:
|
||||
if isinstance(o, QtGui.QGraphicsItem) and isQObjectAlive(o) and o.scene() is None:
|
||||
sys.stderr.write(
|
||||
'Error: graphics item without scene. Make sure ViewBox.close() and GraphicsView.close() are properly called before app shutdown (%s)\n' % (o,))
|
||||
|
||||
s.addItem(o)
|
||||
except RuntimeError: ## occurs if a python wrapper no longer has its underlying C++ object
|
||||
continue
|
||||
|
@ -18,6 +18,7 @@ This class is very heavily featured:
|
||||
"""
|
||||
from ...Qt import QtGui, QtCore, QtSvg, USE_PYSIDE
|
||||
from ... import pixmaps
|
||||
import sys
|
||||
|
||||
if USE_PYSIDE:
|
||||
from .plotConfigTemplate_pyside import *
|
||||
@ -193,14 +194,6 @@ class PlotItem(GraphicsWidget):
|
||||
self.layout.setColumnStretchFactor(1, 100)
|
||||
|
||||
|
||||
## Wrap a few methods from viewBox
|
||||
for m in [
|
||||
'setXRange', 'setYRange', 'setXLink', 'setYLink', 'setAutoPan', 'setAutoVisible',
|
||||
'setRange', 'autoRange', 'viewRect', 'viewRange', 'setMouseEnabled', 'setLimits',
|
||||
'enableAutoRange', 'disableAutoRange', 'setAspectLocked', 'invertY',
|
||||
'register', 'unregister']: ## NOTE: If you update this list, please update the class docstring as well.
|
||||
setattr(self, m, getattr(self.vb, m))
|
||||
|
||||
self.items = []
|
||||
self.curves = []
|
||||
self.itemMeta = weakref.WeakKeyDictionary()
|
||||
@ -298,7 +291,26 @@ class PlotItem(GraphicsWidget):
|
||||
"""Return the :class:`ViewBox <pyqtgraph.ViewBox>` contained within."""
|
||||
return self.vb
|
||||
|
||||
## Wrap a few methods from viewBox.
|
||||
|
||||
#Important: don't use a settattr(m, getattr(self.vb, m)) as we'd be leaving the viebox alive
|
||||
#because we had a reference to an instance method (creating wrapper methods at runtime instead).
|
||||
frame = sys._getframe()
|
||||
for m in [
|
||||
'setXRange', 'setYRange', 'setXLink', 'setYLink', 'setAutoPan', 'setAutoVisible',
|
||||
'setRange', 'autoRange', 'viewRect', 'viewRange', 'setMouseEnabled', 'setLimits',
|
||||
'enableAutoRange', 'disableAutoRange', 'setAspectLocked', 'invertY',
|
||||
'register', 'unregister']: ## NOTE: If you update this list, please update the class docstring as well.
|
||||
def _create_method(name): # @NoSelf
|
||||
def method(self, *args, **kwargs):
|
||||
return getattr(self.vb, name)(*args, **kwargs)
|
||||
method.__name__ = name
|
||||
return method
|
||||
|
||||
frame.f_locals[m] = _create_method(m)
|
||||
|
||||
del _create_method
|
||||
del frame
|
||||
|
||||
def setLogMode(self, x=None, y=None):
|
||||
"""
|
||||
@ -356,10 +368,8 @@ class PlotItem(GraphicsWidget):
|
||||
self.ctrlMenu.setParent(None)
|
||||
self.ctrlMenu = None
|
||||
|
||||
#self.ctrlBtn.setParent(None)
|
||||
#self.ctrlBtn = None
|
||||
#self.autoBtn.setParent(None)
|
||||
#self.autoBtn = None
|
||||
self.autoBtn.setParent(None)
|
||||
self.autoBtn = None
|
||||
|
||||
for k in self.axes:
|
||||
i = self.axes[k]['item']
|
||||
|
@ -5,11 +5,12 @@ from ...Point import Point
|
||||
from ... import functions as fn
|
||||
from .. ItemGroup import ItemGroup
|
||||
from .. GraphicsWidget import GraphicsWidget
|
||||
from ...GraphicsScene import GraphicsScene
|
||||
import weakref
|
||||
from copy import deepcopy
|
||||
from ... import debug as debug
|
||||
from ... import getConfigOption
|
||||
import sys
|
||||
from pyqtgraph.Qt import isQObjectAlive
|
||||
|
||||
__all__ = ['ViewBox']
|
||||
|
||||
@ -240,6 +241,7 @@ class ViewBox(GraphicsWidget):
|
||||
del ViewBox.NamedViews[self.name]
|
||||
|
||||
def close(self):
|
||||
self.clear()
|
||||
self.unregister()
|
||||
|
||||
def implements(self, interface):
|
||||
@ -1653,6 +1655,9 @@ class ViewBox(GraphicsWidget):
|
||||
## called when the application is about to exit.
|
||||
## this disables all callbacks, which might otherwise generate errors if invoked during exit.
|
||||
for k in ViewBox.AllViews:
|
||||
if isQObjectAlive(k):
|
||||
sys.stderr.write('ViewBox should be closed before application exit!')
|
||||
|
||||
try:
|
||||
k.destroyed.disconnect()
|
||||
except RuntimeError: ## signal is already disconnected.
|
||||
|
Loading…
Reference in New Issue
Block a user