Merge branch 'stability_tests' into develop

This commit is contained in:
Luke Campagnola 2014-04-27 15:32:33 -04:00
commit 255b2405d1
5 changed files with 250 additions and 10 deletions

View File

@ -84,6 +84,7 @@ pyqtgraph-0.9.9 [unreleased]
- Fixed AxisItem.__init__(showValues=False) - Fixed AxisItem.__init__(showValues=False)
- Fixed TableWidget append / sort issues - Fixed TableWidget append / sort issues
- Fixed AxisItem not resizing text area when setTicks() is used - Fixed AxisItem not resizing text area when setTicks() is used
- Removed a few cyclic references
pyqtgraph-0.9.8 2013-11-24 pyqtgraph-0.9.8 2013-11-24

View File

@ -2,6 +2,7 @@
from .Qt import QtCore from .Qt import QtCore
from .ptime import time from .ptime import time
from . import ThreadsafeTimer from . import ThreadsafeTimer
import weakref
__all__ = ['SignalProxy'] __all__ = ['SignalProxy']
@ -34,7 +35,7 @@ class SignalProxy(QtCore.QObject):
self.timer = ThreadsafeTimer.ThreadsafeTimer() self.timer = ThreadsafeTimer.ThreadsafeTimer()
self.timer.timeout.connect(self.flush) self.timer.timeout.connect(self.flush)
self.block = False self.block = False
self.slot = slot self.slot = weakref.ref(slot)
self.lastFlushTime = None self.lastFlushTime = None
if slot is not None: if slot is not None:
self.sigDelayed.connect(slot) self.sigDelayed.connect(slot)
@ -80,7 +81,7 @@ class SignalProxy(QtCore.QObject):
except: except:
pass pass
try: try:
self.sigDelayed.disconnect(self.slot) self.sigDelayed.disconnect(self.slot())
except: except:
pass pass

View File

@ -17,6 +17,7 @@ from .. import functions as fn
import numpy as np import numpy as np
from .. import debug as debug from .. import debug as debug
import weakref
__all__ = ['HistogramLUTItem'] __all__ = ['HistogramLUTItem']
@ -42,7 +43,7 @@ class HistogramLUTItem(GraphicsWidget):
""" """
GraphicsWidget.__init__(self) GraphicsWidget.__init__(self)
self.lut = None self.lut = None
self.imageItem = None self.imageItem = lambda: None # fake a dead weakref
self.layout = QtGui.QGraphicsGridLayout() self.layout = QtGui.QGraphicsGridLayout()
self.setLayout(self.layout) self.setLayout(self.layout)
@ -138,7 +139,7 @@ class HistogramLUTItem(GraphicsWidget):
#self.region.setBounds([vr.top(), vr.bottom()]) #self.region.setBounds([vr.top(), vr.bottom()])
def setImageItem(self, img): def setImageItem(self, img):
self.imageItem = img self.imageItem = weakref.ref(img)
img.sigImageChanged.connect(self.imageChanged) img.sigImageChanged.connect(self.imageChanged)
img.setLookupTable(self.getLookupTable) ## send function pointer, not the result img.setLookupTable(self.getLookupTable) ## send function pointer, not the result
#self.gradientChanged() #self.gradientChanged()
@ -150,11 +151,11 @@ class HistogramLUTItem(GraphicsWidget):
self.update() self.update()
def gradientChanged(self): def gradientChanged(self):
if self.imageItem is not None: if self.imageItem() is not None:
if self.gradient.isLookupTrivial(): if self.gradient.isLookupTrivial():
self.imageItem.setLookupTable(None) #lambda x: x.astype(np.uint8)) self.imageItem().setLookupTable(None) #lambda x: x.astype(np.uint8))
else: else:
self.imageItem.setLookupTable(self.getLookupTable) ## send function pointer, not the result self.imageItem().setLookupTable(self.getLookupTable) ## send function pointer, not the result
self.lut = None self.lut = None
#if self.imageItem is not None: #if self.imageItem is not None:
@ -178,14 +179,14 @@ class HistogramLUTItem(GraphicsWidget):
#self.update() #self.update()
def regionChanging(self): def regionChanging(self):
if self.imageItem is not None: if self.imageItem() is not None:
self.imageItem.setLevels(self.region.getRegion()) self.imageItem().setLevels(self.region.getRegion())
self.sigLevelsChanged.emit(self) self.sigLevelsChanged.emit(self)
self.update() self.update()
def imageChanged(self, autoLevel=False, autoRange=False): def imageChanged(self, autoLevel=False, autoRange=False):
profiler = debug.Profiler() profiler = debug.Profiler()
h = self.imageItem.getHistogram() h = self.imageItem().getHistogram()
profiler('get histogram') profiler('get histogram')
if h[0] is None: if h[0] is None:
return return

View File

