Hooke(GUI)
authorillysam <devnull@localhost>
Mon, 1 Mar 2010 17:30:27 +0000 (17:30 +0000)
committerillysam <devnull@localhost>
Mon, 1 Mar 2010 17:30:27 +0000 (17:30 +0000)
hooke.py
- added check for off-screen window position on startup
- added note panel
- added check for existing plugin commands
- fixed access error on close (forgot to deregister a listener)
- added option to hide file extension from playlist and plot title
- added 'do_copylog'
- added general preferences
- modifed UpdatePlot to use individual scale formatter and legends for subplots
- added default settings for scale formatters and legends
- added general preference 'use_zero' to dispaly '0' on axes rather than e.g. '0.00'

hooke configspec.ini/hooke.ini
- added mfp1d driver
- added mfp3d driver
- removed curvetools plugin

drivers/mfp1dexport.py
- corrected spelling

drivers/picoforce.py
- removed detriggerize

drivers/mfp1d.py
- added driver for direct import of MFP-1D files

drivers/mfp3d.py
- added driver for direct import of MFP-3D files

lib/clickedpoint.py
- added object ClickedPoint to lib (was in libhooke before)

lib/curve.py
- added properties decimals, legend and multiplier to Curve
- added classes Data, Decimals, Multiplier and PrefixFormatter

lib/delta.py
- added object Delta to lib

lib/file.py
- rename self.notes to self.note

lib/libhooke.py
- added fit_interval_nm (was in curvetools before)
- added pickup_contact_point(was in curvetools before)
- added remove_extension

lib/playlist.py
- added note support

panels/ploy.py
- added 'x' and 'y' to status text

panels/note.py
- added note panel

plugins/autopeak.ini and autopeak.py
- added options: delta_force, plot_linewidth, plot_size and plot_style

plugins/core.ini
- added 'preferences'options: hide_curve_extension, legend, x_decimals, x_multiplier, y_decimals, y_multiplier and use_zero
- added 'copylog' options: destination and use_LVDT_folder

plugins/export.ini and export.py
- added 'notes' options: filename, folder, prefix and use_playlist_filename
- added do_notes

plugins/flatfilts.ini and flatfilts.py
- corrected some default values
- added warning about not applying the flatten plotmanipulator

plugins/generalvclamp.py
- updated copyright information

plugins/plot.ini and plot.py
- corrected some default values
- added support for options: decimals, legend and multiplier

plugins/procplots.ini and procplots.py
- corrected some default values
- added support for options: decimals, legend and multiplier to do_fft

30 files changed:
config/hooke configspec.ini
config/hooke.ini
drivers/mfp1d.py [new file with mode: 0644]
drivers/mfp1dexport.py
drivers/mfp3d.py [new file with mode: 0644]
drivers/picoforce.py
hooke.py
lib/clickedpoint.py [new file with mode: 0644]
lib/curve.py
lib/delta.py [new file with mode: 0644]
lib/file.py
lib/libhooke.py
lib/playlist.py
lib/prettyformat.py
panels/note.py [new file with mode: 0644]
panels/plot.py
panels/results.py
plugins/autopeak.ini
plugins/autopeak.py
plugins/core.ini
plugins/export.ini
plugins/export.py
plugins/fit.py
plugins/flatfilts.ini
plugins/flatfilts.py
plugins/generalvclamp.py
plugins/plot.ini
plugins/plot.py
plugins/procplots.ini
plugins/procplots.py

index f1cd81e6ffcd2d329cb5dbf8126069094bc1ed00..1f02453d85f3b09bdcfd85158b74b2e80f60177f 100644 (file)
@@ -7,7 +7,9 @@ csvdriver = boolean(default = False)
 hemingclamp = boolean(default = False)\r
 jpk = boolean(default = False)\r
 mcs = boolean(default = False)\r
+mfp1d = boolean(default = True)\r
 mfp1dexport = boolean(default = True)\r
+mfp3d = boolean(default = True)\r
 picoforce = boolean(default = True)\r
 picoforcealt = boolean(default = False)\r
 tutorialdriver = boolean(default = False)\r
@@ -27,7 +29,6 @@ active = string(default = Default)
 \r
 [plugins]\r
 autopeak = boolean(default = True)\r
-curvetools = boolean(default = True)\r
 export = boolean(default = True)\r
 fit = boolean(default = True)\r
 flatfilts = boolean(default = True)\r
index 4c0871c6e35f86c46691155f03e494e8a1dcfa93..f7cb6e1f0bd10ecd6080dee893b795c99d981f08 100644 (file)
@@ -16,6 +16,8 @@ csvdriver = False
 hemingclamp = False\r
 jpk = False\r
 mcs = False\r
+mfp1d = True\r
+mfp3d = True\r
 mfp1dexport = True\r
 picoforce = True\r
 picoforcealt = False\r
@@ -39,7 +41,6 @@ default = Default
 #this section defines which plugins have to be loaded by Hooke\r
 [plugins]\r
 autopeak = True\r
-curvetools = True\r
 export = True\r
 fit = True\r
 flatfilts = True\r
diff --git a/drivers/mfp1d.py b/drivers/mfp1d.py
new file mode 100644 (file)
index 0000000..833eaa0
--- /dev/null
@@ -0,0 +1,267 @@
+#!/usr/bin/env python
+
+'''
+mfp1d.py
+
+Driver for MFP-1D files.
+
+Copyright 2010 by Dr. Rolf Schmidt (Concordia University, Canada)
+This driver is based on the work of R. Naud and A. Seeholzer (see below)
+to read Igor binary waves. Code used with permission.
+
+This program is released under the GNU General Public License version 2.
+'''
+
+# DEFINITION:
+# Reads Igor's (Wavemetric) binary wave format, .ibw, files.
+#
+# ALGORITHM:
+# Parsing proper to version 2, 3, or version 5 (see Technical notes TN003.ifn:
+# http://mirror.optus.net.au/pub/wavemetrics/IgorPro/Technical_Notes/) and data
+# type 2 or 4 (non complex, single or double precision vector, real values).
+#
+# AUTHORS:
+# Matlab version: R. Naud August 2008 (http://lcn.epfl.ch/~naud/Home.html)
+# Python port: A. Seeholzer October 2008
+#
+# VERSION: 0.1
+#
+# COMMENTS:
+# Only tested for version 2 Igor files for now, testing for 3 and 5 remains to be done.
+# More header data could be passed back if wished. For significance of ignored bytes see
+# the technical notes linked above.
+
+import numpy
+import os.path
+import struct
+
+import lib.driver
+import lib.curve
+import lib.plot
+
+__version__='0.0.0.20100225'
+
+class mfp1dDriver(lib.driver.Driver):
+
+    def __init__(self, filename):
+        '''
+        This is a driver to import Asylum Research MFP-1D data.
+        Status: experimental
+        '''
+        self.data = []
+        self.note = []
+        self.retract_velocity = None
+        self.spring_constant = None
+        self.filename = filename
+
+        self.filedata = open(filename,'rU')
+        self.lines = list(self.filedata.readlines())
+        self.filedata.close()
+
+        self.filetype = 'mfp1d'
+        self.experiment = 'smfs'
+
+    def _load_from_file(self, filename, extract_note=False):
+        data = None
+        f = open(filename, 'rb')
+        ####################### ORDERING
+        # machine format for IEEE floating point with big-endian
+        # byte ordering
+        # MacIgor use the Motorola big-endian 'b'
+        # WinIgor use Intel little-endian 'l'
+        # If the first byte in the file is non-zero, then the file is a WinIgor
+        firstbyte = struct.unpack('b', f.read(1))[0]
+        if firstbyte == 0:
+            format = '>'
+        else:
+            format = '<'
+        #######################  CHECK VERSION
+        f.seek(0)
+        version = struct.unpack(format+'h', f.read(2))[0]
+        #######################  READ DATA AND ACCOMPANYING INFO
+        if version == 2 or version == 3:
+            # pre header
+            wfmSize = struct.unpack(format+'i', f.read(4))[0] # The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.
+            noteSize = struct.unpack(format+'i', f.read(4))[0] # The size of the note text.
+            if version==3:
+                formulaSize = struct.unpack(format+'i', f.read(4))[0]
+            pictSize = struct.unpack(format+'i', f.read(4))[0] # Reserved. Write zero. Ignore on read.
+            checksum = struct.unpack(format+'H', f.read(2))[0] # Checksum over this header and the wave header.
+            # wave header
+            dtype = struct.unpack(format+'h', f.read(2))[0]
+            if dtype == 2:
+                dtype = numpy.float32(.0).dtype
+            elif dtype == 4:
+                dtype = numpy.double(.0).dtype
+            else:
+                assert False, "Wave is of type '%i', not supported" % dtype
+            dtype = dtype.newbyteorder(format)
+
+            ignore = f.read(4) # 1 uint32
+            bname = self._flatten(struct.unpack(format+'20c', f.read(20)))
+            ignore = f.read(4) # 2 int16
+            ignore = f.read(4) # 1 uint32
+            dUnits = self._flatten(struct.unpack(format+'4c', f.read(4)))
+            xUnits = self._flatten(struct.unpack(format+'4c', f.read(4)))
+            npnts = struct.unpack(format+'i', f.read(4))[0]
+            amod = struct.unpack(format+'h', f.read(2))[0]
+            dx = struct.unpack(format+'d', f.read(8))[0]
+            x0 = struct.unpack(format+'d', f.read(8))[0]
+            ignore = f.read(4) # 2 int16
+            fsValid = struct.unpack(format+'h', f.read(2))[0]
+            topFullScale = struct.unpack(format+'d', f.read(8))[0]
+            botFullScale = struct.unpack(format+'d', f.read(8))[0]
+            ignore = f.read(16) # 16 int8
+            modDate = struct.unpack(format+'I', f.read(4))[0]
+            ignore = f.read(4) # 1 uint32
+            # Numpy algorithm works a lot faster than struct.unpack
+            data = numpy.fromfile(f, dtype, npnts)
+
+        elif version == 5:
+            # pre header
+            checksum = struct.unpack(format+'H', f.read(2))[0] # Checksum over this header and the wave header.
+            wfmSize = struct.unpack(format+'i', f.read(4))[0] # The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.
+            formulaSize = struct.unpack(format+'i', f.read(4))[0]
+            noteSize = struct.unpack(format+'i', f.read(4))[0] # The size of the note text.
+            dataEUnitsSize = struct.unpack(format+'i', f.read(4))[0]
+            dimEUnitsSize = struct.unpack(format+'4i', f.read(16))
+            dimLabelsSize = struct.unpack(format+'4i', f.read(16))
+            sIndicesSize = struct.unpack(format+'i', f.read(4))[0]
+            optionSize1 = struct.unpack(format+'i', f.read(4))[0]
+            optionSize2 = struct.unpack(format+'i', f.read(4))[0]
+
+            # header
+            ignore = f.read(4)
+            CreationDate =  struct.unpack(format+'I',f.read(4))[0]
+            modData =  struct.unpack(format+'I',f.read(4))[0]
+            npnts =  struct.unpack(format+'i',f.read(4))[0]
+            # wave header
+            dtype = struct.unpack(format+'h',f.read(2))[0]
+            if dtype == 2:
+                dtype = numpy.float32(.0).dtype
+            elif dtype == 4:
+                dtype = numpy.double(.0).dtype
+            else:
+                assert False, "Wave is of type '%i', not supported" % dtype
+            dtype = dtype.newbyteorder(format)
+
+            ignore = f.read(2) # 1 int16
+            ignore = f.read(6) # 6 schar, SCHAR = SIGNED CHAR?         ignore = fread(fid,6,'schar'); #
+            ignore = f.read(2) # 1 int16
+            bname = self._flatten(struct.unpack(format+'32c',f.read(32)))
+            ignore = f.read(4) # 1 int32
+            ignore = f.read(4) # 1 int32
+            ndims = struct.unpack(format+'4i',f.read(16)) # Number of of items in a dimension -- 0 means no data.
+            sfA = struct.unpack(format+'4d',f.read(32))
+            sfB = struct.unpack(format+'4d',f.read(32))
+            dUnits = self._flatten(struct.unpack(format+'4c',f.read(4)))
+            xUnits = self._flatten(struct.unpack(format+'16c',f.read(16)))
+            fsValid = struct.unpack(format+'h',f.read(2))
+            whpad3 = struct.unpack(format+'h',f.read(2))
+            ignore = f.read(16) # 2 double
+            ignore = f.read(40) # 10 int32
+            ignore = f.read(64) # 16 int32
+            ignore = f.read(6) # 3 int16
+            ignore = f.read(2) # 2 char
+            ignore = f.read(4) # 1 int32
+            ignore = f.read(4) # 2 int16
+            ignore = f.read(4) # 1 int32
+            ignore = f.read(8) # 2 int32
+
+            data = numpy.fromfile(f, dtype, npnts)
+            note_str = f.read(noteSize)
+            if extract_note:
+                note_lines = note_str.split('\r')
+                self.note = {}
+                for line in note_lines:
+                    if ':' in line:
+                        key, value = line.split(':', 1)
+                        self.note[key] = value
+                self.retract_velocity = float(self.note['RetractVelocity'])
+                self.spring_constant = float(self.note['SpringC'])
+        else:
+            assert False, "Fileversion is of type '%i', not supported" % dtype
+            data = []
+
+        f.close()
+        if len(data) > 0:
+            data_list = data.tolist()
+            count = len(data_list) / 2
+            return data_list[:count - 1], data_list[count:]
+        else:
+            return None
+
+    def _flatten(self, tup):
+        out = ''
+        for ch in tup:
+            out += ch
+        return out
+
+    def _read_columns(self):
+        extension = lib.curve.Data()
+        retraction = lib.curve.Data()
+
+        extension.y, retraction.y = self._load_from_file(self.filename, extract_note=True)
+        filename = self.filename.replace('deflection', 'LVDT', 1)
+        path, name = os.path.split(filename)
+        filename = os.path.join(path, 'lvdt', name)
+        extension.x, retraction.x = self._load_from_file(filename, extract_note=False)
+        return [[extension.x, extension.y], [retraction.x, retraction.y]]
+
+    def close_all(self):
+        self.filedata.close()
+
+    def is_me(self):
+        if len(self.lines) < 34:
+            return False
+
+        name, extension = os.path.splitext(self.filename)
+        #PullDist, PullDistSign, FastSamplingFrequency, SlowSamplingFrequency, FastDecimationFactor
+        #SlowDecimationFactor, IsDualPull, InitRetDist, RelaxDist, SlowTrigger, RelativeTrigger,
+        #EndOfNote
+        if extension == '.ibw' and 'deflection' in name:
+            if 'EndOfNote' in self.lines:
+                return True
+            else:
+                return False
+        else:
+            return False
+
+    def default_plots(self):
+        '''
+        loads the curve data
+        '''
+        defl_ext, defl_ret = self.deflection()
+
+        extension = lib.curve.Curve()
+        retraction = lib.curve.Curve()
+
+        extension.color = 'red'
+        extension.label = 'extension'
+        extension.style = 'plot'
+        extension.title = 'Force curve'
+        extension.units.x = 'm'
+        extension.units.y = 'N'
+        extension.x = self.data[0][0]
+        extension.y = [i * self.spring_constant for i in defl_ext]
+        retraction.color = 'blue'
+        retraction.label = 'retraction'
+        retraction.style = 'plot'
+        retraction.title = 'Force curve'
+        retraction.units.x = 'm'
+        retraction.units.y = 'N'
+        retraction.x = self.data[1][0]
+        retraction.y = [i * self.spring_constant for i in defl_ret]
+
+        plot = lib.plot.Plot()
+        plot.title = os.path.basename(self.filename)
+        plot.curves.append(extension)
+        plot.curves.append(retraction)
+
+        plot.normalize()
+        return plot
+
+    def deflection(self):
+        if not self.data:
+            self.data = self._read_columns()
+        return self.data[0][1], self.data[1][1]
index 02148664f885378f4ea8b2a3eb47399c96f82342..a2bb8cf51c24d9a3760577dd4bc897494061d6dd 100644 (file)
@@ -3,7 +3,7 @@
 '''
 mfp1dexport.py
 
-Driver for text-exported MFP 1D files.
+Driver for text-exported MFP-1D files.
 
 Copyright 2009 by Massimo Sandal
 with modifications by Dr. Rolf Schmidt (Concordia University, Canada)
@@ -23,7 +23,7 @@ class mfp1dexportDriver(lib.driver.Driver):
 
     def __init__(self, filename):
         '''
