Added a few more files to get flowcharts working

This commit is contained in:
Luke Campagnola 2012-04-04 20:28:48 -04:00
parent 5a9bcc3591
commit 09995e0d12
5 changed files with 260 additions and 47 deletions

194
configfile.py Normal file
View File

@ -0,0 +1,194 @@
# -*- coding: utf-8 -*-
"""
configfile.py - Human-readable text configuration file library
Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more infomation.
Used for reading and writing dictionary objects to a python-like configuration
file format. Data structures may be nested and contain any data type as long
as it can be converted to/from a string using repr and eval.
"""
import re, os, sys
from collections import OrderedDict
GLOBAL_PATH = None # so not thread safe.
import units
class ParseError(Exception):
def __init__(self, message, lineNum, line, fileName=None):
self.lineNum = lineNum
self.line = line
#self.message = message
self.fileName = fileName
Exception.__init__(self, message)
def __str__(self):
if self.fileName is None:
msg = "Error parsing string at line %d:\n" % self.lineNum
else:
msg = "Error parsing config file '%s' at line %d:\n" % (self.fileName, self.lineNum)
msg += "%s\n%s" % (self.line, self.message)
return msg
#raise Exception()
def writeConfigFile(data, fname):
s = genString(data)
fd = open(fname, 'w')
fd.write(s)
fd.close()
def readConfigFile(fname):
#cwd = os.getcwd()
global GLOBAL_PATH
if GLOBAL_PATH is not None:
fname2 = os.path.join(GLOBAL_PATH, fname)
if os.path.exists(fname2):
fname = fname2
GLOBAL_PATH = os.path.dirname(os.path.abspath(fname))
try:
#os.chdir(newDir) ## bad.
fd = open(fname)
s = unicode(fd.read(), 'UTF-8')
fd.close()
s = s.replace("\r\n", "\n")
s = s.replace("\r", "\n")
data = parseString(s)[1]
except ParseError:
sys.exc_info()[1].fileName = fname
raise
except:
print "Error while reading config file %s:"% fname
raise
#finally:
#os.chdir(cwd)
return data
def appendConfigFile(data, fname):
s = genString(data)
fd = open(fname, 'a')
fd.write(s)
fd.close()
def genString(data, indent=''):
s = ''
for k in data:
sk = str(k)
if len(sk) == 0:
print data
raise Exception('blank dict keys not allowed (see data above)')
if sk[0] == ' ' or ':' in sk:
print data
raise Exception('dict keys must not contain ":" or start with spaces [offending key is "%s"]' % sk)
if isinstance(data[k], dict):
s += indent + sk + ':\n'
s += genString(data[k], indent + ' ')
else:
s += indent + sk + ': ' + repr(data[k]) + '\n'
return s
def parseString(lines, start=0):
data = OrderedDict()
if isinstance(lines, basestring):
lines = lines.split('\n')
indent = measureIndent(lines[start])
ln = start - 1
try:
while True:
ln += 1
#print ln
if ln >= len(lines):
break
l = lines[ln]
## Skip blank lines or lines starting with #
if re.match(r'\s*#', l) or not re.search(r'\S', l):
continue
## Measure line indentation, make sure it is correct for this level
lineInd = measureIndent(l)
if lineInd < indent:
ln -= 1
break
if lineInd > indent:
#print lineInd, indent
raise ParseError('Indentation is incorrect. Expected %d, got %d' % (indent, lineInd), ln+1, l)
if ':' not in l:
raise ParseError('Missing colon', ln+1, l)
(k, p, v) = l.partition(':')
k = k.strip()
v = v.strip()
## set up local variables to use for eval
local = units.allUnits.copy()
local['OrderedDict'] = OrderedDict
local['readConfigFile'] = readConfigFile
if len(k) < 1:
raise ParseError('Missing name preceding colon', ln+1, l)
if k[0] == '(' and k[-1] == ')': ## If the key looks like a tuple, try evaluating it.
try:
k1 = eval(k, local)
if type(k1) is tuple:
k = k1
except:
pass
if re.search(r'\S', v) and v[0] != '#': ## eval the value
try:
val = eval(v, local)
except:
ex = sys.exc_info()[1]
raise ParseError("Error evaluating expression '%s': [%s: %s]" % (v, ex.__class__.__name__, str(ex)), (ln+1), l)
else:
if ln+1 >= len(lines) or measureIndent(lines[ln+1]) <= indent:
#print "blank dict"
val = {}
else:
#print "Going deeper..", ln+1
(ln, val) = parseString(lines, start=ln+1)
data[k] = val
#print k, repr(val)
except ParseError:
raise
except:
ex = sys.exc_info()[1]
raise ParseError("%s: %s" % (ex.__class__.__name__, str(ex)), ln+1, l)
#print "Returning shallower..", ln+1
return (ln, data)
def measureIndent(s):
n = 0
while n < len(s) and s[n] == ' ':
n += 1
return n
if __name__ == '__main__':
import tempfile
fn = tempfile.mktemp()
tf = open(fn, 'w')
cf = """
key: 'value'
key2:
key21: 'value'
key22: [1,2,3]
key23: 234 #comment
"""
tf.write(cf)
tf.close()
print "=== Test:==="
print cf
print "============"
data = readConfigFile(fn)
print data
os.remove(fn)

