Merge branch 'stability_tests' into develop
This commit is contained in:
commit
255b2405d1
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
77
pyqtgraph/tests/test_ref_cycles.py
Normal file
77
pyqtgraph/tests/test_ref_cycles.py
Normal 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()
|
160
pyqtgraph/tests/test_stability.py
Normal file
160
pyqtgraph/tests/test_stability.py
Normal 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()
|
Loading…
Reference in New Issue
Block a user