-        This is a driver to import Asylum Research MFP 1D data.
+        This is a driver to import Asylum Research MFP-1D data.
         Status: experimental
         '''
         self.filename = filename
diff --git a/drivers/mfp3d.py b/drivers/mfp3d.py
new file mode 100644 (file)
index 0000000..5988c3f
--- /dev/null
@@ -0,0 +1,267 @@
+#!/usr/bin/env python
+
+'''
+mfp3d.py
+
+Driver for MFP-3D files.
+
+Copyright 2010 by Dr. Rolf Schmidt (Concordia University, Canada)
+This driver is based on the work of R. Naud and A. Seeholzer (see below)
+to read Igor binary waves. Code used with permission.
+
+This program is released under the GNU General Public License version 2.
+'''
+
+# DEFINITION:
+# Reads Igor's (Wavemetric) binary wave format, .ibw, files.
+#
+# ALGORITHM:
+# Parsing proper to version 2, 3, or version 5 (see Technical notes TN003.ifn:
+# http://mirror.optus.net.au/pub/wavemetrics/IgorPro/Technical_Notes/) and data
+# type 2 or 4 (non complex, single or double precision vector, real values).
+#
+# AUTHORS:
+# Matlab version: R. Naud August 2008 (http://lcn.epfl.ch/~naud/Home.html)
+# Python port: A. Seeholzer October 2008
+#
+# VERSION: 0.1
+#
+# COMMENTS:
+# Only tested for version 2 Igor files for now, testing for 3 and 5 remains to be done.
+# More header data could be passed back if wished. For significance of ignored bytes see
+# the technical notes linked above.
+
+import numpy
+import os.path
+import struct
+
+import lib.driver
+import lib.curve
+import lib.plot
+
+__version__='0.0.0.20100225'
+
+class mfp3dDriver(lib.driver.Driver):
+
+    def __init__(self, filename):
+        '''
+        This is a driver to import Asylum Research MFP-3D data.
+        Status: experimental
+        '''
+        self.data = []
+        self.note = []
+        self.retract_velocity = None
+        self.spring_constant = None
+        self.filename = filename
+
+        self.filedata = open(filename,'rU')
+        self.lines = list(self.filedata.readlines())
+        self.filedata.close()
+
+        self.filetype = 'mfp3d'
+        self.experiment = 'smfs'
+
+    def _load_from_file(self, filename, extract_note=False):
+        data = None
+        f = open(filename, 'rb')
+        ####################### ORDERING
+        # machine format for IEEE floating point with big-endian
+        # byte ordering
+        # MacIgor use the Motorola big-endian 'b'
+        # WinIgor use Intel little-endian 'l'
+        # If the first byte in the file is non-zero, then the file is a WinIgor
+        firstbyte = struct.unpack('b', f.read(1))[0]
+        if firstbyte == 0:
+            format = '>'
+        else:
+            format = '<'
+        #######################  CHECK VERSION
+        f.seek(0)
+        version = struct.unpack(format+'h', f.read(2))[0]
+        #######################  READ DATA AND ACCOMPANYING INFO
+        if version == 2 or version == 3:
+            # pre header
+            wfmSize = struct.unpack(format+'i', f.read(4))[0] # The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.
+            noteSize = struct.unpack(format+'i', f.read(4))[0] # The size of the note text.
+            if version==3:
+                formulaSize = struct.unpack(format+'i', f.read(4))[0]
+            pictSize = struct.unpack(format+'i', f.read(4))[0] # Reserved. Write zero. Ignore on read.
+            checksum = struct.unpack(format+'H', f.read(2))[0] # Checksum over this header and the wave header.
+            # wave header
+            dtype = struct.unpack(format+'h', f.read(2))[0]
+            if dtype == 2:
+                dtype = numpy.float32(.0).dtype
+            elif dtype == 4:
+                dtype = numpy.double(.0).dtype
+            else:
+                assert False, "Wave is of type '%i', not supported" % dtype
+            dtype = dtype.newbyteorder(format)
+
+            ignore = f.read(4) # 1 uint32
+            bname = self._flatten(struct.unpack(format+'20c', f.read(20)))
+            ignore = f.read(4) # 2 int16
+            ignore = f.read(4) # 1 uint32
+            dUnits = self._flatten(struct.unpack(format+'4c', f.read(4)))
+            xUnits = self._flatten(struct.unpack(format+'4c', f.read(4)))
+            npnts = struct.unpack(format+'i', f.read(4))[0]
+            amod = struct.unpack(format+'h', f.read(2))[0]
+            dx = struct.unpack(format+'d', f.read(8))[0]
+            x0 = struct.unpack(format+'d', f.read(8))[0]
+            ignore = f.read(4) # 2 int16
+            fsValid = struct.unpack(format+'h', f.read(2))[0]
+            topFullScale = struct.unpack(format+'d', f.read(8))[0]
+            botFullScale = struct.unpack(format+'d', f.read(8))[0]
+            ignore = f.read(16) # 16 int8
+            modDate = struct.unpack(format+'I', f.read(4))[0]
+            ignore = f.read(4) # 1 uint32
+            # Numpy algorithm works a lot faster than struct.unpack
+            data = numpy.fromfile(f, dtype, npnts)
+
+        elif version == 5:
+            # pre header
+            checksum = struct.unpack(format+'H', f.read(2))[0] # Checksum over this header and the wave header.
+            wfmSize = struct.unpack(format+'i', f.read(4))[0] # The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.
+            formulaSize = struct.unpack(format+'i', f.read(4))[0]
+            noteSize = struct.unpack(format+'i', f.read(4))[0] # The size of the note text.
+            dataEUnitsSize = struct.unpack(format+'i', f.read(4))[0]
+            dimEUnitsSize = struct.unpack(format+'4i', f.read(16))
+            dimLabelsSize = struct.unpack(format+'4i', f.read(16))
+            sIndicesSize = struct.unpack(format+'i', f.read(4))[0]
+            optionSize1 = struct.unpack(format+'i', f.read(4))[0]
+            optionSize2 = struct.unpack(format+'i', f.read(4))[0]
+
+            # header
+            ignore = f.read(4)
+            CreationDate =  struct.unpack(format+'I',f.read(4))[0]
+            modData =  struct.unpack(format+'I',f.read(4))[0]
+            npnts =  struct.unpack(format+'i',f.read(4))[0]
+            # wave header
+            dtype = struct.unpack(format+'h',f.read(2))[0]
+            if dtype == 2:
+                dtype = numpy.float32(.0).dtype
+            elif dtype == 4:
+                dtype = numpy.double(.0).dtype
+            else:
+                assert False, "Wave is of type '%i', not supported" % dtype
+            dtype = dtype.newbyteorder(format)
+
+            ignore = f.read(2) # 1 int16
+            ignore = f.read(6) # 6 schar, SCHAR = SIGNED CHAR?         ignore = fread(fid,6,'schar'); #
+            ignore = f.read(2) # 1 int16
+            bname = self._flatten(struct.unpack(format+'32c',f.read(32)))
+            ignore = f.read(4) # 1 int32
+            ignore = f.read(4) # 1 int32
+            ndims = struct.unpack(format+'4i',f.read(16)) # Number of of items in a dimension -- 0 means no data.
+            sfA = struct.unpack(format+'4d',f.read(32))
+            sfB = struct.unpack(format+'4d',f.read(32))
+            dUnits = self._flatten(struct.unpack(format+'4c',f.read(4)))
+            xUnits = self._flatten(struct.unpack(format+'16c',f.read(16)))
+            fsValid = struct.unpack(format+'h',f.read(2))
+            whpad3 = struct.unpack(format+'h',f.read(2))
+            ignore = f.read(16) # 2 double
+            ignore = f.read(40) # 10 int32
+            ignore = f.read(64) # 16 int32
+            ignore = f.read(6) # 3 int16
+            ignore = f.read(2) # 2 char
+            ignore = f.read(4) # 1 int32
+            ignore = f.read(4) # 2 int16
+            ignore = f.read(4) # 1 int32
+            ignore = f.read(8) # 2 int32
+
+            data = numpy.fromfile(f, dtype, npnts)
+            note_str = f.read(noteSize)
+            if extract_note:
+                note_lines = note_str.split('\r')
+                self.note = {}
+                for line in note_lines:
+                    if ':' in line:
+                        key, value = line.split(':', 1)
+                        self.note[key] = value
+                self.retract_velocity = float(self.note['RetractVelocity'])
+                self.spring_constant = float(self.note['SpringConstant'])
+        else:
+            assert False, "Fileversion is of type '%i', not supported" % dtype
+            data = []
+
+        f.close()
+        if len(data) > 0:
+            #we have 3 columns: deflection, LVDT, raw
+            count = npnts / 3
+            deflection = data[:count].tolist()
+            lvdt = data[count:2 * count].tolist()
+            #every column contains data for extension and retraction
+            #we assume the same number of points for each
+            #we could possibly extract this info from the note
+            count = npnts / 6
+            extension = lib.curve.Data()
+            retraction = lib.curve.Data()
+            extension.x = deflection[:count]
+            extension.y = lvdt[:count]
+            retraction.x = deflection[count:]
+            retraction.y = lvdt[count:]
+
+            return extension, retraction
+        else:
+            return None
+
+    def _flatten(self, tup):
+        out = ''
+        for ch in tup:
+            out += ch
+        return out
+
+    def _read_columns(self):
+        extension, retraction = self._load_from_file(self.filename, extract_note=True)
+        return [[extension.x, extension.y], [retraction.x, retraction.y]]
+
+    def close_all(self):
+        self.filedata.close()
+
+    def is_me(self):
+        if len(self.lines) < 34:
+            return False
+
+        name, extension = os.path.splitext(self.filename)
+        if extension == '.ibw':
+            return True
+        else:
+            return False
+
+    def default_plots(self):
+        '''
+        loads the curve data
+        '''
+        defl_ext, defl_ret = self.deflection()
+
+        extension = lib.curve.Curve()
+        retraction = lib.curve.Curve()
+
+        extension.color = 'red'
+        extension.label = 'extension'
+        extension.style = 'plot'
+        extension.title = 'Force curve'
+        extension.units.x = 'm'
+        extension.units.y = 'N'
+        extension.x = self.data[0][0]
+        extension.y = [i * self.spring_constant for i in defl_ext]
+        retraction.color = 'blue'
+        retraction.label = 'retraction'
+        retraction.style = 'plot'
+        retraction.title = 'Force curve'
+        retraction.units.x = 'm'
+        retraction.units.y = 'N'
+        retraction.x = self.data[1][0]
+        retraction.y = [i * self.spring_constant for i in defl_ret]
+
+        plot = lib.plot.Plot()
+        plot.title = os.path.basename(self.filename)
+        plot.curves.append(extension)
+        plot.curves.append(retraction)
+
+        plot.normalize()
+        return plot
+
+    def deflection(self):
+        if not self.data:
+            self.data = self._read_columns()
+        return self.data[0][1], self.data[1][1]
index 5fe17baa6c8187d213a7b3d072a659066802a176..15969a3c8ceee3032ef10deb9e9dfafba0cbca22 100755 (executable)
@@ -507,23 +507,6 @@ class picoforceDriver(lib.driver.Driver):
         '''
         raise "Not implemented yet."
 
