summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOnderwaater <onderwaa@esrf.fr>2015-12-11 13:42:43 +0100
committerOnderwaater <onderwaa@esrf.fr>2015-12-11 13:42:43 +0100
commit08c650a78c7875b143f11d9968638e0410fc31c8 (patch)
tree86e51d1c6f16ae4dcb5bedea4bed7244da14b554
parent19a10c475735c469e5686e6bf9b47fdf08eac6f6 (diff)
code cleanup
-rw-r--r--binoculars/__init__.py57
-rw-r--r--binoculars/backend.py15
-rw-r--r--binoculars/backends/id03.py311
-rw-r--r--binoculars/backends/test.py466
-rwxr-xr-xbinoculars/dispatcher.py54
-rw-r--r--binoculars/errors.py9
-rw-r--r--binoculars/fit.py127
-rwxr-xr-xbinoculars/main.py31
-rw-r--r--binoculars/plot.py45
-rwxr-xr-xbinoculars/space.py165
-rwxr-xr-xbinoculars/util.py156
-rwxr-xr-xscripts/binoculars59
-rwxr-xr-xscripts/binoculars-fitaid207
-rwxr-xr-xscripts/binoculars-gui237
-rwxr-xr-xscripts/binoculars-processgui149
-rwxr-xr-xscripts/binoculars-server13
16 files changed, 1334 insertions, 767 deletions
diff --git a/binoculars/__init__.py b/binoculars/__init__.py
index b57c147..4ed0103 100644
--- a/binoculars/__init__.py
+++ b/binoculars/__init__.py
@@ -1,12 +1,15 @@
-import os,sys
+import os
+import sys
# for scripted useage
-def run(args):
+
+
+def run(args):
'''Parameters
args: string
String as if typed in terminal. The string must consist
of the location of the configuration file and the command
- for specifying the jobs that need to be processed.
+ for specifying the jobs that need to be processed.
All additonal configuration file overides can be included
Returns
@@ -25,14 +28,15 @@ def run(args):
import binoculars.main
binoculars.util.register_python_executable(__file__)
main = binoculars.main.Main.from_args(args.split(' '))
-
+
if isinstance(main.result, binoculars.space.Multiverse):
return main.result.spaces
if type(main.result) == bool:
filenames = main.dispatcher.config.destination.final_filenames()
return tuple(binoculars.space.Space.fromfile(fn) for fn in filenames)
-def load(filename, key = None):
+
+def load(filename, key=None):
''' Parameters
filename: string
Only hdf5 files are acceptable
@@ -52,13 +56,14 @@ def load(filename, key = None):
'''
import binoculars.space
if os.path.exists(filename):
- return binoculars.space.Space.fromfile(filename, key = key)
+ return binoculars.space.Space.fromfile(filename, key=key)
else:
raise IOError("File '{0}' does not exist".format(filename))
+
def save(filename, space):
'''
- Save a space to file
+ Save a space to file
Parameters
filename: string
@@ -76,22 +81,24 @@ def save(filename, space):
>>> binoculars.save('test.hdf5', space)
'''
- import binoculars.space, binoculars.util
+ import binoculars.space
+ import binoculars.util
if isinstance(space, binoculars.space.Space):
ext = os.path.splitext(filename)[-1]
if ext == '.txt':
binoculars.util.space_to_txt(space, filename)
elif ext == '.edf':
binoculars.util.space_to_edf(space, filename)
- else:
+ else:
space.tofile(filename)
else:
raise TypeError("'{0!r}' is not a binoculars space".format(space))
+
def plotspace(space, log=True, clipping=0.0, fit=None, norm=None, colorbar=True, labels=True, **plotopts):
- '''
+ '''
plots a space with the correct axes. The space can be either one or two dimensinal.
-
+
Parameters
space: binoculars space
the space containing the data that needs to be plotted
@@ -121,27 +128,29 @@ def plotspace(space, log=True, clipping=0.0, fit=None, norm=None, colorbar=True,
'''
import matplotlib.pyplot as pyplot
- import binoculars.plot, binoculars.space
+ import binoculars.plot
+ import binoculars.space
if isinstance(space, binoculars.space.Space):
if space.dimension == 3:
from mpl_toolkits.mplot3d import Axes3D
ax = pyplot.gcf().gca(projection='3d')
- return binoculars.plot.plot(space, pyplot.gcf(), ax, log=log, clipping=clipping, fit=None, norm=norm, colorbar=colorbar, labels=labels, **plotopts)
+ return binoculars.plot.plot(space, pyplot.gcf(), ax, log=log, clipping=clipping, fit=None, norm=norm, colorbar=colorbar, labels=labels, **plotopts)
if fit is not None and space.dimension == 2:
ax = pyplot.gcf().add_subplot(121)
- binoculars.plot.plot(space, pyplot.gcf(), ax, log=log, clipping=clipping, fit=None, norm=norm, colorbar=colorbar, labels=labels, **plotopts)
+ binoculars.plot.plot(space, pyplot.gcf(), ax, log=log, clipping=clipping, fit=None, norm=norm, colorbar=colorbar, labels=labels, **plotopts)
ax = pyplot.gcf().add_subplot(122)
- return binoculars.plot.plot(space, pyplot.gcf(), ax, log=log, clipping=clipping, fit=fit, norm=norm, colorbar=colorbar, labels=labels, **plotopts)
+ return binoculars.plot.plot(space, pyplot.gcf(), ax, log=log, clipping=clipping, fit=fit, norm=norm, colorbar=colorbar, labels=labels, **plotopts)
else:
return binoculars.plot.plot(space, pyplot.gcf(), pyplot.gca(), log=log, clipping=clipping, fit=fit, norm=norm, colorbar=colorbar, labels=labels, **plotopts)
else:
raise TypeError("'{0!r}' is not a binoculars space".format(space))
+
def transform(space, labels, resolutions, exprs):
- '''
+ '''
transformation of the coordinates.
-
+
Parameters
space: binoculars space
labels: list
@@ -169,7 +178,8 @@ def transform(space, labels, resolutions, exprs):
Axis twotheta (min=0.066, max=0.519, res=0.003, count=152)
}
'''
- import binoculars.util, binoculars.space
+ import binoculars.util
+ import binoculars.space
if isinstance(space, binoculars.space.Space):
transformation = binoculars.util.transformation_from_expressions(space, exprs)
newspace = space.transform_coordinates(resolutions, labels, transformation)
@@ -177,10 +187,11 @@ def transform(space, labels, resolutions, exprs):
raise TypeError("'{0!r}' is not a binoculars space".format(space))
return newspace
-def fitspace(space, function, guess = None):
- '''
+
+def fitspace(space, function, guess=None):
+ '''
fit the space data.
-
+
Parameters
space: binoculars space
function: list
@@ -191,7 +202,7 @@ def fitspace(space, function, guess = None):
Returns
A binoculars fit object.
-
+
Examples:
>>> fit = binoculars.fitspace(space, 'lorentzian')
>>> print fit.summary
@@ -216,7 +227,7 @@ def fitspace(space, function, guess = None):
def info(filename):
'''
- Explore the file without loading the file, or after loading the file
+ Explore the file without loading the file, or after loading the file
Parameters
filename: filename or space
diff --git a/binoculars/backend.py b/binoculars/backend.py
index bf16bfd..c2e5d5a 100644
--- a/binoculars/backend.py
+++ b/binoculars/backend.py
@@ -4,8 +4,8 @@ from . import util, errors, dispatcher
class ProjectionBase(util.ConfigurableObject):
def parse_config(self, config):
super(ProjectionBase, self).parse_config(config)
- res = config.pop('resolution')# or just give 1 number for all dimensions
- self.config.limits = util.parse_pairs(config.pop('limits', None))#Optional, set the limits of the space object in projected coordinates. Syntax is same as numpy e.g. '0.3:-0.6, -1:5, :'
+ res = config.pop('resolution') # or just give 1 number for all dimensions
+ self.config.limits = util.parse_pairs(config.pop('limits', None)) # Optional, set the limits of the space object in projected coordinates. Syntax is same as numpy e.g. '0.3:-0.6, -1:5, :'
labels = self.get_axis_labels()
if not self.config.limits is None:
for lim in self.config.limits:
@@ -26,14 +26,14 @@ class ProjectionBase(util.ConfigurableObject):
class Job(object):
- weight = 1. # estimate of job difficulty (arbitrary units)
+ weight = 1. # estimate of job difficulty (arbitrary units)
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
class InputBase(util.ConfigurableObject):
- """Generate and process Job()s.
+ """Generate and process Job()s.
Note: there is no guarantee that generate_jobs() and process_jobs() will
be called on the same instance, not even in the same process or on the
@@ -41,7 +41,7 @@ class InputBase(util.ConfigurableObject):
def parse_config(self, config):
super(InputBase, self).parse_config(config)
- self.config.target_weight = int(config.pop('target_weight', 1000))## approximate number of images per job, only useful when running on the oar cluster
+ self.config.target_weight = int(config.pop('target_weight', 1000)) # # approximate number of images per job, only useful when running on the oar cluster
def generate_jobs(self, command):
"""Receives command from user, yields Job() instances"""
@@ -63,12 +63,15 @@ class InputBase(util.ConfigurableObject):
def get_dispatcher(config, main, default=None):
return _get_backend(config, 'dispatcher', dispatcher.DispatcherBase, default=default, args=[main])
+
def get_input(config, default=None):
return _get_backend(config, 'input', InputBase, default=default)
+
def get_projection(config, default=None):
return _get_backend(config, 'projection', ProjectionBase, default=default)
+
def _get_backend(config, section, basecls, default=None, args=[], kwargs={}):
if isinstance(config, util.ConfigSection):
return config.class_(config, *args, **kwargs)
@@ -98,7 +101,7 @@ def _get_backend(config, section, basecls, default=None, args=[], kwargs={}):
names = dict((name.lower(), name) for name in dir(module))
if clsname in names:
cls = getattr(module, names[clsname])
-
+
if issubclass(cls, basecls):
return cls(config, *args, **kwargs)
else:
diff --git a/binoculars/backends/id03.py b/binoculars/backends/id03.py
index 6e618a2..9389517 100644
--- a/binoculars/backends/id03.py
+++ b/binoculars/backends/id03.py
@@ -16,11 +16,12 @@ from .. import backend, errors, util
class pixels(backend.ProjectionBase):
def project(self, wavelength, UB, gamma, delta, theta, mu, chi, phi):
- y,x = numpy.mgrid[slice(None,gamma.shape[0]), slice(None,delta.shape[0])]
- return (y, x)
+ y, x = numpy.mgrid[slice(None, gamma.shape[0]), slice(None, delta.shape[0])]
+ return (y, x)
def get_axis_labels(self):
- return 'y','x'
+ return 'y', 'x'
+
class HKLProjection(backend.ProjectionBase):
# arrays: gamma, delta
@@ -28,14 +29,15 @@ class HKLProjection(backend.ProjectionBase):
def project(self, wavelength, UB, gamma, delta, theta, mu, chi, phi):
R = SixCircle.getHKL(wavelength, UB, gamma=gamma, delta=delta, theta=theta, mu=mu, chi=chi, phi=phi)
shape = gamma.size, delta.size
- H = R[0,:].reshape(shape)
- K = R[1,:].reshape(shape)
- L = R[2,:].reshape(shape)
+ H = R[0, :].reshape(shape)
+ K = R[1, :].reshape(shape)
+ L = R[2, :].reshape(shape)
return (H, K, L)
def get_axis_labels(self):
return 'H', 'K', 'L'
+
class HKProjection(HKLProjection):
def project(self, wavelength, UB, gamma, delta, theta, mu, chi, phi):
H, K, L = super(HKProjection, self).project(wavelength, UB, gamma, delta, theta, mu, chi, phi)
@@ -44,9 +46,10 @@ class HKProjection(HKLProjection):
def get_axis_labels(self):
return 'H', 'K'
+
class specularangles(backend.ProjectionBase):
def project(self, wavelength, UB, gamma, delta, theta, mu, chi, phi):
- delta,gamma = numpy.meshgrid(delta,gamma)
+ delta, gamma = numpy.meshgrid(delta, gamma)
mu *= numpy.pi/180
delta *= numpy.pi/180
gamma *= numpy.pi/180
@@ -54,7 +57,6 @@ class specularangles(backend.ProjectionBase):
phi *= numpy.pi/180
theta *= numpy.pi/180
-
def mat(u, th):
ux, uy, uz = u[0], u[1], u[2]
sint = numpy.sin(th)
@@ -65,25 +67,24 @@ class specularangles(backend.ProjectionBase):
[uy * ux * mcost + uz * sint, cost + uy**2 * mcost, uy * uz - ux * sint],
[uz * ux * mcost - uy * sint, uz * uy * mcost + ux * sint, cost + uz**2 * mcost]])
-
def rot(vx, vy, vz, u, th):
R = mat(u, th)
- return R[0,0] * vx + R[0,1] * vy + R[0,2] * vz, R[1,0] * vx + R[1,1] * vy + R[1,2] * vz, R[2,0] * vx + R[2,1] * vy + R[2,2] * vz
+ return R[0, 0] * vx + R[0, 1] * vy + R[0, 2] * vz, R[1, 0] * vx + R[1, 1] * vy + R[1, 2] * vz, R[2, 0] * vx + R[2, 1] * vy + R[2, 2] * vz
#what are the angles of kin and kout in the sample frame?
#angles in the hexapod frame
koutx, kouty, koutz = numpy.sin(- numpy.pi / 2 + gamma) * numpy.cos(delta), numpy.sin(- numpy.pi / 2 + gamma) * numpy.sin(delta), numpy.cos(- numpy.pi / 2 + gamma)
- kinx, kiny, kinz = numpy.sin(numpy.pi / 2 - mu), 0 , numpy.cos(numpy.pi / 2 - mu)
+ kinx, kiny, kinz = numpy.sin(numpy.pi / 2 - mu), 0, numpy.cos(numpy.pi / 2 - mu)
#now we rotate the frame around hexapod rotation th
- xaxis = numpy.array(rot(1,0,0, numpy.array([0,0,1]), theta))
- yaxis = numpy.array(rot(0,1,0, numpy.array([0,0,1]), theta))
+ xaxis = numpy.array(rot(1, 0, 0, numpy.array([0, 0, 1]), theta))
+ yaxis = numpy.array(rot(0, 1, 0, numpy.array([0, 0, 1]), theta))
#first we rotate the sample around the xaxis
koutx, kouty, koutz = rot(koutx, kouty, koutz, xaxis, chi)
kinx, kiny, kinz = rot(kinx, kiny, kinz, xaxis, chi)
- yaxis = numpy.array(rot(yaxis[0], yaxis[1], yaxis[2], xaxis, chi))# we also have to rotate the yaxis
+ yaxis = numpy.array(rot(yaxis[0], yaxis[1], yaxis[2], xaxis, chi)) # we also have to rotate the yaxis
#then we rotate the sample around the yaxis
koutx, kouty, koutz = rot(koutx, kouty, koutz, yaxis, phi)
@@ -91,8 +92,8 @@ class specularangles(backend.ProjectionBase):
#to calculate the equivalent gamma, delta and mu in the sample frame we rotate the frame around the sample z which is 0,0,1
back = numpy.arctan2(kiny, kinx)
- koutx, kouty, koutz = rot(koutx, kouty, koutz, numpy.array([0,0,1]) , -back)
- kinx, kiny, kinz = rot(kinx, kiny, kinz, numpy.array([0,0,1]) , -back)
+ koutx, kouty, koutz = rot(koutx, kouty, koutz, numpy.array([0, 0, 1]), -back)
+ kinx, kiny, kinz = rot(kinx, kiny, kinz, numpy.array([0, 0, 1]), -back)
mu = numpy.arctan2(kinz, kinx) * numpy.ones_like(delta)
delta = numpy.pi - numpy.arctan2(kouty, koutx)
@@ -105,10 +106,11 @@ class specularangles(backend.ProjectionBase):
delta *= 1 / numpy.pi * 180
gamma *= 1 / numpy.pi * 180
- return (gamma - mu , gamma + mu , delta)
+ return (gamma - mu, gamma + mu, delta)
def get_axis_labels(self):
- return 'g-m','g+m','delta'
+ return 'g-m', 'g+m', 'delta'
+
class ThetaLProjection(backend.ProjectionBase):
# arrays: gamma, delta
@@ -116,13 +118,14 @@ class ThetaLProjection(backend.ProjectionBase):
def project(self, wavelength, UB, gamma, delta, theta, mu, chi, phi):
R = SixCircle.getHKL(wavelength, UB, gamma=gamma, delta=delta, theta=theta, mu=mu, chi=chi, phi=phi)
shape = gamma.size, delta.size
- L = R[2,:].reshape(shape)
+ L = R[2, :].reshape(shape)
theta_array = numpy.ones_like(L) * theta
- return (theta_array,L)
+ return (theta_array, L)
def get_axis_labels(self):
return 'Theta', 'L'
+
class QProjection(backend.ProjectionBase):
def project(self, wavelength, UB, gamma, delta, theta, mu, chi, phi):
shape = gamma.size, delta.size
@@ -130,14 +133,15 @@ class QProjection(backend.ProjectionBase):
sixc.setLambda(wavelength)
sixc.setUB(UB)
R = sixc.getQSurface(gamma=gamma, delta=delta, theta=theta, mu=mu, chi=chi, phi=phi)
- qz = R[0,:].reshape(shape)
- qy = R[1,:].reshape(shape)
- qx = R[2,:].reshape(shape)
+ qz = R[0, :].reshape(shape)
+ qy = R[1, :].reshape(shape)
+ qx = R[2, :].reshape(shape)
return (qz, qy, qx)
def get_axis_labels(self):
return 'qx', 'qy', 'qz'
+
class SphericalQProjection(QProjection):
def project(self, wavelength, UB, gamma, delta, theta, mu, chi, phi):
qz, qy, qx = super(SphericalQProjection, self).project(wavelength, UB, gamma, delta, theta, mu, chi, phi)
@@ -149,6 +153,7 @@ class SphericalQProjection(QProjection):
def get_axis_labels(self):
return 'Q', 'Theta', 'Phi'
+
class CylindricalQProjection(QProjection):
def project(self, wavelength, UB, gamma, delta, theta, mu, chi, phi):
qz, qy, qx = super(CylindricalQProjection, self).project(wavelength, UB, gamma, delta, theta, mu, chi, phi)
@@ -159,6 +164,7 @@ class CylindricalQProjection(QProjection):
def get_axis_labels(self):
return 'qpar', 'qz', 'Phi'
+
class nrQProjection(backend.ProjectionBase):
def project(self, wavelength, UB, gamma, delta, theta, mu, chi, phi):
k0 = 2 * numpy.pi / wavelength
@@ -167,7 +173,7 @@ class nrQProjection(backend.ProjectionBase):
delta *= numpy.pi/180
gamma *= numpy.pi/180
- qy = k0 * (numpy.cos(gamma) * numpy.cos(delta) - numpy.cos(mu)) ## definition of qx, and qy same as spec at theta = 0
+ qy = k0 * (numpy.cos(gamma) * numpy.cos(delta) - numpy.cos(mu)) # definition of qx, and qy same as spec at theta = 0
qx = k0 * (numpy.cos(gamma) * numpy.sin(delta))
qz = k0 * (numpy.sin(gamma) + numpy.sin(mu))
return (qx, qy, qz)
@@ -175,14 +181,16 @@ class nrQProjection(backend.ProjectionBase):
def get_axis_labels(self):
return 'qx', 'qy', 'qz'
+
class TwoThetaProjection(SphericalQProjection):
def project(self, wavelength, UB, gamma, delta, theta, mu, chi, phi):
q, theta, phi = super(TwoThetaProjection, self).project(wavelength, UB, gamma, delta, theta, mu, chi, phi)
- return 2 * numpy.arcsin(q * wavelength / (4 * numpy.pi)) / numpy.pi * 180, # note: we need to return a 1-tuple?
+ return 2 * numpy.arcsin(q * wavelength / (4 * numpy.pi)) / numpy.pi * 180, # note: we need to return a 1-tuple?
def get_axis_labels(self):
return 'TwoTheta'
+
class Qpp(nrQProjection):
def project(self, wavelength, UB, gamma, delta, theta, mu, chi, phi):
qx, qy, qz = super(Qpp, self).project(wavelength, UB, gamma, delta, theta, mu, chi, phi)
@@ -193,32 +201,35 @@ class Qpp(nrQProjection):
def get_axis_labels(self):
return 'Qpar', 'Qz'
-class GammaDeltaTheta(HKLProjection):#just passing on the coordinates, makes it easy to accurately test the theta correction
+
+class GammaDeltaTheta(HKLProjection): # just passing on the coordinates, makes it easy to accurately test the theta correction
def project(self, wavelength, UB, gamma, delta, theta, mu, chi, phi):
- delta,gamma = numpy.meshgrid(delta,gamma)
+ delta, gamma = numpy.meshgrid(delta, gamma)
theta = theta * numpy.ones_like(delta)
- return (gamma, delta, theta)
+ return (gamma, delta, theta)
def get_axis_labels(self):
- return 'Gamma','Delta','Theta'
+ return 'Gamma', 'Delta', 'Theta'
-class GammaDelta(HKLProjection):#just passing on the coordinates, makes it easy to accurately test the theta correction
+
+class GammaDelta(HKLProjection): # just passing on the coordinates, makes it easy to accurately test the theta correction
def project(self, wavelength, UB, gamma, delta, theta, mu, chi, phi):
- delta,gamma = numpy.meshgrid(delta,gamma)
- return (gamma, delta)
+ delta, gamma = numpy.meshgrid(delta, gamma)
+ return (gamma, delta)
def get_axis_labels(self):
- return 'Gamma','Delta'
+ return 'Gamma', 'Delta'
-class GammaDeltaMu(HKLProjection):#just passing on the coordinates, makes it easy to accurately test the theta correction
+class GammaDeltaMu(HKLProjection): # just passing on the coordinates, makes it easy to accurately test the theta correction
def project(self, wavelength, UB, gamma, delta, theta, mu, chi, phi):
- delta,gamma = numpy.meshgrid(delta,gamma)
+ delta, gamma = numpy.meshgrid(delta, gamma)
mu = mu * numpy.ones_like(delta)
- return (gamma, delta, mu)
+ return (gamma, delta, mu)
def get_axis_labels(self):
- return 'Gamma','Delta','Mu'
+ return 'Gamma', 'Delta', 'Mu'
+
class ID03Input(backend.InputBase):
# OFFICIAL API
@@ -244,9 +255,9 @@ class ID03Input(backend.InputBase):
start = 0
try:
pointcount = scan.lines()
- except specfile.error: # no points
+ except specfile.error: # no points
continue
- next(self.get_images(scan, 0, pointcount-1, dry_run=True))# dryrun
+ next(self.get_images(scan, 0, pointcount-1, dry_run=True)) # dryrun
if pointcount > self.config.target_weight * 1.4:
for s in util.chunk_slicer(pointcount, self.config.target_weight):
yield backend.Job(scan=scanno, firstpoint=start+s.start, lastpoint=start+s.stop-1, weight=s.stop-s.start)
@@ -257,45 +268,45 @@ class ID03Input(backend.InputBase):
scan = self.get_delayed_scan(scanno)
if self.config.pr:
- firstpoint, lastpoint = self.config.pr#firstpoint is the first index to be included, lastpoint the last index to be included.
+ firstpoint, lastpoint = self.config.pr # firstpoint is the first index to be included, lastpoint the last index to be included.
else:
firstpoint, lastpoint = 0, self.target(scan) - 1
pointcount = lastpoint - firstpoint + 1
- if self.is_zap(scan): #wait until the scan is finished.
- if not self.wait_for_points(scanno, self.target(scan), timeout=self.config.timeout): #wait for last datapoint
+ if self.is_zap(scan): # wait until the scan is finished.
+ if not self.wait_for_points(scanno, self.target(scan), timeout=self.config.timeout): # wait for last datapoint
for s in util.chunk_slicer(pointcount, self.config.target_weight):
- yield backend.Job(scan=scanno, firstpoint=firstpoint+s.start, lastpoint=firstpoint+s.stop-1, weight=s.stop-s.start)
+ yield backend.Job(scan=scanno, firstpoint=firstpoint+s.start, lastpoint=firstpoint+s.stop-1, weight=s.stop-s.start)
else:
raise errors.BackendError('Image collection timed out. Zapscan was probably aborted')
- elif lastpoint >= 0: #scanlength is known
+ elif lastpoint >= 0: # scanlength is known
for s in util.chunk_slicer(pointcount, self.config.target_weight):
if self.wait_for_points(scanno, firstpoint + s.stop, timeout=self.config.timeout):
- stop = self.get_scan(scanno).lines()
- yield backend.Job(scan=scanno, firstpoint=firstpoint+s.start, lastpoint=stop-1, weight=s.stop-s.start)
- break
+ stop = self.get_scan(scanno).lines()
+ yield backend.Job(scan=scanno, firstpoint=firstpoint+s.start, lastpoint=stop-1, weight=s.stop-s.start)
+ break
else:
- yield backend.Job(scan=scanno, firstpoint=firstpoint+s.start, lastpoint=firstpoint+s.stop-1, weight=s.stop-s.start)
- else: #scanlength is unknown
+ yield backend.Job(scan=scanno, firstpoint=firstpoint+s.start, lastpoint=firstpoint+s.stop-1, weight=s.stop-s.start)
+ else: # scanlength is unknown
step = int(self.config.target_weight / 1.4)
- for start, stop in itertools.izip(itertools.count(0,step), itertools.count(step,step)):
+ for start, stop in itertools.izip(itertools.count(0, step), itertools.count(step, step)):
if self.wait_for_points(scanno, stop, timeout=self.config.timeout):
- stop = self.get_scan(scanno).lines()
- yield backend.Job(scan=scanno, firstpoint=start, lastpoint=stop-1, weight=stop-start)
- break
+ stop = self.get_scan(scanno).lines()
+ yield backend.Job(scan=scanno, firstpoint=start, lastpoint=stop-1, weight=stop-start)
+ break
else:
- yield backend.Job(scan=scanno, firstpoint=start, lastpoint=stop-1, weight=stop-start)
+ yield backend.Job(scan=scanno, firstpoint=start, lastpoint=stop-1, weight=stop-start)
def process_job(self, job):
super(ID03Input, self).process_job(job)
scan = self.get_scan(job.scan)
self.metadict = dict()
try:
- scanparams = self.get_scan_params(scan) # wavelength, UB
- pointparams = self.get_point_params(scan, job.firstpoint, job.lastpoint) # 2D array of diffractometer angles + mon + transm
- images = self.get_images(scan, job.firstpoint, job.lastpoint) # iterator!
-
+ scanparams = self.get_scan_params(scan) # wavelength, UB
+ pointparams = self.get_point_params(scan, job.firstpoint, job.lastpoint) # 2D array of diffractometer angles + mon + transm
+ images = self.get_images(scan, job.firstpoint, job.lastpoint) # iterator!
+
for pp, image in itertools.izip(pointparams, images):
yield self.process_image(scanparams, pp, image)
util.statuseol()
@@ -306,24 +317,24 @@ class ID03Input(backend.InputBase):
def parse_config(self, config):
super(ID03Input, self).parse_config(config)
- self.config.xmask = util.parse_multi_range(config.pop('xmask', None))#Optional, select a subset of the image range in the x direction. all by default
- self.config.ymask = util.parse_multi_range(config.pop('ymask', None))#Optional, select a subset of the image range in the y direction. all by default
- self.config.specfile = config.pop('specfile')#Location of the specfile
- self.config.imagefolder = config.pop('imagefolder', None) #Optional, takes specfile folder tag by default
- self.config.pr = config.pop('pr', None) #Optional, all range by default
- self.config.background = config.pop('background', None) #Optional, if supplied a space of this image is constructed
- self.config.th_offset = float(config.pop('th_offset', 0)) #Optional; Only used in zapscans, zero by default.
+ self.config.xmask = util.parse_multi_range(config.pop('xmask', None)) # Optional, select a subset of the image range in the x direction. all by default
+ self.config.ymask = util.parse_multi_range(config.pop('ymask', None)) # Optional, select a subset of the image range in the y direction. all by default
+ self.config.specfile = config.pop('specfile') # Location of the specfile
+ self.config.imagefolder = config.pop('imagefolder', None) # Optional, takes specfile folder tag by default
+ self.config.pr = config.pop('pr', None) # Optional, all range by default
+ self.config.background = config.pop('background', None) # Optional, if supplied a space of this image is constructed
+ self.config.th_offset = float(config.pop('th_offset', 0)) # Optional; Only used in zapscans, zero by default.
if self.config.xmask is None:
self.config.xmask = slice(None)
if self.config.ymask is None:
self.config.ymask = slice(None)
- self.config.maskmatrix = load_matrix(config.pop('maskmatrix', None)) #Optional, if supplied pixels where the mask is 0 will be removed
+ self.config.maskmatrix = load_matrix(config.pop('maskmatrix', None)) # Optional, if supplied pixels where the mask is 0 will be removed
if self.config.pr:
self.config.pr = util.parse_tuple(self.config.pr, length=2, type=int)
- self.config.sdd = float(config.pop('sdd'))# sample to detector distance (mm)
- self.config.pixelsize = util.parse_tuple(config.pop('pixelsize'), length=2, type=float)# pixel size x/y (mm) (same dimension as sdd)
- self.config.wait_for_data = util.parse_bool(config.pop('wait_for_data', 'false'))#Optional, if true wait until the data appears
- self.config.timeout = int(config.pop('timeout', 180))#Optional, how long the script wait until it assumes the scan is not continuing
+ self.config.sdd = float(config.pop('sdd')) # sample to detector distance (mm)
+ self.config.pixelsize = util.parse_tuple(config.pop('pixelsize'), length=2, type=float) # pixel size x/y (mm) (same dimension as sdd)
+ self.config.wait_for_data = util.parse_bool(config.pop('wait_for_data', 'false')) # Optional, if true wait until the data appears
+ self.config.timeout = int(config.pop('timeout', 180)) # Optional, how long the script wait until it assumes the scan is not continuing
def get_destination_options(self, command):
if not command:
@@ -337,12 +348,12 @@ class ID03Input(backend.InputBase):
spec = specfilewrapper.Specfile(self.config.specfile)
return spec.select('{0}.1'.format(scannumber))
- def get_delayed_scan(self, scannumber, timeout = None):
+ def get_delayed_scan(self, scannumber, timeout=None):
delay = util.loop_delayer(5)
start = time.time()
while 1:
try:
- return self.get_scan(scannumber) #reload entire specfile
+ return self.get_scan(scannumber) # reload entire specfile
except specfile.error:
if timeout is not None and time.time() - start > timeout:
raise errors.BackendError('Scan timed out. There is no data to process')
@@ -358,7 +369,7 @@ class ID03Input(backend.InputBase):
scan = self.get_scan(scannumber)
try:
if scan.lines() >= stop:
- next(delay) #time delay between specfile and edf file
+ next(delay) # time delay between specfile and edf file
return False
except specfile.error:
pass
@@ -373,7 +384,6 @@ class ID03Input(backend.InputBase):
except specfile.error:
raise errors.BackendError('Scan was aborted before images were collected. There is no data to process')
-
def target(self, scan):
if any(tuple(scan.command().startswith(pattern) for pattern in ['hklscan', 'a2scan', 'ascan', 'ringscan'])):
return int(scan.command().split()[-2]) + 1
@@ -414,7 +424,7 @@ class ID03Input(backend.InputBase):
continue
return ret
- @staticmethod
+ @staticmethod
def apply_mask(data, xmask, ymask):
roi = data[ymask, :]
return roi[:, xmask]
@@ -426,13 +436,13 @@ class ID03Input(backend.InputBase):
# zapscans don't contain the UB matrix, this needs to be fixed at ID03
scanno = scan.number()
UB = None
- while 1: # look back in spec file to locate a UB matrix
+ while 1: # look back in spec file to locate a UB matrix
try:
ubscan = self.get_scan(scanno)
except specfilewrapper.specfile.error:
break
try:
- UB = numpy.array(ubscan.header('G')[2].split(' ')[-9:],dtype=numpy.float)
+ UB = numpy.array(ubscan.header('G')[2].split(' ')[-9:], dtype=numpy.float)
except:
scanno -= 1
else:
@@ -443,7 +453,7 @@ class ID03Input(backend.InputBase):
raise errors.ConfigError('UB matrix must be specified in configuration file when processing zapscans')
UB = numpy.array(self.config.UB)
else:
- UB = numpy.array(scan.header('G')[2].split(' ')[-9:],dtype=numpy.float)
+ UB = numpy.array(scan.header('G')[2].split(' ')[-9:], dtype=numpy.float)
wavelength = float(scan.header('G')[1].split(' ')[-1])
self.metadict['UB'] = UB
@@ -451,7 +461,6 @@ class ID03Input(backend.InputBase):
return wavelength, UB
-
def get_images(self, scan, first, last, dry_run=False):
if self.config.background:
if not os.path.exists(self.config.background):
@@ -466,14 +475,14 @@ class ID03Input(backend.InputBase):
else:
if self.is_zap(scan):
scanheaderC = scan.header('C')
- zapscanno = int(scanheaderC[2].split(' ')[-1]) # is different from scanno should be changed in spec!
+ zapscanno = int(scanheaderC[2].split(' ')[-1]) # is different from scanno should be changed in spec!
try:
uccdtagline = scanheaderC[0]
- UCCD = os.path.split(uccdtagline.split()[-1])
+ UCCD = os.path.split(uccdtagline.split()[-1])
except:
print 'warning: UCCD tag not found, use imagefolder for proper file specification'
UCCD = []
- pattern = self._get_pattern(UCCD)
+ pattern = self._get_pattern(UCCD)
matches = self.find_edfs(pattern, zapscanno)
if 0 not in matches:
raise errors.FileError('could not find matching edf for zapscannumber {0} using pattern {1}'.format(zapscanno, pattern))
@@ -492,7 +501,7 @@ class ID03Input(backend.InputBase):
except:
print 'warning: UCCD tag not found, use imagefolder for proper file specification'
UCCD = []
- pattern = self._get_pattern(UCCD)
+ pattern = self._get_pattern(UCCD)
matches = self.find_edfs(pattern, scan.number())
if set(range(first, last + 1)) > set(matches.keys()):
raise errors.FileError("incorrect number of matches for scan {0} using pattern {1}".format(scan.number(), pattern))
@@ -504,22 +513,21 @@ class ID03Input(backend.InputBase):
edf = EdfFile.EdfFile(matches[i])
yield edf.GetData(0)
- def _get_pattern(self,UCCD):
- imagefolder = self.config.imagefolder
- if imagefolder:
- try:
- imagefolder = imagefolder.format(UCCD=UCCD, rUCCD=list(reversed(UCCD)))
- except Exception as e:
- raise errors.ConfigError("invalid 'imagefolder' specification '{0}': {1}".format(self.config.imagefolder, e))
- else:
- if not os.path.exists(imagefolder):
- raise errors.ConfigError("invalid 'imagefolder' specification '{0}'. Path {1} does not exist".format(self.config.imagefolder, imagefolder))
- else:
- imagefolder = os.path.join(*UCCD)
- if not os.path.exists(imagefolder):
- raise errors.ConfigError("invalid UCCD tag '{0}'. The UCCD tag in the specfile does not point to an existing folder. Specify the imagefolder in the configuration file.".format(imagefolder))
- return os.path.join(imagefolder, '*')
-
+ def _get_pattern(self, UCCD):
+ imagefolder = self.config.imagefolder
+ if imagefolder:
+ try:
+ imagefolder = imagefolder.format(UCCD=UCCD, rUCCD=list(reversed(UCCD)))
+ except Exception as e:
+ raise errors.ConfigError("invalid 'imagefolder' specification '{0}': {1}".format(self.config.imagefolder, e))
+ else:
+ if not os.path.exists(imagefolder):
+ raise errors.ConfigError("invalid 'imagefolder' specification '{0}'. Path {1} does not exist".format(self.config.imagefolder, imagefolder))
+ else:
+ imagefolder = os.path.join(*UCCD)
+ if not os.path.exists(imagefolder):
+ raise errors.ConfigError("invalid UCCD tag '{0}'. The UCCD tag in the specfile does not point to an existing folder. Specify the imagefolder in the configuration file.".format(imagefolder))
+ return os.path.join(imagefolder, '*')
class EH1(ID03Input):
@@ -527,14 +535,14 @@ class EH1(ID03Input):
def parse_config(self, config):
super(EH1, self).parse_config(config)
- self.config.centralpixel = util.parse_tuple(config.pop('centralpixel'), length=2, type=int) #x,y
- self.config.hr = config.pop('hr', None) #Optional, hexapod rotations in miliradians. At the entered value the sample is assumed flat, if not entered the sample is assumed flat at the spec values.
- self.config.UB = config.pop('ub', None) #Optional, takes specfile matrix by default
+ self.config.centralpixel = util.parse_tuple(config.pop('centralpixel'), length=2, type=int) # x,y
+ self.config.hr = config.pop('hr', None) # Optional, hexapod rotations in miliradians. At the entered value the sample is assumed flat, if not entered the sample is assumed flat at the spec values.
+ self.config.UB = config.pop('ub', None) # Optional, takes specfile matrix by default
if self.config.UB:
self.config.UB = util.parse_tuple(self.config.UB, length=9, type=float)
if self.config.hr:
self.config.hr = util.parse_tuple(self.config.hr, length=2, type=float)
-
+
def process_image(self, scanparams, pointparams, image):
gamma, delta, theta, chi, phi, mu, mon, transm, hrx, hry = pointparams
wavelength, UB = scanparams
@@ -552,19 +560,19 @@ class EH1(ID03Input):
data = image / mon / transm
if mon == 0:
- raise errors.BackendError('Monitor is zero, this results in empty output. Scannumber = {0}, pointnumber = {1}. Did you forget to open the shutter?'.format(self.dbg_scanno, self.dbg_pointno))
+ raise errors.BackendError('Monitor is zero, this results in empty output. Scannumber = {0}, pointnumber = {1}. Did you forget to open the shutter?'.format(self.dbg_scanno, self.dbg_pointno))
util.status('{4}| gamma: {0}, delta: {1}, theta: {2}, mu: {3}'.format(gamma, delta, theta, mu, time.ctime(time.time())))
# pixels to angles
pixelsize = numpy.array(self.config.pixelsize)
- sdd = self.config.sdd
+ sdd = self.config.sdd
app = numpy.arctan(pixelsize / sdd) * 180 / numpy.pi
- centralpixel = self.config.centralpixel # (column, row) = (delta, gamma)
- gamma_range= -app[1] * (numpy.arange(data.shape[1]) - centralpixel[1]) + gamma
- delta_range= app[0] * (numpy.arange(data.shape[0]) - centralpixel[0]) + delta
+ centralpixel = self.config.centralpixel # (column, row) = (delta, gamma)
+ gamma_range = -app[1] * (numpy.arange(data.shape[1]) - centralpixel[1]) + gamma
+ delta_range = app[0] * (numpy.arange(data.shape[0]) - centralpixel[0]) + delta
# masking
if self.config.maskmatrix is not None:
@@ -581,15 +589,14 @@ class EH1(ID03Input):
delta_grid, gamma_grid = numpy.meshgrid(delta_range, gamma_range)
Pver = 1 - numpy.sin(delta_grid * numpy.pi / 180.)**2 * numpy.cos(gamma_grid * numpy.pi / 180.)**2
intensity /= Pver
-
- return intensity, weights, (wavelength, UB, gamma_range, delta_range, theta, mu, chi, phi)
+ return intensity, weights, (wavelength, UB, gamma_range, delta_range, theta, mu, chi, phi)
def get_point_params(self, scan, first, last):
sl = slice(first, last+1)
GAM, DEL, TH, CHI, PHI, MU, MON, TRANSM, HRX, HRY = range(10)
- params = numpy.zeros((last - first + 1, 10)) # gamma delta theta chi phi mu mon transm
+ params = numpy.zeros((last - first + 1, 10)) # gamma delta theta chi phi mu mon transm
params[:, CHI] = scan.motorpos('Chi')
params[:, PHI] = scan.motorpos('Phi')
@@ -597,8 +604,7 @@ class EH1(ID03Input):
params[:, HRX] = scan.motorpos('hrx')
params[:, HRY] = scan.motorpos('hry')
except:
- raise errors.BackendError('The specfile does not accept hrx and hry as a motor label. Have you selected the right hutch? Scannumber = {0}, pointnumber = {1}'.format(self.dbg_scanno, self.dbg_pointno))
-
+ raise errors.BackendError('The specfile does not accept hrx and hry as a motor label. Have you selected the right hutch? Scannumber = {0}, pointnumber = {1}'.format(self.dbg_scanno, self.dbg_pointno))
if self.is_zap(scan):
if 'th' in scan.alllabels():
@@ -612,7 +618,6 @@ class EH1(ID03Input):
else:
params[:, TH] = scan.motorpos('Theta')
-
params[:, GAM] = scan.motorpos('Gam')
params[:, DEL] = scan.motorpos('Delta')
params[:, MU] = scan.motorpos('Mu')
@@ -620,38 +625,39 @@ class EH1(ID03Input):
params[:, MON] = scan.datacol('zap_mon')[sl]
transm = scan.datacol('zap_transm')
- transm[-1] = transm[-2] # bug in specfile
+ transm[-1] = transm[-2] # bug in specfile
params[:, TRANSM] = transm[sl]
else:
if 'hrx' in scan.alllabels():
- params[:, HRX] = scan.datacol('hrx')[sl]
+ params[:, HRX] = scan.datacol('hrx')[sl]
if 'hry' in scan.alllabels():
- params[:, HRY] = scan.datacol('hry')[sl]
+ params[:, HRY] = scan.datacol('hry')[sl]
params[:, TH] = scan.datacol('thcnt')[sl]
params[:, GAM] = scan.datacol('gamcnt')[sl]
params[:, DEL] = scan.datacol('delcnt')[sl]
try:
- params[:, MON] = scan.datacol(self.monitor_counter)[sl] # differs in EH1/EH2
+ params[:, MON] = scan.datacol(self.monitor_counter)[sl] # differs in EH1/EH2
except:
- raise errors.BackendError('The specfile does not accept {2} as a monitor label. Have you selected the right hutch? Scannumber = {0}, pointnumber = {1}'.format(self.dbg_scanno, self.dbg_pointno, self.monitor_counter))
+ raise errors.BackendError('The specfile does not accept {2} as a monitor label. Have you selected the right hutch? Scannumber = {0}, pointnumber = {1}'.format(self.dbg_scanno, self.dbg_pointno, self.monitor_counter))
params[:, TRANSM] = scan.datacol('transm')[sl]
params[:, MU] = scan.datacol('mucnt')[sl]
-
+
return params
+
class EH2(ID03Input):
monitor_counter = 'Monitor'
def parse_config(self, config):
super(EH2, self).parse_config(config)
- self.config.centralpixel = util.parse_tuple(config.pop('centralpixel'), length=2, type=int) #x,y
- self.config.UB = config.pop('ub', None) #Optional, takes specfile matrix by default
+ self.config.centralpixel = util.parse_tuple(config.pop('centralpixel'), length=2, type=int) # x,y
+ self.config.UB = config.pop('ub', None) # Optional, takes specfile matrix by default
if self.config.UB:
self.config.UB = util.parse_tuple(self.config.UB, length=9, type=float)
-
+
def process_image(self, scanparams, pointparams, image):
gamma, delta, theta, chi, phi, mu, mon, transm = pointparams
wavelength, UB = scanparams
@@ -664,7 +670,7 @@ class EH2(ID03Input):
data = image / mon / transm
if mon == 0:
- raise errors.BackendError('Monitor is zero, this results in empty output. Scannumber = {0}, pointnumber = {1}. Did you forget to open the shutter?'.format(self.dbg_scanno, self.dbg_pointno))
+ raise errors.BackendError('Monitor is zero, this results in empty output. Scannumber = {0}, pointnumber = {1}. Did you forget to open the shutter?'.format(self.dbg_scanno, self.dbg_pointno))
util.status('{4}| gamma: {0}, delta: {1}, theta: {2}, mu: {3}'.format(gamma, delta, theta, mu, time.ctime(time.time())))
@@ -676,7 +682,7 @@ class EH2(ID03Input):
pixelsize = numpy.array(self.config.pixelsize)
app = numpy.arctan(pixelsize / sdd) * 180 / numpy.pi
- centralpixel = self.config.centralpixel # (row, column) = (gamma, delta)
+ centralpixel = self.config.centralpixel # (row, column) = (gamma, delta)
gamma_range = - 1 * app[0] * (numpy.arange(data.shape[0]) - centralpixel[0]) + gamma
delta_range = app[1] * (numpy.arange(data.shape[1]) - centralpixel[1]) + delta
@@ -693,12 +699,12 @@ class EH2(ID03Input):
intensity = numpy.fliplr(intensity)
intensity = numpy.rot90(intensity)
- weights = numpy.fliplr(weights)#TODO: should be done more efficiently. Will prob change with new HKL calculations
+ weights = numpy.fliplr(weights) # TODO: should be done more efficiently. Will prob change with new HKL calculations
weights = numpy.rot90(weights)
-
+
#polarisation correction
delta_grid, gamma_grid = numpy.meshgrid(delta_range, gamma_range)
- Phor = 1 - (numpy.sin(mu * numpy.pi / 180.) * numpy.sin(delta_grid * numpy.pi / 180.) * numpy.cos(gamma_grid* numpy.pi / 180.) + numpy.cos(mu* numpy.pi / 180.) * numpy.sin(gamma_grid* numpy.pi / 180.))**2
+ Phor = 1 - (numpy.sin(mu * numpy.pi / 180.) * numpy.sin(delta_grid * numpy.pi / 180.) * numpy.cos(gamma_grid * numpy.pi / 180.) + numpy.cos(mu * numpy.pi / 180.) * numpy.sin(gamma_grid * numpy.pi / 180.))**2
intensity /= Phor
return intensity, weights, (wavelength, UB, gamma_range, delta_range, theta, mu, chi, phi)
@@ -707,10 +713,10 @@ class EH2(ID03Input):
sl = slice(first, last+1)
GAM, DEL, TH, CHI, PHI, MU, MON, TRANSM = range(8)
- params = numpy.zeros((last - first + 1, 8)) # gamma delta theta chi phi mu mon transm
+ params = numpy.zeros((last - first + 1, 8)) # gamma delta theta chi phi mu mon transm
params[:, CHI] = scan.motorpos('Chi')
params[:, PHI] = scan.motorpos('Phi')
-
+
if self.is_zap(scan):
if 'th' in scan.alllabels():
th = scan.datacol('th')[sl]
@@ -729,7 +735,7 @@ class EH2(ID03Input):
params[:, MON] = scan.datacol('zap_mon')[sl]
transm = scan.datacol('zap_transm')
- transm[-1] = transm[-2] # bug in specfile
+ transm[-1] = transm[-2] # bug in specfile
params[:, TRANSM] = transm[sl]
else:
params[:, TH] = scan.datacol('thcnt')[sl]
@@ -737,19 +743,20 @@ class EH2(ID03Input):
params[:, DEL] = scan.datacol('delcnt')[sl]
try:
- params[:, MON] = scan.datacol(self.monitor_counter)[sl] # differs in EH1/EH2
+ params[:, MON] = scan.datacol(self.monitor_counter)[sl] # differs in EH1/EH2
except:
- raise errors.BackendError('The specfile does not accept {2} as a monitor label. Have you selected the right hutch? Scannumber = {0}, pointnumber = {1}'.format(self.dbg_scanno, self.dbg_pointno, self.monitor_counter))
-
+ raise errors.BackendError('The specfile does not accept {2} as a monitor label. Have you selected the right hutch? Scannumber = {0}, pointnumber = {1}'.format(self.dbg_scanno, self.dbg_pointno, self.monitor_counter))
+
params[:, TRANSM] = scan.datacol('transm')[sl]
params[:, MU] = scan.datacol('mucnt')[sl]
return params
+
class GisaxsDetector(ID03Input):
monitor_counter = 'mon'
def process_image(self, scanparams, pointparams, image):
- ccdy, ccdz, theta, chi, phi, mu, mon, transm= pointparams
+ ccdy, ccdz, theta, chi, phi, mu, mon, transm = pointparams
weights = numpy.ones_like(image)
@@ -761,17 +768,17 @@ class GisaxsDetector(ID03Input):
data = image / mon / transm
if mon == 0:
- raise errors.BackendError('Monitor is zero, this results in empty output. Scannumber = {0}, pointnumber = {1}. Did you forget to open the shutter?'.format(self.dbg_scanno, self.dbg_pointno))
+ raise errors.BackendError('Monitor is zero, this results in empty output. Scannumber = {0}, pointnumber = {1}. Did you forget to open the shutter?'.format(self.dbg_scanno, self.dbg_pointno))
util.status('{4}| ccdy: {0}, ccdz: {1}, theta: {2}, mu: {3}'.format(ccdy, ccdz, theta, mu, time.ctime(time.time())))
# pixels to angles
pixelsize = numpy.array(self.config.pixelsize)
- sdd = self.config.sdd
+ sdd = self.config.sdd
directbeam = (self.config.directbeam[0] - (ccdy - self.config.directbeam_coords[0]) * pixelsize[0], self.config.directbeam[1] - (ccdz - self.config.directbeam_coords[1]) * pixelsize[1])
- gamma_distance = - pixelsize[1] * (numpy.arange(data.shape[1]) - directbeam[1])
- delta_distance = - pixelsize[0] * (numpy.arange(data.shape[0]) - directbeam[0])
+ gamma_distance = - pixelsize[1] * (numpy.arange(data.shape[1]) - directbeam[1])
+ delta_distance = - pixelsize[0] * (numpy.arange(data.shape[0]) - directbeam[0])
gamma_range = numpy.arctan2(gamma_distance, sdd) / numpy.pi * 180 - mu
delta_range = numpy.arctan2(delta_distance, sdd) / numpy.pi * 180
@@ -793,17 +800,16 @@ class GisaxsDetector(ID03Input):
return intensity, weights, (wavelength, UB, gamma_range, delta_range, theta, mu, chi, phi)
-
def parse_config(self, config):
super(GisaxsDetector, self).parse_config(config)
- self.config.directbeam = util.parse_tuple(config.pop('directbeam'), length=2, type=int)
- self.config.directbeam_coords = util.parse_tuple(config.pop('directbeam_coords'), length=2, type=float) #Coordinates of ccdy and ccdz at the direct beam position
+ self.config.directbeam = util.parse_tuple(config.pop('directbeam'), length=2, type=int)
+ self.config.directbeam_coords = util.parse_tuple(config.pop('directbeam_coords'), length=2, type=float) # Coordinates of ccdy and ccdz at the direct beam position
def get_point_params(self, scan, first, last):
sl = slice(first, last+1)
CCDY, CCDZ, TH, CHI, PHI, MU, MON, TRANSM = range(8)
- params = numpy.zeros((last - first + 1, 8)) # gamma delta theta chi phi mu mon transm
+ params = numpy.zeros((last - first + 1, 8)) # gamma delta theta chi phi mu mon transm
params[:, CHI] = scan.motorpos('Chi')
params[:, PHI] = scan.motorpos('Phi')
params[:, CCDY] = scan.motorpos('ccdy')
@@ -812,10 +818,10 @@ class GisaxsDetector(ID03Input):
params[:, TH] = scan.datacol('thcnt')[sl]
try:
- params[:, MON] = scan.datacol(self.monitor_counter)[sl] # differs in EH1/EH2
+ params[:, MON] = scan.datacol(self.monitor_counter)[sl] # differs in EH1/EH2
except:
- raise errors.BackendError('The specfile does not accept {2} as a monitor label. Have you selected the right hutch? Scannumber = {0}, pointnumber = {1}'.format(self.dbg_scanno, self.dbg_pointno, self.monitor_counter))
-
+ raise errors.BackendError('The specfile does not accept {2} as a monitor label. Have you selected the right hutch? Scannumber = {0}, pointnumber = {1}'.format(self.dbg_scanno, self.dbg_pointno, self.monitor_counter))
+
params[:, TRANSM] = scan.datacol('transm')[sl]
params[:, MU] = scan.datacol('mucnt')[sl]
return params
@@ -834,20 +840,19 @@ class GisaxsDetector(ID03Input):
continue
return ret
+
def load_matrix(filename):
if filename == None:
return None
if os.path.exists(filename):
ext = os.path.splitext(filename)[-1]
if ext == '.txt':
- return numpy.array(numpy.loadtxt(filename), dtype = numpy.bool)
+ return numpy.array(numpy.loadtxt(filename), dtype=numpy.bool)
elif ext == '.npy':
- return numpy.array(numpy.load(filename), dtype = numpy.bool)
+ return numpy.array(numpy.load(filename), dtype=numpy.bool)
elif ext == '.edf':
- return numpy.array(EdfFile.EdfFile(filename).getData(0),dtype = numpy.bool)
+ return numpy.array(EdfFile.EdfFile(filename).getData(0), dtype=numpy.bool)
else:
- raise ValueError('unknown extension {0}, unable to load matrix!\n'.format(ext))
+ raise ValueError('unknown extension {0}, unable to load matrix!\n'.format(ext))
else:
- raise IOError('filename: {0} does not exist. Can not load matrix'.format(filename))
-
-
+ raise IOError('filename: {0} does not exist. Can not load matrix'.format(filename))
diff --git a/binoculars/backends/test.py b/binoculars/backends/test.py
new file mode 100644
index 0000000..ad71878
--- /dev/null
+++ b/binoculars/backends/test.py
@@ -0,0 +1,466 @@
+# -*- encoding: utf-8 -*-
+'''
+ This file is part of the binoculars project.
+
+ The BINoculars library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The BINoculars library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the hkl library. If not, see <http://www.gnu.org/licenses/>.
+
+ Copyright (C) 2015 Synchrotron SOLEIL
+ L'Orme des Merisiers Saint-Aubin
+ BP 48 91192 GIF-sur-YVETTE CEDEX
+
+ Copyright (C) 2012-2015 European Synchrotron Radiation Facility
+ Grenoble, France
+
+ Authors: Willem Onderwaater <onderwaa@esrf.fr>
+ Picca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>
+
+'''
+import sys
+import os
+import itertools
+import numpy
+import tables
+import math
+
+from pyFAI.detectors import ALL_DETECTORS
+from math import cos, sin
+from collections import namedtuple
+from numpy.linalg import inv
+from networkx import DiGraph, dijkstra_path
+
+from .. import backend, errors, util
+
+
+class realspace(backend.ProjectionBase):
+ # scalars: mu, theta, [chi, phi, "omitted"] delta, gamR, gamT, ty, wavelength
+ # 3x3 matrix: UB
+ def project(self, index, dataframe, pixels):
+ return pixels[1], pixels[2]
+ #return numpy.meshgrid(numpy.arange(pixels[0].shape[1]), numpy.arange(pixels[0].shape[0]))
+
+ def get_axis_labels(self):
+ return 'x', 'y'
+
+class Pixels(backend.ProjectionBase):
+ # scalars: mu, theta, [chi, phi, "omitted"] delta, gamR, gamT, ty, wavelength
+ # 3x3 matrix: UB
+ def project(self, index, dataframe, pixels):
+ return numpy.meshgrid(numpy.arange(pixels[0].shape[1]), numpy.arange(pixels[0].shape[0]))
+
+ def get_axis_labels(self):
+ return 'x', 'y'
+
+
+class HKLProjection(backend.ProjectionBase):
+ # scalars: mu, theta, [chi, phi, "omitted"] delta, gamR, gamT, ty, wavelength
+ # 3x3 matrix: UB
+ def project(self, index, dataframe, pixels):
+ # put the detector at the right position
+
+ UB = dataframe.diffractometer.ub
+
+ s_axes = rotation_axes(dataframe.diffractometer.axes.graph,
+ dataframe.diffractometer.axes.sample)
+ d_axes = rotation_axes(dataframe.diffractometer.axes.graph,
+ dataframe.diffractometer.axes.detector)
+
+ # the ki vector should be in the NexusFile or easily extracted
+ # from the hkl library.
+ ki = [1, 0, 0]
+ k = 2 * math.pi / dataframe.source.wavelength
+ values = dataframe.mu[index], dataframe.omega[index], dataframe.delta[index], dataframe.gamma[index]
+ s_values = values[0], values[1]
+ d_values = values[0], values[2], values[3]
+ R = reduce(numpy.dot, (zip_with(M, numpy.radians(s_values), s_axes)))
+ P = reduce(numpy.dot, (zip_with(M, numpy.radians(d_values), d_axes)))
+ RUB_1 = inv(numpy.dot(R, UB))
+ RUB_1P = numpy.dot(RUB_1, P)
+ # rotate the detector around x of 90 degrees
+ RUB_1P = numpy.dot(RUB_1P, M(math.pi/2., [1, 0, 0]))
+ kf = normalized(pixels, axis=0)
+ hkl_f = numpy.tensordot(RUB_1P, kf, axes=1)
+ hkl_i = numpy.dot(RUB_1, ki)
+ hkl = hkl_f - hkl_i[:, numpy.newaxis, numpy.newaxis]
+
+ h,k,l = hkl * k
+
+ return (h, k, l)
+
+ def get_axis_labels(self):
+ return 'H', 'K', 'L'
+
+class HKProjection(HKLProjection):
+ def project(self, index, dataframe, pixels):
+ h,k,l = super(HKProjection, self).project(index, dataframe, pixels)
+ return h,k
+
+ def get_axis_labels(self):
+ return 'H', 'K'
+
+
+class QxQyQzProjection(backend.ProjectionBase):
+ def project(self, index, dataframe, pixels):
+ # put the detector at the right position
+
+ # TODO factorize with HklProjection
+ UB = numpy.array([[2* math.pi, 0 , 0],
+ [0 , 0 , 2* math.pi],
+ [0 , -2 * math.pi, 0]])
+
+ s_axes = rotation_axes(dataframe.diffractometer.axes.graph,
+ dataframe.diffractometer.axes.sample)
+ d_axes = rotation_axes(dataframe.diffractometer.axes.graph,
+ dataframe.diffractometer.axes.detector)
+
+ # the ki vector should be in the NexusFile or easily extracted
+ # from the hkl library.
+ ki = [1, 0, 0]
+ k = 2 * math.pi / dataframe.source.wavelength
+ values = dataframe.mu[index], dataframe.omega[index], dataframe.delta[index], dataframe.gamma[index]
+ s_values = values[0], values[1]
+ d_values = values[0], values[2], values[3]
+ R = reduce(numpy.dot, (zip_with(M, numpy.radians(s_values), s_axes)))
+ P = reduce(numpy.dot, (zip_with(M, numpy.radians(d_values), d_axes)))
+ RUB_1 = inv(numpy.dot(R, UB))
+ RUB_1P = numpy.dot(RUB_1, P)
+ # rotate the detector around x of 90 degrees
+ RUB_1P = numpy.dot(RUB_1P, M(math.pi/2., [1, 0, 0]))
+ kf = normalized(pixels, axis=0)
+ hkl_f = numpy.tensordot(RUB_1P, kf, axes=1)
+ hkl_i = numpy.dot(RUB_1, ki)
+ hkl = hkl_f - hkl_i[:, numpy.newaxis, numpy.newaxis]
+
+ qx, qy, qz = hkl * k
+ return qx, qy, qz
+
+ def get_axis_labels(self):
+ return "Qx", "Qy", "Qz"
+
+def get_nxclass(hfile, nxclass, path="/"):
+ """
+ :param hfile: the hdf5 file.
+ :type hfile: tables.file.
+ :param nxclass: the nxclass to extract
+ :type nxclass: str
+ """
+ for node in hfile.walk_nodes(path):
+ try:
+ if nxclass == node._v_attrs['NX_class']:
+ return node
+ except KeyError:
+ pass
+ return None
+
+Axes = namedtuple("Axes", ["sample", "detector", "graph"])
+
+Rotation = namedtuple("Rotation", ["axis", "value"])
+
+
+def get_axes(name):
+ """
+ :param name: the diffractometer name
+ :type name: str
+ """
+ sample = []
+ detector = []
+ graph = DiGraph()
+ if name == 'ZAXIS':
+ # axis
+ graph.add_node("mu", transformation=Rotation([0, 0, 1], 0))
+ graph.add_node("omega", transformation=Rotation([0, -1, 0], 0))
+ graph.add_node("delta", transformation=Rotation([0, -1, 0], 0))
+ graph.add_node("gamma", transformation=Rotation([0, 0, 1], 0))
+
+ # topology
+ graph.add_edges_from([("mu", "omega"),
+ ("mu", "delta"), ("delta", "gamma")])
+
+ sample = dijkstra_path(graph, "mu", "omega")
+ detector = dijkstra_path(graph, "mu", "gamma")
+
+ return Axes(sample, detector, graph)
+
+
+Diffractometer = namedtuple('Diffractometer',
+ ['name', # name of the hkl diffractometer
+ 'ub', # the UB matrix
+ 'axes']) # the Axes namedtuple
+
+
+def get_diffractometer(hfile):
+ """ Construct a Diffractometer from a NeXus file """
+ node = get_nxclass(hfile, 'NXdiffractometer')
+
+ name = node.type[0][:-1]
+ ub = node.UB[:]
+ axes = get_axes(name)
+
+ return Diffractometer(name, ub, axes)
+
+
+Sample = namedtuple("Sample", ["a", "b", "c",
+ "alpha", "beta", "gamma",
+ "ux", "uy", "uz", "graph"])
+
+
+def get_sample(hfile):
+ graph = DiGraph()
+ graph.add_node("ux", transformation=Rotation([1, 0, 0], 0))
+ graph.add_node("uy", transformation=Rotation([0, 1, 0], 0))
+ graph.add_node("uz", transformation=Rotation([0, 0, 1], 0))
+ graph.add_edges_from([("ux", "uy"),
+ ("uy", "uz")])
+
+ return Sample(1.54, 1.54, 1.54, 90, 90, 90, 0, 0, 0, graph)
+
+
+Detector = namedtuple("Detector", ["name"])
+
+
+def get_detector(hfile):
+ return Detector("imxpads140")
+
+Source = namedtuple("Source", ["wavelength"])
+
+
+def get_source(hfile):
+ wavelength = get_nxclass(hfile, 'NXmonochromator').wavelength[0]
+ return Source(wavelength)
+
+
+DataFrame = namedtuple("DataFrame", ["diffractometer",
+ "sample", "detector", "source",
+ "mu", "omega", "delta", "gamma",
+ "image", "graph"])
+
+
+def dataframes(hfile, data_path=None):
+ diffractometer = get_diffractometer(hfile)
+ sample = get_sample(hfile)
+ detector = get_detector(hfile)
+ source = get_source(hfile)
+ graph = DiGraph()
+ # this should be generalized
+ for g in [diffractometer.axes.graph, sample.graph]:
+ graph.add_nodes_from(g)
+ graph.add_edges_from(g.edges())
+ # connect the sample with the right axis.
+ graph.add_edge("omega", "ux")
+
+ for group in hfile.get_node('/'):
+ scan_data = group._f_get_child("scan_data")
+ dataframe = {
+ "diffractometer": diffractometer,
+ "sample": sample,
+ "detector": detector,
+ "source": source,
+ "graph": graph,
+ }
+
+ # now instantiate the pytables objects
+ for key, value in data_path.iteritems():
+ child = scan_data._f_get_child(value)
+ dataframe[key] = child
+
+ yield DataFrame(**dataframe)
+
+
+def M(theta, u):
+ """
+ :param theta: the axis value in radian
+ :type theta: float
+ :param u: the axis vector [x, y, z]
+ :type u: [float, float, float]
+ :return: the rotation matrix
+ :rtype: numpy.ndarray (3, 3)
+ """
+ c = cos(theta)
+ one_minus_c = 1 - c
+ s = sin(theta)
+ return numpy.array([[c + u[0]**2 * one_minus_c,
+ u[0] * u[1] * one_minus_c - u[2] * s,
+ u[0] * u[2] * one_minus_c + u[1] * s],
+ [u[0] * u[1] * one_minus_c + u[2] * s,
+ c + u[1]**2 * one_minus_c,
+ u[1] * u[2] * one_minus_c - u[0] * s],
+ [u[0] * u[2] * one_minus_c - u[1] * s,
+ u[1] * u[2] * one_minus_c + u[0] * s,
+ c + u[2]**2 * one_minus_c]])
+
+
+def rotation_axes(graph, nodes):
+ """
+ :param graph: descrition of the diffractometer geometry
+ :type graph: DiGraph
+ :param nodes: list of the nodes to use
+ :type nodes: list(str)
+ :return: the list of the rotation axes expected by zip_with
+ """
+ return [graph.node[idx]["transformation"].axis for idx in nodes]
+
+
+def zip_with(f, *coll):
+ return itertools.starmap(f, itertools.izip(*coll))
+
+
+def rotation_matrix(values, axes):
+ """
+ :param values: the rotation axes values in radian
+ :type values: list(float)
+ :param axes: the rotation axes
+ :type axes: list of [x, y, z]
+ :return: the rotation matrix
+ :rtype: numpy.ndarray (3, 3)
+ """
+ return reduce(numpy.dot, (zip_with(M, values, axes)))
+
+def get_ki(wavelength):
+ """
+ for now the direction is always along x
+ """
+ TAU = 2 * math.pi
+ return numpy.array([TAU / wavelength, 0, 0])
+
+def normalized(a, axis=-1, order=2):
+ l2 = numpy.atleast_1d(numpy.linalg.norm(a, order, axis))
+ l2[l2 == 0] = 1
+ return a / numpy.expand_dims(l2, axis)
+
+class SIXS(backend.InputBase):
+ # OFFICIAL API
+
+ dbg_scanno = None
+ dbg_pointno = None
+
+ def generate_jobs(self, command):
+ scans = util.parse_multi_range(','.join(command).replace(' ', ','))
+ if not len(scans):
+ sys.stderr.write('error: no scans selected, nothing to do\n')
+ for scanno in scans:
+ util.status('processing scan {0}...'.format(scanno))
+ if self.config.pr:
+ pointcount = self.config.pr[1] - self.config.pr[0] + 1
+ start = self.config.pr[0]
+ else:
+ # just open the file in order to extract the number of step.
+ with tables.open_file(self.get_filename(scanno), 'r') as scan:
+ start = 0
+ pointcount = get_nxclass(scan, "NXdata").UHV_MU.shape[0]
+ if pointcount > self.config.target_weight * 1.4:
+ for s in util.chunk_slicer(pointcount, self.config.target_weight):
+ yield backend.Job(scan=scanno, firstpoint=start+s.start, lastpoint=start+s.stop-1, weight=s.stop-s.start)
+ else:
+ yield backend.Job(scan=scanno, firstpoint=start, lastpoint=start+pointcount-1, weight=pointcount)
+
+ def process_job(self, job):
+ super(SIXS, self).process_job(job)
+ with tables.open_file(self.get_filename(job.scan), 'r') as scan:
+ self.metadict = dict()
+ try:
+ for dataframe in dataframes(scan, self.HPATH):
+ pixels = self.get_pixels(dataframe.detector)
+ for index in range(job.firstpoint, job.lastpoint + 1):
+ yield self.process_image(index, dataframe, pixels)
+ util.statuseol()
+ except Exception as exc:
+ exc.args = errors.addmessage(exc.args, ', An error occured for scan {0} at point {1}. See above for more information'.format(self.dbg_scanno, self.dbg_pointno))
+ raise
+ self.metadata.add_section('sixs_backend', self.metadict)
+
+ def parse_config(self, config):
+ super(SIXS, self).parse_config(config)
+ self.config.xmask = util.parse_multi_range(config.pop('xmask', None))#Optional, select a subset of the image range in the x direction. all by default
+ self.config.ymask = util.parse_multi_range(config.pop('ymask', None))#Optional, select a subset of the image range in the y direction. all by default
+ self.config.nexusfile = config.pop('nexusfile')#Location of the specfile
+ self.config.pr = config.pop('pr', None) #Optional, all range by default
+ if self.config.xmask is None:
+ self.config.xmask = slice(None)
+ if self.config.ymask is None:
+ self.config.ymask = slice(None)
+ if self.config.pr:
+ self.config.pr = util.parse_tuple(self.config.pr, length=2, type=int)
+ self.config.sdd = float(config.pop('sdd'))# sample to detector distance (mm)
+ self.config.centralpixel = util.parse_tuple(config.pop('centralpixel'), length=2, type=int) #x,y
+ self.config.maskmatrix = config.pop('maskmatrix', None)#Optional, if supplied pixels where the mask is 0 will be removed
+ def get_destination_options(self, command):
+ if not command:
+ return False
+ command = ','.join(command).replace(' ', ',')
+ scans = util.parse_multi_range(command)
+ return dict(first=min(scans), last=max(scans), range=','.join(str(scan) for scan in scans))
+
+ # CONVENIENCE FUNCTIONS
+ def get_filename(self, scanno):
+ filename = self.config.nexusfile.format(scanno = str(scanno).zfill(5))
+ if not os.path.exists(filename):
+ raise errors.ConfigError('nexus filename does not exist: {0}'.format(filename))
+ return filename
+
+
+ @staticmethod
+ def apply_mask(data, xmask, ymask):
+ roi = data[ymask, :]
+ return roi[:, xmask]
+
+class FlyScanUHV(SIXS):
+ HPATH = {
+ "image": "xpad_image",
+ "mu": "UHV_MU",
+ "omega": "UHV_OMEGA",
+ "delta": "UHV_DELTA",
+ "gamma": "UHV_GAMMA",
+ }
+
+ def process_image(self, index, dataframe, pixels):
+ util.status(str(index))
+ detector = ALL_DETECTORS[dataframe.detector.name]()
+ maskmatrix = load_matrix(self.config.maskmatrix)
+ if maskmatrix is not None:
+ mask = numpy.bitwise_or(detector.mask, maskmatrix)
+ else:
+ mask = detector.mask
+
+ intensity = dataframe.image[index, ...]
+ weights = numpy.ones_like(intensity)
+ weights *= ~mask
+ #util.status('{4}| gamma: {0}, delta: {1}, theta: {2}, mu: {3}'.format(gamma, delta, theta, mu, time.ctime(time.time())))
+
+ return intensity, weights, (index, dataframe, pixels)
+
+ _pixels = None
+ @property
+ def pixels(self):
+ if self._pixels is None:
+ detector = ALL_DETECTORS[self.config.detector]()
+ dist, poni1, poni2, rot1, rot2, rot3, pixel1, pixel2 = self.detectorparams
+ p1, p2, p3 = detector.calc_cartesian_positions()# coordinates in the detector reference frame, corner = 0,0
+ coords = p1 - poni1, p2 - poni2, numpy.ones(p1.shape) * dist# coordinates in the rotated lab frame
+ rotation_axes = ([1,0,0], [0,1,0], [0,0,1])# as definded in pyFAI
+ R = reduce(numpy.dot, (zip_with(M, numpy.radians(rot1, rot2, rot3]), rotation_axes)))
+ self._pixels = numpy.tensordot(R, coords, axes=1)
+ return self._pixels
+
+def load_matrix(filename):
+ if filename == None:
+ return None
+ if os.path.exists(filename):
+ ext = os.path.splitext(filename)[-1]
+ if ext == '.txt':
+ return numpy.array(numpy.loadtxt(filename), dtype = numpy.bool)
+ elif ext == '.npy':
+ return numpy.array(numpy.load(filename), dtype = numpy.bool)
+ else:
+ raise ValueError('unknown extension {0}, unable to load matrix!\n'.format(ext))
+ else:
+ raise IOError('filename: {0} does not exist. Can not load matrix'.format(filename))
diff --git a/binoculars/dispatcher.py b/binoculars/dispatcher.py
index b491cd5..83b4ee8 100755
--- a/binoculars/dispatcher.py
+++ b/binoculars/dispatcher.py
@@ -10,7 +10,7 @@ from . import util, errors, space
class Destination(object):
type = filename = overwrite = value = config = limits = None
opts = {}
-
+
def set_final_filename(self, filename, overwrite):
self.type = 'final'
self.filename = filename
@@ -55,7 +55,7 @@ class Destination(object):
if not self.limits == None:
base, ext = os.path.splitext(self.filename)
for limlabel in util.limit_to_filelabel(self.limits):
- fn = (base + '_' + limlabel + ext).format(**self.opts)
+ fn = (base + '_' + limlabel + ext).format(**self.opts)
if not self.overwrite:
fn = util.find_unused_filename(fn)
fns.append(fn)
@@ -66,6 +66,7 @@ class Destination(object):
fns.append(fn)
return fns
+
class DispatcherBase(util.ConfigurableObject):
def __init__(self, config, main):
self.main = main
@@ -74,16 +75,16 @@ class DispatcherBase(util.ConfigurableObject):
def parse_config(self, config):
super(DispatcherBase, self).parse_config(config)
self.config.destination = Destination()
- destination = config.pop('destination', 'output.hdf5')# optional 'output.hdf5' by default
- overwrite = util.parse_bool(config.pop('overwrite', 'false'))#by default: numbered files in the form output_###.hdf5:
- self.config.destination.set_final_filename(destination, overwrite)# explicitly parsing the options first helps with the debugging
- self.config.host = config.pop('host', None)# ip adress of the running gui awaiting the spaces
- self.config.port = config.pop('port', None)# port of the running gui awaiting the spaces
- self.config.send_to_gui = util.parse_bool(config.pop('send_to_gui', 'false'))#previewing the data, if true, also specify host and port
-
- def send(self, verses):#provides the possiblity to send the results to the gui over the network
- if self.config.send_to_gui or (self.config.host is not None and self.config.host is not None):#only continue of ip is specified and send_to_server is flagged
- for M in verses:
+ destination = config.pop('destination', 'output.hdf5') # optional 'output.hdf5' by default
+ overwrite = util.parse_bool(config.pop('overwrite', 'false')) #by default: numbered files in the form output_ # .hdf5:
+ self.config.destination.set_final_filename(destination, overwrite) # explicitly parsing the options first helps with the debugging
+ self.config.host = config.pop('host', None) # ip adress of the running gui awaiting the spaces
+ self.config.port = config.pop('port', None) # port of the running gui awaiting the spaces
+ self.config.send_to_gui = util.parse_bool(config.pop('send_to_gui', 'false')) # previewing the data, if true, also specify host and port
+
+ def send(self, verses): # provides the possiblity to send the results to the gui over the network
+ if self.config.send_to_gui or (self.config.host is not None and self.config.host is not None): # only continue of ip is specified and send_to_server is flagged
+ for M in verses:
if self.config.destination.limits is None:
sp = M.spaces[0]
if isinstance(sp, space.Space):
@@ -94,9 +95,9 @@ class DispatcherBase(util.ConfigurableObject):
util.socket_send(self.config.host, int(self.config.port), util.serialize(sp, '{0}_{1}'.format(','.join(self.main.config.command), label)))
yield M
else:
- for M in verses:
+ for M in verses:
yield M
-
+
def has_specific_task(self):
return False
@@ -145,12 +146,12 @@ class Local(ReentrantBase):
def parse_config(self, config):
super(Local, self).parse_config(config)
- self.config.ncores = int(config.pop('ncores', 0))# optionally, specify number of cores (autodetect by default)
+ self.config.ncores = int(config.pop('ncores', 0)) # optionally, specify number of cores (autodetect by default)
if self.config.ncores <= 0:
self.config.ncores = multiprocessing.cpu_count()
def process_jobs(self, jobs):
- if self.config.ncores == 1: # note: SingleCore will be marginally faster
+ if self.config.ncores == 1: # note: SingleCore will be marginally faster
imap = itertools.imap
else:
pool = multiprocessing.Pool(self.config.ncores)
@@ -179,15 +180,17 @@ class Local(ReentrantBase):
return config, ()
# Dispatch many worker processes on an Oar cluster.
+
+
class Oar(ReentrantBase):
### OFFICIAL API
actions = 'user', 'process'
def parse_config(self, config):
super(Oar, self).parse_config(config)
- self.config.tmpdir = config.pop('tmpdir', os.getcwd())#Optional, current directory by default
- self.config.oarsub_options = config.pop('oarsub_options', 'walltime=0:15')# optionally, tweak oarsub parameters
- self.config.executable = config.pop('executable', ' '.join(util.get_python_executable()))# optionally, override default location of python and/or BINoculars installation
+ self.config.tmpdir = config.pop('tmpdir', os.getcwd()) # Optional, current directory by default
+ self.config.oarsub_options = config.pop('oarsub_options', 'walltime=0:15') # optionally, tweak oarsub parameters
+ self.config.executable = config.pop('executable', ' '.join(util.get_python_executable())) # optionally, override default location of python and/or BINoculars installation
def process_jobs(self, jobs):
self.configfiles = []
@@ -278,7 +281,7 @@ class Oar(ReentrantBase):
util.status('{0}: getting status of {1} jobs...'.format(time.ctime(), len(jobs)))
else:
return
-
+
delay = util.loop_delayer(30)
while len(jobs) > remaining:
next(delay)
@@ -295,11 +298,11 @@ class Oar(ReentrantBase):
W += 1
elif state == 'Unknown':
U += 1
- else: # assume state == 'Finishing' or 'Terminated' but don't wait on something unknown
+ else: # assume state == 'Finishing' or 'Terminated' but don't wait on something unknown
del jobs[i]
- i -= 1 # otherwise it skips a job
+ i -= 1 # otherwise it skips a job
i += 1
- util.status('{0}: {1} jobs to go. {2} waiting, {3} running, {4} unknown.'.format(time.ctime(),len(jobs),W,R,U))
+ util.status('{0}: {1} jobs to go. {2} waiting, {3} running, {4} unknown.'.format(time.ctime(), len(jobs), W, R, U))
util.statuseol()
def oar_cleanup(self, jobs):
@@ -310,7 +313,7 @@ class Oar(ReentrantBase):
except Exception as e:
print "unable to remove {0}: {1}".format(f, e)
- errorfn = []
+ errorfn = []
for jobid in jobs:
errorfilename = 'OAR.{0}.stderr'.format(jobid)
@@ -321,9 +324,6 @@ class Oar(ReentrantBase):
if len(errormsg) > 0:
errorfn.append(errorfilename)
print 'Critical error: OAR Job {0} failed with the following error: \n{1}'.format(jobid, errormsg)
-
if len(errorfn) > 0:
print 'Warning! {0} job(s) failed. See above for the details or the error log files: {1}'.format(len(errorfn), ', '.join(errorfn))
-
-
diff --git a/binoculars/errors.py b/binoculars/errors.py
index dc2d8ac..694fa11 100644
--- a/binoculars/errors.py
+++ b/binoculars/errors.py
@@ -1,26 +1,34 @@
# TODO: present exceptions based on errors.ExceptionBase in a gentle way to the user
+
class ExceptionBase(Exception):
pass
+
class ConfigError(ExceptionBase):
pass
+
class FileError(ExceptionBase):
pass
+
class HDF5FileError(FileError):
pass
+
class SubprocessError(ExceptionBase):
pass
+
class BackendError(ExceptionBase):
pass
+
class CommunicationError(ExceptionBase):
pass
+
def addmessage(args, errormsg):
if not args:
arg0 = ''
@@ -28,4 +36,3 @@ def addmessage(args, errormsg):
arg0 = args[0]
arg0 += errormsg
return (arg0, )
-
diff --git a/binoculars/fit.py b/binoculars/fit.py
index 647a005..028b8e5 100644
--- a/binoculars/fit.py
+++ b/binoculars/fit.py
@@ -1,5 +1,6 @@
import numpy
-import scipy.optimize, scipy.special
+import scipy.optimize
+import scipy.special
import inspect
import re
@@ -12,12 +13,12 @@ class FitBase(object):
fitdata = None
def __init__(self, space, guess=None):
- self.space = space
+ self.space = space
args = inspect.getargspec(self.func).args
if space.dimension != len(args[0]):
raise ValueError('dimension mismatch: space has {0}, {1.__class__.__name__} expects {2}'.format(space.dimension, self, len(args[0])))
self.parameters = args[1]
-
+
self.xdata, self.ydata, self.cxdata, self.cydata = self._prepare(self.space)
if guess is not None:
if len(guess) != len(self.parameters):
@@ -42,7 +43,7 @@ class FitBase(object):
def _fitfunc(self, params):
return self.cydata - self.func(self.cxdata, params)
-
+
def _fit(self):
result = scipy.optimize.leastsq(self._fitfunc, self.guess, full_output=True, epsfcn=0.000001)
@@ -55,15 +56,16 @@ class FitBase(object):
self.variance = numpy.diagonal(result[1] * (errdata**2).sum() / (len(errdata) - len(self.result)))
self.fitdata = numpy.ma.array(self.func(self.xdata, self.result), mask=self.ydata.mask)
- self.summary = '\n'.join('%s: %.4g +/- %.4g' % (n, p,v) for (n, p, v) in zip(self.parameters, self.result, self.variance))
+ self.summary = '\n'.join('%s: %.4g +/- %.4g' % (n, p, v) for (n, p, v) in zip(self.parameters, self.result, self.variance))
- return result[4] in (1,2,3,4) # corresponds to True on success, False on failure
+ return result[4] in (1, 2, 3, 4) # corresponds to True on success, False on failure
def __str__(self):
return '{0.__class__.__name__} fit on {1}\n{2}\n{3}'.format(self, self.space, self.message, self.summary)
+
class PeakFitBase(FitBase):
- def __init__(self, space, guess = None, loc = None):
+ def __init__(self, space, guess=None, loc=None):
if loc != None:
self.argmax = tuple(loc)
else:
@@ -71,16 +73,16 @@ class PeakFitBase(FitBase):
super(PeakFitBase, self).__init__(space, guess)
def _guess(self):
- maximum = self.cydata.max() # for background determination
+ maximum = self.cydata.max() # for background determination
background = self.cydata < (numpy.median(self.cydata) + maximum) / 2
- if any(background == True): #the fit will fail if background is flas for all
+ if any(background == True): # the fit will fail if background is flas for all
linparams = self._linfit(list(grid[background] for grid in self.cxdata), self.cydata[background])
else:
linparams = numpy.zeros(len(self.cxdata) + 1)
- simbackground = linparams[-1] + numpy.sum(numpy.vstack(param * grid.flatten() for (param, grid) in zip(linparams[:-1], self.cxdata)) , axis = 0)
+ simbackground = linparams[-1] + numpy.sum(numpy.vstack(param * grid.flatten() for (param, grid) in zip(linparams[:-1], self.cxdata)), axis=0)
signal = self.cydata - simbackground
if self.argmax != None:
@@ -93,10 +95,10 @@ class PeakFitBase(FitBase):
try:
maximum = self.space[argmax] - argmax_bkg
except ValueError:
- maximum = self.cydata.max()
-
+ maximum = self.cydata.max()
+
if numpy.isnan(maximum):
- maximum = self.cydata.max()
+ maximum = self.cydata.max()
self.set_guess(maximum, argmax, linparams)
@@ -106,6 +108,7 @@ class PeakFitBase(FitBase):
matrix = numpy.vstack(coords.flatten() for coords in coordinates).T
return numpy.linalg.lstsq(matrix, intensity)[0]
+
class AutoDimensionFit(FitBase):
def __new__(cls, space, guess=None):
if space.dimension in cls.dimensions:
@@ -115,17 +118,19 @@ class AutoDimensionFit(FitBase):
# utility functions
-def rot2d(x,y,th):
- xrot = x * numpy.cos(th) + y * numpy.sin(th)
- yrot = - x * numpy.sin(th) + y * numpy.cos(th)
- return xrot,yrot
+def rot2d(x, y, th):
+ xrot = x * numpy.cos(th) + y * numpy.sin(th)
+ yrot = - x * numpy.sin(th) + y * numpy.cos(th)
+ return xrot, yrot
+
-def rot3d(x, y ,z , th, ph):
+def rot3d(x, y, z, th, ph):
xrot = numpy.cos(th) * x + numpy.sin(th) * numpy.sin(ph) * y + numpy.sin(th) * numpy.cos(ph) * z
yrot = numpy.cos(ph) * y - numpy.sin(ph) * z
zrot = -numpy.sin(th) * x + numpy.cos(th) * numpy.sin(ph) * y + numpy.cos(th) * numpy.cos(ph) * z
return xrot, yrot, zrot
+
def get_class_by_name(name):
options = {}
for k, v in globals().iteritems():
@@ -140,43 +145,46 @@ def get_class_by_name(name):
# fitting functions
class Lorentzian1D(PeakFitBase):
@staticmethod
- def func((x,), (I, loc ,gamma, slope, offset)):
- return I / ((x - loc)**2 + gamma**2)+ offset + x * slope
+ def func((x, ), (I, loc, gamma, slope, offset)):
+ return I / ((x - loc)**2 + gamma**2) + offset + x * slope
+
+ def set_guess(self, maximum, argmax, linparams):
+ gamma0 = 5 * self.space.axes[0].res # estimated FWHM on 10 pixels
+ self.guess = [maximum, argmax[0], gamma0, linparams[0], linparams[1]]
- def set_guess(self, maximum , argmax, linparams):
- gamma0 = 5 * self.space.axes[0].res #estimated FWHM on 10 pixels
- self.guess = [maximum , argmax[0], gamma0, linparams[0], linparams[1]]
class Lorentzian1DNoBkg(PeakFitBase):
@staticmethod
- def func((x,), (I, loc ,gamma)):
+ def func((x, ), (I, loc, gamma)):
return I / ((x - loc)**2 + gamma**2)
- def set_guess(self, maximum , argmax, linparams):
- gamma0 = 5 * self.space.axes[0].res #estimated FWHM on 10 pixels
- self.guess = [maximum , argmax[0], gamma0]
+ def set_guess(self, maximum, argmax, linparams):
+ gamma0 = 5 * self.space.axes[0].res # estimated FWHM on 10 pixels
+ self.guess = [maximum, argmax[0], gamma0]
+
class PolarLorentzian2Dnobkg(PeakFitBase):
@staticmethod
- def func((x,y), (I, loc0, loc1, gamma0, gamma1, th)):
- a,b = tuple(grid - center for grid, center in zip(rot2d(x,y,th),rot2d(loc0,loc1,th)))
- return (I / (1 + (a / gamma0)**2 + (b / gamma1)**2 ))
+ def func((x, y), (I, loc0, loc1, gamma0, gamma1, th)):
+ a, b = tuple(grid - center for grid, center in zip(rot2d(x, y, th), rot2d(loc0, loc1, th)))
+ return (I / (1 + (a / gamma0)**2 + (b / gamma1)**2))
- def set_guess(self, maximum , argmax, linparams):
- gamma0 = self.space.axes[0].res#estimated FWHM on 10 pixels
+ def set_guess(self, maximum, argmax, linparams):
+ gamma0 = self.space.axes[0].res # estimated FWHM on 10 pixels
gamma1 = self.space.axes[1].res
- self.guess = [maximum , argmax[0], argmax[1], gamma0, gamma1, 0]
+ self.guess = [maximum, argmax[0], argmax[1], gamma0, gamma1, 0]
+
class PolarLorentzian2D(PeakFitBase):
@staticmethod
- def func((x,y), (I, loc0, loc1, gamma0, gamma1, th, slope1, slope2, offset)):
- a,b = tuple(grid - center for grid, center in zip(rot2d(x,y,th),rot2d(loc0,loc1,th)))
- return (I / (1 + (a / gamma0)**2 + (b / gamma1)**2 ) + x * slope1 + y * slope2 + offset)
+ def func((x, y), (I, loc0, loc1, gamma0, gamma1, th, slope1, slope2, offset)):
+ a, b = tuple(grid - center for grid, center in zip(rot2d(x, y, th), rot2d(loc0, loc1, th)))
+ return (I / (1 + (a / gamma0)**2 + (b / gamma1)**2) + x * slope1 + y * slope2 + offset)
- def set_guess(self, maximum , argmax, linparams):
- gamma0 = self.space.axes[0].res#estimated FWHM on 10 pixels
+ def set_guess(self, maximum, argmax, linparams):
+ gamma0 = self.space.axes[0].res # estimated FWHM on 10 pixels
gamma1 = self.space.axes[1].res
- self.guess = [maximum , argmax[0], argmax[1], gamma0, gamma1, 0, linparams[0], linparams[1], linparams[2]]
+ self.guess = [maximum, argmax[0], argmax[1], gamma0, gamma1, 0, linparams[0], linparams[1], linparams[2]]
def integrate_signal(self):
return self.func(self.cxdata, (self.result[0], self.result[1], self.result[2], self.result[3], self.result[4], self.result[5], 0, 0, 0)).sum()
@@ -184,41 +192,44 @@ class PolarLorentzian2D(PeakFitBase):
class Lorentzian2D(PeakFitBase):
@staticmethod
- def func((x,y), (I, loc0, loc1, gamma0, gamma1, th, slope1, slope2, offset)):
- a,b = tuple(grid - center for grid, center in zip(rot2d(x,y,th),rot2d(loc0,loc1,th)))
- return (I / (1 + (a/gamma0)**2) * 1 / (1 + (b/gamma1)**2) + x * slope1 + y * slope2 + offset)
+ def func((x, y), (I, loc0, loc1, gamma0, gamma1, th, slope1, slope2, offset)):
+ a, b = tuple(grid - center for grid, center in zip(rot2d(x, y, th), rot2d(loc0, loc1, th)))
+ return (I / (1 + (a/gamma0)**2) * 1 / (1 + (b/gamma1)**2) + x * slope1 + y * slope2 + offset)
+
+ def set_guess(self, maximum, argmax, linparams):
+ gamma0 = 5 * self.space.axes[0].res # estimated FWHM on 10 pixels
+ gamma1 = 5 * self.space.axes[1].res
+ self.guess = [maximum, argmax[0], argmax[1], gamma0, gamma1, 0, linparams[0], linparams[1], linparams[2]]
- def set_guess(self, maximum , argmax, linparams):
- gamma0 = 5 * self.space.axes[0].res #estimated FWHM on 10 pixels
- gamma1 = 5 * self.space.axes[1].res
- self.guess = [maximum , argmax[0], argmax[1],gamma0, gamma1, 0, linparams[0], linparams[1], linparams[2]]
class Lorentzian2Dnobkg(PeakFitBase):
@staticmethod
- def func((x,y), (I, loc0, loc1, gamma0, gamma1, th)):
- a,b = tuple(grid - center for grid, center in zip(rot2d(x,y,th),rot2d(loc0,loc1,th)))
- return (I / (1 + (a/gamma0)**2) * 1 / (1 + (b/gamma1)**2))
+ def func((x, y), (I, loc0, loc1, gamma0, gamma1, th)):
+ a, b = tuple(grid - center for grid, center in zip(rot2d(x, y, th), rot2d(loc0, loc1, th)))
+ return (I / (1 + (a/gamma0)**2) * 1 / (1 + (b/gamma1)**2))
- def set_guess(self, maximum , argmax, linparams):
- gamma0 = 5 * self.space.axes[0].res #estimated FWHM on 10 pixels
- gamma1 = 5 * self.space.axes[1].res
- self.guess = [maximum , argmax[0], argmax[1],gamma0, gamma1, 0]
+ def set_guess(self, maximum, argmax, linparams):
+ gamma0 = 5 * self.space.axes[0].res # estimated FWHM on 10 pixels
+ gamma1 = 5 * self.space.axes[1].res
+ self.guess = [maximum, argmax[0], argmax[1], gamma0, gamma1, 0]
class Lorentzian(AutoDimensionFit):
dimensions = {1: Lorentzian1D, 2: PolarLorentzian2D}
+
class Gaussian1D(PeakFitBase):
@staticmethod
def func((x,), (loc, I, sigma, offset, slope)):
return I * numpy.exp(-((x-loc)/sigma)**2/2) + offset + x * slope
+
class Voigt1D(PeakFitBase):
@staticmethod
- def func((x,), (I, loc, sigma ,gamma, slope, offset)):
+ def func((x, ), (I, loc, sigma, gamma, slope, offset)):
z = (x - loc + numpy.complex(0, gamma)) / (sigma * numpy.sqrt(2))
- return I * numpy.real(scipy.special.wofz(z))/(sigma * numpy.sqrt(2 * numpy.pi)) + offset + x * slope
+ return I * numpy.real(scipy.special.wofz(z))/(sigma * numpy.sqrt(2 * numpy.pi)) + offset + x * slope
- def set_guess(self, maximum , argmax, linparams):
- gamma0 = 5 * self.space.axes[0].res #estimated FWHM on 10 pixels
- self.guess = [maximum, argmax[0], 0.01 ,gamma0, linparams[0], linparams[1]]
+ def set_guess(self, maximum, argmax, linparams):
+ gamma0 = 5 * self.space.axes[0].res # estimated FWHM on 10 pixels
+ self.guess = [maximum, argmax[0], 0.01, gamma0, linparams[0], linparams[1]]
diff --git a/binoculars/main.py b/binoculars/main.py
index 25af336..188427b 100755
--- a/binoculars/main.py
+++ b/binoculars/main.py
@@ -4,6 +4,7 @@ import argparse
from . import space, backend, util, errors
+
def parse_args(args):
parser = argparse.ArgumentParser(prog='binoculars process')
parser.add_argument('-c', metavar='SECTION:OPTION=VALUE', action='append', type=parse_commandline_config_option, default=[], help='additional configuration option in the form section:option=value')
@@ -11,6 +12,7 @@ def parse_args(args):
parser.add_argument('command', nargs='*', default=[])
return parser.parse_args(args)
+
def parse_commandline_config_option(s):
try:
key, value = s.split('=', 1)
@@ -20,10 +22,11 @@ def parse_commandline_config_option(s):
return section, option, value
-def multiprocessing_main((config, command)): # note the double parenthesis for map() convenience
+def multiprocessing_main((config, command)): # note the double parenthesis for map() convenience
Main.from_object(config, command)
return config.dispatcher.destination.retrieve()
+
class Main(object):
def __init__(self, config, command):
if isinstance(config, util.ConfigSectionGroup):
@@ -47,7 +50,7 @@ class Main(object):
if command:
self.dispatcher.config.destination.set_config(spaceconf)
self.run(command)
-
+
@classmethod
def from_args(cls, args):
args = parse_args(args)
@@ -57,19 +60,19 @@ class Main(object):
raise errors.FileError("configuration file '{0}' does not exist".format(args.configfile))
configobj = False
with open(args.configfile, 'rb') as fp:
- if fp.read(2) == '\x1f\x8b': # gzip marker
+ if fp.read(2) == '\x1f\x8b': # gzip marker
fp.seek(0)
configobj = util.zpi_load(fp)
if not configobj:
# reopen args.configfile as text
- configobj = util.ConfigFile.fromtxtfile(args.configfile, command = args.command , overrides=args.c)
+ configobj = util.ConfigFile.fromtxtfile(args.configfile, command=args.command, overrides=args.c)
return cls(configobj, args.command)
@classmethod
def from_object(cls, config, command):
config.command = command
return cls(config, command)
-
+
def run(self, command):
if self.dispatcher.has_specific_task():
self.dispatcher.run_specific_task(command)
@@ -88,12 +91,12 @@ class Main(object):
def generator():
res = self.projection.config.resolution
labels = self.projection.get_axis_labels()
- for intensity, weights, params in self.input.process_job(job):
+ for intensity, weights, params in self.input.process_job(job):
coords = self.projection.project(*params)
if self.projection.config.limits == None:
- yield space.Multiverse((space.Space.from_image(res, labels, coords, intensity, weights = weights), ))
+ yield space.Multiverse((space.Space.from_image(res, labels, coords, intensity, weights=weights), ))
else:
- yield space.Multiverse(space.Space.from_image(res, labels, coords, intensity, weights = weights, limits = limits) for limits in self.projection.config.limits)
+ yield space.Multiverse(space.Space.from_image(res, labels, coords, intensity, weights=weights, limits=limits) for limits in self.projection.config.limits)
jobverse = space.chunked_sum(generator(), chunksize=25)
for sp in jobverse.spaces:
if isinstance(sp, space.Space):
@@ -111,7 +114,8 @@ class Main(object):
def get_reentrant(self):
return multiprocessing_main
-class Split(Main): #completely ignores the dispatcher, just yields a space per image
+
+class Split(Main): # completely ignores the dispatcher, just yields a space per image
def __init__(self, config, command):
self.command = command
if isinstance(config, util.ConfigSectionGroup):
@@ -128,17 +132,14 @@ class Split(Main): #completely ignores the dispatcher, just yields a space per i
def process_job(self, job):
res = self.projection.config.resolution
labels = self.projection.get_axis_labels()
- for intensity, weights, params in self.input.process_job(job):
+ for intensity, weights, params in self.input.process_job(job):
coords = self.projection.project(*params)
if self.projection.config.limits == None:
- yield space.Multiverse(space.Space.from_image(res, labels, coords, intensity, weights = weights))
+ yield space.Multiverse(space.Space.from_image(res, labels, coords, intensity, weights=weights))
else:
- yield space.Multiverse(space.Space.from_image(res, labels, coords, intensity, weights = weights, limits = limits) for limits in self.projection.config.limits)
+ yield space.Multiverse(space.Space.from_image(res, labels, coords, intensity, weights=weights, limits=limits) for limits in self.projection.config.limits)
def run(self):
for job in self.input.generate_jobs(self.command):
for verse in self.process_job(job):
yield verse
-
-
-
diff --git a/binoculars/plot.py b/binoculars/plot.py
index da04fae..dd16d4a 100644
--- a/binoculars/plot.py
+++ b/binoculars/plot.py
@@ -1,15 +1,18 @@
import numpy
-import matplotlib.colors, matplotlib.cm
+import matplotlib.colors
+import matplotlib.cm
import mpl_toolkits.mplot3d
# Adapted from http://www.ster.kuleuven.be/~pieterd/python/html/plotting/interactive_colorbar.html
# which in turn is based on an example from http://matplotlib.org/users/event_handling.html
+
+
class DraggableColorbar(object):
def __init__(self, cbar, mappable):
self.cbar = cbar
self.mappable = mappable
self.press = None
- self.cycle = sorted([i for i in dir(matplotlib.cm) if hasattr(getattr(matplotlib.cm,i),'N')])
+ self.cycle = sorted([i for i in dir(matplotlib.cm) if hasattr(getattr(matplotlib.cm, i), 'N')])
self.index = self.cycle.index(cbar.get_cmap().name)
self.canvas = self.cbar.patch.figure.canvas
@@ -30,13 +33,13 @@ class DraggableColorbar(object):
self.press = event.x, event.y
def key_press(self, event):
- if event.key=='down':
+ if event.key == 'down':
self.index += 1
- elif event.key=='up':
+ elif event.key == 'up':
self.index -= 1
- if self.index<0:
+ if self.index < 0:
self.index = len(self.cycle)
- elif self.index>=len(self.cycle):
+ elif self.index >= len(self.cycle):
self.index = 0
cmap = self.cycle[self.index]
self.mappable.set_cmap(cmap)
@@ -52,18 +55,18 @@ class DraggableColorbar(object):
if isinstance(self.cbar.norm, matplotlib.colors.LogNorm):
scale = 0.999 * numpy.log10(self.cbar.norm.vmax / self.cbar.norm.vmin)
- if event.button==1:
+ if event.button == 1:
self.cbar.norm.vmin *= scale**numpy.sign(dy)
self.cbar.norm.vmax *= scale**numpy.sign(dy)
- elif event.button==3:
+ elif event.button == 3:
self.cbar.norm.vmin *= scale**numpy.sign(dy)
self.cbar.norm.vmax /= scale**numpy.sign(dy)
else:
scale = 0.03 * (self.cbar.norm.vmax - self.cbar.norm.vmin)
- if event.button==1:
+ if event.button == 1:
self.cbar.norm.vmin -= scale*numpy.sign(dy)
self.cbar.norm.vmax -= scale*numpy.sign(dy)
- elif event.button==3:
+ elif event.button == 3:
self.cbar.norm.vmin -= scale*numpy.sign(dy)
self.cbar.norm.vmax += scale*numpy.sign(dy)
@@ -94,18 +97,18 @@ def get_clipped_norm(data, clipping=0.0, log=True):
vmin, vmax = clip[0], clip[-1]
else:
vmin, vmax = data.min(), data.max()
-
+
if log:
return matplotlib.colors.LogNorm(vmin, vmax)
else:
return matplotlib.colors.Normalize(vmin, vmax)
-def plot(space, fig, ax, log=True, loglog = False, clipping=0.0, fit=None, norm=None, colorbar=True, labels=True, interpolation='nearest', **plotopts):
+def plot(space, fig, ax, log=True, loglog=False, clipping=0.0, fit=None, norm=None, colorbar=True, labels=True, interpolation='nearest', **plotopts):
if space.dimension == 1:
data = space.get_masked()
xrange = numpy.ma.array(space.axes[0][:], mask=data.mask)
- if fit is not None:
+ if fit is not None:
if log:
p1 = ax.semilogy(xrange, data, 'wo', **plotopts)
p2 = ax.semilogy(xrange, fit, 'r', linewidth=2, **plotopts)
@@ -123,7 +126,7 @@ def plot(space, fig, ax, log=True, loglog = False, clipping=0.0, fit=None, norm=
else:
p1 = ax.plot(xrange, data, **plotopts)
p2 = []
-
+
if labels:
ax.set_xlabel(space.axes[0].label)
ax.set_ylabel('Intensity (a.u.)')
@@ -137,24 +140,24 @@ def plot(space, fig, ax, log=True, loglog = False, clipping=0.0, fit=None, norm=
xmax = space.axes[0].max
ymin = space.axes[1].min
ymax = space.axes[1].max
-
+
if not norm:
norm = get_clipped_norm(data, clipping, log)
if fit is not None:
- im = ax.imshow(fit.transpose(), origin='lower', extent=(xmin, xmax, ymin, ymax), aspect='auto', norm = norm, interpolation=interpolation, **plotopts)
+ im = ax.imshow(fit.transpose(), origin='lower', extent=(xmin, xmax, ymin, ymax), aspect='auto', norm=norm, interpolation=interpolation, **plotopts)
else:
- im = ax.imshow(data.transpose(), origin='lower', extent=(xmin, xmax, ymin, ymax), aspect='auto', norm = norm, interpolation=interpolation, **plotopts)
+ im = ax.imshow(data.transpose(), origin='lower', extent=(xmin, xmax, ymin, ymax), aspect='auto', norm=norm, interpolation=interpolation, **plotopts)
if labels:
ax.set_xlabel(space.axes[0].label)
ax.set_ylabel(space.axes[1].label)
if colorbar:
cbarwidget = fig.colorbar(im)
- fig._draggablecbar = DraggableColorbar(cbarwidget, im) # we need to store this instance somewhere
+ fig._draggablecbar = DraggableColorbar(cbarwidget, im) # we need to store this instance somewhere
fig._draggablecbar.connect()
return im
-
+
elif space.dimension == 3:
if not isinstance(ax, mpl_toolkits.mplot3d.Axes3D):
raise ValueError("For 3D plots, the 'ax' parameter must be an Axes3D instance (use for example gca(projection='3d') to get one)")
@@ -166,7 +169,7 @@ def plot(space, fig, ax, log=True, loglog = False, clipping=0.0, fit=None, norm=
data = space.get()
mask = numpy.bitwise_or(~numpy.isfinite(data), data == 0)
gridx, gridy, gridz = tuple(grid[~mask] for grid in space.get_grid())
- im = ax.scatter(gridx, gridy, gridz, c=cmap(norm(data[~mask])), marker = ',', alpha = 0.7,linewidths = 0)
+ im = ax.scatter(gridx, gridy, gridz, c=cmap(norm(data[~mask])), marker=', ', alpha=0.7, linewidths=0)
#p1 = ax.plot_surface(gridx[0,:,:], gridy[0,:,:], gridz[0,:,:], facecolors=cmap(norm(space.project(0).get_masked())), shade=False, cstride=1, rstride=1)
#p2 = ax.plot_surface(gridx[:,-1,:], gridy[:,-1,:], gridz[:,-1,:], facecolors=cmap(norm(space.project(1).get_masked())), shade=False, cstride=1, rstride=1)
@@ -176,7 +179,7 @@ def plot(space, fig, ax, log=True, loglog = False, clipping=0.0, fit=None, norm=
ax.set_xlabel(space.axes[0].label)
ax.set_ylabel(space.axes[1].label)
ax.set_zlabel(space.axes[2].label)
-
+
if fig._draggablecbar:
fig._draggablecbar.disconnect()
diff --git a/binoculars/space.py b/binoculars/space.py
index 0f29e24..cc9138f 100755
--- a/binoculars/space.py
+++ b/binoculars/space.py
@@ -24,7 +24,7 @@ class Axis(object):
Important attributes:
min lower bound
- max upper bound
+ max upper bound
res step size / resolution
label human-readable identifier
@@ -51,7 +51,7 @@ class Axis(object):
@property
def min(self):
return self.imin * self.res
-
+
def __len__(self):
return self.imax - self.imin + 1
@@ -112,7 +112,7 @@ class Axis(object):
return intvalue - self.imin
raise ValueError('cannot get indices, values from [{0}, {1}], axes range [{2}, {3}]'.format(value.min(), value.max(), self.min, self.max))
- def __or__(self, other): # union operation
+ def __or__(self, other): # union operation
if not isinstance(other, Axis):
return NotImplemented
if not self.is_compatible(other):
@@ -143,37 +143,36 @@ class Axis(object):
def rebin(self, factor):
# for integers the following relations hold: a // b == floor(a / b), -(-a // b) == ceil(a / b)
- new = self.__class__(self.imin // factor, -(-self.imax // factor), factor*self.res, self.label)
+ new = self.__class__(self.imin // factor, -(-self.imax // factor), factor*self.res, self.label)
return self.imin % factor, -self.imax % factor, new
def __repr__(self):
return '{0.__class__.__name__} {0.label} (min={0.min}, max={0.max}, res={0.res}, count={1})'.format(self, len(self))
-
- def restrict(self, value):#Useful for plotting
- if isinstance(value, numbers.Number):
- if value < self.min:
- return self.min
- elif value > self.max:
- return self.max
- else:
- return value
- elif isinstance(value, slice):
- if value.step is not None:
- raise IndexError('stride not supported')
- if value.start is None:
- start = None
- else:
- start = self.restrict(value.start)
- if value.stop is None:
- stop = None
- if value.stop == self.max:
- stop = None
- else:
- stop = self.restrict(value.stop)
- if start is not None and stop is not None and start > stop:
- start, stop = stop, start
- return slice(start, stop)
+ def restrict(self, value): # Useful for plotting
+ if isinstance(value, numbers.Number):
+ if value < self.min:
+ return self.min
+ elif value > self.max:
+ return self.max
+ else:
+ return value
+ elif isinstance(value, slice):
+ if value.step is not None:
+ raise IndexError('stride not supported')
+ if value.start is None:
+ start = None
+ else:
+ start = self.restrict(value.start)
+ if value.stop is None:
+ stop = None
+ if value.stop == self.max:
+ stop = None
+ else:
+ stop = self.restrict(value.stop)
+ if start is not None and stop is not None and start > stop:
+ start, stop = stop, start
+ return slice(start, stop)
class Axes(object):
@@ -207,10 +206,10 @@ class Axes(object):
if 'axes' in fp and 'axes_labels' in fp:
# oldest style, float min/max
return cls(tuple(Axis(min, max, res, lbl) for ((min, max, res), lbl) in zip(fp['axes'], fp['axes_labels'])))
- elif 'axes' in fp:#new
+ elif 'axes' in fp: # new
try:
axes = tuple(Axis(int(imin), int(imax), res, lbl) for ((index, fmin, fmax, res, imin, imax), lbl) in zip(fp['axes'].values(), fp['axes'].keys()))
- return cls(tuple(axes[int(values[0])] for values in fp['axes'].values()))#reorder the axes to the way in which they were saved
+ return cls(tuple(axes[int(values[0])] for values in fp['axes'].values())) # reorder the axes to the way in which they were saved
except ValueError:
return cls(tuple(Axis(int(imin), int(imax), res, lbl) for ((imin, imax, res), lbl) in zip(fp['axes'].values(), fp['axes'].keys())))
else:
@@ -223,7 +222,7 @@ class Axes(object):
with util.open_h5py(filename, 'w') as fp:
axes = fp.create_group('axes')
for index, ax in enumerate(self.axes):
- axes.create_dataset(ax.label, data = [index, ax.min, ax.max, ax.res, ax.imin, ax.imax])
+ axes.create_dataset(ax.label, data=[index, ax.min, ax.max, ax.res, ax.imin, ax.imax])
def toarray(self):
return numpy.vstack(numpy.hstack([str(ax.imin), str(ax.imax), str(ax.res), ax.label]) for ax in self.axes)
@@ -291,10 +290,10 @@ class Axes(object):
class EmptySpace(object):
"""Convenience object for sum() and friends. Treated as zero for addition.
Does not share a base class with Space for simplicity."""
- def __init__(self,config=None, metadata=None):
+ def __init__(self, config=None, metadata=None):
self.config = config
self.metadata = metadata
-
+
def __add__(self, other):
if not isinstance(other, Space) and not isinstance(other, EmptySpace):
return NotImplemented
@@ -314,15 +313,14 @@ class EmptySpace(object):
"""Store EmptySpace in HDF5 file."""
with util.atomic_write(filename) as tmpname:
with util.open_h5py(tmpname, 'w') as fp:
- fp.attrs['type'] = 'Empty'
+ fp.attrs['type'] = 'Empty'
def __repr__(self):
return '{0.__class__.__name__}'.format(self)
-
class Space(object):
- """Main data-storing object in BINoculars.
+ """Main data-storing object in BINoculars.
Data is represented on an n-dimensional rectangular grid. Per grid point,
the number of photons (~ intensity) and the number of original data points
(pixels) contribution is stored.
@@ -341,10 +339,10 @@ class Space(object):
self.config = config
self.metadata = metadata
-
+
self.photons = numpy.zeros([len(ax) for ax in self.axes], order='C')
self.contributions = numpy.zeros(self.photons.shape, order='C')
-
+
@property
def dimension(self):
return self.axes.dimension
@@ -400,7 +398,7 @@ class Space(object):
def __repr__(self):
return '{0.__class__.__name__} ({0.dimension} dimensions, {0.npoints} points, {1}) {{\n {2}\n}}'.format(self, util.format_bytes(self.memory_size), '\n '.join(repr(ax) for ax in self.axes))
-
+
def __getitem__(self, key):
"""Slicing only! space[-0.2:0.2, 0.9:1.1] does exactly what the syntax implies.
Ellipsis operator '...' is not supported."""
@@ -414,7 +412,7 @@ class Space(object):
newspace.contributions = self.contributions[newkey].copy()
return newspace
- def get_key(self, key):#needed in the fitaid for visualising the interpolated data
+ def get_key(self, key): # needed in the fitaid for visualising the interpolated data
"""Convert the n-dimensional interval described by key (as used by e.g. __getitem__()) from data coordinates to indices."""
if isinstance(key, numbers.Number) or isinstance(key, slice):
if not len(self.axes) == 1:
@@ -428,7 +426,7 @@ class Space(object):
def project(self, axis, *more_axes):
"""Reduce dimensionality of Space by projecting onto 'axis'.
All data (photons, contributions) is summed along this axis.
-
+
axis the label of the axis or the index
*more_axis also project on these axes"""
@@ -458,7 +456,7 @@ class Space(object):
return numpy.ma.array(data=self.get(), mask=(self.contributions == 0))
def get_variance(self):
- return numpy.ma.array(data=1 / self.contributions, mask = (self.contributions == 0))
+ return numpy.ma.array(data=1 / self.contributions, mask=(self.contributions == 0))
def get_grid(self):
"""Returns the data coordinates of each grid point, as n-tuple of n-dimensinonal arrays.
@@ -480,7 +478,7 @@ class Space(object):
if isinstance(other, numbers.Number):
new = self.copy()
new.photons += other * self.contributions
- return new
+ return new
if not isinstance(other, Space):
return NotImplemented
if not len(self.axes) == len(other.axes) or not all(a.is_compatible(b) for (a, b) in zip(self.axes, other.axes)):
@@ -518,8 +516,8 @@ class Space(object):
def __mul__(self, other):
if isinstance(other, numbers.Number):
new = self.__class__(self.axes, self.config, self.metadata)
- #we would like to keep 1/contributions as the variance
- #var(aX) = a**2var(X)
+ # we would like to keep 1/contributions as the variance
+ # var(aX) = a**2var(X)
new.photons = self.photons / other
new.contributions = self.contributions / other**2
return new
@@ -538,14 +536,14 @@ class Space(object):
def rebin(self, resolutions):
"""Change bin size.
-
+
resolution n-tuple of floats, new resolution of each axis"""
if not len(resolutions) == len(self.axes):
raise ValueError('cannot rebin space with different dimensionality')
if resolutions == tuple(ax.res for ax in self.axes):
return self
- # gather data and transform
+ # gather data and transform
coords = self.get_grid()
intensity = self.get()
weights = self.contributions
@@ -557,13 +555,13 @@ class Space(object):
raise ValueError('dimension mismatch')
newindices = list(self.axes.index(label) for label in labels)
new = self.__class__(tuple(self.axes[index] for index in newindices), self.config, self.metadata)
- new.photons = numpy.transpose(self.photons, axes = newindices)
- new.contributions = numpy.transpose(self.contributions, axes = newindices)
+ new.photons = numpy.transpose(self.photons, axes=newindices)
+ new.contributions = numpy.transpose(self.contributions, axes=newindices)
return new
-
+
def transform_coordinates(self, resolutions, labels, transformation):
# gather data and transform
-
+
coords = self.get_grid()
transcoords = transformation(*coords)
intensity = self.get()
@@ -584,13 +582,13 @@ class Space(object):
if len(coordinates) != len(self.axes):
raise ValueError('dimension mismatch between coordinates and axes')
- intensity = numpy.nan_to_num(intensity).flatten()#invalids should be handeled by setting weight to 0, this ensures the weights can do that
+ intensity = numpy.nan_to_num(intensity).flatten() # invalids should be handeled by setting weight to 0, this ensures the weights can do that
weights = weights.flatten()
indices = numpy.array(tuple(ax.get_index(coord) for (ax, coord) in zip(self.axes, coordinates)))
for i in range(0, len(self.axes)):
for j in range(i+1, len(self.axes)):
- indices[i,:] *= len(self.axes[j])
+ indices[i, :] *= len(self.axes[j])
indices = indices.sum(axis=0).astype(int).flatten()
photons = numpy.bincount(indices, weights=intensity * weights)
@@ -600,8 +598,8 @@ class Space(object):
self.contributions.ravel()[:contributions.size] += contributions
@classmethod
- def from_image(cls, resolutions, labels, coordinates, intensity, weights, limits = None):
- """Create Space from image data.
+ def from_image(cls, resolutions, labels, coordinates, intensity, weights, limits=None):
+ """Create Space from image data.
resolutions n-tuple of axis resolutions
labels n-tuple of axis labels
@@ -611,14 +609,14 @@ class Space(object):
if limits is not None:
invalid = numpy.zeros(intensity.shape).astype(numpy.bool)
for coord, sl in zip(coordinates, limits):
- if sl.start == None and sl.stop != None:
+ if sl.start is None and sl.stop ia not None:
invalid += coord > sl.stop
- elif sl.start != None and sl.stop == None:
+ elif sl.start is not None and sl.stop is None:
invalid += coord < sl.start
- elif sl.start != None and sl.stop != None:
+ elif sl.start is not None and sl.stop os not None:
invalid += numpy.bitwise_or(coord < sl.start, coord > sl.stop)
- if numpy.all(invalid == True):
+ if numpy.all(invalid is True):
return EmptySpace()
coordinates = tuple(coord[~invalid] for coord in coordinates)
intensity = intensity[~invalid]
@@ -651,7 +649,7 @@ class Space(object):
if 'type' in fp.attrs.keys():
if fp.attrs['type'] == 'Empty':
return EmptySpace()
-
+
axes = Axes.fromfile(fp)
config = util.ConfigFile.fromfile(fp)
metadata = util.MetaData.fromfile(fp)
@@ -673,9 +671,10 @@ class Space(object):
raise errors.HDF5FileError("unable to open '{0}' as HDF5 file (original error: {1!r})".format(file, e))
return space
+
class Multiverse(object):
"""A collection of spaces with basic support for addition.
- Only to be used when processing data. This makes it possible to
+ Only to be used when processing data. This makes it possible to
process multiple limit sets in a combination of scans"""
def __init__(self, spaces):
@@ -690,7 +689,7 @@ class Multiverse(object):
return NotImplemented
if not self.dimension == other.dimension:
raise ValueError('cannot add multiverses with different dimensionality')
- return self.__class__(tuple(s + o for s,o in zip(self.spaces, other.spaces)))
+ return self.__class__(tuple(s + o for s, o in zip(self.spaces, other.spaces)))
def __iadd__(self, other):
if not isinstance(other, Multiverse):
@@ -727,9 +726,10 @@ class Multiverse(object):
def __repr__(self):
return '{0.__class__.__name__}\n{1}'.format(self, self.spaces)
+
class EmptyVerse(object):
"""Convenience object for sum() and friends. Treated as zero for addition."""
-
+
def __add__(self, other):
if not isinstance(other, Multiverse):
return NotImplemented
@@ -744,7 +744,8 @@ class EmptyVerse(object):
if not isinstance(other, Multiverse):
return NotImplemented
return other
-
+
+
def union_axes(axes):
axes = tuple(axes)
if len(axes) == 1:
@@ -758,6 +759,7 @@ def union_axes(axes):
first = axes[0]
return first.__class__(mi, ma, first.res, first.label)
+
def union_unequal_axes(axes):
axes = tuple(axes)
if len(axes) == 1:
@@ -768,10 +770,11 @@ def union_unequal_axes(axes):
raise ValueError('cannot unite axes with different label')
mi = min(ax.min for ax in axes)
ma = max(ax.max for ax in axes)
- res = min(ax.res for ax in axes) #making it easier to use the sliderwidget otherwise this hase no meaning
+ res = min(ax.res for ax in axes) # making it easier to use the sliderwidget otherwise this hase no meaning
first = axes[0]
return first.__class__(mi, ma, res, first.label)
+
def sum(spaces):
"""Calculate sum of iterable of Space instances."""
spaces = tuple(space for space in spaces if not isinstance(space, EmptySpace))
@@ -789,11 +792,14 @@ def sum(spaces):
newspace += space
return newspace
+
def verse_sum(verses):
i = iter(M.spaces for M in verses)
return Multiverse(sum(spaces) for spaces in itertools.izip(*i))
# hybrid sum() / __iadd__()
+
+
def chunked_sum(verses, chunksize=10):
"""Calculate sum of iterable of Multiverse instances. Creates intermediate sums to avoid growing a large space at every summation.
@@ -804,7 +810,8 @@ def chunked_sum(verses, chunksize=10):
result += verse_sum(M for M in chunk)
return result
-def iterate_over_axis(space, axis, resolution = None):
+
+def iterate_over_axis(space, axis, resolution=None):
ax = space.axes[space.axes.index(axis)]
if resolution:
bins = get_bins(ax, resolution)
@@ -812,9 +819,10 @@ def iterate_over_axis(space, axis, resolution = None):
yield space.slice(axis, slice(start, stop))
else:
for value in ax:
- yield space.slice(axis, value)
-
-def get_axis_values(axes, axis, resolution = None):
+ yield space.slice(axis, value)
+
+
+def get_axis_values(axes, axis, resolution=None):
ax = axes[axes.index(axis)]
if resolution:
bins = get_bins(ax, resolution)
@@ -822,7 +830,8 @@ def get_axis_values(axes, axis, resolution = None):
else:
return numpy.array(list(ax))
-def iterate_over_axis_keys(axes, axis, resolution = None):
+
+def iterate_over_axis_keys(axes, axis, resolution=None):
axindex = axes.index(axis)
ax = axes[axindex]
k = [slice(None) for i in axes]
@@ -836,6 +845,7 @@ def iterate_over_axis_keys(axes, axis, resolution = None):
k[axindex] = value
yield k
+
def get_bins(ax, resolution):
if float(resolution) < ax.res:
raise ValueError('interval {0} to low, minimum interval is {1}'.format(resolution, ax.res))
@@ -843,6 +853,7 @@ def get_bins(ax, resolution):
mi, ma = ax.min, ax.max
return numpy.linspace(mi, ma, numpy.ceil(1 / numpy.float(resolution) * (ma - mi)))
+
def dstack(spaces, dindices, dlabel, dresolution):
def transform(space, dindex):
resolutions = list(ax.res for ax in space.axes)
@@ -855,6 +866,7 @@ def dstack(spaces, dindices, dlabel, dresolution):
return space.transform_coordinates(resolutions, labels, transformation)
return sum(transform(space, dindex) for space, dindex in itertools.izip(spaces, dindices))
+
def axis_offset(space, label, offset):
exprs = list(ax.label for ax in space.axes)
index = space.axes.index(label)
@@ -862,6 +874,7 @@ def axis_offset(space, label, offset):
transformation = util.transformation_from_expressions(space, exprs)
return space.transform_coordinates((ax.res for ax in space.axes), (ax.label for ax in space.axes), transformation)
+
def bkgsubtract(space, bkg):
if space.dimension == bkg.dimension:
bkg.photons = bkg.photons * space.contributions / bkg.contributions
@@ -876,15 +889,13 @@ def bkgsubtract(space, bkg):
bkg.contributions = contributions
return bkgsubtract(space, bkg)
+
def make_compatible(spaces):
- if not numpy.alen(numpy.unique(len(space.axes) for space in spaces)) == 1:
+ if not numpy.alen(numpy.unique(len(space.axes) for space in spaces)) == 1:
raise ValueError('cannot make spaces with different dimensionality compatible')
ax0 = tuple(ax.label for ax in spaces[0].axes)
- resmax = tuple(numpy.vstack(tuple(ax.res for ax in space.reorder(ax0).axes) for space in spaces).max(axis = 0))
- resmin = tuple(numpy.vstack(tuple(ax.res for ax in space.reorder(ax0).axes) for space in spaces).min(axis = 0))
+ resmax = tuple(numpy.vstack(tuple(ax.res for ax in space.reorder(ax0).axes) for space in spaces).max(axis=0))
+ resmin = tuple(numpy.vstack(tuple(ax.res for ax in space.reorder(ax0).axes) for space in spaces).min(axis=0))
if not resmax == resmin:
print 'Warning: Not all spaces have the same resolution. Resolution will be changed to: {0}'.format(resmax)
return tuple(space.reorder(ax0).rebin2(resmax) for space in spaces)
-
-
-
diff --git a/binoculars/util.py b/binoculars/util.py
index a8a037c..30a09cd 100755
--- a/binoculars/util.py
+++ b/binoculars/util.py
@@ -24,6 +24,7 @@ import re
### ARGUMENT HANDLING
+
class OrderedOperation(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
oops = getattr(namespace, 'ordered_operations', [])
@@ -106,19 +107,19 @@ def handle_ordered_operations(space, args, auto3to2=False):
pass
else:
info.append('projected on {0}'.format(space.axes[projectaxis].label))
- space = space.project(projectaxis)
+ space = space.project(projectaxis)
elif command == 'project':
projectaxis = space.axes.index(opts)
info.append('projected on {0}'.format(space.axes[projectaxis].label))
- space = space.project(projectaxis)
+ space = space.project(projectaxis)
elif command == 'transform':
labels, resolutions, exprs = zip(*parse_transform_args(opts))
transformation = transformation_from_expressions(space, exprs)
info.append('transformed to {0}'.format(', '.join('{0} = {1}'.format(label, expr) for (label, expr) in zip(labels, exprs))))
space = space.transform_coordinates(resolutions, labels, transformation)
-
+
elif command == 'rebin':
if ',' in opts:
factors = tuple(int(i) for i in opts.split(','))
@@ -129,7 +130,7 @@ def handle_ordered_operations(space, args, auto3to2=False):
else:
raise ValueError("unsported Ordered Operation '{0}'".format(command))
- if auto3to2 and space.dimension == 3: # automatic projection on smallest axis
+ if auto3to2 and space.dimension == 3: # automatic projection on smallest axis
projectaxis = numpy.argmin(space.photons.shape)
info.append('projected on {0}'.format(space.axes[projectaxis].label))
space = space.project(projectaxis)
@@ -140,6 +141,8 @@ def handle_ordered_operations(space, args, auto3to2=False):
### STATUS LINES
_status_line_length = 0
+
+
def status(line, eol=False):
"""Prints a status line to sys.stdout, overwriting the previous one.
Set eol to True to append a newline to the end of the line"""
@@ -154,10 +157,12 @@ def status(line, eol=False):
sys.stdout.flush()
+
def statusnl(line):
"""Shortcut for status(..., eol=True)"""
return status(line, eol=True)
+
def statuseol():
"""Starts a new status line, keeping the previous one intact"""
global _status_line_length
@@ -165,6 +170,7 @@ def statuseol():
sys.stdout.write('\n')
sys.stdout.flush()
+
def statuscl():
"""Clears the status line, shortcut for status('')"""
return status('')
@@ -180,14 +186,17 @@ def get_backends():
names.append(os.path.splitext(os.path.basename(module))[0])
return names
+
def get_projections(module):
import backend
return get_base(module, backend.ProjectionBase)
+
def get_inputs(module):
import backend
return get_base(module, backend.InputBase)
+
def get_dispatchers():
import dispatcher
from inspect import isclass
@@ -203,6 +212,7 @@ def get_dispatchers():
return options
+
def get_base(modname, base):
from inspect import isclass
@@ -215,7 +225,7 @@ def get_base(modname, base):
backend = getattr(backends, modname)
items = dir(backend)
-
+
options = []
for item in items:
obj = getattr(backend, item)
@@ -225,23 +235,29 @@ def get_base(modname, base):
return options
### Dispatcher, projection and input configuration options finder
+
+
def get_dispatcher_configkeys(classname):
import dispatcher
cls = getattr(dispatcher, classname)
return get_configkeys(cls)
-
+
+
def get_projection_configkeys(modname, classname):
return get_backend_configkeys(modname, classname)
+
def get_input_configkeys(modname, classname):
return get_backend_configkeys(modname, classname)
+
def get_backend_configkeys(modname, classname):
backends = __import__('backends.{0}'.format(modname), globals(), locals(), [], 1)
backend = getattr(backends, modname)
cls = getattr(backend, classname)
return get_configkeys(cls)
+
def get_configkeys(cls):
from inspect import getsource
items = list()
@@ -255,6 +271,7 @@ def get_configkeys(cls):
cls = cls.__base__
return items
+
def parse_configcode(line):
try:
comment = '#'.join(line.split('#')[1:])
@@ -262,7 +279,7 @@ def parse_configcode(line):
index = line.index('config.pop')
item = line[index:].split('\'')[1]
if item == 'action':
- return #action is reserved for internal use!
+ return # action is reserved for internal use!
return item, comment
except ValueError:
pass
@@ -279,6 +296,7 @@ def parse_range(r):
else:
return []
+
def parse_multi_range(s):
if not s:
return s
@@ -288,6 +306,7 @@ def parse_multi_range(s):
out.extend(parse_range(r))
return out
+
def parse_tuple(s, length=None, type=str):
if not s:
return s
@@ -296,6 +315,7 @@ def parse_tuple(s, length=None, type=str):
raise ValueError('invalid tuple length: expected {0} got {1}'.format(length, len(t)))
return t
+
def parse_bool(s):
l = s.lower()
if l in ('1', 'true', 'yes', 'on'):
@@ -304,6 +324,7 @@ def parse_bool(s):
return False
raise ValueError("invalid input for boolean: '{0}'".format(s))
+
def parse_pairs(s):
if not s:
return s
@@ -326,11 +347,13 @@ def parse_pairs(s):
limits.append(parsed)
return limits
+
def limit_to_filelabel(s):
- return tuple('[{0}]'.format(lim.replace('-', 'm').replace(':', '-').replace(' ','')) for lim in re.findall('\[(.*?)\]', s))
+ return tuple('[{0}]'.format(lim.replace('-', 'm').replace(':', '-').replace(' ', '')) for lim in re.findall('\[(.*?)\]', s))
+
class MetaBase(object):
- def __init__(self, label = None, section = None):
+ def __init__(self, label=None, section=None):
self.sections = []
if label is not None and section is not None:
self.sections.append(label)
@@ -338,8 +361,8 @@ class MetaBase(object):
elif label is not None:
self.sections.append(label)
setattr(self, label, dict())
-
- def add_section(self, label, section = None):
+
+ def add_section(self, label, section=None):
self.sections.append(label)
if section is not None:
setattr(self, label, section)
@@ -365,14 +388,14 @@ class MetaBase(object):
section_dict = {}
attr = getattr(self, section)
for key in attr.keys():
- if isinstance(attr[key], numpy.ndarray):# to be able to include numpy arrays in the serialisation
+ if isinstance(attr[key], numpy.ndarray): # to be able to include numpy arrays in the serialisation
sio = StringIO.StringIO()
numpy.save(sio, attr[key])
sio.seek(0)
- section_dict[key] = binascii.b2a_hex(sio.read())#hex codation is needed to let json work with the string
+ section_dict[key] = binascii.b2a_hex(sio.read()) # hex codation is needed to let json work with the string
else:
section_dict[key] = attr[key]
- sections[section] = section_dict
+ sections[section] = section_dict
return json.dumps(sections)
@classmethod
@@ -380,10 +403,10 @@ class MetaBase(object):
obj = cls()
data = json.loads(s)
for section in data.keys():
- section_dict = data[section]
+ section_dict = data[section]
for key in section_dict.keys():
- if isinstance(section_dict[key], basestring):#find and replace all the numpy serialised objects
- if section_dict[key].startswith('934e554d505901004600'):#numpy marker
+ if isinstance(section_dict[key], basestring): # find and replace all the numpy serialised objects
+ if section_dict[key].startswith('934e554d505901004600'): # numpy marker
sio = StringIO.StringIO()
sio.write(binascii.a2b_hex(section_dict[key]))
sio.seek(0)
@@ -392,8 +415,10 @@ class MetaBase(object):
if section not in obj.sections:
obj.sections.append(section)
return obj
-
-# a collection of metadata objects
+
+# a collection of metadata objects
+
+
class MetaData(object):
def __init__(self):
self.metas = []
@@ -425,7 +450,7 @@ class MetaData(object):
try:
metadata = fp['metadata']
except KeyError as e:
- metadata = [] # when metadata is not present, proceed without Error
+ metadata = [] # when metadata is not present, proceed without Error
for label in metadata:
meta = MetaBase()
for section in metadata[label].keys():
@@ -445,7 +470,7 @@ class MetaData(object):
sectiongroup = metabase.create_group(section)
s = getattr(meta, section)
for key in s.keys():
- sectiongroup.create_dataset(key, data = s[key])
+ sectiongroup.create_dataset(key, data=s[key])
def __repr__(self):
str = '{0.__class__.__name__}{{\n'.format(self)
@@ -466,8 +491,10 @@ class MetaData(object):
return obj
#Contains the unparsed config dicts
+
+
class ConfigFile(MetaBase):
- def __init__(self, origin='n/a', command = []):
+ def __init__(self, origin='n/a', command=[]):
self.origin = origin
self.command = command
super(ConfigFile, self).__init__()
@@ -488,16 +515,16 @@ class ConfigFile(MetaBase):
if 'command' in config.attrs:
configobj.command = json.loads(config.attrs['command'])
for section in config:
- if isinstance(config[section], h5py._hl.group.Group):#new
+ if isinstance(config[section], h5py._hl.group.Group): # new
setattr(configobj, section, dict((key, config[section][key].value) for key in config[section]))
- else:#old
+ else: # old
setattr(configobj, section, dict(config[section]))
except KeyError as e:
- pass # when config is not present, proceed without Error
+ pass # when config is not present, proceed without Error
return configobj
@classmethod
- def fromtxtfile(cls, filename, command = [], overrides=[]):
+ def fromtxtfile(cls, filename, command=[], overrides=[]):
if not os.path.exists(filename):
raise IOError('Error importing configuration file. filename {0} does not exist'.format(filename))
@@ -507,7 +534,7 @@ class ConfigFile(MetaBase):
for section, option, value in overrides:
config.set(section, option, value)
- configobj = cls(filename, command = command)
+ configobj = cls(filename, command=command)
for section in configobj.sections:
setattr(configobj, section, dict((k, v.split('#')[0].strip()) for (k, v) in config.items(section)))
return configobj
@@ -521,7 +548,7 @@ class ConfigFile(MetaBase):
sectiongroup = conf.create_group(section)
s = getattr(self, section)
for key in s.keys():
- sectiongroup.create_dataset(key, data = s[key])
+ sectiongroup.create_dataset(key, data=s[key])
def totxtfile(self, filename):
with open(filename, 'w') as fp:
@@ -539,6 +566,8 @@ class ConfigFile(MetaBase):
return str
#contains one parsed dict, for distribution to dispatcher, input or projection class
+
+
class ConfigSection(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
@@ -547,6 +576,8 @@ class ConfigSection(object):
return copy.deepcopy(self)
#contains the parsed configsections
+
+
class ConfigSectionGroup(object):
def __init__(self, origin='n/a'):
self.origin = origin
@@ -555,8 +586,9 @@ class ConfigSectionGroup(object):
setattr(self, section, ConfigSection())
self.configfile = ConfigFile()
+
class ConfigurableObject(object):
- def __init__(self, config):
+ def __init__(self, config):
if isinstance(config, ConfigSection):
self.config = config
elif not isinstance(config, dict):
@@ -589,10 +621,12 @@ def best_effort_atomic_rename(src, dest):
os.remove(dest)
os.rename(src, dest)
+
def filename_enumerator(filename, start=0):
- base,ext = os.path.splitext(filename)
- for count in itertools.count(start):
- yield '{0}_{2}{1}'.format(base,ext,count)
+ base, ext = os.path.splitext(filename)
+ for count in itertools.count(start):
+ yield '{0}_{2}{1}'.format(base, ext, count)
+
def find_unused_filename(filename):
if not os.path.exists(filename):
@@ -601,15 +635,18 @@ def find_unused_filename(filename):
if not os.path.exists(f):
return f
+
def label_enumerator(label, start=0):
- for count in itertools.count(start):
- yield '{0}_{1}'.format(label,count)
+ for count in itertools.count(start):
+ yield '{0}_{1}'.format(label, count)
+
def find_unused_label(label, labellist):
for l in label_enumerator(label):
if not l in labellist:
return l
+
def yield_when_exists(filelist, timeout=None):
"""Wait for files in 'filelist' to appear, for a maximum of 'timeout' seconds,
yielding them in arbitrary order as soon as they appear.
@@ -628,6 +665,7 @@ def yield_when_exists(filelist, timeout=None):
if timeout is not None and time.time() - start > timeout:
break
+
def wait_for_files(filelist, timeout=None):
"""Wait until the files in 'filelist' have appeared, for a maximum of 'timeout' seconds.
Returns True on success, False on timeout."""
@@ -636,9 +674,11 @@ def wait_for_files(filelist, timeout=None):
pass
return not filelist
+
def wait_for_file(file, timeout=None):
return wait_for_files([file], timeout=timeout)
+
def space_to_edf(space, filename):
from PyMca import EdfFile
@@ -648,6 +688,7 @@ def space_to_edf(space, filename):
edf = EdfFile.EdfFile(filename)
edf.WriteImage(header, space.get_masked().filled(0), DataType="Float")
+
def space_to_txt(space, filename):
data = [coord.flatten() for coord in space.get_grid()]
data.append(space.get_masked().filled(0).flatten())
@@ -658,6 +699,7 @@ def space_to_txt(space, filename):
fp.write('\tintensity\n')
numpy.savetxt(fp, data, fmt='%.6g', delimiter='\t')
+
@contextlib.contextmanager
def open_h5py(fn, mode):
if isinstance(fn, h5py._hl.group.Group):
@@ -675,9 +717,11 @@ def open_h5py(fn, mode):
### VARIOUS
+
def uniqid():
return '{0:08x}'.format(random.randint(0, 2**32-1))
+
def grouper(iterable, n):
while True:
chunk = list(itertools.islice(iterable, n))
@@ -686,13 +730,17 @@ def grouper(iterable, n):
yield chunk
_python_executable = None
+
+
def register_python_executable(scriptname):
global _python_executable
_python_executable = sys.executable, scriptname
+
def get_python_executable():
return _python_executable
+
def chunk_slicer(count, chunksize):
"""yields slice() objects that split an array of length 'count' into equal sized chunks of at most 'chunksize'"""
chunkcount = int(numpy.ceil(float(count) / chunksize))
@@ -700,6 +748,7 @@ def chunk_slicer(count, chunksize):
for i in range(chunkcount):
yield slice(i*realchunksize, min(count, (i+1)*realchunksize))
+
def cluster_jobs(jobs, target_weight):
jobs = sorted(jobs, key=lambda job: job.weight)
@@ -708,27 +757,29 @@ def cluster_jobs(jobs, target_weight):
yield [jobs.pop()]
while jobs:
- cluster = [jobs.pop()] # take the biggest remaining job
+ cluster = [jobs.pop()] # take the biggest remaining job
size = cluster[0].weight
- for i in range(len(jobs)-1, -1, -1): # and exhaustively search for all jobs that can accompany it (biggest first)
+ for i in range(len(jobs)-1, -1, -1): # and exhaustively search for all jobs that can accompany it (biggest first)
if size + jobs[i].weight <= target_weight:
size += jobs[i].weight
cluster.append(jobs.pop(i))
yield cluster
+
def cluster_jobs2(jobs, target_weight):
"""Taking the first n jobs that together add up to target_weight.
Here as opposed to cluster_jobs the total number of jobs does not have to be known beforehand
"""
jobslist = []
for job in jobs:
- jobslist.append(job)
- if sum(j.weight for j in jobslist) >= target_weight:
- yield jobslist[:]
- jobslist = []
- if len(jobslist) > 0:#yield the remainder of the jobs
+ jobslist.append(job)
+ if sum(j.weight for j in jobslist) >= target_weight:
+ yield jobslist[:]
+ jobslist = []
+ if len(jobslist) > 0: # yield the remainder of the jobs
yield jobslist[:]
+
def loop_delayer(delay):
"""Delay a loop such that it runs at most once every 'delay' seconds. Usage example:
delay = loop_delayer(5)
@@ -746,6 +797,7 @@ def loop_delayer(delay):
yield
return generator()
+
def transformation_from_expressions(space, exprs):
def transformation(*coords):
ns = dict((i, getattr(numpy, i)) for i in dir(numpy))
@@ -790,6 +842,7 @@ else:
unpickler = _Unpickler(fileobj)
return unpickler.load()
+
@contextlib.contextmanager
def atomic_write(filename):
"""Atomically write data into 'filename' using a temporary file and os.rename()
@@ -816,6 +869,7 @@ def atomic_write(filename):
if os.path.exists(tmpfile):
os.remove(tmpfile)
+
def zpi_save(obj, filename):
with atomic_write(filename) as tmpfile:
fp = gzip.open(tmpfile, 'wb')
@@ -824,6 +878,7 @@ def zpi_save(obj, filename):
finally:
fp.close()
+
def zpi_load(filename):
if hasattr(filename, 'read'):
fp = gzip.GzipFile(filename.name, fileobj=filename)
@@ -838,14 +893,14 @@ def zpi_load(filename):
def serialize(space, command):
# first 48 bytes contain length of the message, whereby the first 8 give the length of the command, the second 8 the length of the configfile etc..
message = StringIO.StringIO()
- message.write(struct.pack('QQQQQQ',0,0,0,0,0,0))
+ message.write(struct.pack('QQQQQQ', 0, 0, 0, 0, 0, 0))
message.write(command)
commandlength = message.len - 48
message.write(space.config.serialize())
configlength = message.len - commandlength - 48
-
+
message.write(space.metadata.serialize())
metalength = message.len - configlength - commandlength - 48
@@ -864,15 +919,17 @@ def serialize(space, command):
return message
-def packet_slicer(length, size = 1024):#limit the communication to 1024 bytes
+
+def packet_slicer(length, size=1024): # limit the communication to 1024 bytes
while length > size:
length -= size
yield size
yield length
+
def socket_send(ip, port, mssg):
try:
- mssglengths = struct.unpack('QQQQQQ',mssg.read(48))#the lengths of all the components
+ mssglengths = struct.unpack('QQQQQQ', mssg.read(48)) # the lengths of all the components
mssg.seek(0)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -883,21 +940,20 @@ def socket_send(ip, port, mssg):
for packet in packet_slicer(l):
sock.send(mssg.read(packet))
sock.close()
- except socket.error:# in case of failure to send. The data will be saved anyway so any loss of communication unfortunate but not critical
+ except socket.error: # in case of failure to send. The data will be saved anyway so any loss of communication unfortunate but not critical
pass
-def socket_recieve(RequestHandler):#pass one the handler to deal with incoming data
+def socket_recieve(RequestHandler): # pass one the handler to deal with incoming data
def get_msg(length):
msg = StringIO.StringIO()
for packet in packet_slicer(length):
- p = RequestHandler.request.recv(packet, socket.MSG_WAITALL) #wait for full mssg
+ p = RequestHandler.request.recv(packet, socket.MSG_WAITALL) # wait for full mssg
msg.write(p)
if msg.len != length:
raise errors.CommunicationError('recieved message is too short. expected length {0}, recieved length {1}'.format(length, msg.len))
msg.seek(0)
return msg
- command, config, metadata, axes, photons, contributions = tuple(get_msg(msglength) for msglength in struct.unpack('QQQQQQ',RequestHandler.request.recv(48, socket.MSG_WAITALL)))
+ command, config, metadata, axes, photons, contributions = tuple(get_msg(msglength) for msglength in struct.unpack('QQQQQQ', RequestHandler.request.recv(48, socket.MSG_WAITALL)))
return command.read(), config.read(), metadata.read(), numpy.load(axes), numpy.load(photons), numpy.load(contributions)
-
diff --git a/scripts/binoculars b/scripts/binoculars
index cc9a830..8080cad 100755
--- a/scripts/binoculars
+++ b/scripts/binoculars
@@ -22,7 +22,7 @@ except ImportError:
import binoculars.util
-### INFO
+# INFO
def command_info(args):
parser = argparse.ArgumentParser(prog='binoculars info')
parser.add_argument('infile', nargs='+', help='input files, must be .hdf5')
@@ -31,7 +31,7 @@ def command_info(args):
args = parser.parse_args(args)
if args.output:
- if len(args.infile)>1:
+ if len(args.infile) > 1:
print 'only one space file argument is support with extractconfig -> using the first'
config = binoculars.util.ConfigFile.fromfile(args.infile[0])
config.totxtfile(args.output)
@@ -52,7 +52,7 @@ def command_info(args):
print '{!r}'.format(config)
-### CONVERT
+# CONVERT
def command_convert(args):
parser = argparse.ArgumentParser(prog='binoculars convert')
parser.add_argument('--wait', action='store_true', help='wait for input files to appear')
@@ -62,7 +62,7 @@ def command_convert(args):
parser.add_argument('outfile', help='output file, can be .hdf5 or .edf or .txt')
args = parser.parse_args(args)
-
+
if args.wait:
binoculars.util.statusnl('waiting for {0} to appear'.format(args.infile))
binoculars.util.wait_for_file(args.infile)
@@ -99,17 +99,18 @@ def command_convert(args):
sys.exit(1)
-### PLOT
+# PLOT
def command_plot(args):
import matplotlib.pyplot as pyplot
- import binoculars.fit, binoculars.plot
+ import binoculars.fit
+ import binoculars.plot
parser = argparse.ArgumentParser(prog='binoculars plot')
parser.add_argument('infile', nargs='+')
binoculars.util.argparse_common_arguments(parser, 'savepdf', 'savefile', 'clip', 'nolog', 'project', 'slice', 'pslice', 'subtract', 'rebin', 'transform')
parser.add_argument('--multi', default=None, choices=('grid', 'stack'))
- parser.add_argument('--fit', default = None)
- parser.add_argument('--guess', default = None)
+ parser.add_argument('--fit', default=None)
+ parser.add_argument('--guess', default=None)
args = parser.parse_args(args)
if args.subtract:
@@ -168,7 +169,7 @@ def command_plot(args):
label = basename
else:
label = '{0} files'.format(plotcount)
-
+
if args.subtract:
label = '{0} (subtracted {1})'.format(label, os.path.splitext(os.path.basename(args.subtract))[0])
@@ -177,24 +178,22 @@ def command_plot(args):
pyplot.suptitle('{0}, {1}'.format(label, ' '.join(info)))
-
if args.savepdf or args.savefile:
if args.savefile:
pyplot.savefig(args.savefile)
- #print 'saved at {0}'.format(args.savefile)
else:
filename = '{0}_plot.pdf'.format(os.path.splitext(args.infile[0])[0])
filename = binoculars.util.find_unused_filename(filename)
pyplot.savefig(filename)
- #print 'saved at {0}'.format(filename)
else:
pyplot.show()
-### FIT
+# FIT
def command_fit(args):
import matplotlib.pyplot as pyplot
- import binoculars.fit, binoculars.plot
+ import binoculars.fit
+ import binoculars.plot
parser = argparse.ArgumentParser(prog='binoculars fit')
parser.add_argument('infile')
@@ -234,8 +233,8 @@ def command_fit(args):
info = []
key = [slice(None) for i in axes]
key[axindex] = slice(start, stop)
- newspace = binoculars.space.Space.fromfile(args.infile, key)
- left, right = newspace.axes[axindex].min,newspace.axes[axindex].max
+ newspace = binoculars.space.Space.fromfile(args.infile, key)
+ left, right = newspace.axes[axindex].min, newspace.axes[axindex].max
if newspace.dimension == axes.dimension:
newspace = newspace.project(axindex)
@@ -263,20 +262,20 @@ def command_fit(args):
if newspace.dimension == 1:
pyplot.figure(figsize=(12, 9))
pyplot.subplot(111)
- binoculars.plot.plot(newspace, pyplot.gcf(), pyplot.gca(), label=basename, log=not args.nolog, clipping=float(args.clip), fit = fit)
+ binoculars.plot.plot(newspace, pyplot.gcf(), pyplot.gca(), label=basename, log=not args.nolog, clipping=float(args.clip), fit=fit)
elif newspace.dimension == 2:
pyplot.figure(figsize=(12, 9))
pyplot.subplot(121)
- binoculars.plot.plot(newspace, pyplot.gcf(), pyplot.gca(), label=basename, log=not args.nolog, clipping=float(args.clip), fit = None)
+ binoculars.plot.plot(newspace, pyplot.gcf(), pyplot.gca(), label=basename, log=not args.nolog, clipping=float(args.clip), fit=None)
pyplot.subplot(122)
- binoculars.plot.plot(newspace, pyplot.gcf(), pyplot.gca(), label=basename, log=not args.nolog, clipping=float(args.clip), fit = fit)
+ binoculars.plot.plot(newspace, pyplot.gcf(), pyplot.gca(), label=basename, log=not args.nolog, clipping=float(args.clip), fit=fit)
info.append('sliced in {0} from {1} to {2}'.format(axlabel, left, right))
pyplot.suptitle('{0}'.format(' '.join(info)))
-
+
pyplot.savefig(filename.next())
pyplot.close()
-
+
parameters = numpy.vstack(n for n in parameters).T
variance = numpy.vstack(n for n in variance).T
@@ -288,11 +287,11 @@ def command_fit(args):
if paramnames[i] in ['I']:
pyplot.semilogy()
pyplot.xlabel(paramnames[i])
-
- pyplot.suptitle('fit summary of {0}'.format(args.infile))
+
+ pyplot.suptitle('fit summary of {0}'.format(args.infile))
if args.savepdf or args.savefile:
if args.savefile:
- root, ext = os.path.split(args.savefile)
+ root, ext = os.path.split(args.savefile)
pyplot.savefig('{0}_summary{1}'.format(root, ext))
print 'saved at {0}_summary{1}'.format(root, ext)
filename = '{0}_summary{1}'.format(root, '.txt')
@@ -300,7 +299,6 @@ def command_fit(args):
pyplot.savefig('{0}_summary.pdf'.format(os.path.splitext(args.infile)[0]))
print 'saved at {0}_summary.pdf'.format(os.path.splitext(args.infile)[0])
filename = '{0}_summary.txt'.format(os.path.splitext(args.infile)[0])
-
file = open(filename, 'w')
file.write('L\t')
@@ -308,21 +306,20 @@ def command_fit(args):
file.write('\n')
for n in range(parameters.shape[1]):
file.write('{0}\t'.format(fitlabel[n]))
- file.write('\t'.join(numpy.array(parameters[:, n], dtype = numpy.str)))
+ file.write('\t'.join(numpy.array(parameters[:, n], dtype=numpy.str)))
file.write('\n')
file.close()
-### PROCESS
+# PROCESS
def command_process(args):
import binoculars.main
binoculars.util.register_python_executable(__file__)
- binoculars.main.Main.from_args(args)# start of main thread
+ binoculars.main.Main.from_args(args) # start of main thread
-
-### SUBCOMMAND ARGUMENT HANDLING
+# SUBCOMMAND ARGUMENT HANDLING
def usage(msg=''):
print """usage: binoculars COMMAND ...
{1}
@@ -343,7 +340,7 @@ if __name__ == '__main__':
binoculars.space.silence_numpy_errors()
subcommands = {'info': command_info, 'convert': command_convert, 'plot': command_plot, 'fit': command_fit, 'process': command_process}
-
+
if len(sys.argv) < 2:
usage()
subcommand = sys.argv[1]
diff --git a/scripts/binoculars-fitaid b/scripts/binoculars-fitaid
index 5cc56d9..648f2dc 100755
--- a/scripts/binoculars-fitaid
+++ b/scripts/binoculars-fitaid
@@ -5,7 +5,8 @@ import sys
import numpy
import os.path
import itertools
-import matplotlib.figure, matplotlib.image
+import matplotlib.figure
+import matplotlib.image
from PyQt4 import QtGui, QtCore, Qt
from scipy.interpolate import griddata
@@ -13,6 +14,7 @@ from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg, NavigationTool
from matplotlib.pyplot import Rectangle
from scipy.spatial import qhull
+
def set_src():
import sys
import os.path as osp
@@ -39,23 +41,23 @@ class Window(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
- newproject = QtGui.QAction("New project", self)
+ newproject = QtGui.QAction("New project", self)
newproject.triggered.connect(self.newproject)
- loadproject = QtGui.QAction("Open project", self)
+ loadproject = QtGui.QAction("Open project", self)
loadproject.triggered.connect(self.loadproject)
- addspace = QtGui.QAction("Import space", self)
+ addspace = QtGui.QAction("Import space", self)
addspace.triggered.connect(self.add_to_project)
- menu_bar = QtGui.QMenuBar()
- file = menu_bar.addMenu("&File")
+ menu_bar = QtGui.QMenuBar()
+ file = menu_bar.addMenu("&File")
file.addAction(newproject)
file.addAction(loadproject)
file.addAction(addspace)
self.setMenuBar(menu_bar)
- self.statusbar = QtGui.QStatusBar()
+ self.statusbar = QtGui.QStatusBar()
self.tab_widget = QtGui.QTabWidget(self)
self.tab_widget.setTabsClosable(True)
@@ -83,7 +85,7 @@ class Window(QtGui.QMainWindow):
except Exception as e:
QtGui.QMessageBox.critical(self, 'New project', 'Unable to save project to {}: {}'.format(fname, e))
- def loadproject(self, filename = None):
+ def loadproject(self, filename=None):
if not filename:
dialog = QtGui.QFileDialog(self, "Load project");
dialog.setFilter('binoculars fit file (*.fit)');
@@ -105,11 +107,11 @@ class Window(QtGui.QMainWindow):
self.tab_widget.addTab(widget, 'fname')
self.tab_widget.setCurrentWidget(widget)
- def add_to_project(self, filename = None):
+ def add_to_project(self, filename=None):
if self.tab_widget.count() == 0:
QtGui.QMessageBox.warning(self, 'Warning', 'First select a file to store data')
self.newproject()
-
+
if not filename:
dialog = QtGui.QFileDialog(self, "Import spaces");
dialog.setFilter('binoculars space file (*.hdf5)');
@@ -129,16 +131,16 @@ class Window(QtGui.QMainWindow):
else:
widget = self.tab_widget.currentWidget()
widget.addspace(filename)
-
+
class TopWidget(QtGui.QWidget):
- def __init__(self, filename , parent=None):
+ def __init__(self, filename, parent=None):
super(TopWidget, self).__init__(parent)
- hbox = QtGui.QHBoxLayout()
- vbox = QtGui.QVBoxLayout()
- minihbox = QtGui.QHBoxLayout()
- minihbox2 = QtGui.QHBoxLayout()
+ hbox = QtGui.QHBoxLayout()
+ vbox = QtGui.QVBoxLayout()
+ minihbox = QtGui.QHBoxLayout()
+ minihbox2 = QtGui.QHBoxLayout()
self.database = FitData(filename)
self.table = TableWidget(self.database)
@@ -157,13 +159,13 @@ class TopWidget(QtGui.QWidget):
self.tab_widget.addTab(self.plotwidget, 'plot')
self.emptywidget = QtGui.QWidget()
- self.emptywidget.setLayout(vbox)
+ self.emptywidget.setLayout(vbox)
vbox.addWidget(self.table)
vbox.addWidget(self.nav)
self.functions = list()
- self.function_box = QtGui.QComboBox()
+ self.function_box = QtGui.QComboBox()
for function in dir(binoculars.fit):
cls = getattr(binoculars.fit, function)
if isinstance(cls, type) and issubclass(cls, binoculars.fit.PeakFitBase):
@@ -174,7 +176,7 @@ class TopWidget(QtGui.QWidget):
vbox.addWidget(self.function_box)
vbox.addLayout(minihbox)
vbox.addLayout(minihbox2)
-
+
self.all_button = QtGui.QPushButton('fit all')
self.rod_button = QtGui.QPushButton('fit rod')
self.slice_button = QtGui.QPushButton('fit slice')
@@ -205,14 +207,14 @@ class TopWidget(QtGui.QWidget):
splitter.addWidget(self.tab_widget)
self.tab_widget.currentChanged.connect(self.tab_change)
- hbox.addWidget(splitter)
- self.setLayout(hbox)
+ hbox.addWidget(splitter)
+ self.setLayout(hbox)
def tab_change(self, index):
if index == 2:
self.refresh_plot()
- def addspace(self,filename = None):
+ def addspace(self, filename=None):
if filename == None:
filename = str(QtGui.QFileDialog.getOpenFileName(self, 'Open Project', '.', '*.hdf5'))
self.table.addspace(filename)
@@ -258,7 +260,6 @@ class TopWidget(QtGui.QWidget):
self.fit_loc(self.fitwidget.database)
self.fitwidget.plot()
-
def fit_all(self):
def function(index, space):
self.fitwidget.fit(index, space, self.fitclass)
@@ -270,7 +271,6 @@ class TopWidget(QtGui.QWidget):
self.fitwidget.plot()
-
def int_slice(self):
index = self.nav.index()
space = self.fitwidget.database.space_from_index(index)
@@ -278,15 +278,15 @@ class TopWidget(QtGui.QWidget):
self.integratewidget.plot(index)
def int_rod(self):
- self.progressbox(self.integratewidget.database.rodkey,self.integratewidget.integrate, enumerate(self.integratewidget.database), self.integratewidget.database.rodlength())
+ self.progressbox(self.integratewidget.database.rodkey, self.integratewidget.integrate, enumerate(self.integratewidget.database), self.integratewidget.database.rodlength())
self.integratewidget.plot()
def int_all(self):
for rodkey, axis, resolution in self.table.checked():
self.integratewidget.database = RodData(self.database.filename, rodkey, axis, resolution)
- self.progressbox(self.integratewidget.database.rodkey,self.integratewidget.integrate, enumerate(self.integratewidget.database), self.integratewidget.database.rodlength())
+ self.progressbox(self.integratewidget.database.rodkey, self.integratewidget.integrate, enumerate(self.integratewidget.database), self.integratewidget.database.rodlength())
self.integratewidget.plot()
-
+
def fit_loc(self, database):
deg = 2
for param in database.all_attrkeys():
@@ -303,15 +303,16 @@ class TopWidget(QtGui.QWidget):
w[w < 0] = 0
w[w < numpy.median(w)] = 0
if len(x) > 0:
- c = numpy.polynomial.polynomial.polyfit(cx, y, deg, w = w)
+ c = numpy.polynomial.polynomial.polyfit(cx, y, deg, w=w)
newy = numpy.polynomial.polynomial.polyval(x, c)
for index, newval in enumerate(newy):
- database.save_sliceattr(index, 'guessloc{0}'.format(param.lstrip('loc')) , newval)
+ database.save_sliceattr(index, 'guessloc{0}'.format(param.lstrip('loc')), newval)
- def progressbox(self, rodkey , function, iterator, length):
+ def progressbox(self, rodkey, function, iterator, length):
pd = QtGui.QProgressDialog('Processing {0}'.format(rodkey), 'Cancel', 0, length)
pd.setWindowModality(QtCore.Qt.WindowModal)
pd.show()
+
def progress(index, item):
pd.setValue(index)
if pd.wasCanceled():
@@ -322,6 +323,7 @@ class TopWidget(QtGui.QWidget):
progress(index, item)
pd.close()
+
class TableWidget(QtGui.QWidget):
trigger = QtCore.pyqtSignal()
check_changed = QtCore.pyqtSignal()
@@ -335,11 +337,11 @@ class TableWidget(QtGui.QWidget):
self.activeindex = 0
self.table = QtGui.QTableWidget(0, 5)
- self.table.setHorizontalHeaderLabels(['','rod', 'axis', 'res', 'remove'])
-
+ self.table.setHorizontalHeaderLabels(['', 'rod', 'axis', 'res', 'remove'])
+
self.table.cellClicked.connect(self.setlength)
- for index, width in enumerate([25,150,40,50,70]):
+ for index, width in enumerate([25, 150, 40, 50, 70]):
self.table.setColumnWidth(index, width)
for filename, rodkey in zip(database.filelist, database.rods()):
@@ -348,7 +350,7 @@ class TableWidget(QtGui.QWidget):
hbox.addWidget(self.table)
self.setLayout(hbox)
- def addspace(self, filename, rodkey = None):
+ def addspace(self, filename, rodkey=None):
def remove_callback(rodkey):
return lambda: self.remove(rodkey)
@@ -358,7 +360,7 @@ class TableWidget(QtGui.QWidget):
if rodkey == None:
rodkey = short_filename(filename)
if rodkey in self.database.rods():
- newkey = find_unused_rodkey(rodkey, self.database.rods())
+ newkey = find_unused_rodkey(rodkey, self.database.rods())
self.database.copy(rodkey, newkey)
rodkey = newkey
@@ -367,13 +369,12 @@ class TableWidget(QtGui.QWidget):
index = self.table.rowCount()
self.table.insertRow(index)
-
- axes = binoculars.space.Axes.fromfile(filename)
+ axes = binoculars.space.Axes.fromfile(filename)
checkboxwidget = QtGui.QCheckBox()
checkboxwidget.rodkey = rodkey
checkboxwidget.setChecked(0)
- self.table.setCellWidget(index,0, checkboxwidget)
+ self.table.setCellWidget(index, 0, checkboxwidget)
checkboxwidget.clicked.connect(self.check_changed)
item = QtGui.QTableWidgetItem(rodkey)
@@ -386,22 +387,22 @@ class TableWidget(QtGui.QWidget):
if not old_axis == None:
self.table.cellWidget(index, 2).setCurrentIndex(axes.index(old_axis))
elif index > 0:
- self.table.cellWidget(0, 2).setCurrentIndex(self.table.cellWidget(0,2).currentIndex())
+ self.table.cellWidget(0, 2).setCurrentIndex(self.table.cellWidget(0, 2).currentIndex())
resolution = QtGui.QLineEdit()
if not old_resolution == None:
resolution.setText(str(old_resolution))
elif index > 0:
- resolution.setText(self.table.cellWidget(0,3).text())
+ resolution.setText(self.table.cellWidget(0, 3).text())
else:
resolution.setText(str(axes[axes.index(str(axis.currentText()))].res))
-
+
resolution.editingFinished.connect(activechange_callback(index))
self.table.setCellWidget(index, 3, resolution)
-
+
buttonwidget = QtGui.QPushButton('remove')
buttonwidget.clicked.connect(remove_callback(rodkey))
- self.table.setCellWidget(index,4, buttonwidget)
+ self.table.setCellWidget(index, 4, buttonwidget)
def remove(self, rodkey):
table_rodkeys = list(self.table.cellWidget(index, 0).rodkey for index in range(self.table.rowCount()))
@@ -411,7 +412,7 @@ class TableWidget(QtGui.QWidget):
self.database.delete_rod(rodkey)
print 'removed: {0}'.format(rodkey)
- def setlength(self, y, x = 1):
+ def setlength(self, y, x=1):
if x == 1:
self.activeindex = y
rodkey, axis, resolution = self.currentkey()
@@ -421,8 +422,8 @@ class TableWidget(QtGui.QWidget):
def currentkey(self):
rodkey = self.table.cellWidget(self.activeindex, 0).rodkey
- axis = str(self.table.cellWidget(self.activeindex,2).currentText())
- resolution = float(self.table.cellWidget(self.activeindex,3).text())
+ axis = str(self.table.cellWidget(self.activeindex, 2).currentText())
+ resolution = float(self.table.cellWidget(self.activeindex, 3).text())
return rodkey, axis, resolution
def checked(self):
@@ -431,69 +432,71 @@ class TableWidget(QtGui.QWidget):
checkbox = self.table.cellWidget(index, 0)
if checkbox.checkState():
rodkey = self.table.cellWidget(index, 0).rodkey
- axis = str(self.table.cellWidget(index,2).currentText())
- resolution = float(self.table.cellWidget(index,3).text())
+ axis = str(self.table.cellWidget(index, 2).currentText())
+ resolution = float(self.table.cellWidget(index, 3).text())
selection.append((rodkey, axis, resolution))
return selection
+
class FitData(object):
def __init__(self, filename):
self.filename = filename
self.axdict = dict()
-
- with h5py.File(self.filename,'a') as db:
+
+ with h5py.File(self.filename, 'a') as db:
for rodkey in self.rods():
- spacename = db[rodkey].attrs['filename']
+ spacename = db[rodkey].attrs['filename']
if not os.path.exists(spacename):
- warningbox = QtGui.QMessageBox(2, 'Warning', 'Cannot find space {0} at file {1}; locate proper space'.format(rodkey, spacename), buttons = QtGui.QMessageBox.Open)
+ warningbox = QtGui.QMessageBox(2, 'Warning', 'Cannot find space {0} at file {1}; locate proper space'.format(rodkey, spacename), buttons=QtGui.QMessageBox.Open)
warningbox.exec_()
- spacename = str(QtGui.QFileDialog.getOpenFileName(caption = 'Open space {0}'.format(rodkey), directory = '.', filter = '*.hdf5'))
+ spacename = str(QtGui.QFileDialog.getOpenFileName(caption='Open space {0}'.format(rodkey), directory='.', filter='*.hdf5'))
if not spacename:
raise IOError('Select proper input')
db[rodkey].attrs['filename'] = spacename
- self.axdict[rodkey] = binoculars.space.Axes.fromfile(spacename)
+ self.axdict[rodkey] = binoculars.space.Axes.fromfile(spacename)
def create_rod(self, rodkey, spacename):
- with h5py.File(self.filename,'a') as db:
+ with h5py.File(self.filename, 'a') as db:
if rodkey not in db.keys():
db.create_group(rodkey)
- db[rodkey].attrs['filename'] = spacename
+ db[rodkey].attrs['filename'] = spacename
self.axdict[rodkey] = binoculars.space.Axes.fromfile(spacename)
def delete_rod(self, rodkey):
- with h5py.File(self.filename,'a') as db:
+ with h5py.File(self.filename, 'a') as db:
del db[rodkey]
def rods(self):
- with h5py.File(self.filename,'a') as db:
+ with h5py.File(self.filename, 'a') as db:
rods = db.keys()
return rods
def copy(self, oldkey, newkey):
- with h5py.File(self.filename,'a') as db:
+ with h5py.File(self.filename, 'a') as db:
if oldkey in db.keys():
- db.copy(db[oldkey], db, name = newkey)
-
+ db.copy(db[oldkey], db, name=newkey)
+
@property
def filelist(self):
filelist = []
- with h5py.File(self.filename,'a') as db:
+ with h5py.File(self.filename, 'a') as db:
for key in db.iterkeys():
filelist.append(db[key].attrs['filename'])
return filelist
def save(self, rodkey, key, value):
- with h5py.File(self.filename,'a') as db:
- db[rodkey].attrs[str(key)] = value
+ with h5py.File(self.filename, 'a') as db:
+ db[rodkey].attrs[str(key)] = value
def load(self, rodkey, key):
- with h5py.File(self.filename,'a') as db:
+ with h5py.File(self.filename, 'a') as db:
if rodkey in db:
if key in db[rodkey].attrs:
- return db[rodkey].attrs[str(key)]
+ return db[rodkey].attrs[str(key)]
else:
return None
+
class RodData(FitData):
def __init__(self, filename, rodkey, axis, resolution):
super(RodData, self).__init__(filename)
@@ -502,7 +505,7 @@ class RodData(FitData):
self.axis = axis
self.resolution = resolution
- with h5py.File(self.filename,'a') as db:
+ with h5py.File(self.filename, 'a') as db:
if rodkey in db:
if self.slicekey not in db[rodkey]:
db[rodkey].create_group(self.slicekey)
@@ -528,10 +531,10 @@ class RodData(FitData):
bins = binoculars.space.get_bins(ax, self.resolution)
return bins, ax, axindex
-
+
def rodlength(self):
bins, ax, axindex = self.get_bins()
- return numpy.alen(bins) - 1
+ return numpy.alen(bins) - 1
def get_index_value(self, index):
return binoculars.space.get_axis_values(self.axdict[self.rodkey], self.axis, self.resolution)[index]
@@ -543,14 +546,14 @@ class RodData(FitData):
k = [slice(None) for i in axes]
k[axindex] = slice(start, stop)
return k
-
+
def space_from_index(self, index):
- with h5py.File(self.filename,'a') as db:
- filename = db[self.rodkey].attrs['filename']
+ with h5py.File(self.filename, 'a') as db:
+ filename = db[self.rodkey].attrs['filename']
return binoculars.space.Space.fromfile(filename, self.get_key(index)).project(self.axis)
- def save_data(self, index, key, data):
- with h5py.File(self.filename,'a') as db:
+ def save_data(self, index, key, data):
+ with h5py.File(self.filename, 'a') as db:
id = '{0}_{1}_data'.format(int(index), key)
mid = '{0}_{1}_mask'.format(int(index), key)
try:
@@ -562,64 +565,63 @@ class RodData(FitData):
db[self.rodkey][self.slicekey].create_dataset(id, data.shape, dtype=data.dtype, compression='gzip').write_direct(data)
db[self.rodkey][self.slicekey].create_dataset(mid, data.shape, dtype=data.mask.dtype, compression='gzip').write_direct(data.mask)
- def load_data(self, index, key):
- with h5py.File(self.filename,'a') as db:
+ def load_data(self, index, key):
+ with h5py.File(self.filename, 'a') as db:
id = '{0}_{1}_data'.format(int(index), key)
mid = '{0}_{1}_mask'.format(int(index), key)
try:
- return numpy.ma.array(db[self.rodkey][self.slicekey][id][...], mask = db[self.rodkey][self.slicekey][mid][...])
+ return numpy.ma.array(db[self.rodkey][self.slicekey][id][...], mask=db[self.rodkey][self.slicekey][mid][...])
except KeyError:
return None
def save_sliceattr(self, index, key, value):
mkey = 'mask{0}'.format(key)
- with h5py.File(self.filename,'a') as db:
+ with h5py.File(self.filename, 'a') as db:
try:
- group = db[self.rodkey][self.slicekey]['attrs']## else it breaks with the old fitaid
- except KeyError:
+ group = db[self.rodkey][self.slicekey]['attrs'] # # else it breaks with the old fitaid
+ except KeyError:
db[rodkey][slicekey].create_group('attrs')
group = db[self.rodkey][self.slicekey]['attrs']
if not key in group:
group.create_dataset(key, (self.rodlength(),))
- group.create_dataset(mkey, (self.rodlength(),), dtype = numpy.bool).write_direct(numpy.ones(self.rodlength(), dtype = numpy.bool))
+ group.create_dataset(mkey, (self.rodlength(),), dtype=numpy.bool).write_direct(numpy.ones(self.rodlength(), dtype=numpy.bool))
group[key][index] = value
group[mkey][index] = 0
-
def load_sliceattr(self, index, key):
mkey = 'mask{0}'.format(key)
- with h5py.File(self.filename,'a') as db:
+ with h5py.File(self.filename, 'a') as db:
try:
group = db[self.rodkey][self.slicekey]['attrs']
- except KeyError:
+ except KeyError:
db[self.rodkey][self.slicekey].create_group('attrs')
group = db[self.rodkey][self.slicekey]['attrs']
if key in group.keys():
- return numpy.ma.array(group[key][index], mask = group[mkey][index])
- else:
+ return numpy.ma.array(group[key][index], mask=group[mkey][index])
+ else:
return None
def all_attrkeys(self):
- with h5py.File(self.filename,'a') as db:
+ with h5py.File(self.filename, 'a') as db:
group = db[self.rodkey][self.slicekey]['attrs']
- return group.keys()
+ return group.keys()
def all_from_key(self, key):
mkey = 'mask{0}'.format(key)
axes = self.axdict[self.rodkey]
- with h5py.File(self.filename,'a') as db:
+ with h5py.File(self.filename, 'a') as db:
group = db[self.rodkey][self.slicekey]['attrs']
if key in group.keys():
- return binoculars.space.get_axis_values(axes, self.axis, self.resolution), numpy.ma.array(group[key], mask = numpy.array(group[mkey]))
-
+ return binoculars.space.get_axis_values(axes, self.axis, self.resolution), numpy.ma.array(group[key], mask=numpy.array(group[mkey]))
+
def load_loc(self, index):
- loc = list()
- with h5py.File(self.filename,'a') as db:
- count = itertools.count()
- key = 'guessloc{0}'.format(count.next())
- while self.load_sliceattr(index, key) != None:
- loc.append(self.load_sliceattr(index, key))
- key = 'guessloc{0}'.format(count.next())
+ loc = list()
+ with h5py.File(self.filename, 'a') as db:
+ count = itertools.count()
+ key = 'guessloc{0}'.format(count.next())
+ while self.load_sliceattr(index, key) != None:
+ loc.append(self.load_sliceattr(index, key))
+ key = 'guessloc{0}'.format(count.next())
if len(loc) > 0:
return loc
else:
@@ -1250,12 +1252,3 @@ if __name__ == "__main__":
sys.exit(app.exec_())
-
-
-
-
-
-
-
-
-
diff --git a/scripts/binoculars-gui b/scripts/binoculars-gui
index df11bb3..ae2cd67 100755
--- a/scripts/binoculars-gui
+++ b/scripts/binoculars-gui
@@ -12,12 +12,14 @@ import Queue
import socket
import SocketServer
import threading
-import matplotlib.figure, matplotlib.image
+import matplotlib.figure
+import matplotlib.image
from mpl_toolkits.mplot3d import Axes3D
from PyQt4 import QtGui, QtCore, Qt
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg, NavigationToolbar2QTAgg
+
def set_src():
import sys
import os.path as osp
@@ -38,26 +40,28 @@ except ImportError:
import binoculars.util
#RangeSlider is taken from https://www.mail-archive.com/pyqt@riverbankcomputing.com/msg22889.html
+
+
class RangeSlider(QtGui.QSlider):
""" A slider for ranges.
-
+
This class provides a dual-slider for ranges, where there is a defined
maximum and minimum, as is a normal slider, but instead of having a
single slider value, there are 2 slider values.
-
- This class emits the same signals as the QSlider base class, with the
+
+ This class emits the same signals as the QSlider base class, with the
exception of valueChanged
"""
def __init__(self, *args):
super(RangeSlider, self).__init__(*args)
-
+
self._low = self.minimum()
self._high = self.maximum()
-
+
self.pressed_control = QtGui.QStyle.SC_None
self.hover_control = QtGui.QStyle.SC_None
self.click_offset = 0
-
+
# 0 for the low, 1 for the high, -1 for both
self.active_slider = 0
@@ -74,14 +78,13 @@ class RangeSlider(QtGui.QSlider):
def setHigh(self, high):
self._high = high
self.update()
-
-
+
def paintEvent(self, event):
# based on http://qt.gitorious.org/qt/qt/blobs/master/src/gui/widgets/qslider.cpp
painter = QtGui.QPainter(self)
- style = QtGui.QApplication.style()
-
+ style = QtGui.QApplication.style()
+
for i, value in enumerate([self._low, self._high]):
opt = QtGui.QStyleOptionSlider()
self.initStyleOption(opt)
@@ -89,7 +92,7 @@ class RangeSlider(QtGui.QSlider):
# Only draw the groove for the first slider so it doesn't get drawn
# on top of the existing ones every time
if i == 0:
- opt.subControls = QtGui.QStyle.SC_SliderHandle#QtGui.QStyle.SC_SliderGroove | QtGui.QStyle.SC_SliderHandle
+ opt.subControls = QtGui.QStyle.SC_SliderHandle # QtGui.QStyle.SC_SliderGroove | QtGui.QStyle.SC_SliderHandle
else:
opt.subControls = QtGui.QStyle.SC_SliderHandle
@@ -103,35 +106,34 @@ class RangeSlider(QtGui.QSlider):
opt.activeSubControls = self.hover_control
opt.sliderPosition = value
- opt.sliderValue = value
+ opt.sliderValue = value
style.drawComplexControl(QtGui.QStyle.CC_Slider, opt, painter, self)
-
-
+
def mousePressEvent(self, event):
event.accept()
-
+
style = QtGui.QApplication.style()
button = event.button()
-
- # In a normal slider control, when the user clicks on a point in the
+
+ # In a normal slider control, when the user clicks on a point in the
# slider's total range, but not on the slider part of the control the
# control would jump the slider value to where the user clicked.
# For this control, clicks which are not direct hits will slide both
# slider parts
-
+
if button:
opt = QtGui.QStyleOptionSlider()
self.initStyleOption(opt)
self.active_slider = -1
-
+
for i, value in enumerate([self._low, self._high]):
- opt.sliderPosition = value
+ opt.sliderPosition = value
hit = style.hitTestComplexControl(style.CC_Slider, opt, event.pos(), self)
if hit == style.SC_SliderHandle:
self.active_slider = i
self.pressed_control = hit
-
+
self.triggerAction(self.SliderMove)
self.setRepeatAction(self.SliderNoAction)
self.setSliderDown(True)
@@ -145,21 +147,19 @@ class RangeSlider(QtGui.QSlider):
else:
event.ignore()
-
def mouseReleaseEvent(self, event):
self.emit(QtCore.SIGNAL('sliderReleased()'))
-
def mouseMoveEvent(self, event):
if self.pressed_control != QtGui.QStyle.SC_SliderHandle:
event.ignore()
return
-
+
event.accept()
new_pos = self.__pixelPosToRangeValue(self.__pick(event.pos()))
opt = QtGui.QStyleOptionSlider()
self.initStyleOption(opt)
-
+
if self.active_slider < 0:
offset = new_pos - self.click_offset
self._high += offset
@@ -171,7 +171,7 @@ class RangeSlider(QtGui.QSlider):
if self._high > self.maximum():
diff = self.maximum() - self._high
self._low += diff
- self._high += diff
+ self._high += diff
elif self.active_slider == 0:
if new_pos >= self._high:
new_pos = self._high - 1
@@ -186,22 +186,21 @@ class RangeSlider(QtGui.QSlider):
self.update()
self.emit(QtCore.SIGNAL('sliderMoved(int)'), new_pos)
-
+
def __pick(self, pt):
if self.orientation() == QtCore.Qt.Horizontal:
return pt.x()
else:
return pt.y()
-
-
+
def __pixelPosToRangeValue(self, pos):
opt = QtGui.QStyleOptionSlider()
self.initStyleOption(opt)
style = QtGui.QApplication.style()
-
+
gr = style.subControlRect(style.CC_Slider, opt, style.SC_SliderGroove, self)
sr = style.subControlRect(style.CC_Slider, opt, style.SC_SliderHandle, self)
-
+
if self.orientation() == QtCore.Qt.Horizontal:
slider_length = sr.width()
slider_min = gr.x()
@@ -210,60 +209,59 @@ class RangeSlider(QtGui.QSlider):
slider_length = sr.height()
slider_min = gr.y()
slider_max = gr.bottom() - slider_length + 1
-
+
return style.sliderValueFromPosition(self.minimum(), self.maximum(),
pos-slider_min, slider_max-slider_min,
opt.upsideDown)
-
class Window(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
- newproject = QtGui.QAction("New project", self)
+ newproject = QtGui.QAction("New project", self)
newproject.triggered.connect(self.newproject)
- loadproject = QtGui.QAction("Open project", self)
+ loadproject = QtGui.QAction("Open project", self)
loadproject.triggered.connect(self.loadproject)
- saveproject = QtGui.QAction("Save project", self)
+ saveproject = QtGui.QAction("Save project", self)
saveproject.triggered.connect(self.saveproject)
- addspace = QtGui.QAction("Import space", self)
+ addspace = QtGui.QAction("Import space", self)
addspace.triggered.connect(self.add_to_project)
- savespace = QtGui.QAction("Export space", self)
+ savespace = QtGui.QAction("Export space", self)
savespace.triggered.connect(self.exportspace)
- menu_bar = QtGui.QMenuBar()
- file = menu_bar.addMenu("&File")
- file.addAction(newproject)
- file.addAction(loadproject)
+ menu_bar = QtGui.QMenuBar()
+ file = menu_bar.addMenu("&File")
+ file.addAction(newproject)
+ file.addAction(loadproject)
file.addAction(saveproject)
file.addAction(addspace)
- file.addAction(savespace)
+ file.addAction(savespace)
- merge = QtGui.QAction("Merge", self)
+ merge = QtGui.QAction("Merge", self)
merge.triggered.connect(self.merge)
- subtract = QtGui.QAction("Subtract", self)
+ subtract = QtGui.QAction("Subtract", self)
subtract.triggered.connect(self.subtract)
-
- edit = menu_bar.addMenu("&Edit")
- edit.addAction(merge)
- edit.addAction(subtract)
- start_server = QtGui.QAction("Start server queue", self)
- start_server.triggered.connect(lambda: self.open_server(startq = True))
+ edit = menu_bar.addMenu("&Edit")
+ edit.addAction(merge)
+ edit.addAction(subtract)
+
+ start_server = QtGui.QAction("Start server queue", self)
+ start_server.triggered.connect(lambda: self.open_server(startq=True))
- stop_server = QtGui.QAction("Stop server queue", self)
+ stop_server = QtGui.QAction("Stop server queue", self)
stop_server.triggered.connect(self.kill_server)
- recieve = QtGui.QAction("Open for spaces", self)
- recieve.triggered.connect(lambda: self.open_server(startq = False))
+ recieve = QtGui.QAction("Open for spaces", self)
+ recieve.triggered.connect(lambda: self.open_server(startq=False))
- serve = menu_bar.addMenu("&Serve")
+ serve = menu_bar.addMenu("&Serve")
serve.addAction(start_server)
serve.addAction(stop_server)
serve.addAction(recieve)
@@ -289,8 +287,8 @@ class Window(QtGui.QMainWindow):
widget = ProjectWidget([], parent=self)
self.tab_widget.addTab(widget, 'New Project')
self.tab_widget.setCurrentWidget(widget)
-
- def loadproject(self, filename = None):
+
+ def loadproject(self, filename=None):
if not filename:
dialog = QtGui.QFileDialog(self, "Load project");
dialog.setFilter('binoculars project file (*.proj)');
@@ -303,13 +301,13 @@ class Window(QtGui.QMainWindow):
return
for name in fname:
try:
- widget = ProjectWidget.fromfile(str(name), parent = self)
+ widget = ProjectWidget.fromfile(str(name), parent=self)
self.tab_widget.addTab(widget, short_filename(str(name)))
self.tab_widget.setCurrentWidget(widget)
except Exception as e:
QtGui.QMessageBox.critical(self, 'Load project', 'Unable to load project from {}: {}'.format(fname, e))
else:
- widget = ProjectWidget.fromfile(filename, parent = self)
+ widget = ProjectWidget.fromfile(filename, parent=self)
self.tab_widget.addTab(widget, short_filename(filename))
def saveproject(self):
@@ -405,7 +403,7 @@ class Window(QtGui.QMainWindow):
except Exception as e:
QtGui.QMessageBox.critical(self, 'Import spaces', 'Unable to import space {}: {}'.format(fname, e))
- def open_server(self, startq = True):
+ def open_server(self, startq=True):
if len(self.threads) != 0:
print 'Server already running'
else:
@@ -419,7 +417,7 @@ class Window(QtGui.QMainWindow):
if startq:
cmd = ['python', os.path.join(os.path.dirname(__file__), 'binoculars-server.py'), str(self.ip), str(self.port)]
- self.pro = subprocess.Popen(cmd, stdin=None, stdout=None, stderr=None, preexec_fn=os.setsid)
+ self.pro = subprocess.Popen(cmd, stdin=None, stdout=None, stderr=None, preexec_fn=os.setsid)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
@@ -443,7 +441,7 @@ class Window(QtGui.QMainWindow):
self.pro = None
def kill_subprocess(self):
- if not self.pro == None:
+ if not self.pro == None:
os.killpg(self.pro.pid, signal.SIGTERM)
def update(self):
@@ -466,13 +464,15 @@ class Window(QtGui.QMainWindow):
if serverwidget.auto_update.isChecked():
serverwidget.limitwidget.refresh()
+
class UpdateThread(QtCore.QThread):
fq = Queue.Queue()
data_found = QtCore.pyqtSignal(object)
+
def run(self):
delay = binoculars.util.loop_delayer(1)
jobs = []
- labels = []
+ labels = []
while 1:
if not self.q.empty():
command, space = self.q.get()
@@ -487,9 +487,11 @@ class UpdateThread(QtCore.QThread):
else:
next(delay)
+
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
+
class SpaceTCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
command, config, metadata, axes, photons, contributions = binoculars.util.socket_recieve(self)
@@ -502,6 +504,7 @@ class SpaceTCPHandler(SocketServer.BaseRequestHandler):
space.contributions = contributions
self.server.q.put((command, space))
+
class HiddenToolbar(NavigationToolbar2QTAgg):
def __init__(self, show_coords, update_sliders, canvas):
NavigationToolbar2QTAgg.__init__(self, canvas, None)
@@ -526,15 +529,14 @@ class HiddenToolbar(NavigationToolbar2QTAgg):
self.update_sliders(self.inaxes)
-
class ProjectWidget(QtGui.QWidget):
- def __init__(self, filelist, key = None, projection = None, parent = None):
+ def __init__(self, filelist, key=None, projection=None, parent=None):
super(ProjectWidget, self).__init__(parent)
self.parent = parent
self.figure = matplotlib.figure.Figure()
self.canvas = FigureCanvasQTAgg(self.figure)
- self.toolbar = HiddenToolbar(self.show_coords,self.update_sliders, self.canvas)
+ self.toolbar = HiddenToolbar(self.show_coords, self.update_sliders, self.canvas)
self.lin = QtGui.QRadioButton('lin', self)
self.lin.setChecked(False)
@@ -596,22 +598,22 @@ class ProjectWidget(QtGui.QWidget):
QtCore.QObject.connect(self.limitwidget, QtCore.SIGNAL("keydict"), self.update_key)
QtCore.QObject.connect(self.limitwidget, QtCore.SIGNAL("rangechange"), self.update_figure_range)
QtCore.QObject.connect(self.table, QtCore.SIGNAL('plotaxesChanged'), self.plotaxes_changed)
-
+
self.initUI()
self.table.select()
def initUI(self):
self.control_widget = QtGui.QWidget(self)
- hbox = QtGui.QHBoxLayout()
+ hbox = QtGui.QHBoxLayout()
left = QtGui.QVBoxLayout()
- pushbox = QtGui.QHBoxLayout()
+ pushbox = QtGui.QHBoxLayout()
pushbox.addWidget(self.button_save)
pushbox.addWidget(self.button_refresh)
left.addLayout(pushbox)
- radiobox = QtGui.QHBoxLayout()
+ radiobox = QtGui.QHBoxLayout()
self.group = QtGui.QButtonGroup(self)
for label in ['stack', 'grid']:
rb = QtGui.QRadioButton(label, self.control_widget)
@@ -623,7 +625,7 @@ class ProjectWidget(QtGui.QWidget):
radiobox.addWidget(self.log)
radiobox.addWidget(self.loglog)
- datarangebox = QtGui.QHBoxLayout()
+ datarangebox = QtGui.QHBoxLayout()
datarangebox.addWidget(self.samerange)
datarangebox.addWidget(self.legend)
datarangebox.addWidget(self.threed)
@@ -643,31 +645,29 @@ class ProjectWidget(QtGui.QWidget):
splitter.addWidget(self.control_widget)
splitter.addWidget(self.canvas)
- hbox.addWidget(splitter)
+ hbox.addWidget(splitter)
self.setLayout(hbox)
-
def show_coords(self, event):
plotaxes = event.inaxes
if hasattr(plotaxes, 'space'):
if plotaxes.space.dimension == 2:
labels = numpy.array([plotaxes.get_xlabel(), plotaxes.get_ylabel()])
order = [plotaxes.space.axes.index(label) for label in labels]
- labels = labels[order]
+ labels = labels[order]
coords = numpy.array([event.xdata, event.ydata])[order]
try:
rounded_coords = [ax[ax.get_index(coord)] for ax, coord in zip(plotaxes.space.axes, coords)]
intensity = '{0:.2e}'.format(plotaxes.space[list(coords)])
- self.parent.statusbar.showMessage('{0} = {1}, {2} = {3}, Intensity = {4}'.format(labels[0], rounded_coords[0] ,labels[1], rounded_coords[1], intensity))
+ self.parent.statusbar.showMessage('{0} = {1}, {2} = {3}, Intensity = {4}'.format(labels[0], rounded_coords[0], labels[1], rounded_coords[1], intensity))
except ValueError:
self.parent.statusbar.showMessage('out of range')
-
elif plotaxes.space.dimension == 1:
xaxis = plotaxes.space.axes[plotaxes.space.axes.index(plotaxes.get_xlabel())]
if event.xdata in xaxis:
- xcoord = xaxis[xaxis.get_index(event.xdata)]
- intensity = '{0:.2e}'.format(event.ydata)
- self.parent.statusbar.showMessage('{0} = {1}, Intensity = {2}'.format(xaxis.label, xcoord, intensity))
+ xcoord = xaxis[xaxis.get_index(event.xdata)]
+ intensity = '{0:.2e}'.format(event.ydata)
+ self.parent.statusbar.showMessage('{0} = {1}, Intensity = {2}'.format(xaxis.label, xcoord, intensity))
def update_sliders(self, plotaxes):
if not plotaxes == None:
@@ -675,7 +675,7 @@ class ProjectWidget(QtGui.QWidget):
if hasattr(plotaxes, 'space'):
if space.dimension == 2:
labels = numpy.array([plotaxes.get_xlabel(), plotaxes.get_ylabel()])
- limits = list(lim for lim in [plotaxes.get_xlim(), plotaxes.get_ylim()])
+ limits = list(lim for lim in [plotaxes.get_xlim(), plotaxes.get_ylim()])
elif space.dimension == 1:
labels = [plotaxes.get_xlabel()]
limits = [plotaxes.get_xlim()]
@@ -683,7 +683,7 @@ class ProjectWidget(QtGui.QWidget):
for key, value in zip(labels, limits):
keydict[key] = value
self.limitwidget.update_from_zoom(keydict)
-
+
def selectionerror(self, message):
self.limitwidget.setDisabled(True)
self.errormessage(message)
@@ -755,12 +755,12 @@ class ProjectWidget(QtGui.QWidget):
plotoption = None
if self.group.checkedButton():
plotoption = self.group.checkedButton().text()
-
+
spaces = []
for i, filename in enumerate(self.table.selection):
axes = self.table.getax(filename)
- rkey = axes.restricted_key(self.key)
+ rkey = axes.restricted_key(self.key)
if rkey == None:
space = self.table.getspace(filename)
else:
@@ -791,7 +791,7 @@ class ProjectWidget(QtGui.QWidget):
else:
self.toolbar.threed = True
- for i,space in enumerate(spaces):
+ for i, space in enumerate(spaces):
filename = self.table.selection[i]
basename = os.path.splitext(os.path.basename(filename))[0]
if plotcount > 1:
@@ -804,7 +804,7 @@ class ProjectWidget(QtGui.QWidget):
sys.stderr.write('error: cannot display 4 or higher dimensional data, use --project or --slice to decrease dimensionality\n')
sys.exit(1)
else:
- self.ax = self.figure.add_subplot(111)
+ self.ax = self.figure.add_subplot(111)
if plotoption == 'grid':
if dimension == 1 or dimension == 2:
@@ -817,13 +817,13 @@ class ProjectWidget(QtGui.QWidget):
space = space.reorder(list(ax.label for ax in space.axes)[::-1])
self.ax.space = space
- im = binoculars.plot.plot(space, self.figure, self.ax, log = log, loglog = loglog, label = basename, norm = norm[i])
+ im = binoculars.plot.plot(space, self.figure, self.ax, log=log, loglog=loglog, label=basename, norm=norm[i])
self.figure_images.append(im)
-
+
if dimension == 1 and self.legend.checkState():
self.ax.legend()
-
+
self.update_figure_range(self.key_to_str(self.key))
self.canvas.draw()
@@ -835,7 +835,7 @@ class ProjectWidget(QtGui.QWidget):
map(self.table.remove, self.table.selection)
self.table.addspace(filename, True)
except Exception as e:
- QtGui.QMessageBox.critical(self, 'Merge', 'Unable to merge the meshes. {}'.format(e))
+ QtGui.QMessageBox.critical(self, 'Merge', 'Unable to merge the meshes. {}'.format(e))
def subtractspace(self, filename):
try:
@@ -848,7 +848,7 @@ class ProjectWidget(QtGui.QWidget):
self.table.remove(selected_filename)
self.table.addspace(newfilename, True)
except Exception as e:
- QtGui.QMessageBox.critical(self, 'Subtract', 'Unable to subtract the meshes. {}'.format(e))
+ QtGui.QMessageBox.critical(self, 'Subtract', 'Unable to subtract the meshes. {}'.format(e))
def errormessage(self, message):
self.figure.clear()
@@ -869,9 +869,9 @@ class ProjectWidget(QtGui.QWidget):
ax.set_ylim(key[yindex][0], key[yindex][1])
self.canvas.draw()
- def update_colorbar(self,value):
+ def update_colorbar(self, value):
normlist = self.get_normlist()
- for im,norm in zip(self.figure_images, normlist):
+ for im, norm in zip(self.figure_images, normlist):
im.set_norm(norm)
self.canvas.draw()
@@ -883,7 +883,7 @@ class ProjectWidget(QtGui.QWidget):
def str_to_key(s):
return tuple(slice(float(key[0]), float(key[1])) for key in s)
- def tofile(self, filename = None):
+ def tofile(self, filename=None):
dict = {}
dict['filelist'] = self.table.filelist
dict['key'] = self.key_to_str(self.key)
@@ -896,9 +896,9 @@ class ProjectWidget(QtGui.QWidget):
json.dump(dict, fp)
@classmethod
- def fromfile(cls, filename = None, parent = None):
+ def fromfile(cls, filename=None, parent=None):
if filename == None:
- filename = str(QtGui.QFileDialog.getOpenFileName(self, 'Open Project', '.', '*.proj'))
+ filename = str(QtGui.QFileDialog.getOpenFileName(self, 'Open Project', '.', '*.proj'))
try:
with open(filename, 'r') as fp:
dict = json.load(fp)
@@ -908,18 +908,18 @@ class ProjectWidget(QtGui.QWidget):
newlist = []
for fn in dict['filelist']:
if not os.path.exists(fn):
- warningbox = QtGui.QMessageBox(2, 'Warning', 'Cannot find space at path {0}; locate proper space'.format(fn), buttons = QtGui.QMessageBox.Open)
+ warningbox = QtGui.QMessageBox(2, 'Warning', 'Cannot find space at path {0}; locate proper space'.format(fn), buttons=QtGui.QMessageBox.Open)
warningbox.exec_()
- newname = str(QtGui.QFileDialog.getOpenFileName(caption = 'Open space {0}'.format(fn), directory = '.', filter = '*.hdf5'))
+ newname = str(QtGui.QFileDialog.getOpenFileName(caption='Open space {0}'.format(fn), directory='.', filter='*.hdf5'))
newlist.append(newname)
else:
- newlist.append(fn)
+ newlist.append(fn)
- widget = cls(newlist, cls.str_to_key(dict['key']), dict['projection'], parent = parent)
+ widget = cls(newlist, cls.str_to_key(dict['key']), dict['projection'], parent=parent)
return widget
-
- def addspace(self,filename = None, add = False):
+
+ def addspace(self, filename=None, add=False):
if filename == None:
filename = str(QtGui.QFileDialog.getOpenFileName(self, 'Open Project', '.', '*.hdf5'))
self.table.add_space(filename, add)
@@ -938,14 +938,14 @@ class ProjectWidget(QtGui.QWidget):
try:
self.figure.savefig(str(fname))
except Exception as e:
- QtGui.QMessageBox.critical(self, 'Save image', 'Unable to save image to {}: {}'.format(fname, e))
+ QtGui.QMessageBox.critical(self, 'Save image', 'Unable to save image to {}: {}'.format(fname, e))
def space_to_file(self, fname):
ext = os.path.splitext(fname)[-1]
for i, filename in enumerate(self.table.selection):
axes = self.table.getax(filename)
- space = self.table.getspace(filename, key = axes.restricted_key(self.key))
+ space = self.table.getspace(filename, key=axes.restricted_key(self.key))
projection = [ax for ax in self.projection if ax in space.axes]
if projection:
space = space.project(*projection)
@@ -964,24 +964,26 @@ class ProjectWidget(QtGui.QWidget):
elif ext == '.hdf5':
space.tofile(outfile)
self.parent.statusbar.showMessage('saved at {0}'.format(outfile))
-
+
else:
self.parent.statusbar.showMessage('unknown extension {0}, unable to save!\n'.format(ext))
+
def short_filename(filename):
return filename.split('/')[-1].split('.')[0]
+
class SpaceContainer(QtGui.QTableWidgetItem):
def __init__(self, label, space=None):
super(SpaceContainer, self).__init__(short_filename(label))
self.label = label
self.space = space
- def get_space(self, key = None):
+ def get_space(self, key=None):
if self.space == None:
- return binoculars.space.Space.fromfile(self.label, key = key)
+ return binoculars.space.Space.fromfile(self.label, key=key)
else:
- if key == None:
+ if key == None:
key = Ellipsis
return self.space[key]
@@ -1185,7 +1187,7 @@ class LimitWidget(QtGui.QWidget):
self.update_lines()
self.send_signal()
- def update_lines(self, value = 0 ):
+ def update_lines(self, value=0):
for index, slider in enumerate(self.sliders):
self.leftindicator[index].setText(str(self.axes[index][slider.low()]))
self.rightindicator[index].setText(str(self.axes[index][slider.high()]))
@@ -1218,7 +1220,7 @@ class LimitWidget(QtGui.QWidget):
left.setText(str(ax[slider.low()]))
def update_sliders_right(self):
- for ax, left, right , slider in zip(self.axes, self.leftindicator, self.rightindicator, self.sliders):
+ for ax, left, right, slider in zip(self.axes, self.leftindicator, self.rightindicator, self.sliders):
leftvalue = ax.get_index(float(str(left.text())))
try:
rightvalue = ax.get_index(float(str(right.text())))
@@ -1238,9 +1240,9 @@ class LimitWidget(QtGui.QWidget):
def init_checkbox(self):
while numpy.alen(self.state) - self.state.sum() > 2:
- index = numpy.where(self.state == False)[-1]
- self.state[-1] = True
- for box, state in zip(self.checkbox,self.state):
+ index = numpy.where(self.state == False)[-1]
+ self.state[-1] = True
+ for box, state in zip(self.checkbox, self.state):
box.setChecked(state)
def axes_update(self, axes):
@@ -1266,7 +1268,7 @@ class LimitWidget(QtGui.QWidget):
self.update_sliders_right()
self.send_signal()
-
+
def update_from_zoom(self, keydict):
for key in keydict:
index = self.axes.index(key)
@@ -1276,6 +1278,7 @@ class LimitWidget(QtGui.QWidget):
self.update_sliders_right()
self.send_signal()
+
def is_empty(key):
for k in key:
if isinstance(k, slice):
@@ -1294,9 +1297,3 @@ if __name__ == '__main__':
main.show()
sys.exit(app.exec_())
-
-
-
-
-
-
diff --git a/scripts/binoculars-processgui b/scripts/binoculars-processgui
index 5895995..c3a8f1a 100755
--- a/scripts/binoculars-processgui
+++ b/scripts/binoculars-processgui
@@ -28,6 +28,8 @@ except ImportError:
import binoculars.util
#--------------------------------------------CREATE MAIN WINDOW----------------------------------------
+
+
class Window(QtGui.QMainWindow):
def __init__(self):
@@ -68,9 +70,9 @@ class Window(QtGui.QMainWindow):
#we configue the main windows
palette = QtGui.QPalette()
- palette.setColor(QtGui.QPalette.Background,QtCore.Qt.gray)
+ palette.setColor(QtGui.QPalette.Background, QtCore.Qt.gray)
self.setPalette(palette)
- self.setGeometry(50, 100,700,700)
+ self.setGeometry(50, 100, 700, 700)
self.setWindowTitle('Binoculars processgui')
self.show()
@@ -81,11 +83,11 @@ class Window(QtGui.QMainWindow):
self.ListCommand.setColumnWidth(0, 80)
self.ListCommand.setColumnWidth(1, 80)
self.ListCommand.setRowCount(0)
- self.buttonDelete = QtGui.QPushButton('Delete',self)
+ self.buttonDelete = QtGui.QPushButton('Delete', self)
self.connect(self.buttonDelete, QtCore.SIGNAL("clicked()"), self.removeConf)
- self.process = QtGui.QPushButton('run',self)
+ self.process = QtGui.QPushButton('run', self)
self.process.setStyleSheet("background-color: darkred")
- self.connect(self.process, QtCore.SIGNAL("clicked()"),self.run)
+ self.connect(self.process, QtCore.SIGNAL("clicked()"), self.run)
self.wid = QtGui.QWidget()
self.CommandLayout = QtGui.QVBoxLayout()
@@ -95,54 +97,55 @@ class Window(QtGui.QMainWindow):
self.wid.setLayout(self.CommandLayout)
self.Dock = QtGui.QDockWidget()
- self.Dock.setAllowedAreas( QtCore.Qt.LeftDockWidgetArea)
+ self.Dock.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea)
self.Dock.setFeatures(QtGui.QDockWidget.NoDockWidgetFeatures)
self.Dock.setWidget(self.wid)
self.Dock.setMaximumWidth(200)
self.Dock.setMinimumWidth(200)
- self.addDockWidget(QtCore.Qt.DockWidgetArea(1),self.Dock)
-
+ self.addDockWidget(QtCore.Qt.DockWidgetArea(1), self.Dock)
+
def removeConf(self):
- self.ListCommand.removeRow(self.ListCommand.currentRow())
-
- def Add_To_Liste(self,(command, cfg)):
+ self.ListCommand.removeRow(self.ListCommand.currentRow())
+
+ def Add_To_Liste(self, (command, cfg)):
row = self.ListCommand.rowCount()
index = self.tab_widget.currentIndex()
filename = self.tab_widget.tabText(index)
self.ListCommand.insertRow(self.ListCommand.rowCount())
- dic = {filename:cfg}
+ dic = {filename: cfg}
self.item1 = QtGui.QTableWidgetItem(str(command))
self.item1.command = command
self.item2 = QtGui.QTableWidgetItem(str(filename))
self.item2.cfg = dic[filename]
self.ListCommand.setItem(row, 0, self.item1)
self.ListCommand.setItem(row, 1, self.item2)
-
- #We run the script and create a hdf5 file
+
+ #We run the script and create a hdf5 file
def run(self):
maximum = self.ListCommand.rowCount()
pd = QtGui.QProgressDialog('running', 'Cancel', 0, maximum, self)
pd.setWindowModality(QtCore.Qt.WindowModal)
pd.show()
+
def progress(cfg, command):
- if pd.wasCanceled():
+ if pd.wasCanceled():
raise KeyboardInterrupt
QtGui.QApplication.processEvents()
return binoculars.main.Main.from_object(cfg, command)
try:
for index in range(self.ListCommand.rowCount()):
pd.setValue(index)
- cfg = self.ListCommand.item(index,1).cfg
- command = self.ListCommand.item(index,0).command
+ cfg = self.ListCommand.item(index, 1).cfg
+ command = self.ListCommand.item(index, 0).command
print cfg
progress(cfg, command)
self.ListCommand.clear()
self.ListCommand.setRowCount(0)
- except BaseException, e:
+ except BaseException, e:
#cfg = self.ListCommand.item(index,1).cfg
#print cfg
- QtGui.QMessageBox.about(self,"Error","There was an error processing one of the scans: {0}".format(e))
-
+ QtGui.QMessageBox.about(self, "Error", "There was an error processing one of the scans: {0}".format(e))
+
finally:
pd.close()
@@ -151,31 +154,33 @@ class Window(QtGui.QMainWindow):
filename = QtGui.QFileDialog.getOpenFileName(self, 'Open File', '')
confwidget = Conf_Tab(self)
confwidget.read_data(str(filename))
- newIndex = self.tab_widget.addTab(confwidget, os.path.basename(str(filename)))
- QtCore.QObject.connect(confwidget, QtCore.SIGNAL("command"),self.Add_To_Liste)
+ newIndex = self.tab_widget.addTab(confwidget, os.path.basename(str(filename)))
+ QtCore.QObject.connect(confwidget, QtCore.SIGNAL("command"), self.Add_To_Liste)
self.tab_widget.setCurrentIndex(newIndex)
#we call the save function
def Save(self):
filename = QtGui.QFileDialog().getSaveFileName(self, 'Save', '', '*.txt')
- widget = self.tab_widget.currentWidget()
- widget.save(filename)
+ widget = self.tab_widget.currentWidget()
+ widget.save(filename)
- #we call the new tab conf
+ #we call the new tab conf
def New_Config(self):
- widget = Conf_Tab(self)
- self.tab_widget.addTab(widget,'New configfile')
- QtCore.QObject.connect(widget, QtCore.SIGNAL("command"),self.Add_To_Liste)
+ widget = Conf_Tab(self)
+ self.tab_widget.addTab(widget, 'New configfile')
+ QtCore.QObject.connect(widget, QtCore.SIGNAL("command"), self.Add_To_Liste)
#----------------------------------------------------------------------------------------------------
#-----------------------------------------CREATE TABLE-----------------------------------------------
+
+
class Table(QtGui.QWidget):
- def __init__(self, label, parent = None):
+ def __init__(self, label, parent=None):
super(Table, self).__init__()
-
+
# create a QTableWidget
self.table = QtGui.QTableWidget(1, 2, self)
- self.table.setHorizontalHeaderLabels(['Parameter', 'Value','Comment'])
+ self.table.setHorizontalHeaderLabels(['Parameter', 'Value', 'Comment'])
self.table.horizontalHeader().setStretchLastSection(True)
self.table.verticalHeader().setVisible(False)
self.table.setTextElideMode(QtCore.Qt.ElideLeft)
@@ -183,12 +188,12 @@ class Table(QtGui.QWidget):
self.combobox = QtGui.QComboBox()
#add items
self.cell = QtGui.QTableWidgetItem(QtCore.QString("type"))
- self.table.setItem(0, 0,self.cell)
+ self.table.setItem(0, 0, self.cell)
self.table.setCellWidget(0, 1, self.combobox)
- #we create pushbuttons and we call the method when we clic on
+ #we create pushbuttons and we call the method when we clic on
self.btn_add_row = QtGui.QPushButton('+', self)
self.connect(self.btn_add_row, QtCore.SIGNAL('clicked()'), self.add_row)
- self.buttonRemove = QtGui.QPushButton('-',self)
+ self.buttonRemove = QtGui.QPushButton('-', self)
self.connect(self.buttonRemove, QtCore.SIGNAL("clicked()"), self.remove)
#the dispositon of the table and the butttons
@@ -205,32 +210,32 @@ class Table(QtGui.QWidget):
def add_row(self):
self.table.insertRow(self.table.rowCount())
-
+
def remove(self):
- self.table.removeRow(self.table.currentRow())
+ self.table.removeRow(self.table.currentRow())
def get_keys(self):
- return list(str(self.table.item(index,0).text()) for index in range(self.table.rowCount()))
+ return list(str(self.table.item(index, 0).text()) for index in range(self.table.rowCount()))
#Here we take all values from tables
def getParam(self):
for index in range(self.table.rowCount()):
- if not self.table.item == None:
- key = str(self.table.item(index,0).text())
- comment = str(self.table.item(index,0).toolTip())
+ if not self.table.item == None:
+ key = str(self.table.item(index, 0).text())
+ comment = str(self.table.item(index, 0).toolTip())
if index == 0:
yield key, str(self.table.cellWidget(index, 1).currentText()), comment
- elif self.table.item(index,1):
- if len(str(self.table.item(index,1).text())) != 0 and self.table.item(index,0).textColor() == QtGui.QColor('black'):
+ elif self.table.item(index, 1):
+ if len(str(self.table.item(index, 1).text())) != 0 and self.table.item(index, 0).textColor() == QtGui.QColor('black'):
yield key, str(self.table.item(index, 1).text()), comment
- #Here we put all values in tables
+ #Here we put all values in tables
def addData(self, cfg):
for item in cfg:
if item == 'type':
- box = self.table.cellWidget(0,1)
- value = cfg[item].split(':')
- if len(value) > 1 :
+ box = self.table.cellWidget(0, 1)
+ value = cfg[item].split(':')
+ if len(value) > 1:
box.setCurrentIndex(box.findText(value[1], QtCore.Qt.MatchFixedString))
else:
box.setCurrentIndex(box.findText(cfg[item], QtCore.Qt.MatchFixedString))
@@ -240,15 +245,14 @@ class Table(QtGui.QWidget):
for col in range(self.table.columnCount()):
if col == 0:
newitem = QtGui.QTableWidgetItem(item)
- self.table.setItem(row -1, col, newitem)
+ self.table.setItem(row - 1, col, newitem)
if col == 1:
newitem2 = QtGui.QTableWidgetItem(cfg[item])
- self.table.setItem(row -1, col, newitem2)
+ self.table.setItem(row - 1, col, newitem2)
else:
index = self.get_keys().index(item)
- self.table.item(index, 1).setText(cfg[item])
+ self.table.item(index, 1).setText(cfg[item])
-
def addDataConf(self, options):
keys = self.get_keys()
newconfigs = dict((option[0], '') for option in options if option[0] not in keys)
@@ -268,13 +272,15 @@ class Table(QtGui.QWidget):
def add_to_combo(self, items):
self.combobox.clear()
self.combobox.addItems(items)
-
+
#----------------------------------------------------------------------------------------------------
#-----------------------------------------CREATE CONFIG----------------------------------------------
+
+
class Conf_Tab(QtGui.QWidget):
- def __init__(self, parent = None):
+ def __init__(self, parent=None):
- super(Conf_Tab,self).__init__()
+ super(Conf_Tab, self).__init__()
#we create 3 tables
self.Dis = Table(QtGui.QLabel('<strong>Dispatcher :</strong>'))
self.Inp = Table(QtGui.QLabel('<strong>Input :</strong>'))
@@ -289,14 +295,14 @@ class Conf_Tab(QtGui.QWidget):
self.scan.setToolTip('scan selection example: 820 824')
vbox = QtGui.QVBoxLayout()
- hbox = QtGui.QHBoxLayout()
+ hbox = QtGui.QHBoxLayout()
splitter = QtGui.QSplitter(QtCore.Qt.Horizontal)
splitter.addWidget(self.Dis)
splitter.addWidget(self.Inp)
splitter.addWidget(self.Pro)
- hbox.addWidget(splitter)
+ hbox.addWidget(splitter)
- commandbox = QtGui.QHBoxLayout()
+ commandbox = QtGui.QHBoxLayout()
commandbox.addWidget(self.add)
commandbox.addWidget(self.scan)
@@ -312,25 +318,25 @@ class Conf_Tab(QtGui.QWidget):
#Layout.addWidget(self.select,0,0)
#Layout.addWidget(self.Dis,2,1)
#Layout.addWidget(self.Inp,2,0)
- #Layout.addWidget(self.Pro,2,2)
+ #Layout.addWidget(self.Pro,2,2)
#Layout.addWidget(self.add,3,0)
#Layout.addWidget(self.scan,3,1)
self.setLayout(vbox)
-
- #Here we call all methods for selected an ellement on differents combobox
+
+ #Here we call all methods for selected an ellement on differents combobox
self.Dis.add_to_combo(QtCore.QStringList(binoculars.util.get_dispatchers()))
self.select.activated['QString'].connect(self.DataCombo)
self.Inp.combobox.activated.connect(self.DataTableInp)
self.Pro.combobox.activated.connect(self.DataTableInpPro)
- self.Dis.combobox.activated.connect(self.DataTableInpDis)
+ self.Dis.combobox.activated.connect(self.DataTableInpDis)
- def DataCombo(self,text):
+ def DataCombo(self, text):
self.Inp.add_to_combo(QtCore.QStringList(binoculars.util.get_inputs(str(text))))
self.Pro.add_to_combo(QtCore.QStringList(binoculars.util.get_projections(str(text))))
self.DataTableInp()
self.DataTableInpPro()
self.DataTableInpDis()
-
+
def DataTableInp(self):
backend = str(self.select.currentText())
inp = binoculars.util.get_input_configkeys(backend, str(self.Inp.combobox.currentText()))
@@ -347,7 +353,7 @@ class Conf_Tab(QtGui.QWidget):
self.Dis.addDataConf(disp)
#The save method we take all ellements on tables and we put them in this format {0} = {1} #{2}
- def save(self, filename):
+ def save(self, filename):
with open(filename, 'w') as fp:
fp.write('[dispatcher]\n')
# cycles over the iterator object
@@ -356,12 +362,12 @@ class Conf_Tab(QtGui.QWidget):
fp.write('[input]\n')
for key, value, comment in self.Inp.getParam():
if key == 'type':
- value = '{0}:{1}'.format(self.select.currentText(),value)
+ value = '{0}:{1}'.format(self.select.currentText(), value)
fp.write('{0} = {1} #{2}\n'.format(key, value, comment))
fp.write('[projection]\n')
for key, value, comment in self.Pro.getParam():
if key == 'type':
- value = '{0}:{1}'.format(self.select.currentText(),value)
+ value = '{0}:{1}'.format(self.select.currentText(), value)
fp.write('{0} = {1} #{2}\n'.format(key, value, comment))
#This method take the name of objects and values for run the script
@@ -375,12 +381,12 @@ class Conf_Tab(QtGui.QWidget):
for key, value, comment in self.Inp.getParam():
if key == 'type':
- value = '{0}:{1}'.format(str(self.select.currentText()).strip(),value)
- inInp[key] = value
+ value = '{0}:{1}'.format(str(self.select.currentText()).strip(), value)
+ inInp[key] = value
for key, value, comment in self.Pro.getParam():
if key == 'type':
- value = '{0}:{1}'.format(str(self.select.currentText()).strip(),value)
+ value = '{0}:{1}'.format(str(self.select.currentText()).strip(), value)
inPro[key] = value
cfg = binoculars.util.ConfigFile('processgui {0}'.format(time.strftime('%d %b %Y %H:%M:%S', time.localtime())))
@@ -391,7 +397,7 @@ class Conf_Tab(QtGui.QWidget):
#This method take elements on a text file or the binocular script and put them on tables
def read_data(self, filename):
- cfg = binoculars.util.ConfigFile.fromtxtfile(str(filename))
+ cfg = binoculars.util.ConfigFile.fromtxtfile(str(filename))
input_type = cfg.input['type']
backend, value = input_type.strip(' ').split(':')
self.select.setCurrentIndex(self.select.findText(backend, QtCore.Qt.MatchFixedString))
@@ -404,9 +410,9 @@ class Conf_Tab(QtGui.QWidget):
def AddCommand(self):
scan = [str(self.scan.text())]
cfg = self.get_configobj()
- commandconfig = (scan , cfg)
+ commandconfig = (scan, cfg)
self.emit(QtCore.SIGNAL('command'), commandconfig)
-
+
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
@@ -414,4 +420,3 @@ if __name__ == '__main__':
main.show()
sys.exit(app.exec_())
-
diff --git a/scripts/binoculars-server b/scripts/binoculars-server
index 3ede8c8..d947ea3 100755
--- a/scripts/binoculars-server
+++ b/scripts/binoculars-server
@@ -59,6 +59,7 @@ class ProcessTCPHandler(SocketServer.BaseRequestHandler):
finally:
self.request.sendall(response)
+
def parse_job(job):
try:
overrides = []
@@ -72,6 +73,7 @@ def parse_job(job):
message = 'Error parsing the configuration options. {0}'.format(job)
return False, message
+
def process(run_event, ip, port, q):
while run_event.is_set():
if q.empty():
@@ -81,10 +83,10 @@ def process(run_event, ip, port, q):
# assume everything in the list is an override except for command and configfilename
command = str(job['command'])
configfilename = job['configfilename']
- overrides = parse_job(job)[1]# [1] are the succesfully parsed jobs
+ overrides = parse_job(job)[1] # [1] are the succesfully parsed jobs
print 'Start processing: {0}'.format(command)
try:
- configobj = binoculars.util.ConfigFile.fromtxtfile(configfilename, overrides = overrides)
+ configobj = binoculars.util.ConfigFile.fromtxtfile(configfilename, overrides=overrides)
if binoculars.util.parse_bool(configobj.dispatcher['send_to_gui']):
configobj.dispatcher['host'] = ip
configobj.dispatcher['port'] = port
@@ -94,7 +96,7 @@ def process(run_event, ip, port, q):
errorfilename = 'error_{0}.txt'.format(command)
print 'An error occured for scan {0}. For more information see {1}'.format(command, errorfilename)
with open(errorfilename, 'w') as fp:
- traceback.print_exc(file = fp)
+ traceback.print_exc(file=fp)
finally:
print 'Number of jobs left in queue: {0}'.format(q.qsize())
@@ -104,7 +106,7 @@ if __name__ == '__main__':
port = sys.argv[2]
else:
ip = None
- port = None
+ port = None
q = Queue.Queue()
@@ -115,7 +117,7 @@ if __name__ == '__main__':
run_event = threading.Event()
run_event.set()
- process_thread = threading.Thread(target=process, args = (run_event, ip, port, q))
+ process_thread = threading.Thread(target=process, args=(run_event, ip, port, q))
process_thread.start()
server = SocketServer.TCPServer((HOST, PORT), ProcessTCPHandler)
@@ -128,4 +130,3 @@ if __name__ == '__main__':
except KeyboardInterrupt:
run_event.clear()
process_thread.join()
-