merged updates from acq4

This commit is contained in:
Luke Campagnola 2013-03-07 15:29:56 -05:00
parent 21dff0525a
commit 2a27687fb2
7 changed files with 97 additions and 23 deletions

View File

@ -97,6 +97,6 @@ Miscellaneous Functions
.. autofunction:: pyqtgraph.systemInfo
.. autofunction:: pyqtgraph.exit

View File

@ -54,6 +54,7 @@ CONFIG_OPTIONS = {
'editorCommand': None, ## command used to invoke code editor from ConsoleWidgets
'useWeave': True, ## Use weave to speed up some operations, if it is available
'weaveDebug': False, ## Print full error message if weave compile fails
'exitCleanup': True, ## Attempt to work around some exit crash bugs in PyQt and PySide
}
@ -190,9 +191,20 @@ from .SignalProxy import *
from .colormap import *
from .ptime import time
##############################################################
## PyQt and PySide both are prone to crashing on exit.
## There are two general approaches to dealing with this:
## 1. Install atexit handlers that assist in tearing down to avoid crashes.
## This helps, but is never perfect.
## 2. Terminate the process before python starts tearing down
## This is potentially dangerous
## Attempts to work around exit crashes:
import atexit
def cleanup():
if not getConfigOption('exitCleanup'):
return
ViewBox.quit() ## tell ViewBox that it doesn't need to deregister views anymore.
## Workaround for Qt exit crash:
@ -212,6 +224,38 @@ def cleanup():
atexit.register(cleanup)
## Optional function for exiting immediately (with some manual teardown)
def exit():
"""
Causes python to exit without garbage-collecting any objects, and thus avoids
calling object destructor methods. This is a sledgehammer workaround for
a variety of bugs in PyQt and Pyside that cause crashes on exit.
This function does the following in an attempt to 'safely' terminate
the process:
* Invoke atexit callbacks
* Close all open file handles
* os._exit()
Note: there is some potential for causing damage with this function if you
are using objects that _require_ their destructors to be called (for example,
to properly terminate log files, disconnect from devices, etc). Situations
like this are probably quite rare, but use at your own risk.
"""
## first disable our own cleanup function; won't be needing it.
setConfigOptions(exitCleanup=False)
## invoke atexit callbacks
atexit._run_exitfuncs()
## close file handles
os.closerange(3, 4096) ## just guessing on the maximum descriptor count..
os._exit(os.EX_OK)
## Convenience functions for command-line use

View File

@ -169,7 +169,7 @@ class ConsoleWidget(QtGui.QWidget):
def execMulti(self, nextLine):
self.stdout.write(nextLine+"\n")
#self.stdout.write(nextLine+"\n")
if nextLine.strip() != '':
self.multiline += "\n" + nextLine
return
@ -372,4 +372,4 @@ class ConsoleWidget(QtGui.QWidget):
return False
return True

View File