-    def detriggerize(self, forcext):
-        '''
-        Cuts away the trigger-induced s**t on the extension curve.
-        DEPRECATED
-        cutindex=2
-        startvalue=forcext[0]
-
-        for index in range(len(forcext)-1,2,-2):
-           if forcext[index]>startvalue:
-                cutindex=index
-           else:
-                break
-
-        return cutindex
-        '''
-        return 0
-
     def is_me(self):
         '''
         self-identification of file type magic
index 8c2167b3f7f8eef18431a42316173c39caf8a208..7033ed62fb8d715786b97380950f914411d7466f 100644 (file)
--- a/hooke.py
+++ b/hooke.py
@@ -17,8 +17,9 @@ from configobj import ConfigObj
 import copy\r
 import os.path\r
 import platform\r
+import shutil\r
 import time\r
-#import wx\r
+\r
 import wx.html\r
 import wx.lib.agw.aui as aui\r
 import wx.lib.evtmgr as evtmgr\r
@@ -40,11 +41,14 @@ except ImportError: # if it's not there locally, try the wxPython lib.
 lh.hookeDir = os.path.abspath(os.path.dirname(__file__))\r
 from config.config import config\r
 import drivers\r
+import lib.clickedpoint\r
+import lib.curve\r
 import lib.delta\r
 import lib.playlist\r
 import lib.plotmanipulator\r
 import lib.prettyformat\r
 import panels.commands\r
+import panels.note\r
 import panels.perspectives\r
 import panels.playlist\r
 import panels.plot\r
@@ -62,9 +66,6 @@ __codename__ = lh.HOOKE_VERSION[1]
 __releasedate__ = lh.HOOKE_VERSION[2]\r
 __release_name__ = lh.HOOKE_VERSION[1]\r
 \r
-#TODO: add general preferences to Hooke\r
-#this might be useful\r
-#ID_Config = wx.NewId()\r
 ID_About = wx.NewId()\r
 ID_Next = wx.NewId()\r
 ID_Previous = wx.NewId()\r
@@ -72,6 +73,7 @@ ID_Previous = wx.NewId()
 ID_ViewAssistant = wx.NewId()\r
 ID_ViewCommands = wx.NewId()\r
 ID_ViewFolders = wx.NewId()\r
+ID_ViewNote = wx.NewId()\r
 ID_ViewOutput = wx.NewId()\r
 ID_ViewPlaylists = wx.NewId()\r
 ID_ViewProperties = wx.NewId()\r
@@ -90,8 +92,21 @@ class Hooke(wx.App):
         self.SetAppName('Hooke')\r
         self.SetVendorName('')\r
 \r
-        windowPosition = (config['main']['left'], config['main']['top'])\r
-        windowSize = (config['main']['width'], config['main']['height'])\r
+        window_height = config['main']['height']\r
+        window_left= config['main']['left']\r
+        window_top = config['main']['top']\r
+        window_width = config['main']['width']\r
+\r
+        #sometimes, the ini file gets confused and sets 'left'\r
+        #and 'top' to large negative numbers\r
+        #let's catch and fix this\r
+        #keep small negative numbers, the user might want those\r
+        if window_left < -window_width:\r
+            window_left = 0\r
+        if window_top < -window_height:\r
+            window_top = 0\r
+        window_position = (window_left, window_top)\r
+        window_size = (window_width, window_height)\r
 \r
         #setup the splashscreen\r
         if config['splashscreen']['show']:\r
@@ -132,7 +147,7 @@ class Hooke(wx.App):
         def make_command_class(*bases):\r
             #create metaclass with plugins and plotmanipulators\r
             return type(HookeFrame)("HookeFramePlugged", bases + (HookeFrame,), {})\r
-        frame = make_command_class(*plugin_objects)(parent=None, id=wx.ID_ANY, title='Hooke', pos=windowPosition, size=windowSize)\r
+        frame = make_command_class(*plugin_objects)(parent=None, id=wx.ID_ANY, title='Hooke', pos=window_position, size=window_size)\r
         frame.Show(True)\r
         self.SetTopWindow(frame)\r
 \r
@@ -187,14 +202,15 @@ class HookeFrame(wx.Frame):
         self.panelFolders = self.CreatePanelFolders()\r
         self.panelPlaylists = self.CreatePanelPlaylists()\r
         self.panelProperties = self.CreatePanelProperties()\r
+        self.panelNote = self.CreatePanelNote()\r
         self.panelOutput = self.CreatePanelOutput()\r
         self.panelResults = self.CreatePanelResults()\r
         self.plotNotebook = self.CreateNotebook()\r
-        #self.textCtrlCommandLine=self.CreateCommandLine()\r
 \r
         # add panes\r
         self._mgr.AddPane(self.panelFolders, aui.AuiPaneInfo().Name('Folders').Caption('Folders').Left().CloseButton(True).MaximizeButton(False))\r
         self._mgr.AddPane(self.panelPlaylists, aui.AuiPaneInfo().Name('Playlists').Caption('Playlists').Left().CloseButton(True).MaximizeButton(False))\r
+        self._mgr.AddPane(self.panelNote, aui.AuiPaneInfo().Name('Note').Caption('Note').Left().CloseButton(True).MaximizeButton(False))\r
         self._mgr.AddPane(self.plotNotebook, aui.AuiPaneInfo().Name('Plots').CenterPane().PaneBorder(False))\r
         self._mgr.AddPane(self.panelCommands, aui.AuiPaneInfo().Name('Commands').Caption('Settings and commands').Right().CloseButton(True).MaximizeButton(False))\r
         self._mgr.AddPane(self.panelProperties, aui.AuiPaneInfo().Name('Properties').Caption('Properties').Right().CloseButton(True).MaximizeButton(False))\r
@@ -245,6 +261,8 @@ class HookeFrame(wx.Frame):
         plugin_config = ConfigObj(ini_path)\r
         #self.config.merge(plugin_config)\r
         self.configs['core'] = plugin_config\r
+        #existing_commands contains: {command: plugin}\r
+        existing_commands = {}\r
         #make sure we execute _plug_init() for every command line plugin we import\r
         for plugin in self.config['plugins']:\r
             if self.config['plugins'][plugin]:\r
@@ -265,9 +283,18 @@ class HookeFrame(wx.Frame):
                         #add to plugins\r
                         commands = eval('dir(module.' + plugin+ '.' + plugin + 'Commands)')\r
                         #keep only commands (ie names that start with 'do_')\r
-                        #TODO: check for existing commands and warn the user!\r
                         commands = [command for command in commands if command.startswith('do_')]\r
                         if commands:\r
+                            for command in commands:\r
+                                if existing_commands.has_key(command):\r
+                                    message_str = 'Adding "' + command + '" in plugin "' + plugin + '".\n\n'\r
+                                    message_str += '"' + command + '" already exists in "' + str(existing_commands[command]) + '".\n\n'\r
+                                    message_str += 'Only "' + command + '" in "' + str(existing_commands[command]) + '" will work.\n\n'\r
+                                    message_str += 'Please rename one of the commands in the source code and restart Hooke or disable one of the plugins.'\r
+                                    dialog = wx.MessageDialog(self, message_str, 'Warning', wx.OK|wx.ICON_WARNING|wx.CENTER)\r
+                                    dialog.ShowModal()\r
+                                    dialog.Destroy()\r
+                                existing_commands[command] = plugin\r
                             self.plugins[plugin] = commands\r
                         try:\r
                             #initialize the plugin\r
@@ -276,11 +303,12 @@ class HookeFrame(wx.Frame):
                             pass\r
                     except ImportError:\r
                         pass\r
-        #initialize the commands tree\r
+        #add commands from hooke.py i.e. 'core' commands\r
         commands = dir(HookeFrame)\r
         commands = [command for command in commands if command.startswith('do_')]\r
         if commands:\r
             self.plugins['core'] = commands\r
+        #initialize the commands tree\r
         self.panelCommands.Initialize(self.plugins)\r
         for command in dir(self):\r
             if command.startswith('plotmanip_'):\r
@@ -288,7 +316,6 @@ class HookeFrame(wx.Frame):
 \r
         #load default list, if possible\r
         self.do_loadlist(self.config['core']['list'])\r
-        #self.do_loadlist()\r
 \r
     def _BindEvents(self):\r
         #TODO: figure out if we can use the eventManager for menu ranges\r
@@ -324,6 +351,7 @@ class HookeFrame(wx.Frame):
         evtmgr.eventManager.Register(self.OnExecute, wx.EVT_BUTTON, self.panelCommands.ExecuteButton)\r
         evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self.panelCommands.CommandsTree)\r
         evtmgr.eventManager.Register(self.OnTreeCtrlItemActivated, wx.EVT_TREE_ITEM_ACTIVATED, self.panelCommands.CommandsTree)\r
+        evtmgr.eventManager.Register(self.OnUpdateNote, wx.EVT_BUTTON, self.panelNote.UpdateButton)\r
         #property editor\r
         self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged)\r
         #results panel\r
@@ -373,6 +401,8 @@ class HookeFrame(wx.Frame):
                     self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown())\r
                 if pane.name == 'Commands':\r
                     self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown())\r
+                if pane.name == 'Note':\r
+                    self.MenuBar.FindItemById(ID_ViewNote).Check(pane.window.IsShown())\r
                 if pane.name == 'Properties':\r
                     self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown())\r
                 if pane.name == 'Output':\r
@@ -397,6 +427,8 @@ class HookeFrame(wx.Frame):
         #commands tree\r
         evtmgr.eventManager.DeregisterListener(self.OnExecute)\r
         evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlCommandsSelectionChanged)\r
+        evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlItemActivated)\r
+        evtmgr.eventManager.DeregisterListener(self.OnUpdateNote)\r
 \r
     def AddPlaylist(self, playlist=None, name='Untitled'):\r
         if playlist and playlist.count > 0:\r
@@ -421,10 +453,11 @@ class HookeFrame(wx.Frame):
             playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)\r
             #add all files to the Playlist tree\r
 #            files = {}\r
+            hide_curve_extension = self.GetBoolFromConfig('core', 'preferences', 'hide_curve_extension')\r
             for index, file_to_add in enumerate(playlist.files):\r
-                #TODO: optionally remove the extension from the name of the curve\r
-                #item_text, extension = os.path.splitext(curve.name)\r
-                #curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, item_text, 1)\r
+                #optionally remove the extension from the name of the curve\r
+                if hide_curve_extension:\r
+                    file_to_add.name = lh.remove_extension(file_to_add.name)\r
                 file_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, file_to_add.name, 1)\r
                 if index == playlist.index:\r
                     self.panelPlaylists.PlaylistsTree.SelectItem(file_ID)\r
@@ -438,6 +471,7 @@ class HookeFrame(wx.Frame):
             #self.playlists[playlist.name] = [playlist, figure]\r
             self.panelPlaylists.PlaylistsTree.Expand(playlist_root)\r
             self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
+            self.UpdateNote()\r
             self.UpdatePlot()\r
 \r
     def AppendToOutput(self, text):\r
@@ -485,6 +519,9 @@ class HookeFrame(wx.Frame):
         folder = self.config['core']['workdir']\r
         return wx.GenericDirCtrl(self, -1, dir=folder, size=(200, 250), style=wx.DIRCTRL_SHOW_FILTERS, filter=filters, defaultFilter=index)\r
 \r
+    def CreatePanelNote(self):\r
+        return panels.note.Note(self)\r
+\r
     def CreatePanelOutput(self):\r
         return wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)\r
 \r
@@ -531,8 +568,8 @@ class HookeFrame(wx.Frame):
         view_menu.AppendCheckItem(ID_ViewAssistant, 'Assistant\tF9')\r
         view_menu.AppendCheckItem(ID_ViewResults, 'Results\tF10')\r
         view_menu.AppendCheckItem(ID_ViewOutput, 'Output\tF11')\r
+        view_menu.AppendCheckItem(ID_ViewNote, 'Note\tF12')\r
         #perspectives\r
-#        perspectives_menu = self.CreatePerspectivesMenu()\r
         perspectives_menu = wx.Menu()\r
 \r
         #help\r
@@ -540,7 +577,6 @@ class HookeFrame(wx.Frame):
         help_menu.Append(wx.ID_ABOUT, 'About Hooke')\r
         #put it all together\r
         menu_bar.Append(file_menu, 'File')\r
-#        menu_bar.Append(edit_menu, 'Edit')\r
         menu_bar.Append(view_menu, 'View')\r
         menu_bar.Append(perspectives_menu, "Perspectives")\r
         self.UpdatePerspectivesMenu()\r
@@ -834,6 +870,7 @@ class HookeFrame(wx.Frame):
             if playlist.count > 1:\r
                 playlist.next()\r
                 self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
+                self.UpdateNote()\r
                 self.UpdatePlot()\r
 \r
     def OnNotebookPageClose(self, event):\r
@@ -866,6 +903,7 @@ class HookeFrame(wx.Frame):
                     playlist = self.GetActivePlaylist()\r
                     playlist.index = index\r
                     self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
+                    self.UpdateNote()\r
                     self.UpdatePlot()\r
             #if you uncomment the following line, the tree will collapse/expand as well\r
             #event.Skip()\r
@@ -909,6 +947,7 @@ class HookeFrame(wx.Frame):
         if playlist.count > 1:\r
             playlist.previous()\r
             self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
+            self.UpdateNote()\r
             self.UpdatePlot()\r
 \r
     def OnPropGridChanged (self, event):\r
@@ -927,29 +966,8 @@ class HookeFrame(wx.Frame):
     def OnRestorePerspective(self, event):\r
         name = self.MenuBar.FindItemById(event.GetId()).GetLabel()\r
         self._RestorePerspective(name)\r
-#        self._mgr.LoadPerspective(self._perspectives[name])\r
-#        self.config['perspectives']['active'] = name\r
-#        self._mgr.Update()\r
-#        all_panes = self._mgr.GetAllPanes()\r
-#        for pane in all_panes:\r
-#            if not pane.name.startswith('toolbar'):\r
-#                if pane.name == 'Assistant':\r
-#                    self.MenuBar.FindItemById(ID_ViewAssistant).Check(pane.window.IsShown())\r
-#                if pane.name == 'Folders':\r
-#                    self.MenuBar.FindItemById(ID_ViewFolders).Check(pane.window.IsShown())\r
-#                if pane.name == 'Playlists':\r
-#                    self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown())\r
-#                if pane.name == 'Commands':\r
-#                    self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown())\r
-#                if pane.name == 'Properties':\r
-#                    self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown())\r
-#                if pane.name == 'Output':\r
-#                    self.MenuBar.FindItemById(ID_ViewOutput).Check(pane.window.IsShown())\r
-#                if pane.name == 'Results':\r
-#                    self.MenuBar.FindItemById(ID_ViewResults).Check(pane.window.IsShown())\r
 \r
     def OnResultsCheck(self, index, flag):\r
-        #TODO: fix for multiple results\r
         results = self.GetActivePlot().results\r
         if results.has_key(self.results_str):\r
             results[self.results_str].results[index].visible = flag\r
@@ -1060,6 +1078,13 @@ class HookeFrame(wx.Frame):
     def OnTreeCtrlItemActivated(self, event):\r
         self.OnExecute(event)\r
 \r
+    def OnUpdateNote(self, event):\r
+        '''\r
+        Saves the note to the active file.\r
+        '''\r
+        active_file = self.GetActiveFile()\r
+        active_file.note = self.panelNote.Editor.GetValue()\r
+\r
     def OnView(self, event):\r
         menu_id = event.GetId()\r
         menu_item = self.MenuBar.FindItemById(menu_id)\r
@@ -1075,9 +1100,9 @@ class HookeFrame(wx.Frame):
 \r
     def _clickize(self, xvector, yvector, index):\r
         '''\r
