From: W. Trevor King Date: Sat, 8 May 2010 11:20:02 +0000 (-0400) Subject: Moved hooke.libhookecurve -> hooke.curve and rewrote HookeCurve -> Curve class. X-Git-Url: http://git.tremily.us/?p=hooke.git;a=commitdiff_plain;h=ba4daa53462d81c9302a91d959612d534efc6be7 Moved hooke.libhookecurve -> hooke.curve and rewrote HookeCurve -> Curve class. New class Data subclasses numpy.ndarray for faster data handling. Changes: * hooke.libhookecurve.PlotObject -> hooke.ui.gui.plot.PlotObject * hooke.libhookecurve.Driver -> hooke.driver.Driver * Raise new exception hooke.driver.NotRecognized if no driver matches (vs. old print message and return false). * Curve stores data in a list (vs. old dict). Often data order is important. The names that used to key the data dict can now go into Data.info['name']. * HookeCurve.fits -> Curve.info['fits'] * HookeCurve.plots is GUI data, but if the UI needs it, it should be stored in Curve.info['plots'] * Since Curve.data is an array, HookeCurve.add_data() -> Curve.data.append(), HookeCurve.delete_data() -> Curve.data.remove(), and HookeCurve.set_data() -> Curve.data[i] = data --- diff --git a/hooke/curve.py b/hooke/curve.py new file mode 100644 index 0000000..99c481b --- /dev/null +++ b/hooke/curve.py @@ -0,0 +1,68 @@ +import os.path +import numpy + +from .driver import NotRecognized + +class Data (numpy.ndarray): + """Stores a single, continuous data set. + + Adds :attr:`info` :class:`dict` to the standard :class:`numpy.ndarray`. + + See :mod:`numpy.doc.subclassing` for the peculiarities of + subclassing :class:`numpy.ndarray`. + """ + def __new__(self, subtype, shape, dtype=numpy.float, buffer=None, offset=0, + strides=None, order=None, info=None): + """Create the ndarray instance of our type, given the usual + input arguments. This will call the standard ndarray + constructor, but return an object of our type. + """ + obj = np.ndarray.__new__(subtype=subtype, shape=shape, dtype=dtype, + buffer=buffer, offset=offset, strides=strides, + order=order) + # add the new attribute to the created instance + if info == None: + info = {} + obj.info = info + # Finally, we must return the newly created object: + return obj + + def __array_finalize__(self, obj): + """Set any extra attributes from the original object when + creating a new view object.""" + # reset the attribute from passed original object + self.info = getattr(obj, 'info', {}) + # We do not need to return anything + + +class Curve (object): + """A grouped set of :class:`Data` runs from the same file with metadata. + + For an approach/retract force spectroscopy experiment, the group + would consist of the approach data and the retract data. Metadata + would be the temperature, cantilever spring constant, etc. + """ + def __init__(self, path): + #the data dictionary contains: {name of data: list of data sets [{[x], [y]}] + self.path = path + self.driver = None + self.data = [] + self.info = None + self.name = os.path.basename(path) + self.notes = '' + + def identify(self, drivers): + """Identify the appropriate :class:`hooke.driver.Driver` for + the curve file (`.path`). + """ + for driver in drivers: + current_driver = driver(self.path) + if current_driver.is_me(): + self.driver = current_driver # remember the working driver + return + raise NotRecognized(self.path) + + def load(self): + """Use the driver to read the curve into memory. + """ + pass diff --git a/hooke/driver/__init__.py b/hooke/driver/__init__.py index e69de29..4b9c176 100644 --- a/hooke/driver/__init__.py +++ b/hooke/driver/__init__.py @@ -0,0 +1,32 @@ +class NotRecognized (ValueError): + def __init__(self, path): + msg = 'Not a recognizable curve format: %s' % self.path + ValueError.__init__(self, msg) + self.path = path + +class Driver(object): + ''' + Base class for file format drivers. + + To be overridden + ''' + def __init__(self): + self.experiment = '' + self.filetype = '' + + def is_me(self): + ''' + This method must read the file and return True if the filetype can be managed by the driver, False if not. + ''' + return False + + def close_all(self): + ''' + This method must close all the open files of the driver, explicitly. + ''' + return None + + def default_plots(self): + dummy_default = PlotObject() + dummy_default.vectors.append([[[0]],[[0]]]) + return [dummy_default] diff --git a/hooke/driver/csvdriver.py b/hooke/driver/csvdriver.py index 1d05e23..73232d2 100644 --- a/hooke/driver/csvdriver.py +++ b/hooke/driver/csvdriver.py @@ -12,7 +12,7 @@ If the number of columns is odd, the last column is ignored. (c)Massimo Sandal, 2008 ''' -from .. import libhookecurve as lhc +from .. import curve as lhc from .. import libhooke as lh import csv diff --git a/hooke/driver/hdf5.py b/hooke/driver/hdf5.py index 90ff923..e9d2a41 100644 --- a/hooke/driver/hdf5.py +++ b/hooke/driver/hdf5.py @@ -9,7 +9,7 @@ Alberto Gomez-Casado (c) 2010 Massimo Sandal (c) 2009 ''' -from .. import libhookecurve as lhc +from .. import curve as lhc from .. import libhooke as lh class hdf5Driver(lhc.Driver): diff --git a/hooke/driver/hemingclamp.py b/hooke/driver/hemingclamp.py index 3145404..6be0b45 100644 --- a/hooke/driver/hemingclamp.py +++ b/hooke/driver/hemingclamp.py @@ -14,7 +14,7 @@ __changelog__=''' 2007_02_07: Initial implementation ''' import string -from .. import libhookecurve as lhc +from .. import curve as lhc class DataChunk(list): '''Dummy class to provide ext and ret methods to the data list. diff --git a/hooke/driver/jpk.py b/hooke/driver/jpk.py index 090b09a..2888778 100644 --- a/hooke/driver/jpk.py +++ b/hooke/driver/jpk.py @@ -1,5 +1,5 @@ import string -from .. import libhookecurve as lhc +from .. import curve as lhc class DataChunk(list): #Dummy class to provide ext and ret methods to the data list. diff --git a/hooke/driver/mcs.py b/hooke/driver/mcs.py index 6562d90..2d7c419 100644 --- a/hooke/driver/mcs.py +++ b/hooke/driver/mcs.py @@ -6,7 +6,7 @@ driver for mcs fluorescence files Massimo Sandal, Allen Chen (c) 2009 ''' -from .. import libhookecurve as lhc +from .. import curve as lhc from .. import libhooke as lh import struct diff --git a/hooke/driver/mfp1dexport.py b/hooke/driver/mfp1dexport.py index 3f1dbab..8239fc3 100644 --- a/hooke/driver/mfp1dexport.py +++ b/hooke/driver/mfp1dexport.py @@ -9,7 +9,7 @@ Massimo Sandal (c) 2009 import os from .. import libhooke as lh -from .. import libhookecurve as lhc +from .. import curve as lhc __version__='0.0.0.20090923' diff --git a/hooke/driver/mfp3d.py b/hooke/driver/mfp3d.py index 658106f..f182df5 100644 --- a/hooke/driver/mfp3d.py +++ b/hooke/driver/mfp3d.py @@ -37,7 +37,7 @@ import numpy import os.path import struct -from .. import libhookecurve as lhc +from .. import curve as lhc __version__='0.0.0.20100310' diff --git a/hooke/driver/picoforce.py b/hooke/driver/picoforce.py index 51e9d21..170fc9f 100644 --- a/hooke/driver/picoforce.py +++ b/hooke/driver/picoforce.py @@ -13,7 +13,7 @@ import struct from scipy import arange #from .. import libhooke as lh -from .. import libhookecurve as lhc +from .. import curve as lhc __version__='0.0.0.20090923' diff --git a/hooke/driver/picoforcealt.py b/hooke/driver/picoforcealt.py index 836fb45..8f7a494 100644 --- a/hooke/driver/picoforcealt.py +++ b/hooke/driver/picoforcealt.py @@ -12,7 +12,7 @@ This program is released under the GNU General Public License version 2. import re, struct from scipy import arange -from .. import libhookecurve as lhc +from .. import curve as lhc __version__='0.0.0.20081706' diff --git a/hooke/driver/tutorialdriver.py b/hooke/driver/tutorialdriver.py index 8644eef..f30107e 100644 --- a/hooke/driver/tutorialdriver.py +++ b/hooke/driver/tutorialdriver.py @@ -45,7 +45,7 @@ END that is, two plots with two datasets each. ''' -from .. import libhookecurve as lhc #We need to import this library to define some essential data types +from .. import curve as lhc #We need to import this library to define some essential data types class tutorialdriverDriver(lhc.Driver): ''' @@ -155,7 +155,7 @@ class tutorialdriverDriver(lhc.Driver): This is the method that returns the plots to Hooke. It must return a list with *one* or *two* PlotObjects. - See the libhookecurve.py source code to see how PlotObjects are defined and work in detail. + See the curve.py source code to see how PlotObjects are defined and work in detail. ''' gen_vectors=self._generate_vectors() @@ -193,7 +193,7 @@ class tutorialdriverDriver(lhc.Driver): - title: string , the plot title. for each plot. - Again, see libhookecurve.py comments for details. + Again, see curve.py comments for details. ''' main_plot.units=['unit of x','unit of y'] main_plot.destination=0 diff --git a/hooke/formatter.py b/hooke/formatter.py index c5868e9..9297d10 100644 --- a/hooke/formatter.py +++ b/hooke/formatter.py @@ -14,7 +14,7 @@ This program is released under the GNU General Public License version 2. from matplotlib.ticker import ScalarFormatter -from . import libhookecurve as lhc +from . import curve as lhc class EngrFormatter(ScalarFormatter): diff --git a/hooke/hooke_cli.py b/hooke/hooke_cli.py index b36b65a..69b8018 100644 --- a/hooke/hooke_cli.py +++ b/hooke/hooke_cli.py @@ -42,7 +42,7 @@ from sys import version as python_version import platform from .libhooke import PlaylistXML -from . import libhookecurve as lhc +from . import curve as lhc from . import libinput as linp from . import liboutlet as lout diff --git a/hooke/libhookecurve.py b/hooke/libhookecurve.py deleted file mode 100644 index ea68109..0000000 --- a/hooke/libhookecurve.py +++ /dev/null @@ -1,173 +0,0 @@ -import os.path - -class HookeCurve(object): - - def __init__(self, path): - #the data dictionary contains: {name of data: list of data sets [{[x], [y]}] - self.data = {} - self.driver = Driver() - self.path = path - self.fits = {} - self.name = os.path.basename(path) - self.notes = '' - self.plots = None - - def add_data(self, name, x, y, color=None, result=None, style=None, visible=True): - data = {'x': x, 'y': y, 'color': color, 'result': result, 'style': style, 'visible': visible} - if self.data.has_key(name): - #get the existing list of xydata - list_data = self.data[name] - else: - #we need a new list of xydata - list_data = [] - #append the xydata to the list - list_data.append(data) - #append or change the corresponding dictionary entry - self.data[name] = list_data - - #def add_result(self, name, index, result): - #if self.data.has_key(name): - ##get the existing list of xydata - #list_data = self.data[name][index] - #list_data['result'] = result - - def delete_data(self, name): - if self.data.has_key(name): - del self.data[name] - - def identify(self, drivers): - ''' - identifies a curve and returns the corresponding object - ''' - for driver in drivers: - current_driver = driver(self.filename) - if current_driver.is_me(): - #bring on all the driver, with its load of methods etc. - #so we can access the whole of it. - self.driver = current_driver - return True - print 'Not a recognizable curve format: ', self.path - return False - - def set_data(self, name, x, y, color=None, result=None, style=None, visible=True): - data = {'x': x, 'y': y, 'color': color, 'result': result, 'style': style, 'visible': visible} - #append the xydata to the list - list_data = [data] - #append or change the corresponding dictionary entry - self.data[name] = list_data - - -class Driver(object): - ''' - Base class for file format drivers. - - To be overridden - ''' - def __init__(self): - self.experiment = '' - self.filetype = '' - - def is_me(self): - ''' - This method must read the file and return True if the filetype can be managed by the driver, False if not. - ''' - return False - - def close_all(self): - ''' - This method must close all the open files of the driver, explicitly. - ''' - return None - - def default_plots(self): - dummy_default = PlotObject() - dummy_default.vectors.append([[[0]],[[0]]]) - return [dummy_default] - - -class PlotObject(object): - - def __init__(self): - - ''' - the plot destination - 0=top - 1=bottom - ''' - self.destination=0 - - ''' - self.vectors is a multidimensional array: - self.vectors[0]=plot1 - self.vectors[1]=plot2 - self.vectors[2]=plot3 - etc. - - 2 curves in a x,y plot are: - [[[x1],[y1]],[[x2],[y2]]] - for example: - x1 y1 x2 y2 - [[[1,2,3,4],[10,20,30,40]],[[3,6,9,12],[30,60,90,120]]] - x1 = self.vectors[0][0] - y1 = self.vectors[0][1] - x2 = self.vectors[1][0] - y2 = self.vectors[1][1] - ''' - self.vectors=[] - - ''' - self.units is simpler. for each plot with N axes (x,y,z...) only N labels - can be made, regardless of the number of superimposed plots - so units for the double plot above is: [unitx, unity] - - units are strings - ''' - self.units=['', ''] - - ''' - xaxes and yaxes directions. 0, 0 means the common +X=right, +Y=top directions - ''' - self.xaxes = 0 - self.yaxes = 0 - - self.filename = '' - self.title = '' #title - - ''' - styles: defines what is the style of the current plots. If undefined or None, it is line plot. - If an element of the list is 'scatter', the corresponding dataset - is drawn with scattered points and not a continuous line. - ''' - self.styles = [] - - ''' - colors: define what is the colour of the current plots - ''' - self.colors = [] - - def add_set(self, x, y): - ''' - Adds an x, y data set to the vectors. - ''' - self.vectors.append([]) - self.vectors[-1].append(x) - self.vectors[-1].append(y) - - def remove_set(self, whichset): - ''' - Removes a set - ''' - #TODO: do we need 'waste' here? - waste = self.vectors.pop(whichset) - - def normalize_vectors(self): - ''' - Trims the vector lengths as to be equal in a plot. - ''' - for index in range(0,len(self.vectors)): - vectors_to_plot=self.vectors[index] - lengths=[len(vector) for vector in vectors_to_plot] - if min(lengths) != max(lengths): - for indexplot in range(0,len(vectors_to_plot)): - self.vectors[index][indexplot] = self.vectors[index][indexplot][0:min(lengths)] - diff --git a/hooke/playlist.py b/hooke/playlist.py index a7a261e..26ab8dc 100644 --- a/hooke/playlist.py +++ b/hooke/playlist.py @@ -4,7 +4,7 @@ import os.path import xml.dom.minidom from . import hooke as hooke -from . import libhookecurve as lhc +from . import curve as lhc from . import libhooke as lh class Playlist(object): diff --git a/hooke/plugin/cut.py b/hooke/plugin/cut.py index ec72efd..33010e4 100644 --- a/hooke/plugin/cut.py +++ b/hooke/plugin/cut.py @@ -17,7 +17,7 @@ class CutCommand (Command): name='cut', arguments=[ Argument(name='curve', type='curve', optional=False, help=""" -:class:``hooke.Curve`` to cut from. +:class:``hooke.curve.Curve`` to cut from. """.strip()), Argument(name='block', aliases=['set'], type='int', default=0, help=""" diff --git a/hooke/plugin/flatfilts-rolf.py b/hooke/plugin/flatfilts-rolf.py index 2c06ae9..0aa34f0 100644 --- a/hooke/plugin/flatfilts-rolf.py +++ b/hooke/plugin/flatfilts-rolf.py @@ -21,7 +21,7 @@ import os.path #import pickle import libpeakspot as lps -#import libhookecurve as lhc +#import curve as lhc import hookecurve as lhc import libhooke as lh import wxversion diff --git a/hooke/plugin/flatfilts.py b/hooke/plugin/flatfilts.py index faacc11..2b5a79b 100644 --- a/hooke/plugin/flatfilts.py +++ b/hooke/plugin/flatfilts.py @@ -23,7 +23,7 @@ from numpy import diff #import pickle from .. import libpeakspot as lps -from .. import libhookecurve as lhc +from .. import curve as lhc class flatfiltsCommands(object): diff --git a/hooke/plugin/generalclamp.py b/hooke/plugin/generalclamp.py index b3a6126..0ac2164 100644 --- a/hooke/plugin/generalclamp.py +++ b/hooke/plugin/generalclamp.py @@ -9,7 +9,7 @@ import wxversion wxversion.select(WX_GOOD) from wx import PostEvent -from .. import libhookecurve as lhc +from .. import curve as lhc class generalclampCommands(object): diff --git a/hooke/plugin/macro.py b/hooke/plugin/macro.py index 2a1ca99..d2f92d4 100644 --- a/hooke/plugin/macro.py +++ b/hooke/plugin/macro.py @@ -8,7 +8,7 @@ Records, saves and executes batches of commands import os.path import string -from .. import libhookecurve as lhc +from .. import curve as lhc from .. import libinput as linput class macroCommands(object): diff --git a/hooke/plugin/massanalysis.py b/hooke/plugin/massanalysis.py index a9ea615..74e4836 100644 --- a/hooke/plugin/massanalysis.py +++ b/hooke/plugin/massanalysis.py @@ -12,7 +12,7 @@ import numpy as np import csv from .. import libpeakspot as lps -from .. import libhookecurve as lhc +from .. import curve as lhc from .. import libhooke as lh class massanalysisCommands(object): diff --git a/hooke/plugin/pcluster.py b/hooke/plugin/pcluster.py index cf3987a..479c71c 100644 --- a/hooke/plugin/pcluster.py +++ b/hooke/plugin/pcluster.py @@ -13,7 +13,7 @@ import os.path import time import pylab as pyl -from .. import libhookecurve as lhc +from .. import curve as lhc import warnings diff --git a/hooke/plugin/procplots.py b/hooke/plugin/procplots.py index 12136a6..c7ac871 100644 --- a/hooke/plugin/procplots.py +++ b/hooke/plugin/procplots.py @@ -14,7 +14,7 @@ import scipy as sp import scipy.signal import copy -from .. import libhookecurve as lhc +from .. import curve as lhc class procplotsCommands(object): diff --git a/hooke/plugin/superimpose.py b/hooke/plugin/superimpose.py index 448ded3..708cafd 100644 --- a/hooke/plugin/superimpose.py +++ b/hooke/plugin/superimpose.py @@ -4,7 +4,7 @@ wxversion.select(WX_GOOD) from wx import PostEvent from numpy import arange, mean -from .. import libhookecurve as lhc +from .. import curve as lhc class superimposeCommands(object): diff --git a/hooke/plugin/tutorial.py b/hooke/plugin/tutorial.py index 61461f6..fde2a9f 100644 --- a/hooke/plugin/tutorial.py +++ b/hooke/plugin/tutorial.py @@ -8,7 +8,7 @@ internals. import numpy as np -from .. import libhookecurve as lhc +from .. import curve as lhc ''' SYNTAX OF DATA TYPE DECLARATION: @@ -90,7 +90,7 @@ class tutorialCommands(object): ''' '''self.current_list - TYPE: [ libhookecurve.HookeCurve ], len=variable + TYPE: [ curve.HookeCurve ], len=variable contains the actual playlist of Hooke curve objects. Each HookeCurve object represents a reference to a data file. We will see later in detail how do they work. @@ -106,14 +106,14 @@ class tutorialCommands(object): print 'pointer: ',self.pointer '''self.current - TYPE: libhookecurve.HookeCurve + TYPE: curve.HookeCurve contains the current curve displayed. We will see later how it works. ''' print 'current:',self.current '''self.plots - TYPE: [ libhookecurve.PlotObject ], len=1,2 + TYPE: [ curve.PlotObject ], len=1,2 contains the current default plots. Each PlotObject contains all info needed to display the plot: apart from the data vectors, the title, destination @@ -191,7 +191,7 @@ class tutorialCommands(object): def do_myfirstplot(self,args): ''' In this function, we see how to create a PlotObject and send it to the screen. - ***Read the code of PlotObject in libhookecurve.py before!***. + ***Read the code of PlotObject in curve.py before!***. ''' #We generate some interesting data to plot for this example. @@ -201,7 +201,7 @@ class tutorialCommands(object): ydata2=[item**3 for item in xdata2] #Create the object. - #The PlotObject class lives in the libhookecurve library. + #The PlotObject class lives in the curve library. myplot=lhc.PlotObject() ''' The *data* of the plot live in the .vectors list. @@ -344,13 +344,13 @@ class tutorialCommands(object): self.current --- - Contains the current libhookecurve.HookeCurve container object. + Contains the current curve.HookeCurve container object. A HookeCurve object defines only two default attributes: * self.current.path = string The path of the current displayed curve - * self.current.curve = libhookecurve.Driver + * self.current.curve = curve.Driver The curve object. This is not only generated by the driver, this IS a driver instance in itself. This means that in self.current.curve you can access the diff --git a/hooke/ui/gui/plot.py b/hooke/ui/gui/plot.py new file mode 100644 index 0000000..23c64a1 --- /dev/null +++ b/hooke/ui/gui/plot.py @@ -0,0 +1,86 @@ +class PlotObject(object): + + def __init__(self): + + ''' + the plot destination + 0=top + 1=bottom + ''' + self.destination=0 + + ''' + self.vectors is a multidimensional array: + self.vectors[0]=plot1 + self.vectors[1]=plot2 + self.vectors[2]=plot3 + etc. + + 2 curves in a x,y plot are: + [[[x1],[y1]],[[x2],[y2]]] + for example: + x1 y1 x2 y2 + [[[1,2,3,4],[10,20,30,40]],[[3,6,9,12],[30,60,90,120]]] + x1 = self.vectors[0][0] + y1 = self.vectors[0][1] + x2 = self.vectors[1][0] + y2 = self.vectors[1][1] + ''' + self.vectors=[] + + ''' + self.units is simpler. for each plot with N axes (x,y,z...) only N labels + can be made, regardless of the number of superimposed plots + so units for the double plot above is: [unitx, unity] + + units are strings + ''' + self.units=['', ''] + + ''' + xaxes and yaxes directions. 0, 0 means the common +X=right, +Y=top directions + ''' + self.xaxes = 0 + self.yaxes = 0 + + self.filename = '' + self.title = '' #title + + ''' + styles: defines what is the style of the current plots. If undefined or None, it is line plot. + If an element of the list is 'scatter', the corresponding dataset + is drawn with scattered points and not a continuous line. + ''' + self.styles = [] + + ''' + colors: define what is the colour of the current plots + ''' + self.colors = [] + + def add_set(self, x, y): + ''' + Adds an x, y data set to the vectors. + ''' + self.vectors.append([]) + self.vectors[-1].append(x) + self.vectors[-1].append(y) + + def remove_set(self, whichset): + ''' + Removes a set + ''' + #TODO: do we need 'waste' here? + waste = self.vectors.pop(whichset) + + def normalize_vectors(self): + ''' + Trims the vector lengths as to be equal in a plot. + ''' + for index in range(0,len(self.vectors)): + vectors_to_plot=self.vectors[index] + lengths=[len(vector) for vector in vectors_to_plot] + if min(lengths) != max(lengths): + for indexplot in range(0,len(vectors_to_plot)): + self.vectors[index][indexplot] = self.vectors[index][indexplot][0:min(lengths)] +