View File

@ -14,7 +14,7 @@ from Terminal import Terminal
from numpy import ndarray
import library
from pyqtgraph.debug import printExc
import configfile
import pyqtgraph.configfile as configfile
import pyqtgraph.dockarea as dockarea
import pyqtgraph as pg
import FlowchartGraphicsView

View File

@ -197,49 +197,4 @@ class HistogramDetrend(CtrlNode):
return functions.histogramDetrend(data, window=ws, bins=bn)
class ExpDeconvolve(CtrlNode):
"""Exponential deconvolution filter."""
nodeName = 'ExpDeconvolve'
uiTemplate = [
('tau', 'spin', {'value': 10e-3, 'step': 1, 'minStep': 100e-6, 'dec': True, 'range': [0.0, None], 'suffix': 's', 'siPrefix': True})
]
def processData(self, data):
tau = self.ctrls['tau'].value()
return functions.expDeconvolve(data, tau)
#dt = 1
#if isinstance(data, MetaArray):
#dt = data.xvals(0)[1] - data.xvals(0)[0]
#d = data[:-1] + (self.ctrls['tau'].value() / dt) * (data[1:] - data[:-1])
#if isinstance(data, MetaArray):
#info = data.infoCopy()
#if 'values' in info[0]:
#info[0]['values'] = info[0]['values'][:-1]
#return MetaArray(d, info=info)
#else:
#return d
class ExpReconvolve(CtrlNode):
"""Exponential reconvolution filter. Only works with MetaArrays that were previously deconvolved."""
nodeName = 'ExpReconvolve'
#uiTemplate = [
#('tau', 'spin', {'value': 10e-3, 'step': 1, 'minStep': 100e-6, 'dec': True, 'range': [0.0, None], 'suffix': 's', 'siPrefix': True})
#]
def processData(self, data):
return functions.expReconvolve(data)
class Tauiness(CtrlNode):
"""Sliding-window exponential fit"""
nodeName = 'Tauiness'
uiTemplate = [
('window', 'intSpin', {'value': 100, 'min': 3, 'max': 1000000}),
('skip', 'intSpin', {'value': 10, 'min': 0, 'max': 10000000})
]
def processData(self, data):
return functions.tauiness(data, self.ctrls['window'].value(), self.ctrls['skip'].value())

View File

@ -3,7 +3,7 @@ from collections import OrderedDict
import os, types
from pyqtgraph.debug import printExc
from ..Node import Node
import reload
import pyqtgraph.reload as reload
NODE_LIST = OrderedDict() ## maps name:class for all registered Node subclasses

64
units.py Normal file
View File

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
## Very simple unit support:
## - creates variable names like 'mV' and 'kHz'
## - the value assigned to the variable corresponds to the scale prefix
## (mV = 0.001)
## - the actual units are purely cosmetic for making code clearer:
##
## x = 20*pA is identical to x = 20*1e-12
## No unicode variable names (μ,Ω) allowed until python 3
SI_PREFIXES = 'yzafpnum kMGTPEZY'
UNITS = 'm,s,g,W,J,V,A,F,T,Hz,Ohm,S,N,C,px,b,B'.split(',')
allUnits = {}
def addUnit(p, n):
g = globals()
v = 1000**n
for u in UNITS:
g[p+u] = v
allUnits[p+u] = v
for p in SI_PREFIXES:
if p == ' ':
p = ''
n = 0
elif p == 'u':
n = -2
else:
n = SI_PREFIXES.index(p) - 8
addUnit(p, n)
cm = 0.01
def evalUnits(unitStr):
"""
Evaluate a unit string into ([numerators,...], [denominators,...])
Examples:
N m/s^2 => ([N, m], [s, s])
A*s / V => ([A, s], [V,])
"""
pass
def formatUnits(units):
"""
Format a unit specification ([numerators,...], [denominators,...])
into a string (this is the inverse of evalUnits)
"""
pass
def simplify(units):
"""
Cancel units that appear in both numerator and denominator, then attempt to replace
groups of units with single units where possible (ie, J/s => W)
"""
pass