-        returns a ClickedPoint() object from an index and vectors of x, y coordinates\r
+        Returns a ClickedPoint() object from an index and vectors of x, y coordinates\r
         '''\r
-        point = lh.ClickedPoint()\r
+        point = lib.clickedpoint.ClickedPoint()\r
         point.index = index\r
         point.absolute_coords = xvector[index], yvector[index]\r
         point.find_graph_coords(xvector, yvector)\r
@@ -1085,7 +1110,7 @@ class HookeFrame(wx.Frame):
 \r
     def _delta(self, message='Click 2 points', whatset=lh.RETRACTION):\r
         '''\r
-        calculates the difference between two clicked points\r
+        Calculates the difference between two clicked points\r
         '''\r
         clicked_points = self._measure_N_points(N=2, message=message, whatset=whatset)\r
 \r
@@ -1107,7 +1132,7 @@ class HookeFrame(wx.Frame):
         General helper function for N-points measurements\r
         By default, measurements are done on the retraction\r
         '''\r
-        if message != '':\r
+        if message:\r
             dialog = wx.MessageDialog(None, message, 'Info', wx.OK)\r
             dialog.ShowModal()\r
 \r
@@ -1120,7 +1145,7 @@ class HookeFrame(wx.Frame):
 \r
         points = []\r
         for clicked_point in clicked_points:\r
