diff options
author | Onderwaater <onderwaa@esrf.fr> | 2015-12-11 13:42:43 +0100 |
---|---|---|
committer | Onderwaater <onderwaa@esrf.fr> | 2015-12-11 13:42:43 +0100 |
commit | 08c650a78c7875b143f11d9968638e0410fc31c8 (patch) | |
tree | 86e51d1c6f16ae4dcb5bedea4bed7244da14b554 | |
parent | 19a10c475735c469e5686e6bf9b47fdf08eac6f6 (diff) |
code cleanup
-rw-r--r-- | binoculars/__init__.py | 57 | ||||
-rw-r--r-- | binoculars/backend.py | 15 | ||||
-rw-r--r-- | binoculars/backends/id03.py | 311 | ||||
-rw-r--r-- | binoculars/backends/test.py | 466 | ||||
-rwxr-xr-x | binoculars/dispatcher.py | 54 | ||||
-rw-r--r-- | binoculars/errors.py | 9 | ||||
-rw-r--r-- | binoculars/fit.py | 127 | ||||
-rwxr-xr-x | binoculars/main.py | 31 | ||||
-rw-r--r-- | binoculars/plot.py | 45 | ||||
-rwxr-xr-x | binoculars/space.py | 165 | ||||
-rwxr-xr-x | binoculars/util.py | 156 | ||||
-rwxr-xr-x | scripts/binoculars | 59 | ||||
-rwxr-xr-x | scripts/binoculars-fitaid | 207 | ||||
-rwxr-xr-x | scripts/binoculars-gui | 237 | ||||
-rwxr-xr-x | scripts/binoculars-processgui | 149 | ||||
-rwxr-xr-x | scripts/binoculars-server | 13 |
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() - |