Finished off hooke.hooke and fleshed out hooke.ui.
[hooke.git] / hooke / curve.py
1 """The `curve` module provides :class:`Curve` and :class:`Data` for
2 storing force curves.
3 """
4
5 import os.path
6 import numpy
7
8
9 class NotRecognized (ValueError):
10     def __init__(self, curve):
11         msg = 'Not a recognizable curve format: %s' % curve.path
12         ValueError.__init__(self, msg)
13         self.curve = curve
14
15 class Data (numpy.ndarray):
16     """Stores a single, continuous data set.
17
18     Adds :attr:`info` :class:`dict` to the standard :class:`numpy.ndarray`.
19
20     See :mod:`numpy.doc.subclassing` for the peculiarities of
21     subclassing :class:`numpy.ndarray`.
22     """
23     def __new__(self, subtype, shape, dtype=numpy.float, buffer=None, offset=0,
24                 strides=None, order=None, info=None):
25         """Create the ndarray instance of our type, given the usual
26         input arguments.  This will call the standard ndarray
27         constructor, but return an object of our type.
28         """
29         obj = np.ndarray.__new__(subtype=subtype, shape=shape, dtype=dtype,
30                                  buffer=buffer, offset=offset, strides=strides,
31                                  order=order)
32         # add the new attribute to the created instance
33         if info == None:
34             info = {}
35         obj.info = info
36         # Finally, we must return the newly created object:
37         return obj
38
39     def __array_finalize__(self, obj):
40         """Set any extra attributes from the original object when
41         creating a new view object."""
42         # reset the attribute from passed original object
43         self.info = getattr(obj, 'info', {})
44         # We do not need to return anything
45
46
47 class Curve (object):
48     """A grouped set of :class:`Data` runs from the same file with metadata.
49
50     For an approach/retract force spectroscopy experiment, the group
51     would consist of the approach data and the retract data.  Metadata
52     would be the temperature, cantilever spring constant, etc.
53
54     Two important :attr:`info` settings are `filetype` and
55     `experiment`.  These are two strings that can be used by Hooke
56     commands/plugins to understand what they are looking at.
57
58     * `.info['filetype']` should contain the name of the exact
59       filetype defined by the driver (so that filetype-speciofic
60       commands can know if they're dealing with the correct filetype).
61     * `.info['experiment']` should contain an instance of a
62       :class:`hooke.experiment.Experiment` subclass to identify the
63       experiment type.  For example, various
64       :class:`hooke.driver.Driver`\s can read in force-clamp data, but
65       Hooke commands could like to know if they're looking at force
66       clamp data, regardless of their origin.
67     """
68     def __init__(self, path, info=None):
69         #the data dictionary contains: {name of data: list of data sets [{[x], [y]}]
70         self.path = path
71         self.driver = None
72         self.data = []
73         if info == None:
74             info = {}
75         self.info = info
76         self.name = os.path.basename(path)
77
78     def identify(self, drivers):
79         """Identify the appropriate :class:`hooke.driver.Driver` for
80         the curve file (`.path`).
81         """
82         for driver in drivers:
83             if driver.is_me(self.path):
84                 self.driver = driver # remember the working driver
85                 return
86         raise NotRecognized(self)
87
88     def load(self):
89         """Use the driver to read the curve into memory.
90         """
91         pass