-            point = lh.ClickedPoint()\r
+            point = lib.clickedpoint.ClickedPoint()\r
             point.absolute_coords = clicked_point[0], clicked_point[1]\r
             point.dest = 0\r
             #TODO: make this optional?\r
@@ -1131,6 +1156,29 @@ class HookeFrame(wx.Frame):
             points.append(point)\r
         return points\r
 \r
+    def do_copylog(self):\r
+        '''\r
+        Copies all files in the current playlist that have a note to the destination folder.\r
+        destination: select folder where you want the files to be copied\r
+        use_LVDT_folder: when checked, the files will be copied to a folder called 'LVDT' in the destination folder (for MFP-1D files only)\r
+        '''\r
+        playlist = self.GetActivePlaylist()\r
+        if playlist is not None:\r
+            destination = self.GetStringFromConfig('core', 'copylog', 'destination')\r
+            if not os.path.isdir(destination):\r
+                os.makedirs(destination)\r
+            for current_file in playlist.files:\r
+                if current_file.note:\r
+                    shutil.copy(current_file.filename, destination)\r
+                    if current_file.driver.filetype == 'mfp1d':\r
+                        filename = current_file.filename.replace('deflection', 'LVDT', 1)\r
+                        path, name = os.path.split(filename)\r
+                        filename = os.path.join(path, 'lvdt', name)\r
+                        use_LVDT_folder = self.GetBoolFromConfig('core', 'copylog', 'use_LVDT_folder')\r
+                        if use_LVDT_folder:\r
+                            destination = os.path.join(destination, 'LVDT')\r
+                        shutil.copy(filename, destination)\r
+\r
     def do_plotmanipulators(self):\r
         '''\r
         Please select the plotmanipulators you would like to use\r
@@ -1140,6 +1188,14 @@ class HookeFrame(wx.Frame):
         '''\r
         self.UpdatePlot()\r
 \r
+    def do_preferences(self):\r
+        '''\r
+        Please set general preferences for Hooke here.\r
+        hide_curve_extension: hides the extension of the force curve files.\r
+                              not recommended for 'picoforce' files\r
+        '''\r
+        pass\r
+\r
     def do_test(self):\r
         '''\r
         Use this command for testing purposes. You find do_test in hooke.py.\r
@@ -1162,9 +1218,21 @@ class HookeFrame(wx.Frame):
         self.AppendToOutput('NumPy version: ' + numpy_version)\r
         self.AppendToOutput('---')\r
         self.AppendToOutput('Platform: ' + str(platform.uname()))\r
-        #TODO: adapt to 'new' config\r
-        #self.AppendToOutput('---')\r
-        #self.AppendToOutput('Loaded plugins:', self.config['loaded_plugins'])\r
+        self.AppendToOutput('******************************')\r
+        self.AppendToOutput('Loaded plugins')\r
+        self.AppendToOutput('---')\r
+\r
+        #sort the plugins into alphabetical order\r
+        plugins_list = [key for key, value in self.plugins.iteritems()]\r
+        plugins_list.sort()\r
+        for plugin in plugins_list:\r
+            self.AppendToOutput(plugin)\r
+\r
+    def UpdateNote(self):\r
+        #update the note for the active file\r
+        active_file = self.GetActiveFile()\r
+        if active_file is not None:\r
+            self.panelNote.Editor.SetValue(active_file.note)\r
 \r
     def UpdatePerspectivesMenu(self):\r
         #add perspectives to menubar and _perspectives\r
@@ -1178,7 +1246,7 @@ class HookeFrame(wx.Frame):
                     perspectiveFile = open(filename, 'rU')\r
                     perspective = perspectiveFile.readline()\r
                     perspectiveFile.close()\r
-                    if perspective != '':\r
+                    if perspective:\r
                         name, extension = os.path.splitext(perspectiveFilename)\r
                         if extension == '.txt':\r
                             self._perspectives[name] = perspective\r
@@ -1219,42 +1287,46 @@ class HookeFrame(wx.Frame):
         if playlist is not None:\r
             if playlist.index >= 0:\r
                 self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
+                self.UpdateNote()\r
                 self.UpdatePlot()\r
 \r
     def UpdatePlot(self, plot=None):\r
 \r
         def add_to_plot(curve):\r
             if curve.visible and curve.x and curve.y:\r
+                #get the index of the subplot to use as destination\r
                 destination = (curve.destination.column - 1) * number_of_rows + curve.destination.row - 1\r
+                #set all parameters for the plot\r
                 axes_list[destination].set_title(curve.title)\r
-                axes_list[destination].set_xlabel(multiplier_x + curve.units.x)\r
-                axes_list[destination].set_ylabel(multiplier_y + curve.units.y)\r
+                axes_list[destination].set_xlabel(curve.multiplier.x + curve.units.x)\r
+                axes_list[destination].set_ylabel(curve.multiplier.y + curve.units.y)\r
+                #set the formatting details for the scale\r
+                formatter_x = lib.curve.PrefixFormatter(curve.decimals.x, curve.multiplier.x, use_zero)\r
+                formatter_y = lib.curve.PrefixFormatter(curve.decimals.y, curve.multiplier.y, use_zero)\r
+                axes_list[destination].xaxis.set_major_formatter(formatter_x)\r
+                axes_list[destination].yaxis.set_major_formatter(formatter_y)\r
                 if curve.style == 'plot':\r
                     axes_list[destination].plot(curve.x, curve.y, color=curve.color, label=curve.label, lw=curve.linewidth, zorder=1)\r
                 if curve.style == 'scatter':\r
                     axes_list[destination].scatter(curve.x, curve.y, color=curve.color, label=curve.label, s=curve.size, zorder=2)\r
-\r
-        def get_format_x(x, pos):\r
-            'The two args are the value and tick position'\r
-            multiplier = lib.prettyformat.get_exponent(multiplier_x)\r
-            decimals_str = '%.' + str(decimals_x) + 'f'\r
-            return decimals_str % (x/(10 ** multiplier))\r
-\r
-        def get_format_y(x, pos):\r
-            'The two args are the value and tick position'\r
-            multiplier = lib.prettyformat.get_exponent(multiplier_y)\r
-            decimals_str = '%.' + str(decimals_y) + 'f'\r
-            return decimals_str % (x/(10 ** multiplier))\r
-\r
-        decimals_x = self.GetIntFromConfig('plot', 'x_decimals')\r
-        decimals_y = self.GetIntFromConfig('plot', 'y_decimals')\r
-        multiplier_x = self.GetStringFromConfig('plot', 'x_multiplier')\r
-        multiplier_y = self.GetStringFromConfig('plot', 'y_multiplier')\r
+                #add the legend if necessary\r
+                if curve.legend:\r
+                    axes_list[destination].legend()\r
 \r
         if plot is None:\r
             active_file = self.GetActiveFile()\r
             if not active_file.driver:\r
+                #the first time we identify a file, the following need to be set\r
                 active_file.identify(self.drivers)\r
+                for curve in active_file.plot.curves:\r
+                    curve.decimals.x = self.GetIntFromConfig('core', 'preferences', 'x_decimals')\r
+                    curve.decimals.y = self.GetIntFromConfig('core', 'preferences', 'y_decimals')\r
+                    curve.legend = self.GetBoolFromConfig('core', 'preferences', 'legend')\r
+                    curve.multiplier.x = self.GetStringFromConfig('core', 'preferences', 'x_multiplier')\r
+                    curve.multiplier.y = self.GetStringFromConfig('core', 'preferences', 'y_multiplier')\r
+            if active_file.driver is None:\r
+                self.AppendToOutput('Invalid file: ' + active_file.filename)\r
+                return\r
             self.displayed_plot = copy.deepcopy(active_file.plot)\r
             #add raw curves to plot\r
             self.displayed_plot.raw_curves = copy.deepcopy(self.displayed_plot.curves)\r
@@ -1267,31 +1339,29 @@ class HookeFrame(wx.Frame):
             self.displayed_plot = copy.deepcopy(plot)\r
 \r
         figure = self.GetActiveFigure()\r
-\r
         figure.clear()\r
-        figure.suptitle(self.displayed_plot.title, fontsize=14)\r
-\r
+        #use '0' instead of e.g. '0.00' for scales\r
+        use_zero = self.GetBoolFromConfig('core', 'preferences', 'use_zero')\r
+        #optionally remove the extension from the title of the plot\r
+        hide_curve_extension = self.GetBoolFromConfig('core', 'preferences', 'hide_curve_extension')\r
+        if hide_curve_extension:\r
+            title = lh.remove_extension(self.displayed_plot.title)\r
+        else:\r
+            title = self.displayed_plot.title\r
+        figure.suptitle(title, fontsize=14)\r
+        #create the list of all axes necessary (rows and columns)\r
         axes_list =[]\r
-\r
         number_of_columns = max([curve.destination.column for curve in self.displayed_plot.curves])\r
         number_of_rows = max([curve.destination.row for curve in self.displayed_plot.curves])\r
-\r
         for index in range(number_of_rows * number_of_columns):\r
             axes_list.append(figure.add_subplot(number_of_rows, number_of_columns, index + 1))\r
-\r
-        for axes in axes_list:\r
-            formatter_x = FuncFormatter(get_format_x)\r
-            formatter_y = FuncFormatter(get_format_y)\r
-            axes.xaxis.set_major_formatter(formatter_x)\r
-            axes.yaxis.set_major_formatter(formatter_y)\r
-\r
+        #add all curves to the corresponding plots\r
         for curve in self.displayed_plot.curves:\r
             add_to_plot(curve)\r
 \r
         #make sure the titles of 'subplots' do not overlap with the axis labels of the 'main plot'\r
         figure.subplots_adjust(hspace=0.3)\r
 \r
-        #TODO: add multiple results support to fit in curve.results:\r
         #display results\r
         self.panelResults.ClearResults()\r
         if self.displayed_plot.results.has_key(self.results_str):\r
@@ -1300,11 +1370,7 @@ class HookeFrame(wx.Frame):
             self.panelResults.DisplayResults(self.displayed_plot.results[self.results_str])\r
         else:\r
             self.panelResults.ClearResults()\r
-\r
-        legend = self.GetBoolFromConfig('plot', 'legend')\r
-        for axes in axes_list:\r
-            if legend:\r
-                axes.legend()\r
+        #refresh the plot\r
         figure.canvas.draw()\r
 \r
 if __name__ == '__main__':\r
@@ -1320,5 +1386,3 @@ if __name__ == '__main__':
     app = Hooke(redirect=redirect)\r
 \r
     app.MainLoop()\r
-\r
-\r
diff --git a/lib/clickedpoint.py b/lib/clickedpoint.py
new file mode 100644 (file)
index 0000000..377ff2a
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/env python\r
+\r
+'''\r
+clickedpoint.py\r
+\r
+ClickedPoint class for Hooke.\r
+\r
+Copyright 2010 by Dr. Rolf Schmidt (Concordia University, Canada)\r
+\r
+This program is released under the GNU General Public License version 2.\r
+'''\r
+\r
+from scipy import arange\r
+\r
+class ClickedPoint(object):\r
+    '''\r
+    This class defines what a clicked point on the curve plot is.\r
+    '''\r
+    def __init__(self):\r
+\r
+        self.is_marker = None #boolean ; decides if it is a marker\r
+        self.is_line_edge = None #boolean ; decides if it is the edge of a line (unused)\r
+        self.absolute_coords = (None, None) #(float,float) ; the absolute coordinates of the clicked point on the graph\r
+        self.graph_coords = (None, None) #(float,float) ; the coordinates of the plot that are nearest in X to the clicked point\r
+        self.index = None #integer ; the index of the clicked point with respect to the vector selected\r
+        self.dest = None #0 or 1 ; 0=top plot 1=bottom plot\r
+\r
+    def find_graph_coords(self, xvector, yvector):\r
+        '''\r
+        Given a clicked point on the plot, finds the nearest point in the dataset (in X) that\r
+        corresponds to the clicked point.\r
+        '''\r
+        dists = []\r
+        for index in arange(1, len(xvector), 1):\r
+            dists.append(((self.absolute_coords[0] - xvector[index]) ** 2)+((self.absolute_coords[1] - yvector[index]) ** 2))\r
+\r
+        self.index=dists.index(min(dists))\r
+        self.graph_coords=(xvector[self.index], yvector[self.index])\r
index 0d81a966daf68e2336db81fc14f92f6b2c2db9b4..a6143c9fa620e24f97a99edb4e6350d15a44e0f1 100644 (file)
@@ -10,13 +10,19 @@ Copyright 2010 by Dr. Rolf Schmidt (Concordia University, Canada)
 This program is released under the GNU General Public License version 2.\r
 '''\r
 \r