@ -521,6 +521,8 @@ class ConnectionItem(GraphicsObject):
self.target = target
self.length = 0
self.hovered = False
self.path = None
self.shapePath = None
#self.line = QtGui.QGraphicsLineItem(self)
self.source.getViewBox().addItem(self)
self.updateLine()
@ -544,13 +546,18 @@ class ConnectionItem(GraphicsObject):
else:
return
self.prepareGeometryChange()
self.resetTransform()
ang = (stop-start).angle(Point(0, 1))
if ang is None:
ang = 0
self.rotate(ang)
self.setPos(start)
self.length = (start-stop).length()
self.path = QtGui.QPainterPath()
self.path.moveTo(start)
self.path.cubicTo(Point(stop.x(), start.y()), Point(start.x(), stop.y()), Point(stop.x(), stop.y()))
self.shapePath = None
#self.resetTransform()
#ang = (stop-start).angle(Point(0, 1))
#if ang is None:
#ang = 0
#self.rotate(ang)
#self.setPos(start)
#self.length = (start-stop).length()
self.update()
#self.line.setLine(start.x(), start.y(), stop.x(), stop.y())
@ -582,12 +589,23 @@ class ConnectionItem(GraphicsObject):
def boundingRect(self):
#return self.line.boundingRect()
px = self.pixelWidth()
return QtCore.QRectF(-5*px, 0, 10*px, self.length)
return self.shape().boundingRect()
##return self.line.boundingRect()
#px = self.pixelWidth()
#return QtCore.QRectF(-5*px, 0, 10*px, self.length)
def viewRangeChanged(self):
self.shapePath = None
self.prepareGeometryChange()
#def shape(self):
#return self.line.shape()
def shape(self):
if self.shapePath is None:
if self.path is None:
return QtGui.QPainterPath()
stroker = QtGui.QPainterPathStroker()
px = self.pixelWidth()
stroker.setWidth(px*8)
self.shapePath = stroker.createStroke(self.path)
return self.shapePath
def paint(self, p, *args):
if self.isSelected():
@ -598,4 +616,6 @@ class ConnectionItem(GraphicsObject):
else:
p.setPen(fn.mkPen(100, 100, 250, width=1))
p.drawLine(0, 0, 0, self.length)
#p.drawLine(0, 0, 0, self.length)
p.drawPath(self.path)

View File

@ -1046,10 +1046,10 @@ class ViewBox(GraphicsWidget):
xr = item.dataBounds(0, frac=frac[0], orthoRange=orthoRange[0])
yr = item.dataBounds(1, frac=frac[1], orthoRange=orthoRange[1])
pxPad = 0 if not hasattr(item, 'pixelPadding') else item.pixelPadding()
if xr is None or xr == (None, None):
if xr is None or (xr[0] is None and xr[1] is None):
useX = False
xr = (0,0)
if yr is None or yr == (None, None):
if yr is None or (yr[0] is None and yr[1] is None):
useY = False
yr = (0,0)

View File

@ -169,6 +169,13 @@ class EnumColorMapItem(ptree.types.GroupParameter):
self.fieldName = name
vals = opts.get('values', [])
childs = [{'name': v, 'type': 'color'} for v in vals]
childs = []
for v in vals:
ch = ptree.Parameter.create(name=str(v), type='color')
ch.maskValue = v
childs.append(ch)
ptree.types.GroupParameter.__init__(self,
name=name, autoIncrementName=True, removable=True, renamable=True,
children=[
@ -191,8 +198,7 @@ class EnumColorMapItem(ptree.types.GroupParameter):
colors[:] = default
for v in self.param('Values'):
n = v.name()
mask = data == n
mask = data == v.maskValue
c = np.array(fn.colorTuple(v.value())) / 255.
colors[mask] = c
#scaled = np.clip((data-self['Min']) / (self['Max']-self['Min']), 0, 1)

View File

@ -92,14 +92,18 @@ class RangeFilterItem(ptree.types.SimpleParameter):
def generateMask(self, data):
vals = data[self.fieldName]
return (vals >= mn) & (vals < mx) ## Use inclusive minimum and non-inclusive maximum. This makes it easier to create non-overlapping selections
return (vals >= self['Min']) & (vals < self['Max']) ## Use inclusive minimum and non-inclusive maximum. This makes it easier to create non-overlapping selections
class EnumFilterItem(ptree.types.SimpleParameter):
def __init__(self, name, opts):
self.fieldName = name
vals = opts.get('values', [])
childs = [{'name': v, 'type': 'bool', 'value': True} for v in vals]
childs = []
for v in vals:
ch = ptree.Parameter.create(name=str(v), type='bool', value=True)
ch.maskValue = v
childs.append(ch)
ptree.types.SimpleParameter.__init__(self,
name=name, autoIncrementName=True, type='bool', value=True, removable=True, renamable=True,
children=childs)
@ -110,6 +114,6 @@ class EnumFilterItem(ptree.types.SimpleParameter):
for c in self:
if c.value() is True:
continue
key = c.name()
key = c.maskValue
mask &= vals != key
return mask