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 TableWidget append / sort issues
- Fixed AxisItem not resizing text area when setTicks() is used
- Removed a few cyclic references
pyqtgraph-0.9.8 2013-11-24

View File

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

View File

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