+from  matplotlib.ticker import Formatter\r
+import lib.prettyformat\r
+\r
 class Curve(object):\r
 \r
     def __init__(self):\r
         self.color = 'blue'\r
+        self.decimals = Decimals()\r
         self.destination = Destination()\r
         self.label = ''\r
+        self.legend = False\r
         self.linewidth = 1\r
+        self.multiplier = Multiplier()\r
         self.size = 0.5\r
         self.style = 'plot'\r
         self.title = ''\r
@@ -26,6 +32,20 @@ class Curve(object):
         self.y = []\r
 \r
 \r
+class Data(object):\r
+\r
+    def __init__(self):\r
+        self.x = []\r
+        self.y = []\r
+\r
+\r
+class Decimals(object):\r
+\r
+    def __init__(self):\r
+        self.x = 2\r
+        self.y = 2\r
+\r
+\r
 class Destination(object):\r
 \r
     def __init__(self):\r
@@ -33,6 +53,32 @@ class Destination(object):
         self.row = 1\r
 \r
 \r
+class Multiplier(object):\r
+\r
+    def __init__(self):\r
+        self.x = 'n'\r
+        self.y = 'p'\r
+\r
+\r
+class PrefixFormatter(Formatter):\r
+    '''\r
+    Formatter (matplotlib) class that uses power prefixes.\r
+    '''\r
+    def __init__(self, decimals=2, multiplier='n', use_zero=True):\r
+        self.decimals = decimals\r
+        self.multiplier = multiplier\r
+        self.use_zero = use_zero\r
+\r
+    def __call__(self, x, pos=None):\r
+        'Return the format for tick val *x* at position *pos*'\r
+        if self.use_zero:\r
+            if x == 0:\r
+                return '0'\r
+        multiplier = lib.prettyformat.get_exponent(self.multiplier)\r
+        decimals_str = '%.' + str(self.decimals) + 'f'\r
+        return decimals_str % (x / (10 ** multiplier))\r
+\r
+\r
 class Units(object):\r
 \r
     def __init__(self):\r
diff --git a/lib/delta.py b/lib/delta.py
new file mode 100644 (file)
index 0000000..7996daa
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+'''
+delta.py
+
+Delta class for Hooke to describe differences between 2 points.
+
+Copyright 2010 by Dr. Rolf Schmidt (Concordia University, Canada)
+
+This program is released under the GNU General Public License version 2.
+'''
+
+from lib.curve import Units
+
+class Point(object):
+
+    def __init__(self):
+        self.x = 0
+        self.y = 0
+
+class Delta(object):
+
+    def __init__(self):
+        self.point1 = Point()
+        self.point2 = Point()
+        self.units = Units()\r
+\r
+    def get_delta_x(self):\r
+        return self.point1.x - self.point2.x\r
+\r
+    def get_delta_y(self):\r
+        return self.point1.y - self.point2.y\r
+\r
+
index c7ff7d3141182e7c0f95e64f853ba57e9c98b26f..6af41db343b4a607881691a15a142b8cf830bbb6 100644 (file)
@@ -17,7 +17,7 @@ class File(object):
 \r
     def __init__(self, filename=None, drivers=None):\r
         self.driver = None\r
-        self.notes = ''\r
+        self.note = ''\r
         self.plot = lib.plot.Plot()\r
         if filename is None:\r
             self.filename = None\r
index 11d8565545f5b84cbc52d3efb43f54fcab9327e6..c377ed065dbdf3fa0608e575c4307ef8dc864f46 100644 (file)
@@ -26,6 +26,12 @@ hookeDir=''
 EXTENSION = 0
 RETRACTION = 1
 
+def coth(z):
+    '''
+    Hyperbolic cotangent.
+    '''
+    return (numpy.exp(2 * z) + 1) / (numpy.exp(2 * z) - 1)
+
 def delete_empty_lines_from_xmlfile(filename):
     #the following 3 lines are needed to strip newlines.
     #Otherwise, since newlines are XML elements too, the parser would read them
@@ -35,56 +41,53 @@ def delete_empty_lines_from_xmlfile(filename):
     aFile=''.join(aFile)
     return aFile
 
+def fit_interval_nm(start_index, x_vect, nm, backwards):
+    '''
+    Calculates the number of points to fit, given a fit interval in nm
+    start_index: index of point
+    plot: plot to use
+    backwards: if true, finds a point backwards.
+    '''
+    c = 0
+    i = start_index
+    maxlen=len(x_vect)
+    while abs(x_vect[i] - x_vect[start_index]) * (10**9) < nm:
+        if i == 0 or i == maxlen-1: #we reached boundaries of vector!
+            return c
+        if backwards:
+            i -= 1
+        else:
+            i += 1
+        c += 1
+    return c
+
 def get_file_path(filename, folders = []):
     if os.path.dirname(filename) == '' or os.path.isabs(filename) == False:
         path = ''
         for folder in folders:
             path = os.path.join(path, folder)
         filename = os.path.join(hookeDir, path, filename)
-
     return filename
 
-def coth(z):
+def pickup_contact_point(filename=''):
     '''
-    hyperbolic cotangent
+    Picks up the contact point by left-clicking.
     '''
-    return (numpy.exp(2 * z) + 1) / (numpy.exp(2 * z) - 1)
-
-class ClickedPoint(object):
+    contact_point = self._measure_N_points(N=1, message='Please click on the contact point.')[0]
+    contact_point_index = contact_point.index
+    self.wlccontact_point = contact_point
+    self.wlccontact_index = contact_point.index
+    self.wlccurrent = filename
+    return contact_point, contact_point_index
+
+def remove_extension(filename):
     '''
-    this class defines what a clicked point on the curve plot is
+    Removes the extension from a filename.
     '''
-    def __init__(self):
-
-        self.is_marker = None #boolean ; decides if it is a marker
-        self.is_line_edge = None #boolean ; decides if it is the edge of a line (unused)
-        self.absolute_coords = (None, None) #(float,float) ; the absolute coordinates of the clicked point on the graph
-        self.graph_coords = (None, None) #(float,float) ; the coordinates of the plot that are nearest in X to the clicked point
-        self.index = None #integer ; the index of the clicked point with respect to the vector selected
-        self.dest = None #0 or 1 ; 0=top plot 1=bottom plot
-
-    def find_graph_coords(self, xvector, yvector):
-        '''
-        Given a clicked point on the plot, finds the nearest point in the dataset (in X) that
-        corresponds to the clicked point.
-        '''
-        dists = []
-        for index in scipy.arange(1, len(xvector), 1):
-            dists.append(((self.absolute_coords[0] - xvector[index]) ** 2)+((self.absolute_coords[1] - yvector[index]) ** 2))
-
-        self.index=dists.index(min(dists))
-        self.graph_coords=(xvector[self.index], yvector[self.index])
+    name, extension = os.path.splitext(filename)
+    return name
 
 #CSV-HELPING FUNCTIONS