@ -0,0 +1,77 @@
"""
Test for unwanted reference cycles
"""
import pyqtgraph as pg
import numpy as np
import gc, weakref
app = pg.mkQApp()
def assert_alldead(refs):
for ref in refs:
assert ref() is None
def qObjectTree(root):
"""Return root and its entire tree of qobject children"""
childs = [root]
for ch in pg.QtCore.QObject.children(root):
childs += qObjectTree(ch)
return childs
def mkrefs(*objs):
"""Return a list of weakrefs to each object in *objs.
QObject instances are expanded to include all child objects.
"""
allObjs = {}
for obj in objs:
if isinstance(obj, pg.QtCore.QObject):
obj = qObjectTree(obj)
else:
obj = [obj]
for o in obj:
allObjs[id(o)] = o
return map(weakref.ref, allObjs.values())
def test_PlotWidget():
def mkobjs(*args, **kwds):
w = pg.PlotWidget(*args, **kwds)
data = pg.np.array([1,5,2,4,3])
c = w.plot(data, name='stuff')
w.addLegend()
# test that connections do not keep objects alive
w.plotItem.vb.sigRangeChanged.connect(mkrefs)
app.focusChanged.connect(w.plotItem.vb.invertY)
# return weakrefs to a bunch of objects that should die when the scope exits.
return mkrefs(w, c, data, w.plotItem, w.plotItem.vb, w.plotItem.getMenu(), w.plotItem.getAxis('left'))
for i in range(5):
assert_alldead(mkobjs())
def test_ImageView():
def mkobjs():
iv = pg.ImageView()
data = np.zeros((10,10,5))
iv.setImage(data)
return mkrefs(iv, iv.imageItem, iv.view, iv.ui.histogram, data)
for i in range(5):
assert_alldead(mkobjs())
def test_GraphicsWindow():
def mkobjs():
w = pg.GraphicsWindow()
p1 = w.addPlot()
v1 = w.addViewBox()
return mkrefs(w, p1, v1)
for i in range(5):
assert_alldead(mkobjs())
if __name__ == '__main__':
ot = test_PlotItem()

View File

@ -0,0 +1,160 @@
"""
PyQt/PySide stress test:
Create lots of random widgets and graphics items, connect them together randomly,
the tear them down repeatedly.
The purpose of this is to attempt to generate segmentation faults.
"""
from PyQt4.QtTest import QTest
import pyqtgraph as pg
from random import seed, randint
import sys, gc, weakref
app = pg.mkQApp()
seed(12345)
widgetTypes = [
pg.PlotWidget,
pg.ImageView,
pg.GraphicsView,
pg.QtGui.QWidget,
pg.QtGui.QTreeWidget,
pg.QtGui.QPushButton,
]
itemTypes = [
pg.PlotCurveItem,
pg.ImageItem,
pg.PlotDataItem,
pg.ViewBox,
pg.QtGui.QGraphicsRectItem
]
widgets = []
items = []
allWidgets = weakref.WeakSet()
def crashtest():
global allWidgets
try:
gc.disable()
actions = [
createWidget,
#setParent,
forgetWidget,
showWidget,
processEvents,
#raiseException,
#addReference,
]
thread = WorkThread()
thread.start()
while True:
try:
action = randItem(actions)
action()
print('[%d widgets alive, %d zombie]' % (len(allWidgets), len(allWidgets) - len(widgets)))
except KeyboardInterrupt:
print("Caught interrupt; send another to exit.")
try:
for i in range(100):
QTest.qWait(100)
except KeyboardInterrupt:
thread.terminate()
break
except:
sys.excepthook(*sys.exc_info())
finally:
gc.enable()
class WorkThread(pg.QtCore.QThread):
'''Intended to give the gc an opportunity to run from a non-gui thread.'''
def run(self):
i = 0
while True:
i += 1
if (i % 1000000) == 0:
print('--worker--')
def randItem(items):
return items[randint(0, len(items)-1)]
def p(msg):
print(msg)
sys.stdout.flush()
def createWidget():
p('create widget')
global widgets, allWidgets
if len(widgets) > 50:
return
widget = randItem(widgetTypes)()
widget.setWindowTitle(widget.__class__.__name__)
widgets.append(widget)
allWidgets.add(widget)
p(" %s" % widget)
return widget
def setParent():
p('set parent')
global widgets
if len(widgets) < 2:
return
child = parent = None
while child is parent:
child = randItem(widgets)
parent = randItem(widgets)
p(" %s parent of %s" % (parent, child))
child.setParent(parent)
def forgetWidget():
p('forget widget')
global widgets
if len(widgets) < 1:
return
widget = randItem(widgets)
p(' %s' % widget)
widgets.remove(widget)
def showWidget():
p('show widget')
global widgets
if len(widgets) < 1:
return
widget = randItem(widgets)
p(' %s' % widget)
widget.show()
def processEvents():
p('process events')
QTest.qWait(25)
class TstException(Exception):
pass
def raiseException():
p('raise exception')
raise TstException("A test exception")
def addReference():
p('add reference')
global widgets
if len(widgets) < 1:
return
obj1 = randItem(widgets)
obj2 = randItem(widgets)
p(' %s -> %s' % (obj1, obj2))
obj1._testref = obj2
if __name__ == '__main__':
test_stability()