-def transposed2(lists, defval=0):
-    '''
-    transposes a list of lists, i.e. from [[a,b,c],[x,y,z]] to [[a,x],[b,y],[c,z]] without losing
-    elements
-    (by Zoran Isailovski on the Python Cookbook online)
-    '''
-    if not lists: return []
-    return map(lambda *row: [elem or defval for elem in row], *lists)
-
 def csv_write_dictionary(f, data, sorting='COLUMNS'):
     '''
     Writes a CSV file from a dictionary, with keys as first column or row
@@ -105,3 +108,13 @@ def csv_write_dictionary(f, data, sorting='COLUMNS'):
 
     if sorting=='ROWS':
         print 'Not implemented!' #FIXME: implement it.
+
+def transposed2(lists, defval=0):
+    '''
+    transposes a list of lists, i.e. from [[a,b,c],[x,y,z]] to [[a,x],[b,y],[c,z]] without losing
+    elements
+    (by Zoran Isailovski on the Python Cookbook online)
+    '''
+    if not lists: return []
+    return map(lambda *row: [elem or defval for elem in row], *lists)
+
index 92b9629b46b5aecf128de85a099300f956019e12..90a730fec687ab575c583ca1f718612cf713af2a 100644 (file)
@@ -91,11 +91,19 @@ class Playlist(object):
             #rebuild a data structure from the xml attributes\r
             #the next two lines are here for backwards compatibility, newer playlist files use 'filename' instead of 'path'\r
             if element.hasAttribute('path'):\r
+                #path, name = os.path.split(element.getAttribute('path'))\r
+                #path = path.split(os.sep)\r
+                #filename = lib.libhooke.get_file_path(name, path)\r
                 filename = element.getAttribute('path')\r
             if element.hasAttribute('filename'):\r
+                #path, name = os.path.split(element.getAttribute('filename'))\r
+                #path = path.split(os.sep)\r
+                #filename = lib.libhooke.get_file_path(name, path)\r
                 filename = element.getAttribute('filename')\r
             if os.path.isfile(filename):\r
                 data_file = lib.file.File(filename)\r
+                if element.hasAttribute('note'):\r
+                    data_file.note = element.getAttribute('note')\r
                 self.files.append(data_file)\r
         self.count = len(self.files)\r
         if self.count > 0:\r
index bbcbcf02da50724e807708d094a1b350b7341d32..5ace11b631985b8a0f9a49ef030527defdd63ba1 100644 (file)
@@ -36,7 +36,7 @@ def pretty_format(value, unit='', decimals=-1, multiplier=0, leading_spaces=Fals
     if multiplier == 0:\r
         multiplier=get_multiplier(value)\r
     unit_str = ''\r
-    if unit != '':\r
+    if unit:\r
         unit_str = ' ' + get_prefix(multiplier) + unit\r
     if decimals >= 0:\r
         format_str = '% ' + repr(leading_spaces_int + decimals) + '.' + repr(decimals) + 'f'\r
diff --git a/panels/note.py b/panels/note.py
new file mode 100644 (file)
index 0000000..2257d88
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env python\r
+\r
+'''\r
+note.py\r
+\r
+Note panel for Hooke.\r
+\r
+Copyright 2010 by Dr. Rolf Schmidt (Concordia University, Canada)\r
+\r
+This program is released under the GNU General Public License version 2.\r
+'''\r
+import wx\r
+\r
+class Note(wx.Panel):\r
+\r
+    def __init__(self, parent):\r
+        wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS|wx.NO_BORDER, size=(160, 200))\r
+\r
+        self.Editor = wx.TextCtrl(self, style=wx.TE_MULTILINE)\r
+\r
+        self.UpdateButton = wx.Button(self, -1, 'Update note')\r
+\r
+        sizer = wx.BoxSizer(wx.VERTICAL)\r
+        sizer.Add(self.Editor, 1, wx.EXPAND)\r
+        sizer.Add(self.UpdateButton, 0, wx.EXPAND)\r
+\r
+        self.SetSizer(sizer)\r
+        self.SetAutoLayout(True)\r
index 4ea3243237fdca062bdf40b31d573bbb62a8e8ef..7a1515e4991e0801ff4f8a9e93c62a20f098f637 100644 (file)
@@ -145,7 +145,7 @@ class PlotPanel(wx.Panel):
 \r
             #line.figure.canvas.draw()\r
         if self.display_coordinates:\r
-            coordinateString = ''.join([str(event.xdata), ' ', str(event.ydata)])\r
+            coordinateString = ''.join(['x: ', str(event.xdata), ' y: ', str(event.ydata)])\r
             #TODO: pretty format\r
             self.SetStatusText(coordinateString)\r
 \r
index 4b10f8373e188ccb244199bb4f7294874f2c62e6..c53de1186f26bc0330cee9fad8c5877d54d3d3d8 100644 (file)
@@ -39,7 +39,7 @@ class Results(wx.Panel):
         #Returns the width of a string in pixels\r
         #Unfortunately, it does not work terribly well (although it should).\r
         #Thus, we have to add a bit afterwards.\r
-        #Annoys the heck out of me (me being Rolf).\r
+        #Annoys the heck out of me (illysam).\r
         font = self.results_list.GetFont()\r
         dc = wx.WindowDC(self.results_list)\r
         dc.SetFont(font)\r
index 88fb6c0edee823885bbf4fecd1cc4cec9454d37e..726252a35b3079994fec0cd36bf7dbd5793dbffd 100644 (file)
         type = color\r
         value = "(255,0,128)"\r
     \r
+    [[delta_force]]\r
+        default = 10\r
+        minimum = 0\r
+        type = integer\r
+        value = 10\r
+    \r
     [[fit_function]]\r
         default = wlc\r
         elements = wlc, fjc, fjcPEG\r
         default = 0.35e-9\r
         minimum = 0\r
         type = float\r
-        value = 0.175\r
+        value = 0.35\r
+    \r
+    [[plot_linewidth]]\r
+        default = 1\r
+        maximum = 10000\r
+        minimum = 1\r
+        type = integer\r
+        value = 2\r
+    \r
+    [[plot_size]]\r
+        default = 20\r
+        maximum = 10000\r
+        minimum = 1\r
+        type = integer\r
+        value = 4\r
+    \r
+    [[plot_style]]\r
+        default = scatter\r
+        elements = plot, scatter\r
+        type = enum\r
+        value = plot\r
     \r
     [[rebase]]\r
         default = False\r
index 67a85af289ebc569dc6ef01800f3f2e60bebb64b..56d132e1e57b87ae361133f7c0fb9cfce300adca 100644 (file)
@@ -92,6 +92,8 @@ class autopeakCommands:
                     outside of which the peak is automatically discarded (in nm)
         auto_min_p: Minimum persistence length (if using WLC) or Kuhn length (if using FJC)
                     outside of which the peak is automatically discarded (in nm)
+        delta_force: defines the force window in points to locate the peak minimum (default: 10)
+                     do not change unless you know what you are doing
         '''
 
         #default variables
@@ -102,11 +104,16 @@ class autopeakCommands:
         auto_right_baseline = self.GetFloatFromConfig('autopeak', 'auto_right_baseline')
         baseline_clicks = self.GetStringFromConfig('autopeak', 'baseline_clicks')
         color = self.GetColorFromConfig('autopeak', 'color')
+        delta_force = self.GetIntFromConfig('autopeak', 'delta_force')
         fit_function = self.GetStringFromConfig('autopeak', 'fit_function')
         fit_points = self.GetIntFromConfig('autopeak', 'auto_fit_points')
         noauto = self.GetBoolFromConfig('autopeak', 'noauto')
+        #persistence_length has to be given in nm
         persistence_length = self.GetFloatFromConfig('autopeak', 'persistence_length')
         #rebase: redefine the baseline
+        plot_linewidth = self.GetIntFromConfig('autopeak', 'plot_linewidth')
+        plot_size = self.GetIntFromConfig('autopeak', 'plot_size')
+        plot_style = self.GetStringFromConfig('autopeak', 'plot_style')
         rebase = self.GetBoolFromConfig('autopeak', 'rebase')
         reclick = self.GetBoolFromConfig('autopeak', 'reclick')
         slope_span = self.GetIntFromConfig('autopeak', 'auto_slope_span')
@@ -115,7 +122,7 @@ class autopeakCommands:
         if not usepl:
             pl_value = None
         else:
-            pl_value = persistence_length / 10 ** 9
+            pl_value = persistence_length
         usepoints = self.GetBoolFromConfig('autopeak', 'usepoints')
         whatset_str = self.GetStringFromConfig('autopeak', 'whatset')
         if whatset_str == 'extension':
@@ -123,9 +130,6 @@ class autopeakCommands:
         if whatset_str == 'retraction':
             whatset = lh.RETRACTION
 
-        #TODO: should this be variable?
-        delta_force = 10
-
         #setup header column labels for results
         if fit_function == 'wlc':
             fit_results = lib.results.ResultsWLC()
@@ -162,10 +166,10 @@ class autopeakCommands:
 
         #--Contact point arguments
         if reclick:
-            contact_point, contact_point_index = self.pickup_contact_point(filename=filename)
+            contact_point, contact_point_index = lh.pickup_contact_point(filename=filename)
         elif noauto:
             if self.wlccontact_index is None or self.wlccurrent != filename:
-                contact_point, contact_point_index = self.pickup_contact_point(filename=filename)
+                contact_point, contact_point_index = lh.pickup_contact_point(filename=filename)
             else:
                 contact_point = self.wlccontact_point
                 contact_point_index = self.wlccontact_index
@@ -191,13 +195,13 @@ class autopeakCommands:
         if rebase or (self.basecurrent != filename) or self.basepoints is None:
             if baseline_clicks == 'automatic':
                 self.basepoints = []
-                base_index_0 = peak_location[-1] + self.fit_interval_nm(peak_location[-1], retraction.x, auto_right_baseline, False)
+                base_index_0 = peak_location[-1] + lh.fit_interval_nm(peak_location[-1], retraction.x, auto_right_baseline, False)
                 self.basepoints.append(self._clickize(retraction.x, retraction.y, base_index_0))
-                base_index_1 = self.basepoints[0].index + self.fit_interval_nm(self.basepoints[0].index, retraction.x, auto_left_baseline, False)
+                base_index_1 = self.basepoints[0].index + lh.fit_interval_nm(self.basepoints[0].index, retraction.x, auto_left_baseline, False)
                 self.basepoints.append(self._clickize(retraction.x, retraction.y, base_index_1))
             if baseline_clicks == '1 point':
                 self.basepoints=self._measure_N_points(N=1, message='Click on 1 point to select the baseline.', whatset=whatset)
-                base_index_1 = self.basepoints[0].index + self.fit_interval_nm(self.basepoints[0].index, retraction.x, auto_left_baseline, False)
+                base_index_1 = self.basepoints[0].index + lh.fit_interval_nm(self.basepoints[0].index, retraction.x, auto_left_baseline, False)
                 self.basepoints.append(self._clickize(retraction.x, retraction.y, base_index_1))
             if baseline_clicks == '2 points':
                 self.basepoints=self._measure_N_points(N=2, message='Click on 2 points to select the baseline.', whatset=whatset)
@@ -212,7 +216,7 @@ class autopeakCommands:
             #WLC FITTING
             #define fit interval
             if not usepoints:
-                fit_points = self.fit_interval_nm(peak, retraction.x, auto_fit_nm, True)
+                fit_points = lh.fit_interval_nm(peak, retraction.x, auto_fit_nm, True)
             peak_point = self._clickize(retraction.x, retraction.y, peak)
             other_fit_point=self._clickize(retraction.x, retraction.y, peak - fit_points)
 
@@ -279,6 +283,9 @@ class autopeakCommands:
             if len(fit_result.result) > 0:
                 fit_result.color = color
                 fit_result.label = fit_function + '_' + str(index)
+                fit_result.linewidth = plot_linewidth
+                fit_result.size = plot_size
+                fit_result.style = plot_style
                 fit_result.title = retraction.title
                 fit_result.units.x = retraction.units.x
                 fit_result.units.y = retraction.units.y
index bcfeb5c4291b9c9f277b9c1171e88c3a2d0de14c..763b1a68bbd31edf3aa19eefbf15c82f96817cb7 100644 (file)
         default = False\r
         type = boolean\r
         value = False\r
+\r
+[preferences]\r
+    [[hide_curve_extension]]\r
+        default = False\r
+        type = boolean\r
+        value = False\r
+    \r
+    [[legend]]\r
+        default = False\r
+        type = boolean\r
+        value = False\r
+    \r
+    [[x_decimals]]\r
+        default = 2\r
+        maximum = 10\r
+        minimum = 0\r
+        type = integer\r
+        value = 2\r
+    \r
+    [[x_multiplier]]\r
+        default = n\r
+        elements = n, p\r
+        type = enum\r
+        value = n\r
+    \r
+    [[y_decimals]]\r
+        default = 2\r
+        maximum = 10\r
+        minimum = 0\r
+        type = integer\r
+        value = 2\r
+    \r
+    [[y_multiplier]]\r
+        default = n\r
+        elements = n, p\r
+        type = enum\r
+        value = n\r
+    \r
+    [[use_zero]]\r
+        default = True\r
+        type = boolean\r
+        value = True\r
+\r
+[copylog]\r
+    [[destination]]\r
+        default = ""\r
+        type = folder\r
+        value = \r
+    \r
+    [[use_LVDT_folder]]\r
+        default = False\r
+        type = boolean\r
+        value = False\r
index a46f0bb8c2bd3fa1636ac2dd40e222f40aeba73d..9598aa46e2e118e5eebabcf519f47e21a3d2e458 100644 (file)
         type = string\r
         value = _\r
 \r
+[notes]\r
+    [[filename]]\r
+        default = notes\r
+        type = string\r
+        value = notes\r
+    \r
+    [[folder]]\r
+        default = ""\r
+        type = folder\r
+        value = \r
+    \r
+    [[prefix]]\r
+        default = notes_\r
+        type = string\r
+        value = notes_\r
+    \r
+    [[use_playlist_filename]]\r
+        default = False\r
+        type = boolean\r
+        value = False\r
+\r
 [results]\r
     [[append]]\r
         default = True\r
index 171f9430dd500beede280e6b18fc7b0f1c8b0def..3623caaab6eb3735ab1457494ac364a3d56d68cb 100644 (file)
@@ -31,7 +31,6 @@ class exportCommands(object):
         '''
         Exports all fitting results (if available) in a columnar ASCII format.
         '''
-
         ext = self.GetStringFromConfig('export', 'fits', 'ext')
         folder = self.GetStringFromConfig('export', 'fits', 'folder')
         prefix = self.GetStringFromConfig('export', 'fits', 'prefix')
@@ -111,6 +110,33 @@ class exportCommands(object):
             output_file.write('\n'.join(output))
             output_file.close
 
+    def do_notes(self):
+        '''
+        Exports the note for all the files in a playlist.
+        '''
+        filename = self.GetStringFromConfig('export', 'notes', 'filename')
+        folder = self.GetStringFromConfig('export', 'notes', 'folder')
+        prefix = self.GetStringFromConfig('export', 'notes', 'prefix')
+        use_playlist_filename = self.GetBoolFromConfig('export', 'notes', 'use_playlist_filename')
+        
+        playlist = self.GetActivePlaylist()
+        output_str = ''
+        for current_file in playlist.files:
+            output_str = ''.join([output_str, current_file.filename, '  |  ', current_file.note, '\n'])
+        if output_str:
+            output_str = ''.join(['Notes taken at ', time.asctime(), '\n', playlist.filename, '\n', output_str])
+            if use_playlist_filename:
+                path, filename = os.path.split(playlist.filename)
+                filename = lh.remove_extension(filename)
+            filename = ''.join([prefix, filename, '.txt'])
+            filename = os.path.join(folder, filename)
+            output_file = open(filename, 'w')
+            output_file.write(output_str)
+            output_file.close
+        else:
+            dialog = wx.MessageDialog(None, 'No notes found, file not saved.', 'Info', wx.OK)
+            dialog.ShowModal()
+
     def do_overlay(self):
         '''
         Exports all retraction files in a playlist with the same scale.
@@ -161,7 +187,7 @@ class exportCommands(object):
             for row_index, row in enumerate(new_x):
                 output_str += ''.join([str(new_x[row_index]), ', ', str(new_y[row_index]), '\n'])
 
-            if output_str != '':
+            if output_str:
                 filename = ''.join([filename_prefix, current_file.name])
                 filename = current_file.filename.replace(current_file.name, filename)
                 output_file = open(filename, 'w')
@@ -203,15 +229,15 @@ class exportCommands(object):
                             line_str = current_file.plot.results[key].get_result_as_string(index)
                             line_str = ''.join([line_str, separator, current_file.filename])
                             output_str = ''.join([output_str, line_str, '\n'])
-        if output_str != '':
+        if output_str:
             output_str = ''.join(['Analysis started ', time.asctime(), '\n', output_str])
 
             if append and os.path.isfile(filename):
-               output_file = open(filename,'a')
-           else:
-               output_file = open(filename, 'w')
-            output_file.write(output_str)
-            output_file.close
+                output_file = open(filename,'a')
+            else:
+                output_file = open(filename, 'w')
+                output_file.write(output_str)
+                output_file.close
         else:
             dialog = wx.MessageDialog(None, 'No results found, file not saved.', 'Info', wx.OK)
             dialog.ShowModal()
index dda328f3f75b0c3e20dae396fef1c736ab2eec03..0ba16a43c3c7e78549b59b88a1598e9992f525f5 100644 (file)
@@ -24,6 +24,8 @@ import numpy as np
 import scipy.stats
 import scipy.odr
 
+import lib.clickedpoint
+
 class fitCommands(object):
     '''
     Do not use any of the following commands directly:
@@ -610,7 +612,7 @@ class fitCommands(object):
                 contact_point_index=self.wlccontact_index
         else:
             cindex=self.find_contact_point()
-            contact_point=lh.ClickedPoint()
+            contact_point = lib.clickedpoint.ClickedPoint()
             contact_point.absolute_coords=displayed_plot.vectors[1][0][cindex], displayed_plot.vectors[1][1][cindex]
             contact_point.find_graph_coords(displayed_plot.vectors[1][0], displayed_plot.vectors[1][1])
             contact_point.is_marker=True
@@ -796,7 +798,7 @@ class fitCommands(object):
         return xext,ysub,contact
 
         #now, exploit a ClickedPoint instance to calculate index...
-        dummy=lh.ClickedPoint()
+        dummy=lib.clickedpoint.ClickedPoint()
         dummy.absolute_coords=(x_intercept,y_intercept)
         dummy.find_graph_coords(xret2,yret)
 
index 3a0168b9b123bfb79faf2b5dd48cb923512d840c..110e5651767ce19cf9e91be5ca76fd1fa6defb17 100644 (file)
@@ -15,7 +15,6 @@
     [[convolution]]\r
         default = "[6.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0]"\r
         type = string\r
-        #value = '[6.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0]'\r
         value = "[6.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0]"\r
     \r
     [[maxcut]]\r
@@ -31,7 +30,7 @@
         maximum = 100\r
         minimum = 0\r
         type = integer\r
-        value = 2\r
+        value = 7\r
     \r
     [[mindeviation]]\r
         default = 5\r
         maximum = 10000\r
         minimum = 1\r
         type = integer\r
-        value = 7\r
+        value = 4\r
     \r
     [[min_deviation]]\r
         default = 9\r
         maximum = 100\r
         minimum = 1\r
         type = float\r
-        value = 10\r
+        value = 9\r
 \r
 [peaks]\r
     [[color]]\r
         default = black\r
         type = color\r
-        value = "(128,128,128)"\r
+        value = "(0,0,0)"\r
     \r
     [[size]]\r
         default = 20\r
         maximum = 10000\r
         minimum = 1\r
         type = integer\r
-        value = 50\r
+        value = 20\r
index 9b540fd1c643e16c38cacab325b4f92dc7e06e03..351dadab63d52bb6748123fef4b9b511123bd5b3 100644 (file)
@@ -132,9 +132,6 @@ class flatfiltsCommands:
     def has_peaks(self, plot=None, plugin=None):
         '''
         Finds peak position in a force curve.
-        FIXME: should be moved to peakspot.py
-        #TODO: should this really be moved? this is obviously tied into flatfilts/convfilt
-        #flatfilts.py is where 'has_peaks' belongs
         '''
 
         if plugin is None:
@@ -284,7 +281,14 @@ class flatfiltsCommands:
                 current_file.peak_size = peak_size
                 features.append(file_index - 1)
 
-        #TODO: warn when flatten is not applied?
+        #Warn that no flattening had been done.
+        if not self.HasPlotmanipulator('plotmanip_flatten'):
+            self.AppendToOutput('Flatten manipulator was not found. Processing was done without flattening.')
+        else:
+            if not self.AppliesPlotmanipulator('flatten'):
+                self.AppendToOutput('Flatten manipulator was not applied.')
+                self.AppendToOutput('Try to enable the flatten plotmanipulator for better results.')
+
         if not features:
             self.AppendToOutput('Found nothing interesting. Check the playlist, could be a bug or criteria could be too stringent.')
         else:
index 749b9ef703fe2c9b71e7da9d35211bd9e12fecfc..c0b575019493996dd1e7d15d6104264d8fee41db 100644 (file)
@@ -5,7 +5,8 @@ generalvclamp.py
 
 Plugin regarding general velocity clamp measurements
 
-Copyright 2008 by Massimo Sandal (University of Bologna, Italy)
+Copyright 2008 by Massimo Sandal, Fabrizio Benedetti, Marco Brucale, Bruno Samori (University of Bologna, Italy),
+and Alberto Gomez-Casado (University of Twente) 
 with modifications by Dr. Rolf Schmidt (Concordia University, Canada)
 
 This program is released under the GNU General Public License version 2.
index f84bba6175ad5e6789512f297206fa848ef68988..0c0264e8bcc427474f6af44bd850698e7b088db7 100644 (file)
@@ -9,7 +9,7 @@
         maximum = 10\r
         minimum = 0\r
         type = integer\r
-        value = 1\r
+        value = 2\r
     \r
     [[x_multiplier]]\r
         default = n\r
index 0e8db7aa5080313195f977cff49a89c67295c008..fca35ff6f770ca1ee4adeda3d63a36b961449ce0 100644 (file)
@@ -13,4 +13,12 @@ This program is released under the GNU General Public License version 2.
 class plotCommands:
 
     def do_plot(self):
+        active_file = self.GetActiveFile()
+        for curve in active_file.plot.curves:
+            curve.decimals.x = self.GetIntFromConfig('plot', 'x_decimals')
+            curve.decimals.y = self.GetIntFromConfig('plot', 'y_decimals')
+            curve.legend = self.GetBoolFromConfig('plot', 'legend')
+            curve.multiplier.x = self.GetStringFromConfig('plot', 'x_multiplier')
+            curve.multiplier.y = self.GetStringFromConfig('plot', 'y_multiplier')
+
         self.UpdatePlot();
index 90c11e1dc97f3175500a08dd0ea7654fe9c962ef..42e3aa528dc18f8bdb86f3df29e2c21bd29858f7 100644 (file)
@@ -74,7 +74,7 @@
         default = retraction\r
         elements = extension, retraction, both\r
         type = enum\r
-        value = both\r
+        value = retraction\r
 \r
 [procplots]\r
     [[centerzero]]\r
index 222caed79d440a00c410795c8dec37406e826585..ac36993c429b2c558f11875cdd5084cc416edd24 100644 (file)
@@ -20,6 +20,7 @@ from numpy import arange, diff, fft, median
 from scipy.signal import medfilt
 
 from lib.peakspot import conv_dx
+import lib.prettyformat
 
 class procplotsCommands:
 
@@ -69,7 +70,6 @@ class procplotsCommands:
 
         self.UpdatePlot(plot)
 
-
     def do_derivplot(self):
         '''
         Plots the discrete differentiation of the currently displayed force curve.
@@ -117,7 +117,6 @@ class procplotsCommands:
         -------
         Syntax: subtplot
         '''
-        #TODO: what is sub_filter supposed to do?
 
         #TODO: add option to keep previous subtplot
         plot = self.GetDisplayedPlotCorrected()
@@ -273,10 +272,17 @@ class procplotsCommands:
         for index in whatset:
             fft_curve = self.fft_plot(copy.deepcopy(plot.curves[index]), boundaries)
 
+            fft_curve.decimals.x = 3
+            fft_curve.decimals.y = 0
             fft_curve.destination.column = column
             fft_curve.destination.row = row
+            fft_curve.label = plot.curves[index].label
+            fft_curve.legend = True
+            fft_curve.multiplier.x = lib.prettyformat.get_prefix(max(fft_curve.x))
+            fft_curve.multiplier.y = lib.prettyformat.get_prefix(max(fft_curve.y))
+            #fft_curve.multiplier.y = ''
             fft_curve.title = 'FFT'
-            fft_curve.units.x = 'frequency'
+            fft_curve.units.x = 'Hz'
             fft_curve.units.y = 'power'
             plot.curves.append(fft_curve)