Convert from DOS to UNIX line endings.
authorW. Trevor King <wking@drexel.edu>
Wed, 5 May 2010 15:33:22 +0000 (11:33 -0400)
committerW. Trevor King <wking@drexel.edu>
Wed, 5 May 2010 15:33:22 +0000 (11:33 -0400)
This keeps the project consistent, since all my new files will have
UNIX line endings.  If people use tools that can't handle UNIX line
endings, we can always convert back later:

  for f in $(find . | grep -v '\.git'); do unix2dos "$f"; done

20 files changed:
conf/autopeak.ini
conf/fit.ini
conf/flatfilts.ini
conf/general.ini
conf/generalvclamp.ini
conf/hooke.conf
conf/pca_config.txt
conf/pcluster.ini
conf/procplots.ini
hooke/hooke.py
hooke/playlist.py
hooke/plugin/__init__.py
hooke/plugin/showconvoluted.py
hooke/ui/gui/hookeplaylist.py
hooke/ui/gui/hookepropertyeditor.py
hooke/ui/gui/hookeresults.py
hooke/ui/gui/prettyformat.py
hooke/ui/gui/results.py
mfp_igor_scripts/ExportMFP1D.ipf
mfp_igor_scripts/ExportMFP1DMenu.ipf

index 3f6cf98..7fd1d87 100644 (file)
@@ -1,91 +1,91 @@
-[autopeak]\r
-    [[auto_fit_nm]]\r
-        default = 5\r
-        minimum = 0\r
-        type = float\r
-        value = 5\r
-\r
-    [[auto_fit_points]]\r
-        default = 50\r
-        minimum = 0\r
-        type = integer\r
-        value = 50\r
-\r
-    [[auto_left_baseline]]\r
-        default = 20\r
-        minimum = 0\r
-        type = float\r
-        value = 20\r
-\r
-    [[auto_max_p]]\r
-        default = 10\r
-        minimum = 0\r
-        type = float\r
-        value = 10\r
-\r
-    [[auto_min_p]]\r
-        default = 0.005\r
-        minimum = 0\r
-        type = float\r
-        value = 0.005\r
-\r
-    [[auto_right_baseline]]\r
-        default = 20\r
-        minimum = 0\r
-        type = float\r
-        value = 20\r
-\r
-    [[auto_slope_span]]\r
-        default = 20\r
-        minimum = 0\r
-        type = integer\r
-        value = 20\r
-\r
-    [[baseline_clicks]]\r
-        default = 0\r
-        maximum = 20\r
-        minimum = 0\r
-        type = integer\r
-        value = 0\r
-\r
-    [[noauto]]\r
-        default = False\r
-        type = boolean\r
-        value = False\r
-\r
-    [[noflatten]]\r
-        default = False\r
-        type = boolean\r
-        value = False\r
-\r
-    [[pl]]\r
-        default = 5\r
-        minimum = 0\r
-        type = float\r
-        value = 5\r
-\r
-    [[rebase]]\r
-        default = True\r
-        type = boolean\r
-        value = False\r
-\r
-    [[reclick]]\r
-        default = False\r
-        type = boolean\r
-        value = False\r
-\r
-    [[temperature]]\r
-        default = 293\r
-        minimum = 0\r
-        type = float\r
-        value = 293\r
-\r
-    [[usepl]]\r
-        default = False\r
-        type = boolean\r
-        value = False\r
-\r
-    [[usepoints]]\r
-        default = False\r
-        type = boolean\r
-        value = False\r
+[autopeak]
+    [[auto_fit_nm]]
+        default = 5
+        minimum = 0
+        type = float
+        value = 5
+
+    [[auto_fit_points]]
+        default = 50
+        minimum = 0
+        type = integer
+        value = 50
+
+    [[auto_left_baseline]]
+        default = 20
+        minimum = 0
+        type = float
+        value = 20
+
+    [[auto_max_p]]
+        default = 10
+        minimum = 0
+        type = float
+        value = 10
+
+    [[auto_min_p]]
+        default = 0.005
+        minimum = 0
+        type = float
+        value = 0.005
+
+    [[auto_right_baseline]]
+        default = 20
+        minimum = 0
+        type = float
+        value = 20
+
+    [[auto_slope_span]]
+        default = 20
+        minimum = 0
+        type = integer
+        value = 20
+
+    [[baseline_clicks]]
+        default = 0
+        maximum = 20
+        minimum = 0
+        type = integer
+        value = 0
+
+    [[noauto]]
+        default = False
+        type = boolean
+        value = False
+
+    [[noflatten]]
+        default = False
+        type = boolean
+        value = False
+
+    [[pl]]
+        default = 5
+        minimum = 0
+        type = float
+        value = 5
+
+    [[rebase]]
+        default = True
+        type = boolean
+        value = False
+
+    [[reclick]]
+        default = False
+        type = boolean
+        value = False
+
+    [[temperature]]
+        default = 293
+        minimum = 0
+        type = float
+        value = 293
+
+    [[usepl]]
+        default = False
+        type = boolean
+        value = False
+
+    [[usepoints]]
+        default = False
+        type = boolean
+        value = False
index d84e157..1fd229f 100644 (file)
@@ -1,3 +1,3 @@
-[fit]\r
-flatten = 1\r
+[fit]
+flatten = 1
 temperature = 301
\ No newline at end of file
index 391b47a..6f2bea9 100644 (file)
@@ -1,84 +1,84 @@
-[convfilt]\r
-    [[blindwindow]]\r
-        default = 20\r
-        maximum = 10000\r
-        minimum = 0\r
-        type = float\r
-        value = 20\r
-\r
-    [[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 = '[11.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0]'\r
-\r
-    [[maxcut]]\r
-        default = 0.2\r
-        maximum = 1\r
-        minimum = 0\r
-        type = float\r
-        value = 0.2\r
-        visible = False\r
-\r
-    [[medianfilter]]\r
-        default = 7\r
-        maximum = 100\r
-        minimum = 0\r
-        type = integer\r
-        value = 7\r
-\r
-    [[mindeviation]]\r
-        default = 5\r
-        maximum = 100\r
-        minimum = 0\r
-        type = float\r
-        value = 5\r
-\r
-    [[minpeaks]]\r
-        default = 5\r
-        maximum = 20\r
-        minimum = 0\r
-        type = integer\r
-        value = 1\r
-\r
-    [[positive]]\r
-        default = False\r
-        type = boolean\r
-        value = False\r
-\r
-    [[seedouble]]\r
-        default = 10\r
-        maximum = 1000\r
-        minimum = 0\r
-        type = integer\r
-        value = 10\r
-\r
-    [[stable]]\r
-        default = 0.005\r
-        maximum = 1\r
-        minimum = 0\r
-        type = float\r
-        value = 0.005\r
-\r
-[flatfilt]\r
-    #[[median_filter]]\r
-        #default = 7\r
-        #maximum = 100\r
-        #minimum = 0\r
-        #type = integer\r
-        #value = 7\r
-\r
-    [[min_npks]]\r
-        default = 4\r
-        maximum = 10000\r
-        minimum = 1\r
-        type = integer\r
-        value = 4\r
-\r
-    [[min_deviation]]\r
-        default = 9\r
-        maximum = 100\r
-        minimum = 1\r
-        type = float\r
-        value = 9\r
-\r
+[convfilt]
+    [[blindwindow]]
+        default = 20
+        maximum = 10000
+        minimum = 0
+        type = float
+        value = 20
+
+    [[convolution]]
+        default = '[6.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0]'
+        type = string
+        #value = '[6.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0]'
+        value = '[11.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0]'
+
+    [[maxcut]]
+        default = 0.2
+        maximum = 1
+        minimum = 0
+        type = float
+        value = 0.2
+        visible = False
+
+    [[medianfilter]]
+        default = 7
+        maximum = 100
+        minimum = 0
+        type = integer
+        value = 7
+
+    [[mindeviation]]
+        default = 5
+        maximum = 100
+        minimum = 0
+        type = float
+        value = 5
+
+    [[minpeaks]]
+        default = 5
+        maximum = 20
+        minimum = 0
+        type = integer
+        value = 1
+
+    [[positive]]
+        default = False
+        type = boolean
+        value = False
+
+    [[seedouble]]
+        default = 10
+        maximum = 1000
+        minimum = 0
+        type = integer
+        value = 10
+
+    [[stable]]
+        default = 0.005
+        maximum = 1
+        minimum = 0
+        type = float
+        value = 0.005
+
+[flatfilt]
+    #[[median_filter]]
+        #default = 7
+        #maximum = 100
+        #minimum = 0
+        #type = integer
+        #value = 7
+
+    [[min_npks]]
+        default = 4
+        maximum = 10000
+        minimum = 1
+        type = integer
+        value = 4
+
+    [[min_deviation]]
+        default = 9
+        maximum = 100
+        minimum = 1
+        type = float
+        value = 9
+
index 7b5e2cc..ca15d39 100644 (file)
@@ -1,22 +1,22 @@
-[genlist]\r
-    [[folder]]\r
-        default = ''\r
-        type = folder\r
-        value = 'R:\Programming\Python\Gui\data\dwell'\r
-\r
-    [[filemask]]\r
-        default = '*.*'\r
-        type = string\r
-        value = '*.*'\r
-\r
-[loadlist]\r
-    [[filename]]\r
-        default = 'test.hkp'\r
-        type = filename\r
-        value = 'R:\Programming\Python\Gui\untitled.hkp'\r
-\r
-[savelist]\r
-    [[filename]]\r
-        default = 'untitled.hkp'\r
-        type = filename\r
+[genlist]
+    [[folder]]
+        default = ''
+        type = folder
+        value = 'R:\Programming\Python\Gui\data\dwell'
+
+    [[filemask]]
+        default = '*.*'
+        type = string
+        value = '*.*'
+
+[loadlist]
+    [[filename]]
+        default = 'test.hkp'
+        type = filename
+        value = 'R:\Programming\Python\Gui\untitled.hkp'
+
+[savelist]
+    [[filename]]
+        default = 'untitled.hkp'
+        type = filename
         value = 'R:\Programming\Python\Gui\untitled.hkp'
\ No newline at end of file
index 45132fa..57af149 100644 (file)
@@ -1,19 +1,19 @@
-[generalvclamp]\r
-    [[flatten]]\r
-        default = True\r
-        type = boolean\r
-        value = True\r
-\r
-    [[max_cycles]]\r
-        default = 1\r
-        maximum = 100\r
-        minimum = 0\r
-        type = integer\r
-        value = 1\r
-\r
-    [[force_multiplier]]\r
-        default = 1\r
-        maximum = 100\r
-        minimum = 0\r
-        type = float\r
+[generalvclamp]
+    [[flatten]]
+        default = True
+        type = boolean
+        value = True
+
+    [[max_cycles]]
+        default = 1
+        maximum = 100
+        minimum = 0
+        type = integer
+        value = 1
+
+    [[force_multiplier]]
+        default = 1
+        maximum = 100
+        minimum = 0
+        type = float
         value = 1
\ No newline at end of file
index 9236b81..3741a2b 100644 (file)
-<?xml version="1.0" ?>\r
-<!-- To comment something, put dashes and ! like here -->\r
-<config>\r
-\r
-\r
-<!--\r
-This section defines the Hooke installation.  confpath is hardcoded,\r
-since it's to find the config file(s) before your read them.\r
-    -->\r
-<install>\r
-    <docpath>\r
-        ./doc/\r
-    </docpath>\r
-</install>\r
-\r
-<!-- Internal variables. -->\r
-<display\r
-    ext="1"\r
-    colour_ext="None"\r
-    ret="1"\r
-    colour_ret="None"\r
-    correct="1"\r
-    colour_correct="None"\r
-    contact_point="0"\r
-    medfilt="0"\r
-    xaxes="0"\r
-    yaxes="0"\r
-    flatten="1"\r
-    fit_function="wlc"\r
-    temperature="301"\r
-    auto_fit_points="50"\r
-    auto_slope_span="20"\r
-    auto_delta_force="10"\r
-    auto_fit_nm="5"\r
-    auto_min_p="0.005"\r
-    auto_max_p="10"\r
-    baseline_clicks="0"\r
-    auto_left_baseline="20"\r
-    auto_right_baseline="20"\r
-    force_multiplier="1"\r
-    fc_showphase="0"\r
-    fc_showimposed="0"\r
-    fc_interesting="0"\r
-    tccd_threshold="0"\r
-    tccd_coincident="0"\r
-    centerzero="1"\r
-/>\r
-\r
-<!--\r
-The following section defines the default playlist to load at startup.\r
-    -->\r
-<defaultlist>\r
-    ./hooke/test/test.hkp\r
-</defaultlist>\r
-\r
-<!--\r
-This section defines which plugins have to be loaded by Hooke.\r
-     -->\r
-<plugins>\r
-    <fit/>\r
-    <curvetools/>\r
-    <procplots/>\r
-    <flatfilts/>\r
-    <generalclamp/>\r
-    <!-- dummyguiplug/ -->\r
-    <!-- superimpose/ -->\r
-    <generalvclamp/>\r
-    <massanalysis/>\r
-    <viewer/>\r
-    <!-- tutorial/ -->\r
-    <macro/>\r
-    <autopeak/>\r
-    <pcluster/>\r
-    <generaltccd/>\r
-    <multidistance/>\r
-    <jumpstat/>\r
-    <review/>\r
-    <multifit/>\r
-</plugins>\r
-\r
-<!--\r
-This section defines which drivers have to be loaded by Hooke.\r
-    -->\r
-<drivers>\r
-    <!--picoforce/-->\r
-    <picoforcealt/>\r
-    <hemingclamp/>\r
-    <csvdriver/>\r
-    <!-- tutorialdriver/ -->\r
-    <jpk/>\r
-    <mfp1dexport/>\r
-    <mcs/>\r
-    <!-- hdf5/ -->\r
-    <mfp3d/>\r
-</drivers>\r
-\r
-<!--\r
-This section defines which plot manipulators have to be loaded by Hooke,\r
-and -IMPORTANTLY- their order.\r
-    -->\r
-<plotmanips>\r
-    <correct/>\r
-    <median/>\r
-    <!-- absvalue/ -->\r
-    <flatten/>\r
-    <multiplier/>\r
-    <clamp/>\r
-    <threshold/>\r
+<?xml version="1.0" ?>
+<!-- To comment something, put dashes and ! like here -->
+<config>
+
+
+<!--
+This section defines the Hooke installation.  confpath is hardcoded,
+since it's to find the config file(s) before your read them.
+    -->
+<install>
+    <docpath>
+        ./doc/
+    </docpath>
+</install>
+
+<!-- Internal variables. -->
+<display
+    ext="1"
+    colour_ext="None"
+    ret="1"
+    colour_ret="None"
+    correct="1"
+    colour_correct="None"
+    contact_point="0"
+    medfilt="0"
+    xaxes="0"
+    yaxes="0"
+    flatten="1"
+    fit_function="wlc"
+    temperature="301"
+    auto_fit_points="50"
+    auto_slope_span="20"
+    auto_delta_force="10"
+    auto_fit_nm="5"
+    auto_min_p="0.005"
+    auto_max_p="10"
+    baseline_clicks="0"
+    auto_left_baseline="20"
+    auto_right_baseline="20"
+    force_multiplier="1"
+    fc_showphase="0"
+    fc_showimposed="0"
+    fc_interesting="0"
+    tccd_threshold="0"
+    tccd_coincident="0"
+    centerzero="1"
+/>
+
+<!--
+The following section defines the default playlist to load at startup.
+    -->
+<defaultlist>
+    ./hooke/test/test.hkp
+</defaultlist>
+
+<!--
+This section defines which plugins have to be loaded by Hooke.
+     -->
+<plugins>
+    <fit/>
+    <curvetools/>
+    <procplots/>
+    <flatfilts/>
+    <generalclamp/>
+    <!-- dummyguiplug/ -->
+    <!-- superimpose/ -->
+    <generalvclamp/>
+    <massanalysis/>
+    <viewer/>
+    <!-- tutorial/ -->
+    <macro/>
+    <autopeak/>
+    <pcluster/>
+    <generaltccd/>
+    <multidistance/>
+    <jumpstat/>
+    <review/>
+    <multifit/>
+</plugins>
+
+<!--
+This section defines which drivers have to be loaded by Hooke.
+    -->
+<drivers>
+    <!--picoforce/-->
+    <picoforcealt/>
+    <hemingclamp/>
+    <csvdriver/>
+    <!-- tutorialdriver/ -->
+    <jpk/>
+    <mfp1dexport/>
+    <mcs/>
+    <!-- hdf5/ -->
+    <mfp3d/>
+</drivers>
+
+<!--
+This section defines which plot manipulators have to be loaded by Hooke,
+and -IMPORTANTLY- their order.
+    -->
+<plotmanips>
+    <correct/>
+    <median/>
+    <!-- absvalue/ -->
+    <flatten/>
+    <multiplier/>
+    <clamp/>
+    <threshold/>
     <coincident/>
-    <centerzero/>\r
-</plotmanips>\r
-\r
-</config>\r
+    <centerzero/>
+</plotmanips>
+
+</config>
index 8eb72a9..4ce44ef 100644 (file)
@@ -1,33 +1,33 @@
-1,3,6,7,8,9x15,10,11\r
-0.000000\r
-1,2,3,4,5,6,7,8,9,10,11\r
-debug=false\r
-------------------------------------------\r
-#1 colonne della prima pca\r
-#2 limite filtro della densità (x es: "0.000008"; "0" per calcolo automatico.. 3,242311147)\r
-#3 colonne della secona pca (non usato)\r
-#4 attiva modalità debug (stampa anche immagini/coordinate di pca/density)\r
-------------------------------------------\r
-str(peak_number)+     # non considerato\r
-str(delta_mean)+      # 0\r
-str(delta_median)+    # 1 -\r
-str(force_mean)+      # 2\r
-str(force_median)+    # 3 -\r
-str(first_peak_cl)+   # 4 -\r
-str(last_peak_cl)+    # 5 -\r
-str(max_force)+       # 6\r
-str(min_force)+       # 7\r
-str(max_delta)+       # 8\r
-str(min_delta)+       # 9\r
-str(delta_stdev)+     # 10\r
-str(forces_stdev)+    # 11\r
-str(peaks_diff)+      # 12\r
-------------------------------------------\r
-Lancio di pCluster e pca:\r
- - cd 20080917_4s4wt_10mMtris_all\r
- - genlist *.*\r
- - setconv blindwindow 50\r
- - pcluster pl=0.35\r
-------------------------------------------\r
-Lancio della sola pca\r
+1,3,6,7,8,9x15,10,11
+0.000000
+1,2,3,4,5,6,7,8,9,10,11
+debug=false
+------------------------------------------
+#1 colonne della prima pca
+#2 limite filtro della densità (x es: "0.000008"; "0" per calcolo automatico.. 3,242311147)
+#3 colonne della secona pca (non usato)
+#4 attiva modalità debug (stampa anche immagini/coordinate di pca/density)
+------------------------------------------
+str(peak_number)+     # non considerato
+str(delta_mean)+      # 0
+str(delta_median)+    # 1 -
+str(force_mean)+      # 2
+str(force_median)+    # 3 -
+str(first_peak_cl)+   # 4 -
+str(last_peak_cl)+    # 5 -
+str(max_force)+       # 6
+str(min_force)+       # 7
+str(max_delta)+       # 8
+str(min_delta)+       # 9
+str(delta_stdev)+     # 10
+str(forces_stdev)+    # 11
+str(peaks_diff)+      # 12
+------------------------------------------
+Lancio di pCluster e pca:
+ - cd 20080917_4s4wt_10mMtris_all
+ - genlist *.*
+ - setconv blindwindow 50
+ - pcluster pl=0.35
+------------------------------------------
+Lancio della sola pca
  - pca 20080922_gb1x8_tris/pCluster_20090627_1328/coordinate_20080922_gb1x8_tris_blind50.txt
\ No newline at end of file
index 1984027..a7f7168 100644 (file)
@@ -1,10 +1,10 @@
-[pcluster]\r
-auto_fit_nm = 5\r
-auto_fit_points = 50\r
-auto_left_baseline = 20\r
-auto_max_p = 10\r
-auto_min_p = 0.005\r
-auto_right_baseline = 20\r
-auto_slope_span = 20\r
-baseline_clicks = 0\r
+[pcluster]
+auto_fit_nm = 5
+auto_fit_points = 50
+auto_left_baseline = 20
+auto_max_p = 10
+auto_min_p = 0.005
+auto_right_baseline = 20
+auto_slope_span = 20
+baseline_clicks = 0
 temperature = 301
\ No newline at end of file
index ab6d1a5..2558ba5 100644 (file)
@@ -1,12 +1,12 @@
-[procplots]\r
-    [[median]]\r
-        default = 0\r
-        maximum = 100\r
-        minimum = 0\r
-        type = integer\r
-        value = 0\r
-\r
-    [[correct]]\r
-        default = True\r
-        type = boolean\r
+[procplots]
+    [[median]]
+        default = 0
+        maximum = 100
+        minimum = 0
+        type = integer
+        value = 0
+
+    [[correct]]
+        default = True
+        type = boolean
         value = True
\ No newline at end of file
index 3662b51..547d7cc 100644 (file)
-#!/usr/bin/env python\r
-\r
-'''\r
-HOOKE - A force spectroscopy review & analysis tool\r
-\r
-Copyright (C) 2008-2010 Massimo Sandal (University of Bologna, Italy).\r
-                        Rolf Schmidt (Concordia University, Canada).\r
-\r
-This program is released under the GNU General Public License version 2.\r
-'''\r
-\r
-from libhooke import WX_GOOD\r
-\r
-import wxversion\r
-wxversion.select(WX_GOOD)\r
-import copy\r
-import cStringIO\r
-import os\r
-import os.path\r
-import sys\r
-import glob\r
-import time\r
-\r
-import imp\r
-import wx\r
-import wx.html\r
-import wx.aui\r
-import wxmpl\r
-import wx.lib.agw.aui as aui\r
-import wx.propgrid as wxpg\r
-\r
-import libhooke as lh\r
-from config import config\r
-import drivers\r
-import plugins\r
-import hookecommands\r
-import hookeplaylist\r
-import hookepropertyeditor\r
-import hookeresults\r
-import playlist\r
-\r
-global __version__\r
-\r
-__version__ = lh.HOOKE_VERSION[0]\r
-__release_name__ = lh.HOOKE_VERSION[1]\r
-\r
-#TODO: order menu items, get rid of all unused IDs\r
-ID_ExportText = wx.NewId()\r
-ID_ExportImage = wx.NewId()\r
-ID_Config = wx.NewId()\r
-ID_About = wx.NewId()\r
-ID_Next = wx.NewId()\r
-ID_Previous = wx.NewId()\r
-\r
-ID_ViewAssistant = wx.NewId()\r
-ID_ViewCommands = wx.NewId()\r
-ID_ViewFolders = wx.NewId()\r
-ID_ViewOutput = wx.NewId()\r
-ID_ViewPlaylists = wx.NewId()\r
-ID_ViewProperties = wx.NewId()\r
-ID_ViewResults = wx.NewId()\r
-\r
-ID_CommandsList = wx.NewId()\r
-ID_CommandsListBox = wx.NewId()\r
-\r
-ID_TextContent = wx.NewId()\r
-ID_TreeContent = wx.NewId()\r
-ID_HTMLContent = wx.NewId()\r
-ID_SizeReportContent = wx.NewId()\r
-ID_DeletePerspective = wx.NewId()\r
-ID_SavePerspective = wx.NewId()\r
-\r
-ID_FirstPerspective = ID_SavePerspective + 1000\r
-#I hope we'll never have more than 1000 perspectives\r
-ID_FirstPlot = ID_SavePerspective + 2000\r
-\r
-class Hooke(wx.App):\r
-\r
-    def OnInit(self):\r
-        self.SetAppName('Hooke')\r
-        self.SetVendorName('')\r
-\r
-        #set the Hooke directory\r
-        lh.hookeDir = os.path.abspath(os.path.dirname(__file__))\r
-\r
-        windowPosition = (config['main']['left'], config['main']['top'])\r
-        windowSize = (config['main']['width'], config['main']['height'])\r
-\r
-        #setup the splashscreen\r
-        if config['splashscreen']['show']:\r
-            filename = lh.get_file_path('hooke.jpg', ['resources'])\r
-            if os.path.isfile(filename):\r
-                bitmap = wx.Image(filename).ConvertToBitmap()\r
-                splashStyle = wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT\r
-                splashDuration = config['splashscreen']['duration']\r
-                wx.SplashScreen(bitmap, splashStyle, splashDuration, None, -1)\r
-                wx.Yield()\r
-                '''\r
-                we need for the splash screen to disappear\r
-                for whatever reason splashDuration and sleep do not correspond to each other\r
-                at least not on Windows\r
-                maybe it's because duration is in milliseconds and sleep in seconds\r
-                thus we need to increase the sleep time a bit\r
-                a factor of 1.2 seems to work quite well\r
-                '''\r
-                sleepFactor = 1.2\r
-                time.sleep(sleepFactor * splashDuration / 1000)\r
-\r
-        plugin_objects = []\r
-        for plugin in config['plugins']:\r
-            if config['plugins'][plugin]:\r
-                filename = ''.join([plugin, '.py'])\r
-                path = lh.get_file_path(filename, ['plugins'])\r
-                if os.path.isfile(path):\r
-                    #get the corresponding filename and path\r
-                    plugin_name = ''.join(['plugins.', plugin])\r
-                    module = __import__(plugin_name)\r
-                    #get the file that contains the plugin\r
-                    class_file = getattr(plugins, plugin)\r
-                    #get the class that contains the commands\r
-                    class_object = getattr(class_file, plugin + 'Commands')\r
-                    plugin_objects.append(class_object)\r
-\r
-        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.Show(True)\r
-        self.SetTopWindow(frame)\r
-\r
-        return True\r
-\r
-    def OnExit(self):\r
-        #TODO: write values to ini file if necessary\r
-        return True\r
-\r
-\r
-class HookeFrame(wx.Frame):\r
-\r
-    def __init__(self, parent, id=-1, title='', pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE|wx.SUNKEN_BORDER|wx.CLIP_CHILDREN):\r
-        #call parent constructor\r
-        wx.Frame.__init__(self, parent, id, title, pos, size, style)\r
-        self.config = config\r
-        self.CreateApplicationIcon()\r
-        #self.configs contains: {the name of the Commands file: corresponding ConfigObj}\r
-        self.configs = {}\r
-        ##self.playlists contains: {the name of the playlist: [playlist, tabIndex, plotID]}\r
-        #self.playlists = {}\r
-        #self.plugins contains: {the name of the plugin: [caption, function]}\r
-        self.plugins = {}\r
-        #self.plotmanipulators list contains: [the name of the plotmanip, function, name of the module]\r
-        self.plotmanipulators = []\r
-\r
-        #tell FrameManager to manage this frame\r
-        self._mgr = aui.AuiManager()\r
-        self._mgr.SetManagedWindow(self)\r
-        #set the gradient style\r
-        self._mgr.GetArtProvider().SetMetric(aui.AUI_DOCKART_GRADIENT_TYPE, aui.AUI_GRADIENT_NONE)\r
-        #set transparent drag\r
-        self._mgr.SetFlags(self._mgr.GetFlags() ^ aui.AUI_MGR_TRANSPARENT_DRAG)\r
-\r
-        # set up default notebook style\r
-        self._notebook_style = aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER\r
-        self._notebook_theme = 0\r
-\r
-        #holds the perspectives: {name, [index, perspectiveStr]}\r
-        self._perspectives = {}\r
-\r
-        # min size for the frame itself isn't completely done.\r
-        # see the end up FrameManager::Update() for the test\r
-        # code. For now, just hard code a frame minimum size\r
-        self.SetMinSize(wx.Size(400, 300))\r
-        #create panels here\r
-        self.panelAssistant = self.CreatePanelAssistant()\r
-        self.panelCommands = self.CreatePanelCommands()\r
-        self.panelFolders = self.CreatePanelFolders()\r
-        self.panelPlaylists = self.CreatePanelPlaylists()\r
-        self.panelProperties = self.CreatePanelProperties()\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.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
-        self._mgr.AddPane(self.panelAssistant, aui.AuiPaneInfo().Name('Assistant').Caption('Assistant').Right().CloseButton(True).MaximizeButton(False))\r
-        self._mgr.AddPane(self.panelOutput, aui.AuiPaneInfo().Name('Output').Caption('Output').Bottom().CloseButton(True).MaximizeButton(False))\r
-        self._mgr.AddPane(self.panelResults, aui.AuiPaneInfo().Name('Results').Caption('Results').Bottom().CloseButton(True).MaximizeButton(False))\r
-        #self._mgr.AddPane(self.textCtrlCommandLine, aui.AuiPaneInfo().Name('CommandLine').CaptionVisible(False).Fixed().Bottom().Layer(2).CloseButton(False).MaximizeButton(False))\r
-        #self._mgr.AddPane(panelBottom, aui.AuiPaneInfo().Name("panelCommandLine").Bottom().Position(1).CloseButton(False).MaximizeButton(False))\r
-\r
-        # add the toolbars to the manager\r
-        self.toolbar=self.CreateToolBar()\r
-        self.toolbarNavigation=self.CreateToolBarNavigation()\r
-        self._mgr.AddPane(self.toolbar, aui.AuiPaneInfo().Name('toolbar').Caption('Toolbar').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))\r
-        self._mgr.AddPane(self.toolbarNavigation, aui.AuiPaneInfo().Name('toolbarNavigation').Caption('Navigation').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))\r
-        # "commit" all changes made to FrameManager\r
-        self._mgr.Update()\r
-        #create the menubar after the panes so that the default perspective\r
-        #is created with all panes open\r
-        self.CreateMenuBar()\r
-        self.statusbar = self.CreateStatusBar()\r
-        self._BindEvents()\r
-        #TODO: select item on startup (whatever item)\r
-        #self.listCtrlCommands.Select(0)\r
-        #self.OnListboxSelect(None)\r
-        name = self.config['perspectives']['active']\r
-        menu_item = self.GetPerspectiveMenuItem(name)\r
-        self.OnRestorePerspective(menu_item)\r
-        self.playlists = self.panelPlaylists.Playlists\r
-        #define the list of active drivers\r
-        self.drivers = []\r
-        for driver in self.config['drivers']:\r
-            if self.config['drivers'][driver]:\r
-                #get the corresponding filename and path\r
-                filename = ''.join([driver, '.py'])\r
-                path = lh.get_file_path(filename, ['drivers'])\r
-                #the driver is active for driver[1] == 1\r
-                if os.path.isfile(path):\r
-                    #driver files are located in the 'drivers' subfolder\r
-                    driver_name = ''.join(['drivers.', driver])\r
-                    module = __import__(driver_name)\r
-                    class_file = getattr(drivers, driver)\r
-                    for command in dir(class_file):\r
-                        if command.endswith('Driver'):\r
-                            self.drivers.append(getattr(class_file, command))\r
-        #import all active plugins and plotmanips\r
-        #the plotmanip_functions contains: {the name of the plotmanip: [method, class_object]}\r
-        plotmanip_functions = {}\r
-        #add 'general.ini' to self.configs (this is not a plugin and thus must be imported seperately)\r
-        ini_path = lh.get_file_path('general.ini', ['plugins'])\r
-        plugin_config = ConfigObj(ini_path)\r
-        #self.config.merge(plugin_config)\r
-        self.configs['general'] = plugin_config\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
-                filename = ''.join([plugin, '.py'])\r
-                path = lh.get_file_path(filename, ['plugins'])\r
-                if os.path.isfile(path):\r
-                    #get the corresponding filename and path\r
-                    plugin_name = ''.join(['plugins.', plugin])\r
-                    try:\r
-                        #import the module\r
-                        module = __import__(plugin_name)\r
-                        #prepare the ini file for inclusion\r
-                        ini_path = path.replace('.py', '.ini')\r
-                        #include ini file\r
-                        plugin_config = ConfigObj(ini_path)\r
-                        #self.config.merge(plugin_config)\r
-                        self.configs[plugin] = plugin_config\r
-                        #add to plugins\r
-                        commands = eval('dir(module.' + plugin+ '.' + plugin + 'Commands)')\r
-                        #keep only commands (ie names that start with 'do_')\r
-                        commands = [command for command in commands if command.startswith('do_')]\r
-                        if commands:\r
-                            self.plugins[plugin] = commands\r
-                        try:\r
-                            #initialize the plugin\r
-                            eval('module.' + plugin+ '.' + plugin + 'Commands._plug_init(self)')\r
-                        except AttributeError:\r
-                            pass\r
-                    except ImportError:\r
-                        pass\r
-        #initialize the commands tree\r
-        commands = dir(HookeFrame)\r
-        commands = [command for command in commands if command.startswith('do_')]\r
-        if commands:\r
-            self.plugins['general'] = commands\r
-        self.panelCommands.Initialize(self.plugins)\r
-        for command in dir(self):\r
-            if command.startswith('plotmanip_'):\r
-                plotmanip_functions[command] = [command, getattr(self, command)]\r
-        for name in self.config['plotmanipulators']['names']:\r
-            if self.config['plotmanipulators'].as_bool(name):\r
-                command_name = ''.join(['plotmanip_', name])\r
-                if command_name in plotmanip_functions:\r
-                    self.plotmanipulators.append(plotmanip_functions[command_name])\r
-        #load default list, if possible\r
-        self.do_loadlist(self.config['general']['list'])\r
-\r
-    def _BindEvents(self):\r
-        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)\r
-        self.Bind(wx.EVT_SIZE, self.OnSize)\r
-        self.Bind(wx.EVT_CLOSE, self.OnClose)\r
-        # Show How To Use The Closing Panes Event\r
-        self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPaneClose)\r
-        self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnNotebookPageClose)\r
-        #menu\r
-        self.Bind(wx.EVT_MENU, self.OnClose, id=wx.ID_EXIT)\r
-        self.Bind(wx.EVT_MENU, self.OnAbout, id=wx.ID_ABOUT)\r
-        #view\r
-        self.Bind(wx.EVT_MENU_RANGE, self.OnView, id=ID_ViewAssistant, id2=ID_ViewResults)\r
-        #perspectives\r
-        self.Bind(wx.EVT_MENU, self.OnDeletePerspective, id=ID_DeletePerspective)\r
-        self.Bind(wx.EVT_MENU, self.OnSavePerspective, id=ID_SavePerspective)\r
-        self.Bind(wx.EVT_MENU_RANGE, self.OnRestorePerspective, id=ID_FirstPerspective, id2=ID_FirstPerspective+1000)\r
-        #toolbar\r
-        self.Bind(wx.EVT_TOOL, self.OnExportImage, id=ID_ExportImage)\r
-        self.Bind(wx.EVT_TOOL, self.OnNext, id=ID_Next)\r
-        self.Bind(wx.EVT_TOOL, self.OnPrevious, id=ID_Previous)\r
-        #self.Bind(.EVT_AUITOOLBAR_TOOL_DROPDOWN, self.OnDropDownToolbarItem, id=ID_DropDownToolbarItem)\r
-        #dir control\r
-        treeCtrl = self.panelFolders.GetTreeCtrl()\r
-        #tree.Bind(wx.EVT_LEFT_UP, self.OnDirCtrl1LeftUp)\r
-        #tree.Bind(wx.EVT_LEFT_DOWN, self.OnGenericDirCtrl1LeftDown)\r
-        treeCtrl.Bind(wx.EVT_LEFT_DCLICK, self.OnDirCtrlLeftDclick)\r
-        #playlist tree\r
-        self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DOWN, self.OnPlaylistsLeftDown)\r
-        self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DCLICK, self.OnPlaylistsLeftDclick)\r
-        #commands tree\r
-        self.panelCommands.ExecuteButton.Bind(wx.EVT_BUTTON, self.OnExecute)\r
-        self.panelCommands.CommandsTree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeCtrlCommandsLeftDown)\r
-        #property editor\r
-        self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged)\r
-        self.panelProperties.pg.Bind(wxpg.EVT_PG_SELECTED, self.OnPropGridSelect)\r
-        #results panel\r
-        self.panelResults.results_list.OnCheckItem = self.OnResultsCheck\r
-\r
-    def _GetActiveCurveIndex(self):\r
-        playlist = self.GetActivePlaylist()\r
-        #get the selected item from the tree\r
-        selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()\r
-        #test if a playlist or a curve was double-clicked\r
-        if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):\r
-            return -1\r
-        else:\r
-            count = 0\r
-            selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)\r
-            while selected_item.IsOk():\r
-                count += 1\r
-                selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)\r
-            return count\r
-\r
-    def _GetActivePlaylistName(self):\r
-        #get the selected item from the tree\r
-        selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()\r
-        #test if a playlist or a curve was double-clicked\r
-        if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):\r
-            playlist_item = selected_item\r
-        else:\r
-            #get the name of the playlist\r
-            playlist_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)\r
-        #now we have a playlist\r
-        return self.panelPlaylists.PlaylistsTree.GetItemText(playlist_item)\r
-\r
-    def _GetPlaylistTab(self, name):\r
-        for index, page in enumerate(self.plotNotebook._tabs._pages):\r
-            if page.caption == name:\r
-                return index\r
-        return -1\r
-\r
-    def _GetUniquePlaylistName(self, name):\r
-        playlist_name = name\r
-        count = 1\r
-        while playlist_name in self.playlists:\r
-            playlist_name = ''.join([name, str(count)])\r
-            count += 1\r
-        return playlist_name\r
-\r
-    def _SavePerspectiveToFile(self, name, perspective):\r
-        filename = ''.join([name, '.txt'])\r
-        filename = lh.get_file_path(filename, ['perspectives'])\r
-        perspectivesFile = open(filename, 'w')\r
-        perspectivesFile.write(perspective)\r
-        perspectivesFile.close()\r
-\r
-    def AddPlaylist(self, playlist=None, name='Untitled'):\r
-        #TODO: change cursor or progressbar (maybe in statusbar)\r
-        #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))\r
-        if playlist and playlist.count > 0:\r
-            playlist.name = self._GetUniquePlaylistName(name)\r
-            playlist.reset()\r
-            self.AddToPlaylists(playlist)\r
-\r
-    def AddPlaylistFromFiles(self, files=[], name='Untitled'):\r
-        #TODO: change cursor or progressbar (maybe in statusbar)\r
-        #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))\r
-        if files:\r
-            playlist = Playlist.Playlist(self.drivers)\r
-            for item in files:\r
-                playlist.add_curve(item)\r
-        if playlist.count > 0:\r
-            playlist.name = self._GetUniquePlaylistName(name)\r
-            playlist.reset()\r
-            self.AddToPlaylists(playlist)\r
-\r
-    def AddToPlaylists(self, playlist):\r
-        if playlist.has_curves:\r
-            #setup the playlist in the Playlist tree\r
-            tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()\r
-            playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)\r
-            #add all curves to the Playlist tree\r
-            curves = {}\r
-            for index, curve in enumerate(playlist.curves):\r
-                ##remove the extension from the name of the curve\r
-                ##TODO: optional?\r
-                #item_text, extension = os.path.splitext(curve.name)\r
-                #curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, item_text, 1)\r
-                curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, curve.name, 1)\r
-                if index == playlist.index:\r
-                    self.panelPlaylists.PlaylistsTree.SelectItem(curve_ID)\r
-            playlist.reset()\r
-            #create the plot tab and add playlist to the dictionary\r
-            plotPanel = wxmpl.PlotPanel(self, ID_FirstPlot + len(self.playlists))\r
-            notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True)\r
-            tab_index = self.plotNotebook.GetSelection()\r
-            figure = plotPanel.get_figure()\r
-            self.playlists[playlist.name] = [playlist, figure]\r
-            self.panelPlaylists.PlaylistsTree.Expand(playlist_root)\r
-            self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
-            self.UpdatePlot()\r
-\r
-    def AppendToOutput(self, text):\r
-        self.panelOutput.AppendText(''.join([text, '\n']))\r
-\r
-    def CreateApplicationIcon(self):\r
-        iconFile = 'resources' + os.sep + 'microscope.ico'\r
-        icon = wx.Icon(iconFile, wx.BITMAP_TYPE_ICO)\r
-        self.SetIcon(icon)\r
-\r
-    def CreateCommandLine(self):\r
-        return wx.TextCtrl(self, -1, '', style=wx.NO_BORDER|wx.EXPAND)\r
-\r
-    def CreatePanelAssistant(self):\r
-        panel = wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)\r
-        panel.SetEditable(False)\r
-        return panel\r
-\r
-    def CreatePanelCommands(self):\r
-        return hookecommands.Commands(self)\r
-\r
-    def CreatePanelFolders(self):\r
-        #set file filters\r
-        filters = self.config['folders']['filters']\r
-        index = self.config['folders'].as_int('filterindex')\r
-        #set initial directory\r
-        folder = self.config['general']['workdir']\r
-        return wx.GenericDirCtrl(self, -1, dir=folder, size=(200, 250), style=wx.DIRCTRL_SHOW_FILTERS, filter=filters, defaultFilter=index)\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
-    def CreatePanelPlaylists(self):\r
-        return hookeplaylist.Playlists(self)\r
-\r
-    def CreatePanelProperties(self):\r
-        return hookepropertyeditor.PropertyEditor(self)\r
-\r
-    def CreatePanelResults(self):\r
-        return hookeresults.Results(self)\r
-\r
-    def CreatePanelWelcome(self):\r
-        ctrl = wx.html.HtmlWindow(self, -1, wx.DefaultPosition, wx.Size(400, 300))\r
-        introStr = '<h1>Welcome to Hooke</h1>' + \\r
-                 '<h3>Features</h3>' + \\r
-                 '<ul>' + \\r
-                 '<li>View, annotate, measure force curves</li>' + \\r
-                 '<li>Worm-like chain fit of force peaks</li>' + \\r
-                 '<li>Automatic convolution-based filtering of empty curves</li>' + \\r
-                 '<li>Automatic fit and measurement of multiple force peaks</li>' + \\r
-                 '<li>Handles force-clamp force experiments (experimental)</li>' + \\r
-                 '<li>It is extensible by users by means of plugins and drivers</li>' + \\r
-                 '</ul>' + \\r
-                 '<p>See the <a href="/p/hooke/wiki/DocumentationIndex">DocumentationIndex</a> for more information</p>'\r
-        ctrl.SetPage(introStr)\r
-        return ctrl\r
-\r
-    def CreateMenuBar(self):\r
-        menu_bar = wx.MenuBar()\r
-        #file\r
-        file_menu = wx.Menu()\r
-        file_menu.Append(wx.ID_OPEN, '&Open playlist\tCtrl-O')\r
-        file_menu.Append(wx.ID_SAVE, 'Save playlist\tCtrl-S')\r
-        file_menu.AppendSeparator()\r
-        file_menu.Append(wx.ID_EXIT, 'Exit\tCtrl-Q')\r
-        #edit\r
-        edit_menu = wx.Menu()\r
-        edit_menu.Append(ID_ExportText, 'Export text...')\r
-        edit_menu.Append(ID_ExportImage, 'Export image...')\r
-        edit_menu.AppendSeparator();\r
-        edit_menu.Append(ID_Config, 'Preferences')\r
-        #view\r
-        view_menu = wx.Menu()\r
-        view_menu.AppendCheckItem(ID_ViewFolders, 'Folders\tF5')\r
-        view_menu.AppendCheckItem(ID_ViewPlaylists, 'Playlists\tF6')\r
-        view_menu.AppendCheckItem(ID_ViewCommands, 'Commands\tF7')\r
-        view_menu.AppendCheckItem(ID_ViewProperties, 'Properties\tF8')\r
-        view_menu.AppendCheckItem(ID_ViewAssistant, 'Assistant\tF9')\r
-        view_menu.AppendCheckItem(ID_ViewResults, 'Results\tF10')\r
-        view_menu.AppendCheckItem(ID_ViewOutput, 'Output\tF11')\r
-        #perspectives\r
-        self._perspectives_menu = self.CreatePerspectivesMenu()\r
-        #help\r
-        help_menu = wx.Menu()\r
-        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(self._perspectives_menu, "Perspectives")\r
-        menu_bar.Append(help_menu, 'Help')\r
-\r
-        self.SetMenuBar(menu_bar)\r
-\r
-    def CreateNotebook(self):\r
-        # create the notebook off-window to avoid flicker\r
-        client_size = self.GetClientSize()\r
-        ctrl = aui.AuiNotebook(self, -1, wx.Point(client_size.x, client_size.y), wx.Size(430, 200), self._notebook_style)\r
-        arts = [aui.AuiDefaultTabArt, aui.AuiSimpleTabArt, aui.VC71TabArt, aui.FF2TabArt, aui.VC8TabArt, aui.ChromeTabArt]\r
-        art = arts[self._notebook_theme]()\r
-        ctrl.SetArtProvider(art)\r
-        #uncomment if we find a nice icon\r
-        #page_bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))\r
-        ctrl.AddPage(self.CreatePanelWelcome(), "Welcome", False)\r
-        return ctrl\r
-\r
-    def CreatePerspectivesMenu(self):\r
-        menu = wx.Menu()\r
-        menu.Append(ID_SavePerspective, "Save Perspective")\r
-        menu.Append(ID_DeletePerspective, "Delete Perspective")\r
-        menu.AppendSeparator()\r
-        #add perspectives to menubar and _perspectives\r
-        perspectivesDirectory = os.path.join(lh.hookeDir, 'perspectives')\r
-        if os.path.isdir(perspectivesDirectory):\r
-            perspectiveFileNames = os.listdir(perspectivesDirectory)\r
-            for perspectiveFilename in perspectiveFileNames:\r
-                filename = lh.get_file_path(perspectiveFilename, ['perspectives'])\r
-                if os.path.isfile(filename):\r
-                    perspectiveFile = open(filename, 'rU')\r
-                    perspective = perspectiveFile.readline()\r
-                    perspectiveFile.close()\r
-                    if perspective != '':\r
-                        name, extension = os.path.splitext(perspectiveFilename)\r
-                        if extension == '.txt':\r
-                            menuItem = menu.AppendRadioItem(ID_FirstPerspective + len(self._perspectives), name)\r
-                            self._perspectives[name] = [len(self._perspectives), perspective]\r
-                            if self.config['perspectives']['active'] == name:\r
-                                menuItem.Check()\r
-        #in case there are no perspectives\r
-        if not self._perspectives:\r
-            perspective = self._mgr.SavePerspective()\r
-            self.config['perspectives']['default'] = 'Default'\r
-            self._perspectives['Default'] = [0, perspective]\r
-            menuItem = menu.AppendRadioItem(ID_FirstPerspective, 'Default')\r
-            menuItem.Check()\r
-            self._SavePerspectiveToFile('Default', perspective)\r
-        return menu\r
-\r
-    def CreateStatusbar(self):\r
-        statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)\r
-        statusbar.SetStatusWidths([-2, -3])\r
-        statusbar.SetStatusText('Ready', 0)\r
-        welcomeString=u'Welcome to Hooke (version '+__version__+', '+__release_name__+')!'\r
-        statusbar.SetStatusText(welcomeString, 1)\r
-        return statusbar\r
-\r
-    def CreateToolBar(self):\r
-        toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)\r
-        toolbar.SetToolBitmapSize(wx.Size(16,16))\r
-        toolbar_bmp1 = wx.ArtProvider_GetBitmap(wx.ART_QUESTION, wx.ART_OTHER, wx.Size(16, 16))\r
-        toolbar_bmpOpen = wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN, wx.ART_OTHER, wx.Size(16, 16))\r
-        toolbar_bmpSave = wx.ArtProvider_GetBitmap(wx.ART_FILE_SAVE, wx.ART_OTHER, wx.Size(16, 16))\r
-        toolbar_bmpExportText = wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))\r
-        toolbar_bmpExportImage = wx.ArtProvider_GetBitmap(wx.ART_MISSING_IMAGE, wx.ART_OTHER, wx.Size(16, 16))\r
-        toolbar.AddLabelTool(101, 'Open', toolbar_bmpOpen)\r
-        toolbar.AddLabelTool(102, 'Save', toolbar_bmpSave)\r
-        toolbar.AddSeparator()\r
-        toolbar.AddLabelTool(ID_ExportText, 'Export text...', toolbar_bmpExportText)\r
-        toolbar.AddLabelTool(ID_ExportImage, 'Export image...', toolbar_bmpExportImage)\r
-        toolbar.Realize()\r
-        return toolbar\r
-\r
-    def CreateToolBarNavigation(self):\r
-        toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)\r
-        toolbar.SetToolBitmapSize(wx.Size(16,16))\r
-        toolbar_bmpBack = wx.ArtProvider_GetBitmap(wx.ART_GO_BACK, wx.ART_OTHER, wx.Size(16, 16))\r
-        toolbar_bmpForward = wx.ArtProvider_GetBitmap(wx.ART_GO_FORWARD, wx.ART_OTHER, wx.Size(16, 16))\r
-        toolbar.AddLabelTool(ID_Previous, 'Previous', toolbar_bmpBack, shortHelp='Previous curve')\r
-        toolbar.AddLabelTool(ID_Next, 'Next', toolbar_bmpForward, shortHelp='Next curve')\r
-        toolbar.Realize()\r
-        return toolbar\r
-\r
-    def DeleteFromPlaylists(self, name):\r
-        if name in self.playlists:\r
-            del self.playlists[name]\r
-        tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()\r
-        item, cookie = self.panelPlaylists.PlaylistsTree.GetFirstChild(tree_root)\r
-        while item.IsOk():\r
-            playlist_name = self.panelPlaylists.PlaylistsTree.GetItemText(item)\r
-            if playlist_name == name:\r
-                try:\r
-                    self.panelPlaylists.PlaylistsTree.Delete(item)\r
-                except:\r
-                    pass\r
-            item = self.panelPlaylists.PlaylistsTree.GetNextSibling(item)\r
-        self.OnPlaylistsLeftDclick(None)\r
-\r
-    def DeletePlotPage(self, name):\r
-        index = self._GetPlaylistTab(name)\r
-        plot = self.playlists[name][1]\r
-        plot = None\r
-        self.plotNotebook.RemovePage(index)\r
-        self.DeleteFromPlaylists(name)\r
-\r
-    def GetActiveCurve(self):\r
-        playlist = self.GetActivePlaylist()\r
-        if playlist is not None:\r
-            return playlist.get_active_curve()\r
-        return None\r
-\r
-    def GetActivePlaylist(self):\r
-        playlist_name = self._GetActivePlaylistName()\r
-        if playlist_name in self.playlists:\r
-            return self.playlists[playlist_name][0]\r
-        return None\r
-\r
-    def GetActivePlot(self):\r
-        curve = self.GetActiveCurve()\r
-        if curve is not None:\r
-            return curve.plots[0]\r
-        return None\r
-\r
-    def GetDockArt(self):\r
-        return self._mgr.GetArtProvider()\r
-\r
-    def GetBoolFromConfig(self, *args):\r
-        if len(args) == 2:\r
-            plugin = args[0]\r
-            section = args[0]\r
-            key = args[1]\r
-        elif len(args) == 3:\r
-            plugin = args[0]\r
-            section = args[1]\r
-            key = args[2]\r
-        if self.configs.has_key(plugin):\r
-            config = self.configs[plugin]\r
-            return config[section][key].as_bool('value')\r
-        return None\r
-\r
-    def GetFloatFromConfig(self, *args):\r
-        if len(args) == 2:\r
-            plugin = args[0]\r
-            section = args[0]\r
-            key = args[1]\r
-        elif len(args) == 3:\r
-            plugin = args[0]\r
-            section = args[1]\r
-            key = args[2]\r
-        if self.configs.has_key(plugin):\r
-            config = self.configs[plugin]\r
-            return config[section][key].as_float('value')\r
-        return None\r
-\r
-    def GetIntFromConfig(self, *args):\r
-        if len(args) == 2:\r
-            plugin = args[0]\r
-            section = args[0]\r
-            key = args[1]\r
-        elif len(args) == 3:\r
-            plugin = args[0]\r
-            section = args[1]\r
-            key = args[2]\r
-        if self.configs.has_key(plugin):\r
-            config = self.configs[plugin]\r
-            return config[section][key].as_int('value')\r
-        return None\r
-\r
-    def GetStringFromConfig(self, *args):\r
-        if len(args) == 2:\r
-            plugin = args[0]\r
-            section = args[0]\r
-            key = args[1]\r
-        elif len(args) == 3:\r
-            plugin = args[0]\r
-            section = args[1]\r
-            key = args[2]\r
-        if self.configs.has_key(plugin):\r
-            config = self.configs[plugin]\r
-            return config[section][key]['value']\r
-        return None\r
-\r
-    def GetPerspectiveMenuItem(self, name):\r
-        index = self._perspectives[name][0]\r
-        perspective_Id = ID_FirstPerspective + index\r
-        menu_item = self.MenuBar.FindItemById(perspective_Id)\r
-        return menu_item\r
-\r
-    def HasPlotmanipulator(self, name):\r
-        '''\r
-        returns True if the plotmanipulator 'name' is loaded, False otherwise\r
-        '''\r
-        for plotmanipulator in self.plotmanipulators:\r
-            if plotmanipulator[0] == name:\r
-                return True\r
-        return False\r
-\r
-    def OnAbout(self, event):\r
-        msg = 'Hooke\n\n'+\\r
-            'A free, open source data analysis platform\n'+\\r
-            '(c) 2006-2008 Massimo Sandal\n\n'+\\r
-            '(c) 2009 Dr. Rolf Schmidt\n\n'+\\r
-            'Released under the GNU GPL v2'\r
-        dialog = wx.MessageDialog(self, msg, "About Hooke", wx.OK | wx.ICON_INFORMATION)\r
-        dialog.ShowModal()\r
-        dialog.Destroy()\r
-\r
-    def OnClose(self, event):\r
-        #apply changes\r
-        self.config['main']['height'] = str(self.GetSize().GetHeight())\r
-        self.config['main']['left'] = str(self.GetPosition()[0])\r
-        self.config['main']['top'] = str(self.GetPosition()[1])\r
-        self.config['main']['width'] = str(self.GetSize().GetWidth())\r
-        # Writing the configuration file to 'hooke.ini'\r
-        self.config.write()\r
-        self._mgr.UnInit()\r
-        del self._mgr\r
-        self.Destroy()\r
-\r
-    def OnDeletePerspective(self, event):\r
-        pass\r
-\r
-    def OnDirCtrlLeftDclick(self, event):\r
-        file_path = self.panelFolders.GetPath()\r
-        if os.path.isfile(file_path):\r
-            if file_path.endswith('.hkp'):\r
-                self.do_loadlist(file_path)\r
-            else:\r
-                pass\r
-        event.Skip()\r
-\r
-    def OnEraseBackground(self, event):\r
-        event.Skip()\r
-\r
-    def OnExecute(self, event):\r
-        item = self.panelCommands.CommandsTree.GetSelection()\r
-        if item.IsOk():\r
-            if self.panelCommands.CommandsTree.ItemHasChildren(item):\r
-                pass\r
-            else:\r
-                #get the plugin\r
-                parent = self.panelCommands.CommandsTree.GetItemParent(item)\r
-            if not self.panelCommands.CommandsTree.ItemHasChildren(item):\r
-                parent_text = self.panelCommands.CommandsTree.GetItemText(parent)\r
-                item_text = self.panelCommands.CommandsTree.GetItemText(item)\r
-                if item_text in ['genlist', 'loadlist', 'savelist']:\r
-                    property_values = self.panelProperties.GetPropertyValues()\r
-                    arg_str = ''\r
-                    for item in property_values:\r
-                        arg_str = ''.join([arg_str, item, '=r"', str(property_values[item]), '", '])\r
-                    command = ''.join(['self.do_', item_text, '(', arg_str, ')'])\r
-                else:\r
-                    command = ''.join(['self.do_', item_text, '()'])\r
-                exec(command)\r
-        pass\r
-\r
-    def OnExit(self, event):\r
-        self.Close()\r
-\r
-    def OnExportImage(self, event):\r
-        pass\r
-\r
-    def OnNext(self, event):\r
-        '''\r
-        NEXT\r
-        Go to the next curve in the playlist.\r
-        If we are at the last curve, we come back to the first.\r
-        -----\r
-        Syntax: next, n\r
-        '''\r
-        selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()\r
-        if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):\r
-            #GetFirstChild returns a tuple\r
-            #we only need the first element\r
-            next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(selected_item)[0]\r
-        else:\r
-            next_item = self.panelPlaylists.PlaylistsTree.GetNextSibling(selected_item)\r
-            if not next_item.IsOk():\r
-                parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)\r
-                #GetFirstChild returns a tuple\r
-                #we only need the first element\r
-                next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(parent_item)[0]\r
-        self.panelPlaylists.PlaylistsTree.SelectItem(next_item, True)\r
-        playlist = self.playlists[self._GetActivePlaylistName()][0]\r
-        if playlist.count > 1:\r
-            playlist.next()\r
-            self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
-            self.UpdatePlot()\r
-\r
-    def OnNotebookPageClose(self, event):\r
-        ctrl = event.GetEventObject()\r
-        playlist_name = ctrl.GetPageText(ctrl._curpage)\r
-        self.DeleteFromPlaylists(playlist_name)\r
-\r
-    def OnPaneClose(self, event):\r
-        event.Skip()\r
-\r
-    def OnPlaylistsLeftDclick(self, event):\r
-        playlist_name = self._GetActivePlaylistName()\r
-        #if that playlist already exists\r
-        #we check if it is the active playlist (ie selected in panelPlaylists)\r
-        #and switch to it if necessary\r
-        if playlist_name in self.playlists:\r
-            index = self.plotNotebook.GetSelection()\r
-            current_playlist = self.plotNotebook.GetPageText(index)\r
-            #new_playlist = self.playlists[playlist_name][0]\r
-            #if current_playlist != new_playlist:\r
-            if current_playlist != playlist_name:\r
-                index = self._GetPlaylistTab(playlist_name)\r
-                self.plotNotebook.SetSelection(index)\r
-            #if a curve was double-clicked\r
-            item = self.panelPlaylists.PlaylistsTree.GetSelection()\r
-            #TODO: fix with get_active_curve\r
-            if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):\r
-                index = self._GetActiveCurveIndex()\r
-            else:\r
-                index = 0\r
-            if index >= 0:\r
-                playlist = self.playlists[playlist_name][0]\r
-                playlist.index = index\r
-                self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
-                self.UpdatePlot()\r
-        #if you uncomment the following line, the tree will collapse/expand as well\r
-        #event.Skip()\r
-\r
-    def OnPlaylistsLeftDown(self, event):\r
-        hit_item, hit_flags = self.panelPlaylists.PlaylistsTree.HitTest(event.GetPosition())\r
-        if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:\r
-            #self.SetFocus()\r
-            self.panelPlaylists.PlaylistsTree.SelectItem(hit_item)\r
-            playlist_name = self._GetActivePlaylistName()\r
-            playlist = self.playlists[playlist_name][0]\r
-            #if a curve was clicked\r
-            item = self.panelPlaylists.PlaylistsTree.GetSelection()\r
-            if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):\r
-                #TODO: fix with get_active_curve\r
-                index = self._GetActiveCurveIndex()\r
-                if index >= 0:\r
-                    #playlist = self.playlists[playlist_name][0]\r
-                    playlist.index = index\r
-                    #self.playlists[playlist_name][0].index = index\r
-            #else:\r
-                ##self.playlists[playlist_name][0].index = 0\r
-                #playlist.index = index\r
-            self.playlists[playlist_name][0] = playlist\r
-        event.Skip()\r
-\r
-    def OnPrevious(self, event):\r
-        '''\r
-        PREVIOUS\r
-        Go to the previous curve in the playlist.\r
-        If we are at the first curve, we jump to the last.\r
-        -------\r
-        Syntax: previous, p\r
-        '''\r
-        #playlist = self.playlists[self._GetActivePlaylistName()][0]\r
-        #select the previous curve and tell the user if we wrapped around\r
-        #self.AppendToOutput(playlist.previous())\r
-        selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()\r
-        if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):\r
-            previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(selected_item)\r
-        else:\r
-            previous_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)\r
-            if not previous_item.IsOk():\r
-                parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)\r
-                previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(parent_item)\r
-        self.panelPlaylists.PlaylistsTree.SelectItem(previous_item, True)\r
-        playlist = self.playlists[self._GetActivePlaylistName()][0]\r
-        if playlist.count > 1:\r
-            playlist.previous()\r
-            self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
-            self.UpdatePlot()\r
-\r
-    def OnPropGridChanged (self, event):\r
-        prop = event.GetProperty()\r
-        if prop:\r
-            item_section = self.panelProperties.SelectedTreeItem\r
-            item_plugin = self.panelCommands.CommandsTree.GetItemParent(item_section)\r
-            plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)\r
-            config = self.configs[plugin]\r
-            property_section = self.panelCommands.CommandsTree.GetItemText(item_section)\r
-            property_key = prop.GetName()\r
-            property_value = prop.GetValue()\r
-            config[property_section][property_key]['value'] = property_value\r
-\r
-    def OnPropGridSelect(self, event):\r
-        pass\r
-\r
-    def OnRestorePerspective(self, event):\r
-        name = self.MenuBar.FindItemById(event.GetId()).GetLabel()\r
-        self._mgr.LoadPerspective(self._perspectives[name][1])\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
-        curve = self.GetActiveCurve()\r
-        result = curve.data['results'][index]['visible'] = flag\r
-        self.UpdatePlot()\r
-\r
-    def OnSavePerspective(self, event):\r
-        def nameExists(name):\r
-            for item in self._perspectives_menu.GetMenuItems():\r
-                if item.GetText() == name:\r
-                    return True\r
-            return False\r
-\r
-        done = False\r
-        while not done:\r
-            dialog = wx.TextEntryDialog(self, 'Enter a name for the new perspective:', 'Save perspective')\r
-            dialog.SetValue('New perspective')\r
-            if dialog.ShowModal() != wx.ID_OK:\r
-                return\r
-            else:\r
-                name = dialog.GetValue()\r
-\r
-            if nameExists(name):\r
-                dialogConfirm = wx.MessageDialog(self, 'A file with this name already exists.\n\nDo you want to replace it?', 'Confirm', wx.YES_NO|wx.ICON_QUESTION|wx.CENTER)\r
-                if dialogConfirm.ShowModal() == wx.ID_YES:\r
-                    done = True\r
-            else:\r
-                done = True\r
-\r
-        perspective = self._mgr.SavePerspective()\r
-\r
-        if nameExists(name):\r
-            #check the corresponding menu item\r
-            menuItem = self.GetPerspectiveMenuItem(name)\r
-            #replace the perspectiveStr in _pespectives\r
-            index = self._perspectives[name][0]\r
-            self._perspectives[name] = [index, perspective]\r
-        else:\r
-            #simply add the new perspective to _perspectives\r
-            index = len(self._perspectives)\r
-            self._perspectives[name] = [len(self._perspectives), perspective]\r
-            menuItem = self._perspectives_menu.AppendRadioItem(ID_FirstPerspective + len(self._perspectives), name)\r
-\r
-        menuItem.Check()\r
-        self._SavePerspectiveToFile(name, perspective)\r
-        #uncheck all perspective menu items\r
-        #as these are radio items, one item has to be checked at all times\r
-        #the line 'menuItem.Check()' above actually checks a second item\r
-        #but does not toggle\r
-        #so we need to uncheck all other items afterwards\r
-        #weirdly enough, menuitem.Toggle() doesn't do this properly either\r
-        for item in self._perspectives_menu.GetMenuItems():\r
-            if item.IsCheckable():\r
-                if item.GetLabel() != name:\r
-                    item.Check(False)\r
-\r
-    def OnView(self, event):\r
-        menu_id = event.GetId()\r
-        menu_item = self.MenuBar.FindItemById(menu_id)\r
-        menu_label = menu_item.GetLabel()\r
-\r
-        pane = self._mgr.GetPane(menu_label)\r
-        pane.Show(not pane.IsShown())\r
-        #if we don't do the following, the Folders pane does not resize properly on hide/show\r
-        if pane.caption == 'Folders' and pane.IsShown() and pane.IsDocked():\r
-            #folders_size = pane.GetSize()\r
-            self.panelFolders.Fit()\r
-        self._mgr.Update()\r
-\r
-    def OnSize(self, event):\r
-        event.Skip()\r
-\r
-    def OnTreeCtrlCommandsLeftDown(self, event):\r
-        hit_item, hit_flags = self.panelCommands.CommandsTree.HitTest(event.GetPosition())\r
-        if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:\r
-            self.panelCommands.CommandsTree.SelectItem(hit_item)\r
-            self.panelProperties.SelectedTreeItem = hit_item\r
-            #if a command was clicked\r
-            properties = []\r
-            if not self.panelCommands.CommandsTree.ItemHasChildren(hit_item):\r
-                item_plugin = self.panelCommands.CommandsTree.GetItemParent(hit_item)\r
-                plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)\r
-                if self.configs.has_key(plugin):\r
-                    #config = self.panelCommands.CommandsTree.GetPyData(item_plugin)\r
-                    config = self.configs[plugin]\r
-                    section = self.panelCommands.CommandsTree.GetItemText(hit_item)\r
-                    #display docstring in help window\r
-                    doc_string = eval('self.do_' + section + '.__doc__')\r
-                    if section in config:\r
-                        for option in config[section]:\r
-                            properties.append([option, config[section][option]])\r
-            else:\r
-                module = self.panelCommands.CommandsTree.GetItemText(hit_item)\r
-                if module != 'general':\r
-                    doc_string = eval('plugins.' + module + '.' + module + 'Commands.__doc__')\r
-                else:\r
-                    doc_string = 'The module "general" contains Hooke core functionality'\r
-            if doc_string is not None:\r
-                self.panelAssistant.ChangeValue(doc_string)\r
-            hookepropertyeditor.PropertyEditor.Initialize(self.panelProperties, properties)\r
-        event.Skip()\r
-\r
-    def UpdatePlaylists(self):\r
-        #setup the playlist in the Playlist tree\r
-        tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()\r
-        playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)\r
-        #add all curves to the Playlist tree\r
-        curves = {}\r
-        for index, curve in enumerate(playlist.curves):\r
-            ##remove the extension from the name of the curve\r
-            ##TODO: optional?\r
-            #item_text, extension = os.path.splitext(curve.name)\r
-            #curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, item_text, 1)\r
-            curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, curve.name, 1)\r
-            if index == playlist.index:\r
-                self.panelPlaylists.PlaylistsTree.SelectItem(curve_ID)\r
-        #create the plot tab and add playlist to the dictionary\r
-        plotPanel = wxmpl.PlotPanel(self, ID_FirstPlot + len(self.playlists))\r
-        notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True)\r
-        #tab_index = self.plotNotebook.GetSelection()\r
-        figure = plotPanel.get_figure()\r
-        #self.playlists[playlist.name] = [playlist, tab_index, figure]\r
-        self.playlists[playlist.name] = [playlist, figure]\r
-        self.panelPlaylists.PlaylistsTree.Expand(playlist_root)\r
-        self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
-        self.UpdatePlot()\r
-\r
-#HELPER FUNCTIONS\r
-#Everything sending an event should be here\r
-    def _measure_N_points(self, N, whatset=1):\r
-        '''\r
-        general helper function for N-points measures\r
-        '''\r
-        wx.PostEvent(self.frame,self.list_of_events['measure_points'](num_of_points=N, set=whatset))\r
-        while 1:\r
-            try:\r
-                points=self.frame.events_from_gui.get()\r
-                break\r
-            except Empty:\r
-                pass\r
-        return points\r
-\r
-    def _clickize(self, xvector, yvector, index):\r
-        '''\r
-        returns a ClickedPoint() object from an index and vectors of x, y coordinates\r
-        '''\r
-        point = lh.ClickedPoint()\r
-        point.index = index\r
-        point.absolute_coords = xvector[index], yvector[index]\r
-        point.find_graph_coords(xvector, yvector)\r
-        return point\r
-\r
-#PLAYLIST INTERACTION COMMANDS\r
-#-------------------------------\r
-    def do_genlist(self, folder=lh.hookeDir, filemask='*.*'):\r
-        '''\r
-        GENLIST\r
-        Generates a file playlist.\r
-        Note it doesn't *save* it: see savelist for this.\r
-\r
-        If [input files] is a directory, it will use all files in the directory for playlist.\r
-        So:\r
-        genlist dir\r
-        genlist dir/\r
-        genlist dir/*.*\r
-\r
-        are all equivalent syntax.\r
-        ------------\r
-        Syntax: genlist [input files]\r
-        '''\r
-        #args list is: input folder, file mask\r
-        if os.path.isdir(folder):\r
-            path = os.path.join(folder, filemask)\r
-            #expanding correctly the input list with the glob module :)\r
-            files = glob.glob(path)\r
-            files.sort()\r
-            #TODO: change cursor or progressbar (maybe in statusbar)\r
-            #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))\r
-            playlist = playlist.Playlist(self.drivers)\r
-            for item in files:\r
-                curve = playlist.add_curve(item)\r
-                plot = copy.deepcopy(curve.plots[0])\r
-                #add the 'raw' data\r
-                curve.add_data('raw', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')\r
-                curve.add_data('raw', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')\r
-                #apply all active plotmanipulators and add the 'manipulated' data\r
-                for plotmanipulator in self.plotmanipulators:\r
-                    plot = plotmanipulator[1](plot, curve)\r
-                    curve.set_data('manipulated', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')\r
-                    curve.add_data('manipulated', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')\r
-            if playlist.count > 0:\r
-                playlist.name = self._GetUniquePlaylistName(os.path.basename(folder))\r
-                playlist.reset()\r
-                self.AddToPlaylists(playlist)\r
-            self.AppendToOutput(playlist.get_status_string())\r
-        else:\r
-            self.AppendToOutput(''.join(['Cannot find folder ', folder]))\r
-\r
-    def do_loadlist(self, filename):\r
-        '''\r
-        LOADLIST\r
-        Loads a file playlist\r
-        -----------\r
-        Syntax: loadlist [playlist file]\r
-        '''\r
-        #TODO: check for duplicate playlists, ask the user for a unique name\r
-        #if self.playlist_name in self.playlists:\r
-\r
-        #add hkp extension if necessary\r
-        if not filename.endswith('.hkp'):\r
-            filename = ''.join([filename, '.hkp'])\r
-        #prefix with 'hookeDir' if just a filename or a relative path\r
-        filename = lh.get_file_path(filename)\r
-        if os.path.isfile(filename):\r
-            #TODO: change cursor\r
-            #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))\r
-            playlist_new = playlist.Playlist(self.drivers)\r
-            playlist_new.load(filename)\r
-            if playlist_new.count > 0:\r
-                for curve in playlist_new.curves:\r
-                    plot = copy.deepcopy(curve.plots[0])\r
-                    for plotmanip in self.plotmanipulators:\r
-                        #to_plot = plotmanip[1](to_plot, curve)\r
-                        plot = plotmanip[1](plot, curve)\r
-                        curve.set_data('manipulated', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')\r
-                        curve.add_data('manipulated', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')\r
-                self.AddToPlaylists(playlist_new)\r
-            #else:\r
-                ##TODO: display dialog\r
-            self.AppendToOutput(playlist_new.get_status_string())\r
-            #TODO: change cursor\r
-            #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))\r
-        else:\r
-            #TODO: display dialog\r
-            self.AppendToOutput(''.join['File ', filename, ' not found.\n'])\r
-        pass\r
-\r
-    def do_savelist(self, filename):\r
-        '''\r
-        SAVELIST\r
-        Saves the current file playlist on disk.\r
-        ------------\r
-        Syntax: savelist [filename]\r
-        '''\r
-\r
-        #self.playlist_generics['pointer'] = self._GetActiveCurveIndex\r
-        pointer = self._GetActiveCurveIndex()\r
-        #autocomplete filename if not specified\r
-        if not filename.endswith('.hkp'):\r
-            filename = filename.join(['.hkp'])\r
-\r
-        playlist = self.GetActivePlaylist()\r
-        playlist.set_XML()\r
-        playlist.save(filename)\r
-\r
-#PLOT INTERACTION COMMANDS\r
-#-------------------------------\r
-    def UpdatePlot(self):\r
-        def add_plot(plot):\r
-            if plot['visible'] and plot['x'] and plot['y']:\r
-                color = plot['color']\r
-                style = plot['style']\r
-                if style == 'plot':\r
-                    axes.plot(plot['x'], plot['y'], color=color, zorder=1)\r
-                if style == 'scatter':\r
-                    axes.scatter(plot['x'], plot['y'], color=color, s=0.5, zorder=2)\r
-\r
-        def add_plot2(plot):\r
-            if plot.visible and plot.x and plot.y:\r
-                if plot.style == 'plot':\r
-                    axes.plot(plot.x, plot.y, color=plot.color, zorder=1)\r
-                if plot.style == 'scatter':\r
-                    axes.scatter(plot.x, plot.y, color=plot.color, s=0.5, zorder=2)\r
-\r
-        playlist_name = self._GetActivePlaylistName()\r
-        index = self._GetActiveCurveIndex()\r
-        playlist = self.playlists[playlist_name][0]\r
-        curve = playlist.get_active_curve()\r
-        plot = playlist.get_active_plot()\r
-        figure = self.playlists[playlist_name][1]\r
-\r
-        figure.clf()\r
-        exclude = None\r
-        if curve.data.has_key('manipulated'):\r
-            exclude = 'raw'\r
-        elif curve.data.has_key('raw'):\r
-            exclude = 'manipulated'\r
-\r
-        if exclude is not None:\r
-            #TODO: what is this good for?\r
-            if not hasattr(self, 'subplot'):\r
-                axes = figure.add_subplot(111)\r
-\r
-            axes.set_title(plot.title)\r
-            axes.set_xlabel(plot.units[0])\r
-            axes.set_ylabel(plot.units[1])\r
-\r
-            for set_of_plots in curve.data:\r
-                if set_of_plots != exclude:\r
-                    plots = curve.data[set_of_plots]\r
-                    for each_plot in plots:\r
-                        add_plot(each_plot)\r
-\r
-            #TODO: add multiple results support\r
-            #for fit in curve.fits:\r
-            if curve.fits.has_key('wlc'):\r
-                for plot in curve.fits['wlc'].results:\r
-                    add_plot2(plot)\r
-                self.panelResults.DisplayResults(curve.fits['wlc'])\r
-            else:\r
-                self.panelResults.ClearResults()\r
-\r
-            axes.figure.canvas.draw()\r
-        else:\r
-            self.AppendToOutput('Not able to plot.')\r
-\r
-\r
-ID_PaneBorderSize = wx.ID_HIGHEST + 1\r
-ID_SashSize = ID_PaneBorderSize + 1\r
-ID_CaptionSize = ID_PaneBorderSize + 2\r
-ID_BackgroundColor = ID_PaneBorderSize + 3\r
-ID_SashColor = ID_PaneBorderSize + 4\r
-ID_InactiveCaptionColor =  ID_PaneBorderSize + 5\r
-ID_InactiveCaptionGradientColor = ID_PaneBorderSize + 6\r
-ID_InactiveCaptionTextColor = ID_PaneBorderSize + 7\r
-ID_ActiveCaptionColor = ID_PaneBorderSize + 8\r
-ID_ActiveCaptionGradientColor = ID_PaneBorderSize + 9\r
-ID_ActiveCaptionTextColor = ID_PaneBorderSize + 10\r
-ID_BorderColor = ID_PaneBorderSize + 11\r
-ID_GripperColor = ID_PaneBorderSize + 12\r
-\r
-\r
-#----------------------------------------------------------------------\r
-\r
-if __name__ == '__main__':\r
-\r
-    ## now, silence a deprecation warning for py2.3\r
-    #import warnings\r
-    #warnings.filterwarnings("ignore", "integer", DeprecationWarning, "wxPython.gdi")\r
-\r
-    redirect=True\r
-    if __debug__:\r
-        redirect=False\r
-    app = Hooke(redirect=redirect)\r
-\r
-    app.MainLoop()\r
-\r
-\r
+#!/usr/bin/env python
+
+'''
+HOOKE - A force spectroscopy review & analysis tool
+
+Copyright (C) 2008-2010 Massimo Sandal (University of Bologna, Italy).
+                        Rolf Schmidt (Concordia University, Canada).
+
+This program is released under the GNU General Public License version 2.
+'''
+
+from libhooke import WX_GOOD
+
+import wxversion
+wxversion.select(WX_GOOD)
+import copy
+import cStringIO
+import os
+import os.path
+import sys
+import glob
+import time
+
+import imp
+import wx
+import wx.html
+import wx.aui
+import wxmpl
+import wx.lib.agw.aui as aui
+import wx.propgrid as wxpg
+
+import libhooke as lh
+from config import config
+import drivers
+import plugins
+import hookecommands
+import hookeplaylist
+import hookepropertyeditor
+import hookeresults
+import playlist
+
+global __version__
+
+__version__ = lh.HOOKE_VERSION[0]
+__release_name__ = lh.HOOKE_VERSION[1]
+
+#TODO: order menu items, get rid of all unused IDs
+ID_ExportText = wx.NewId()
+ID_ExportImage = wx.NewId()
+ID_Config = wx.NewId()
+ID_About = wx.NewId()
+ID_Next = wx.NewId()
+ID_Previous = wx.NewId()
+
+ID_ViewAssistant = wx.NewId()
+ID_ViewCommands = wx.NewId()
+ID_ViewFolders = wx.NewId()
+ID_ViewOutput = wx.NewId()
+ID_ViewPlaylists = wx.NewId()
+ID_ViewProperties = wx.NewId()
+ID_ViewResults = wx.NewId()
+
+ID_CommandsList = wx.NewId()
+ID_CommandsListBox = wx.NewId()
+
+ID_TextContent = wx.NewId()
+ID_TreeContent = wx.NewId()
+ID_HTMLContent = wx.NewId()
+ID_SizeReportContent = wx.NewId()
+ID_DeletePerspective = wx.NewId()
+ID_SavePerspective = wx.NewId()
+
+ID_FirstPerspective = ID_SavePerspective + 1000
+#I hope we'll never have more than 1000 perspectives
+ID_FirstPlot = ID_SavePerspective + 2000
+
+class Hooke(wx.App):
+
+    def OnInit(self):
+        self.SetAppName('Hooke')
+        self.SetVendorName('')
+
+        #set the Hooke directory
+        lh.hookeDir = os.path.abspath(os.path.dirname(__file__))
+
+        windowPosition = (config['main']['left'], config['main']['top'])
+        windowSize = (config['main']['width'], config['main']['height'])
+
+        #setup the splashscreen
+        if config['splashscreen']['show']:
+            filename = lh.get_file_path('hooke.jpg', ['resources'])
+            if os.path.isfile(filename):
+                bitmap = wx.Image(filename).ConvertToBitmap()
+                splashStyle = wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT
+                splashDuration = config['splashscreen']['duration']
+                wx.SplashScreen(bitmap, splashStyle, splashDuration, None, -1)
+                wx.Yield()
+                '''
+                we need for the splash screen to disappear
+                for whatever reason splashDuration and sleep do not correspond to each other
+                at least not on Windows
+                maybe it's because duration is in milliseconds and sleep in seconds
+                thus we need to increase the sleep time a bit
+                a factor of 1.2 seems to work quite well
+                '''
+                sleepFactor = 1.2
+                time.sleep(sleepFactor * splashDuration / 1000)
+
+        plugin_objects = []
+        for plugin in config['plugins']:
+            if config['plugins'][plugin]:
+                filename = ''.join([plugin, '.py'])
+                path = lh.get_file_path(filename, ['plugins'])
+                if os.path.isfile(path):
+                    #get the corresponding filename and path
+                    plugin_name = ''.join(['plugins.', plugin])
+                    module = __import__(plugin_name)
+                    #get the file that contains the plugin
+                    class_file = getattr(plugins, plugin)
+                    #get the class that contains the commands
+                    class_object = getattr(class_file, plugin + 'Commands')
+                    plugin_objects.append(class_object)
+
+        def make_command_class(*bases):
+            #create metaclass with plugins and plotmanipulators
+            return type(HookeFrame)("HookeFramePlugged", bases + (HookeFrame,), {})
+        frame = make_command_class(*plugin_objects)(parent=None, id=wx.ID_ANY, title='Hooke', pos=windowPosition, size=windowSize)
+        frame.Show(True)
+        self.SetTopWindow(frame)
+
+        return True
+
+    def OnExit(self):
+        #TODO: write values to ini file if necessary
+        return True
+
+
+class HookeFrame(wx.Frame):
+
+    def __init__(self, parent, id=-1, title='', pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE|wx.SUNKEN_BORDER|wx.CLIP_CHILDREN):
+        #call parent constructor
+        wx.Frame.__init__(self, parent, id, title, pos, size, style)
+        self.config = config
+        self.CreateApplicationIcon()
+        #self.configs contains: {the name of the Commands file: corresponding ConfigObj}
+        self.configs = {}
+        ##self.playlists contains: {the name of the playlist: [playlist, tabIndex, plotID]}
+        #self.playlists = {}
+        #self.plugins contains: {the name of the plugin: [caption, function]}
+        self.plugins = {}
+        #self.plotmanipulators list contains: [the name of the plotmanip, function, name of the module]
+        self.plotmanipulators = []
+
+        #tell FrameManager to manage this frame
+        self._mgr = aui.AuiManager()
+        self._mgr.SetManagedWindow(self)
+        #set the gradient style
+        self._mgr.GetArtProvider().SetMetric(aui.AUI_DOCKART_GRADIENT_TYPE, aui.AUI_GRADIENT_NONE)
+        #set transparent drag
+        self._mgr.SetFlags(self._mgr.GetFlags() ^ aui.AUI_MGR_TRANSPARENT_DRAG)
+
+        # set up default notebook style
+        self._notebook_style = aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER
+        self._notebook_theme = 0
+
+        #holds the perspectives: {name, [index, perspectiveStr]}
+        self._perspectives = {}
+
+        # min size for the frame itself isn't completely done.
+        # see the end up FrameManager::Update() for the test
+        # code. For now, just hard code a frame minimum size
+        self.SetMinSize(wx.Size(400, 300))
+        #create panels here
+        self.panelAssistant = self.CreatePanelAssistant()
+        self.panelCommands = self.CreatePanelCommands()
+        self.panelFolders = self.CreatePanelFolders()
+        self.panelPlaylists = self.CreatePanelPlaylists()
+        self.panelProperties = self.CreatePanelProperties()
+        self.panelOutput = self.CreatePanelOutput()
+        self.panelResults = self.CreatePanelResults()
+        self.plotNotebook = self.CreateNotebook()
+        #self.textCtrlCommandLine=self.CreateCommandLine()
+
+        # add panes
+        self._mgr.AddPane(self.panelFolders, aui.AuiPaneInfo().Name('Folders').Caption('Folders').Left().CloseButton(True).MaximizeButton(False))
+        self._mgr.AddPane(self.panelPlaylists, aui.AuiPaneInfo().Name('Playlists').Caption('Playlists').Left().CloseButton(True).MaximizeButton(False))
+        self._mgr.AddPane(self.plotNotebook, aui.AuiPaneInfo().Name('Plots').CenterPane().PaneBorder(False))
+        self._mgr.AddPane(self.panelCommands, aui.AuiPaneInfo().Name('Commands').Caption('Settings and commands').Right().CloseButton(True).MaximizeButton(False))
+        self._mgr.AddPane(self.panelProperties, aui.AuiPaneInfo().Name('Properties').Caption('Properties').Right().CloseButton(True).MaximizeButton(False))
+        self._mgr.AddPane(self.panelAssistant, aui.AuiPaneInfo().Name('Assistant').Caption('Assistant').Right().CloseButton(True).MaximizeButton(False))
+        self._mgr.AddPane(self.panelOutput, aui.AuiPaneInfo().Name('Output').Caption('Output').Bottom().CloseButton(True).MaximizeButton(False))
+        self._mgr.AddPane(self.panelResults, aui.AuiPaneInfo().Name('Results').Caption('Results').Bottom().CloseButton(True).MaximizeButton(False))
+        #self._mgr.AddPane(self.textCtrlCommandLine, aui.AuiPaneInfo().Name('CommandLine').CaptionVisible(False).Fixed().Bottom().Layer(2).CloseButton(False).MaximizeButton(False))
+        #self._mgr.AddPane(panelBottom, aui.AuiPaneInfo().Name("panelCommandLine").Bottom().Position(1).CloseButton(False).MaximizeButton(False))
+
+        # add the toolbars to the manager
+        self.toolbar=self.CreateToolBar()
+        self.toolbarNavigation=self.CreateToolBarNavigation()
+        self._mgr.AddPane(self.toolbar, aui.AuiPaneInfo().Name('toolbar').Caption('Toolbar').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
+        self._mgr.AddPane(self.toolbarNavigation, aui.AuiPaneInfo().Name('toolbarNavigation').Caption('Navigation').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
+        # "commit" all changes made to FrameManager
+        self._mgr.Update()
+        #create the menubar after the panes so that the default perspective
+        #is created with all panes open
+        self.CreateMenuBar()
+        self.statusbar = self.CreateStatusBar()
+        self._BindEvents()
+        #TODO: select item on startup (whatever item)
+        #self.listCtrlCommands.Select(0)
+        #self.OnListboxSelect(None)
+        name = self.config['perspectives']['active']
+        menu_item = self.GetPerspectiveMenuItem(name)
+        self.OnRestorePerspective(menu_item)
+        self.playlists = self.panelPlaylists.Playlists
+        #define the list of active drivers
+        self.drivers = []
+        for driver in self.config['drivers']:
+            if self.config['drivers'][driver]:
+                #get the corresponding filename and path
+                filename = ''.join([driver, '.py'])
+                path = lh.get_file_path(filename, ['drivers'])
+                #the driver is active for driver[1] == 1
+                if os.path.isfile(path):
+                    #driver files are located in the 'drivers' subfolder
+                    driver_name = ''.join(['drivers.', driver])
+                    module = __import__(driver_name)
+                    class_file = getattr(drivers, driver)
+                    for command in dir(class_file):
+                        if command.endswith('Driver'):
+                            self.drivers.append(getattr(class_file, command))
+        #import all active plugins and plotmanips
+        #the plotmanip_functions contains: {the name of the plotmanip: [method, class_object]}
+        plotmanip_functions = {}
+        #add 'general.ini' to self.configs (this is not a plugin and thus must be imported seperately)
+        ini_path = lh.get_file_path('general.ini', ['plugins'])
+        plugin_config = ConfigObj(ini_path)
+        #self.config.merge(plugin_config)
+        self.configs['general'] = plugin_config
+        #make sure we execute _plug_init() for every command line plugin we import
+        for plugin in self.config['plugins']:
+            if self.config['plugins'][plugin]:
+                filename = ''.join([plugin, '.py'])
+                path = lh.get_file_path(filename, ['plugins'])
+                if os.path.isfile(path):
+                    #get the corresponding filename and path
+                    plugin_name = ''.join(['plugins.', plugin])
+                    try:
+                        #import the module
+                        module = __import__(plugin_name)
+                        #prepare the ini file for inclusion
+                        ini_path = path.replace('.py', '.ini')
+                        #include ini file
+                        plugin_config = ConfigObj(ini_path)
+                        #self.config.merge(plugin_config)
+                        self.configs[plugin] = plugin_config
+                        #add to plugins
+                        commands = eval('dir(module.' + plugin+ '.' + plugin + 'Commands)')
+                        #keep only commands (ie names that start with 'do_')
+                        commands = [command for command in commands if command.startswith('do_')]
+                        if commands:
+                            self.plugins[plugin] = commands
+                        try:
+                            #initialize the plugin
+                            eval('module.' + plugin+ '.' + plugin + 'Commands._plug_init(self)')
+                        except AttributeError:
+                            pass
+                    except ImportError:
+                        pass
+        #initialize the commands tree
+        commands = dir(HookeFrame)
+        commands = [command for command in commands if command.startswith('do_')]
+        if commands:
+            self.plugins['general'] = commands
+        self.panelCommands.Initialize(self.plugins)
+        for command in dir(self):
+            if command.startswith('plotmanip_'):
+                plotmanip_functions[command] = [command, getattr(self, command)]
+        for name in self.config['plotmanipulators']['names']:
+            if self.config['plotmanipulators'].as_bool(name):
+                command_name = ''.join(['plotmanip_', name])
+                if command_name in plotmanip_functions:
+                    self.plotmanipulators.append(plotmanip_functions[command_name])
+        #load default list, if possible
+        self.do_loadlist(self.config['general']['list'])
+
+    def _BindEvents(self):
+        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+        self.Bind(wx.EVT_CLOSE, self.OnClose)
+        # Show How To Use The Closing Panes Event
+        self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPaneClose)
+        self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnNotebookPageClose)
+        #menu
+        self.Bind(wx.EVT_MENU, self.OnClose, id=wx.ID_EXIT)
+        self.Bind(wx.EVT_MENU, self.OnAbout, id=wx.ID_ABOUT)
+        #view
+        self.Bind(wx.EVT_MENU_RANGE, self.OnView, id=ID_ViewAssistant, id2=ID_ViewResults)
+        #perspectives
+        self.Bind(wx.EVT_MENU, self.OnDeletePerspective, id=ID_DeletePerspective)
+        self.Bind(wx.EVT_MENU, self.OnSavePerspective, id=ID_SavePerspective)
+        self.Bind(wx.EVT_MENU_RANGE, self.OnRestorePerspective, id=ID_FirstPerspective, id2=ID_FirstPerspective+1000)
+        #toolbar
+        self.Bind(wx.EVT_TOOL, self.OnExportImage, id=ID_ExportImage)
+        self.Bind(wx.EVT_TOOL, self.OnNext, id=ID_Next)
+        self.Bind(wx.EVT_TOOL, self.OnPrevious, id=ID_Previous)
+        #self.Bind(.EVT_AUITOOLBAR_TOOL_DROPDOWN, self.OnDropDownToolbarItem, id=ID_DropDownToolbarItem)
+        #dir control
+        treeCtrl = self.panelFolders.GetTreeCtrl()
+        #tree.Bind(wx.EVT_LEFT_UP, self.OnDirCtrl1LeftUp)
+        #tree.Bind(wx.EVT_LEFT_DOWN, self.OnGenericDirCtrl1LeftDown)
+        treeCtrl.Bind(wx.EVT_LEFT_DCLICK, self.OnDirCtrlLeftDclick)
+        #playlist tree
+        self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DOWN, self.OnPlaylistsLeftDown)
+        self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DCLICK, self.OnPlaylistsLeftDclick)
+        #commands tree
+        self.panelCommands.ExecuteButton.Bind(wx.EVT_BUTTON, self.OnExecute)
+        self.panelCommands.CommandsTree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeCtrlCommandsLeftDown)
+        #property editor
+        self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged)
+        self.panelProperties.pg.Bind(wxpg.EVT_PG_SELECTED, self.OnPropGridSelect)
+        #results panel
+        self.panelResults.results_list.OnCheckItem = self.OnResultsCheck
+
+    def _GetActiveCurveIndex(self):
+        playlist = self.GetActivePlaylist()
+        #get the selected item from the tree
+        selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
+        #test if a playlist or a curve was double-clicked
+        if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
+            return -1
+        else:
+            count = 0
+            selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
+            while selected_item.IsOk():
+                count += 1
+                selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
+            return count
+
+    def _GetActivePlaylistName(self):
+        #get the selected item from the tree
+        selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
+        #test if a playlist or a curve was double-clicked
+        if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
+            playlist_item = selected_item
+        else:
+            #get the name of the playlist
+            playlist_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
+        #now we have a playlist
+        return self.panelPlaylists.PlaylistsTree.GetItemText(playlist_item)
+
+    def _GetPlaylistTab(self, name):
+        for index, page in enumerate(self.plotNotebook._tabs._pages):
+            if page.caption == name:
+                return index
+        return -1
+
+    def _GetUniquePlaylistName(self, name):
+        playlist_name = name
+        count = 1
+        while playlist_name in self.playlists:
+            playlist_name = ''.join([name, str(count)])
+            count += 1
+        return playlist_name
+
+    def _SavePerspectiveToFile(self, name, perspective):
+        filename = ''.join([name, '.txt'])
+        filename = lh.get_file_path(filename, ['perspectives'])
+        perspectivesFile = open(filename, 'w')
+        perspectivesFile.write(perspective)
+        perspectivesFile.close()
+
+    def AddPlaylist(self, playlist=None, name='Untitled'):
+        #TODO: change cursor or progressbar (maybe in statusbar)
+        #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
+        if playlist and playlist.count > 0:
+            playlist.name = self._GetUniquePlaylistName(name)
+            playlist.reset()
+            self.AddToPlaylists(playlist)
+
+    def AddPlaylistFromFiles(self, files=[], name='Untitled'):
+        #TODO: change cursor or progressbar (maybe in statusbar)
+        #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
+        if files:
+            playlist = Playlist.Playlist(self.drivers)
+            for item in files:
+                playlist.add_curve(item)
+        if playlist.count > 0:
+            playlist.name = self._GetUniquePlaylistName(name)
+            playlist.reset()
+            self.AddToPlaylists(playlist)
+
+    def AddToPlaylists(self, playlist):
+        if playlist.has_curves:
+            #setup the playlist in the Playlist tree
+            tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
+            playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)
+            #add all curves to the Playlist tree
+            curves = {}
+            for index, curve in enumerate(playlist.curves):
+                ##remove the extension from the name of the curve
+                ##TODO: optional?
+                #item_text, extension = os.path.splitext(curve.name)
+                #curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, item_text, 1)
+                curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, curve.name, 1)
+                if index == playlist.index:
+                    self.panelPlaylists.PlaylistsTree.SelectItem(curve_ID)
+            playlist.reset()
+            #create the plot tab and add playlist to the dictionary
+            plotPanel = wxmpl.PlotPanel(self, ID_FirstPlot + len(self.playlists))
+            notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True)
+            tab_index = self.plotNotebook.GetSelection()
+            figure = plotPanel.get_figure()
+            self.playlists[playlist.name] = [playlist, figure]
+            self.panelPlaylists.PlaylistsTree.Expand(playlist_root)
+            self.statusbar.SetStatusText(playlist.get_status_string(), 0)
+            self.UpdatePlot()
+
+    def AppendToOutput(self, text):
+        self.panelOutput.AppendText(''.join([text, '\n']))
+
+    def CreateApplicationIcon(self):
+        iconFile = 'resources' + os.sep + 'microscope.ico'
+        icon = wx.Icon(iconFile, wx.BITMAP_TYPE_ICO)
+        self.SetIcon(icon)
+
+    def CreateCommandLine(self):
+        return wx.TextCtrl(self, -1, '', style=wx.NO_BORDER|wx.EXPAND)
+
+    def CreatePanelAssistant(self):
+        panel = wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
+        panel.SetEditable(False)
+        return panel
+
+    def CreatePanelCommands(self):
+        return hookecommands.Commands(self)
+
+    def CreatePanelFolders(self):
+        #set file filters
+        filters = self.config['folders']['filters']
+        index = self.config['folders'].as_int('filterindex')
+        #set initial directory
+        folder = self.config['general']['workdir']
+        return wx.GenericDirCtrl(self, -1, dir=folder, size=(200, 250), style=wx.DIRCTRL_SHOW_FILTERS, filter=filters, defaultFilter=index)
+
+    def CreatePanelOutput(self):
+        return wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
+
+    def CreatePanelPlaylists(self):
+        return hookeplaylist.Playlists(self)
+
+    def CreatePanelProperties(self):
+        return hookepropertyeditor.PropertyEditor(self)
+
+    def CreatePanelResults(self):
+        return hookeresults.Results(self)
+
+    def CreatePanelWelcome(self):
+        ctrl = wx.html.HtmlWindow(self, -1, wx.DefaultPosition, wx.Size(400, 300))
+        introStr = '<h1>Welcome to Hooke</h1>' + \
+                 '<h3>Features</h3>' + \
+                 '<ul>' + \
+                 '<li>View, annotate, measure force curves</li>' + \
+                 '<li>Worm-like chain fit of force peaks</li>' + \
+                 '<li>Automatic convolution-based filtering of empty curves</li>' + \
+                 '<li>Automatic fit and measurement of multiple force peaks</li>' + \
+                 '<li>Handles force-clamp force experiments (experimental)</li>' + \
+                 '<li>It is extensible by users by means of plugins and drivers</li>' + \
+                 '</ul>' + \
+                 '<p>See the <a href="/p/hooke/wiki/DocumentationIndex">DocumentationIndex</a> for more information</p>'
+        ctrl.SetPage(introStr)
+        return ctrl
+
+    def CreateMenuBar(self):
+        menu_bar = wx.MenuBar()
+        #file
+        file_menu = wx.Menu()
+        file_menu.Append(wx.ID_OPEN, '&Open playlist\tCtrl-O')
+        file_menu.Append(wx.ID_SAVE, 'Save playlist\tCtrl-S')
+        file_menu.AppendSeparator()
+        file_menu.Append(wx.ID_EXIT, 'Exit\tCtrl-Q')
+        #edit
+        edit_menu = wx.Menu()
+        edit_menu.Append(ID_ExportText, 'Export text...')
+        edit_menu.Append(ID_ExportImage, 'Export image...')
+        edit_menu.AppendSeparator();
+        edit_menu.Append(ID_Config, 'Preferences')
+        #view
+        view_menu = wx.Menu()
+        view_menu.AppendCheckItem(ID_ViewFolders, 'Folders\tF5')
+        view_menu.AppendCheckItem(ID_ViewPlaylists, 'Playlists\tF6')
+        view_menu.AppendCheckItem(ID_ViewCommands, 'Commands\tF7')
+        view_menu.AppendCheckItem(ID_ViewProperties, 'Properties\tF8')
+        view_menu.AppendCheckItem(ID_ViewAssistant, 'Assistant\tF9')
+        view_menu.AppendCheckItem(ID_ViewResults, 'Results\tF10')
+        view_menu.AppendCheckItem(ID_ViewOutput, 'Output\tF11')
+        #perspectives
+        self._perspectives_menu = self.CreatePerspectivesMenu()
+        #help
+        help_menu = wx.Menu()
+        help_menu.Append(wx.ID_ABOUT, 'About Hooke')
+        #put it all together
+        menu_bar.Append(file_menu, 'File')
+        menu_bar.Append(edit_menu, 'Edit')
+        menu_bar.Append(view_menu, 'View')
+        menu_bar.Append(self._perspectives_menu, "Perspectives")
+        menu_bar.Append(help_menu, 'Help')
+
+        self.SetMenuBar(menu_bar)
+
+    def CreateNotebook(self):
+        # create the notebook off-window to avoid flicker
+        client_size = self.GetClientSize()
+        ctrl = aui.AuiNotebook(self, -1, wx.Point(client_size.x, client_size.y), wx.Size(430, 200), self._notebook_style)
+        arts = [aui.AuiDefaultTabArt, aui.AuiSimpleTabArt, aui.VC71TabArt, aui.FF2TabArt, aui.VC8TabArt, aui.ChromeTabArt]
+        art = arts[self._notebook_theme]()
+        ctrl.SetArtProvider(art)
+        #uncomment if we find a nice icon
+        #page_bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))
+        ctrl.AddPage(self.CreatePanelWelcome(), "Welcome", False)
+        return ctrl
+
+    def CreatePerspectivesMenu(self):
+        menu = wx.Menu()
+        menu.Append(ID_SavePerspective, "Save Perspective")
+        menu.Append(ID_DeletePerspective, "Delete Perspective")
+        menu.AppendSeparator()
+        #add perspectives to menubar and _perspectives
+        perspectivesDirectory = os.path.join(lh.hookeDir, 'perspectives')
+        if os.path.isdir(perspectivesDirectory):
+            perspectiveFileNames = os.listdir(perspectivesDirectory)
+            for perspectiveFilename in perspectiveFileNames:
+                filename = lh.get_file_path(perspectiveFilename, ['perspectives'])
+                if os.path.isfile(filename):
+                    perspectiveFile = open(filename, 'rU')
+                    perspective = perspectiveFile.readline()
+                    perspectiveFile.close()
+                    if perspective != '':
+                        name, extension = os.path.splitext(perspectiveFilename)
+                        if extension == '.txt':
+                            menuItem = menu.AppendRadioItem(ID_FirstPerspective + len(self._perspectives), name)
+                            self._perspectives[name] = [len(self._perspectives), perspective]
+                            if self.config['perspectives']['active'] == name:
+                                menuItem.Check()
+        #in case there are no perspectives
+        if not self._perspectives:
+            perspective = self._mgr.SavePerspective()
+            self.config['perspectives']['default'] = 'Default'
+            self._perspectives['Default'] = [0, perspective]
+            menuItem = menu.AppendRadioItem(ID_FirstPerspective, 'Default')
+            menuItem.Check()
+            self._SavePerspectiveToFile('Default', perspective)
+        return menu
+
+    def CreateStatusbar(self):
+        statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
+        statusbar.SetStatusWidths([-2, -3])
+        statusbar.SetStatusText('Ready', 0)
+        welcomeString=u'Welcome to Hooke (version '+__version__+', '+__release_name__+')!'
+        statusbar.SetStatusText(welcomeString, 1)
+        return statusbar
+
+    def CreateToolBar(self):
+        toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)
+        toolbar.SetToolBitmapSize(wx.Size(16,16))
+        toolbar_bmp1 = wx.ArtProvider_GetBitmap(wx.ART_QUESTION, wx.ART_OTHER, wx.Size(16, 16))
+        toolbar_bmpOpen = wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN, wx.ART_OTHER, wx.Size(16, 16))
+        toolbar_bmpSave = wx.ArtProvider_GetBitmap(wx.ART_FILE_SAVE, wx.ART_OTHER, wx.Size(16, 16))
+        toolbar_bmpExportText = wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))
+        toolbar_bmpExportImage = wx.ArtProvider_GetBitmap(wx.ART_MISSING_IMAGE, wx.ART_OTHER, wx.Size(16, 16))
+        toolbar.AddLabelTool(101, 'Open', toolbar_bmpOpen)
+        toolbar.AddLabelTool(102, 'Save', toolbar_bmpSave)
+        toolbar.AddSeparator()
+        toolbar.AddLabelTool(ID_ExportText, 'Export text...', toolbar_bmpExportText)
+        toolbar.AddLabelTool(ID_ExportImage, 'Export image...', toolbar_bmpExportImage)
+        toolbar.Realize()
+        return toolbar
+
+    def CreateToolBarNavigation(self):
+        toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)
+        toolbar.SetToolBitmapSize(wx.Size(16,16))
+        toolbar_bmpBack = wx.ArtProvider_GetBitmap(wx.ART_GO_BACK, wx.ART_OTHER, wx.Size(16, 16))
+        toolbar_bmpForward = wx.ArtProvider_GetBitmap(wx.ART_GO_FORWARD, wx.ART_OTHER, wx.Size(16, 16))
+        toolbar.AddLabelTool(ID_Previous, 'Previous', toolbar_bmpBack, shortHelp='Previous curve')
+        toolbar.AddLabelTool(ID_Next, 'Next', toolbar_bmpForward, shortHelp='Next curve')
+        toolbar.Realize()
+        return toolbar
+
+    def DeleteFromPlaylists(self, name):
+        if name in self.playlists:
+            del self.playlists[name]
+        tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
+        item, cookie = self.panelPlaylists.PlaylistsTree.GetFirstChild(tree_root)
+        while item.IsOk():
+            playlist_name = self.panelPlaylists.PlaylistsTree.GetItemText(item)
+            if playlist_name == name:
+                try:
+                    self.panelPlaylists.PlaylistsTree.Delete(item)
+                except:
+                    pass
+            item = self.panelPlaylists.PlaylistsTree.GetNextSibling(item)
+        self.OnPlaylistsLeftDclick(None)
+
+    def DeletePlotPage(self, name):
+        index = self._GetPlaylistTab(name)
+        plot = self.playlists[name][1]
+        plot = None
+        self.plotNotebook.RemovePage(index)
+        self.DeleteFromPlaylists(name)
+
+    def GetActiveCurve(self):
+        playlist = self.GetActivePlaylist()
+        if playlist is not None:
+            return playlist.get_active_curve()
+        return None
+
+    def GetActivePlaylist(self):
+        playlist_name = self._GetActivePlaylistName()
+        if playlist_name in self.playlists:
+            return self.playlists[playlist_name][0]
+        return None
+
+    def GetActivePlot(self):
+        curve = self.GetActiveCurve()
+        if curve is not None:
+            return curve.plots[0]
+        return None
+
+    def GetDockArt(self):
+        return self._mgr.GetArtProvider()
+
+    def GetBoolFromConfig(self, *args):
+        if len(args) == 2:
+            plugin = args[0]
+            section = args[0]
+            key = args[1]
+        elif len(args) == 3:
+            plugin = args[0]
+            section = args[1]
+            key = args[2]
+        if self.configs.has_key(plugin):
+            config = self.configs[plugin]
+            return config[section][key].as_bool('value')
+        return None
+
+    def GetFloatFromConfig(self, *args):
+        if len(args) == 2:
+            plugin = args[0]
+            section = args[0]
+            key = args[1]
+        elif len(args) == 3:
+            plugin = args[0]
+            section = args[1]
+            key = args[2]
+        if self.configs.has_key(plugin):
+            config = self.configs[plugin]
+            return config[section][key].as_float('value')
+        return None
+
+    def GetIntFromConfig(self, *args):
+        if len(args) == 2:
+            plugin = args[0]
+            section = args[0]
+            key = args[1]
+        elif len(args) == 3:
+            plugin = args[0]
+            section = args[1]
+            key = args[2]
+        if self.configs.has_key(plugin):
+            config = self.configs[plugin]
+            return config[section][key].as_int('value')
+        return None
+
+    def GetStringFromConfig(self, *args):
+        if len(args) == 2:
+            plugin = args[0]
+            section = args[0]
+            key = args[1]
+        elif len(args) == 3:
+            plugin = args[0]
+            section = args[1]
+            key = args[2]
+        if self.configs.has_key(plugin):
+            config = self.configs[plugin]
+            return config[section][key]['value']
+        return None
+
+    def GetPerspectiveMenuItem(self, name):
+        index = self._perspectives[name][0]
+        perspective_Id = ID_FirstPerspective + index
+        menu_item = self.MenuBar.FindItemById(perspective_Id)
+        return menu_item
+
+    def HasPlotmanipulator(self, name):
+        '''
+        returns True if the plotmanipulator 'name' is loaded, False otherwise
+        '''
+        for plotmanipulator in self.plotmanipulators:
+            if plotmanipulator[0] == name:
+                return True
+        return False
+
+    def OnAbout(self, event):
+        msg = 'Hooke\n\n'+\
+            'A free, open source data analysis platform\n'+\
+            '(c) 2006-2008 Massimo Sandal\n\n'+\
+            '(c) 2009 Dr. Rolf Schmidt\n\n'+\
+            'Released under the GNU GPL v2'
+        dialog = wx.MessageDialog(self, msg, "About Hooke", wx.OK | wx.ICON_INFORMATION)
+        dialog.ShowModal()
+        dialog.Destroy()
+
+    def OnClose(self, event):
+        #apply changes
+        self.config['main']['height'] = str(self.GetSize().GetHeight())
+        self.config['main']['left'] = str(self.GetPosition()[0])
+        self.config['main']['top'] = str(self.GetPosition()[1])
+        self.config['main']['width'] = str(self.GetSize().GetWidth())
+        # Writing the configuration file to 'hooke.ini'
+        self.config.write()
+        self._mgr.UnInit()
+        del self._mgr
+        self.Destroy()
+
+    def OnDeletePerspective(self, event):
+        pass
+
+    def OnDirCtrlLeftDclick(self, event):
+        file_path = self.panelFolders.GetPath()
+        if os.path.isfile(file_path):
+            if file_path.endswith('.hkp'):
+                self.do_loadlist(file_path)
+            else:
+                pass
+        event.Skip()
+
+    def OnEraseBackground(self, event):
+        event.Skip()
+
+    def OnExecute(self, event):
+        item = self.panelCommands.CommandsTree.GetSelection()
+        if item.IsOk():
+            if self.panelCommands.CommandsTree.ItemHasChildren(item):
+                pass
+            else:
+                #get the plugin
+                parent = self.panelCommands.CommandsTree.GetItemParent(item)
+            if not self.panelCommands.CommandsTree.ItemHasChildren(item):
+                parent_text = self.panelCommands.CommandsTree.GetItemText(parent)
+                item_text = self.panelCommands.CommandsTree.GetItemText(item)
+                if item_text in ['genlist', 'loadlist', 'savelist']:
+                    property_values = self.panelProperties.GetPropertyValues()
+                    arg_str = ''
+                    for item in property_values:
+                        arg_str = ''.join([arg_str, item, '=r"', str(property_values[item]), '", '])
+                    command = ''.join(['self.do_', item_text, '(', arg_str, ')'])
+                else:
+                    command = ''.join(['self.do_', item_text, '()'])
+                exec(command)
+        pass
+
+    def OnExit(self, event):
+        self.Close()
+
+    def OnExportImage(self, event):
+        pass
+
+    def OnNext(self, event):
+        '''
+        NEXT
+        Go to the next curve in the playlist.
+        If we are at the last curve, we come back to the first.
+        -----
+        Syntax: next, n
+        '''
+        selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
+        if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
+            #GetFirstChild returns a tuple
+            #we only need the first element
+            next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(selected_item)[0]
+        else:
+            next_item = self.panelPlaylists.PlaylistsTree.GetNextSibling(selected_item)
+            if not next_item.IsOk():
+                parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
+                #GetFirstChild returns a tuple
+                #we only need the first element
+                next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(parent_item)[0]
+        self.panelPlaylists.PlaylistsTree.SelectItem(next_item, True)
+        playlist = self.playlists[self._GetActivePlaylistName()][0]
+        if playlist.count > 1:
+            playlist.next()
+            self.statusbar.SetStatusText(playlist.get_status_string(), 0)
+            self.UpdatePlot()
+
+    def OnNotebookPageClose(self, event):
+        ctrl = event.GetEventObject()
+        playlist_name = ctrl.GetPageText(ctrl._curpage)
+        self.DeleteFromPlaylists(playlist_name)
+
+    def OnPaneClose(self, event):
+        event.Skip()
+
+    def OnPlaylistsLeftDclick(self, event):
+        playlist_name = self._GetActivePlaylistName()
+        #if that playlist already exists
+        #we check if it is the active playlist (ie selected in panelPlaylists)
+        #and switch to it if necessary
+        if playlist_name in self.playlists:
+            index = self.plotNotebook.GetSelection()
+            current_playlist = self.plotNotebook.GetPageText(index)
+            #new_playlist = self.playlists[playlist_name][0]
+            #if current_playlist != new_playlist:
+            if current_playlist != playlist_name:
+                index = self._GetPlaylistTab(playlist_name)
+                self.plotNotebook.SetSelection(index)
+            #if a curve was double-clicked
+            item = self.panelPlaylists.PlaylistsTree.GetSelection()
+            #TODO: fix with get_active_curve
+            if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
+                index = self._GetActiveCurveIndex()
+            else:
+                index = 0
+            if index >= 0:
+                playlist = self.playlists[playlist_name][0]
+                playlist.index = index
+                self.statusbar.SetStatusText(playlist.get_status_string(), 0)
+                self.UpdatePlot()
+        #if you uncomment the following line, the tree will collapse/expand as well
+        #event.Skip()
+
+    def OnPlaylistsLeftDown(self, event):
+        hit_item, hit_flags = self.panelPlaylists.PlaylistsTree.HitTest(event.GetPosition())
+        if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:
+            #self.SetFocus()
+            self.panelPlaylists.PlaylistsTree.SelectItem(hit_item)
+            playlist_name = self._GetActivePlaylistName()
+            playlist = self.playlists[playlist_name][0]
+            #if a curve was clicked
+            item = self.panelPlaylists.PlaylistsTree.GetSelection()
+            if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
+                #TODO: fix with get_active_curve
+                index = self._GetActiveCurveIndex()
+                if index >= 0:
+                    #playlist = self.playlists[playlist_name][0]
+                    playlist.index = index
+                    #self.playlists[playlist_name][0].index = index
+            #else:
+                ##self.playlists[playlist_name][0].index = 0
+                #playlist.index = index
+            self.playlists[playlist_name][0] = playlist
+        event.Skip()
+
+    def OnPrevious(self, event):
+        '''
+        PREVIOUS
+        Go to the previous curve in the playlist.
+        If we are at the first curve, we jump to the last.
+        -------
+        Syntax: previous, p
+        '''
+        #playlist = self.playlists[self._GetActivePlaylistName()][0]
+        #select the previous curve and tell the user if we wrapped around
+        #self.AppendToOutput(playlist.previous())
+        selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
+        if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
+            previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(selected_item)
+        else:
+            previous_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
+            if not previous_item.IsOk():
+                parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
+                previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(parent_item)
+        self.panelPlaylists.PlaylistsTree.SelectItem(previous_item, True)
+        playlist = self.playlists[self._GetActivePlaylistName()][0]
+        if playlist.count > 1:
+            playlist.previous()
+            self.statusbar.SetStatusText(playlist.get_status_string(), 0)
+            self.UpdatePlot()
+
+    def OnPropGridChanged (self, event):
+        prop = event.GetProperty()
+        if prop:
+            item_section = self.panelProperties.SelectedTreeItem
+            item_plugin = self.panelCommands.CommandsTree.GetItemParent(item_section)
+            plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
+            config = self.configs[plugin]
+            property_section = self.panelCommands.CommandsTree.GetItemText(item_section)
+            property_key = prop.GetName()
+            property_value = prop.GetValue()
+            config[property_section][property_key]['value'] = property_value
+
+    def OnPropGridSelect(self, event):
+        pass
+
+    def OnRestorePerspective(self, event):
+        name = self.MenuBar.FindItemById(event.GetId()).GetLabel()
+        self._mgr.LoadPerspective(self._perspectives[name][1])
+        self.config['perspectives']['active'] = name
+        self._mgr.Update()
+        all_panes = self._mgr.GetAllPanes()
+        for pane in all_panes:
+            if not pane.name.startswith('toolbar'):
+                if pane.name == 'Assistant':
+                    self.MenuBar.FindItemById(ID_ViewAssistant).Check(pane.window.IsShown())
+                if pane.name == 'Folders':
+                    self.MenuBar.FindItemById(ID_ViewFolders).Check(pane.window.IsShown())
+                if pane.name == 'Playlists':
+                    self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown())
+                if pane.name == 'Commands':
+                    self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown())
+                if pane.name == 'Properties':
+                    self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown())
+                if pane.name == 'Output':
+                    self.MenuBar.FindItemById(ID_ViewOutput).Check(pane.window.IsShown())
+                if pane.name == 'Results':
+                    self.MenuBar.FindItemById(ID_ViewResults).Check(pane.window.IsShown())
+
+    def OnResultsCheck(self, index, flag):
+        curve = self.GetActiveCurve()
+        result = curve.data['results'][index]['visible'] = flag
+        self.UpdatePlot()
+
+    def OnSavePerspective(self, event):
+        def nameExists(name):
+            for item in self._perspectives_menu.GetMenuItems():
+                if item.GetText() == name:
+                    return True
+            return False
+
+        done = False
+        while not done:
+            dialog = wx.TextEntryDialog(self, 'Enter a name for the new perspective:', 'Save perspective')
+            dialog.SetValue('New perspective')
+            if dialog.ShowModal() != wx.ID_OK:
+                return
+            else:
+                name = dialog.GetValue()
+
+            if nameExists(name):
+                dialogConfirm = wx.MessageDialog(self, 'A file with this name already exists.\n\nDo you want to replace it?', 'Confirm', wx.YES_NO|wx.ICON_QUESTION|wx.CENTER)
+                if dialogConfirm.ShowModal() == wx.ID_YES:
+                    done = True
+            else:
+                done = True
+
+        perspective = self._mgr.SavePerspective()
+
+        if nameExists(name):
+            #check the corresponding menu item
+            menuItem = self.GetPerspectiveMenuItem(name)
+            #replace the perspectiveStr in _pespectives
+            index = self._perspectives[name][0]
+            self._perspectives[name] = [index, perspective]
+        else:
+            #simply add the new perspective to _perspectives
+            index = len(self._perspectives)
+            self._perspectives[name] = [len(self._perspectives), perspective]
+            menuItem = self._perspectives_menu.AppendRadioItem(ID_FirstPerspective + len(self._perspectives), name)
+
+        menuItem.Check()
+        self._SavePerspectiveToFile(name, perspective)
+        #uncheck all perspective menu items
+        #as these are radio items, one item has to be checked at all times
+        #the line 'menuItem.Check()' above actually checks a second item
+        #but does not toggle
+        #so we need to uncheck all other items afterwards
+        #weirdly enough, menuitem.Toggle() doesn't do this properly either
+        for item in self._perspectives_menu.GetMenuItems():
+            if item.IsCheckable():
+                if item.GetLabel() != name:
+                    item.Check(False)
+
+    def OnView(self, event):
+        menu_id = event.GetId()
+        menu_item = self.MenuBar.FindItemById(menu_id)
+        menu_label = menu_item.GetLabel()
+
+        pane = self._mgr.GetPane(menu_label)
+        pane.Show(not pane.IsShown())
+        #if we don't do the following, the Folders pane does not resize properly on hide/show
+        if pane.caption == 'Folders' and pane.IsShown() and pane.IsDocked():
+            #folders_size = pane.GetSize()
+            self.panelFolders.Fit()
+        self._mgr.Update()
+
+    def OnSize(self, event):
+        event.Skip()
+
+    def OnTreeCtrlCommandsLeftDown(self, event):
+        hit_item, hit_flags = self.panelCommands.CommandsTree.HitTest(event.GetPosition())
+        if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:
+            self.panelCommands.CommandsTree.SelectItem(hit_item)
+            self.panelProperties.SelectedTreeItem = hit_item
+            #if a command was clicked
+            properties = []
+            if not self.panelCommands.CommandsTree.ItemHasChildren(hit_item):
+                item_plugin = self.panelCommands.CommandsTree.GetItemParent(hit_item)
+                plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
+                if self.configs.has_key(plugin):
+                    #config = self.panelCommands.CommandsTree.GetPyData(item_plugin)
+                    config = self.configs[plugin]
+                    section = self.panelCommands.CommandsTree.GetItemText(hit_item)
+                    #display docstring in help window
+                    doc_string = eval('self.do_' + section + '.__doc__')
+                    if section in config:
+                        for option in config[section]:
+                            properties.append([option, config[section][option]])
+            else:
+                module = self.panelCommands.CommandsTree.GetItemText(hit_item)
+                if module != 'general':
+                    doc_string = eval('plugins.' + module + '.' + module + 'Commands.__doc__')
+                else:
+                    doc_string = 'The module "general" contains Hooke core functionality'
+            if doc_string is not None:
+                self.panelAssistant.ChangeValue(doc_string)
+            hookepropertyeditor.PropertyEditor.Initialize(self.panelProperties, properties)
+        event.Skip()
+
+    def UpdatePlaylists(self):
+        #setup the playlist in the Playlist tree
+        tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
+        playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)
+        #add all curves to the Playlist tree
+        curves = {}
+        for index, curve in enumerate(playlist.curves):
+            ##remove the extension from the name of the curve
+            ##TODO: optional?
+            #item_text, extension = os.path.splitext(curve.name)
+            #curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, item_text, 1)
+            curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, curve.name, 1)
+            if index == playlist.index:
+                self.panelPlaylists.PlaylistsTree.SelectItem(curve_ID)
+        #create the plot tab and add playlist to the dictionary
+        plotPanel = wxmpl.PlotPanel(self, ID_FirstPlot + len(self.playlists))
+        notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True)
+        #tab_index = self.plotNotebook.GetSelection()
+        figure = plotPanel.get_figure()
+        #self.playlists[playlist.name] = [playlist, tab_index, figure]
+        self.playlists[playlist.name] = [playlist, figure]
+        self.panelPlaylists.PlaylistsTree.Expand(playlist_root)
+        self.statusbar.SetStatusText(playlist.get_status_string(), 0)
+        self.UpdatePlot()
+
+#HELPER FUNCTIONS
+#Everything sending an event should be here
+    def _measure_N_points(self, N, whatset=1):
+        '''
+        general helper function for N-points measures
+        '''
+        wx.PostEvent(self.frame,self.list_of_events['measure_points'](num_of_points=N, set=whatset))
+        while 1:
+            try:
+                points=self.frame.events_from_gui.get()
+                break
+            except Empty:
+                pass
+        return points
+
+    def _clickize(self, xvector, yvector, index):
+        '''
+        returns a ClickedPoint() object from an index and vectors of x, y coordinates
+        '''
+        point = lh.ClickedPoint()
+        point.index = index
+        point.absolute_coords = xvector[index], yvector[index]
+        point.find_graph_coords(xvector, yvector)
+        return point
+
+#PLAYLIST INTERACTION COMMANDS
+#-------------------------------
+    def do_genlist(self, folder=lh.hookeDir, filemask='*.*'):
+        '''
+        GENLIST
+        Generates a file playlist.
+        Note it doesn't *save* it: see savelist for this.
+
+        If [input files] is a directory, it will use all files in the directory for playlist.
+        So:
+        genlist dir
+        genlist dir/
+        genlist dir/*.*
+
+        are all equivalent syntax.
+        ------------
+        Syntax: genlist [input files]
+        '''
+        #args list is: input folder, file mask
+        if os.path.isdir(folder):
+            path = os.path.join(folder, filemask)
+            #expanding correctly the input list with the glob module :)
+            files = glob.glob(path)
+            files.sort()
+            #TODO: change cursor or progressbar (maybe in statusbar)
+            #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
+            playlist = playlist.Playlist(self.drivers)
+            for item in files:
+                curve = playlist.add_curve(item)
+                plot = copy.deepcopy(curve.plots[0])
+                #add the 'raw' data
+                curve.add_data('raw', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')
+                curve.add_data('raw', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')
+                #apply all active plotmanipulators and add the 'manipulated' data
+                for plotmanipulator in self.plotmanipulators:
+                    plot = plotmanipulator[1](plot, curve)
+                    curve.set_data('manipulated', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')
+                    curve.add_data('manipulated', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')
+            if playlist.count > 0:
+                playlist.name = self._GetUniquePlaylistName(os.path.basename(folder))
+                playlist.reset()
+                self.AddToPlaylists(playlist)
+            self.AppendToOutput(playlist.get_status_string())
+        else:
+            self.AppendToOutput(''.join(['Cannot find folder ', folder]))
+
+    def do_loadlist(self, filename):
+        '''
+        LOADLIST
+        Loads a file playlist
+        -----------
+        Syntax: loadlist [playlist file]
+        '''
+        #TODO: check for duplicate playlists, ask the user for a unique name
+        #if self.playlist_name in self.playlists:
+
+        #add hkp extension if necessary
+        if not filename.endswith('.hkp'):
+            filename = ''.join([filename, '.hkp'])
+        #prefix with 'hookeDir' if just a filename or a relative path
+        filename = lh.get_file_path(filename)
+        if os.path.isfile(filename):
+            #TODO: change cursor
+            #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
+            playlist_new = playlist.Playlist(self.drivers)
+            playlist_new.load(filename)
+            if playlist_new.count > 0:
+                for curve in playlist_new.curves:
+                    plot = copy.deepcopy(curve.plots[0])
+                    for plotmanip in self.plotmanipulators:
+                        #to_plot = plotmanip[1](to_plot, curve)
+                        plot = plotmanip[1](plot, curve)
+                        curve.set_data('manipulated', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')
+                        curve.add_data('manipulated', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')
+                self.AddToPlaylists(playlist_new)
+            #else:
+                ##TODO: display dialog
+            self.AppendToOutput(playlist_new.get_status_string())
+            #TODO: change cursor
+            #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
+        else:
+            #TODO: display dialog
+            self.AppendToOutput(''.join['File ', filename, ' not found.\n'])
+        pass
+
+    def do_savelist(self, filename):
+        '''
+        SAVELIST
+        Saves the current file playlist on disk.
+        ------------
+        Syntax: savelist [filename]
+        '''
+
+        #self.playlist_generics['pointer'] = self._GetActiveCurveIndex
+        pointer = self._GetActiveCurveIndex()
+        #autocomplete filename if not specified
+        if not filename.endswith('.hkp'):
+            filename = filename.join(['.hkp'])
+
+        playlist = self.GetActivePlaylist()
+        playlist.set_XML()
+        playlist.save(filename)
+
+#PLOT INTERACTION COMMANDS
+#-------------------------------
+    def UpdatePlot(self):
+        def add_plot(plot):
+            if plot['visible'] and plot['x'] and plot['y']:
+                color = plot['color']
+                style = plot['style']
+                if style == 'plot':
+                    axes.plot(plot['x'], plot['y'], color=color, zorder=1)
+                if style == 'scatter':
+                    axes.scatter(plot['x'], plot['y'], color=color, s=0.5, zorder=2)
+
+        def add_plot2(plot):
+            if plot.visible and plot.x and plot.y:
+                if plot.style == 'plot':
+                    axes.plot(plot.x, plot.y, color=plot.color, zorder=1)
+                if plot.style == 'scatter':
+                    axes.scatter(plot.x, plot.y, color=plot.color, s=0.5, zorder=2)
+
+        playlist_name = self._GetActivePlaylistName()
+        index = self._GetActiveCurveIndex()
+        playlist = self.playlists[playlist_name][0]
+        curve = playlist.get_active_curve()
+        plot = playlist.get_active_plot()
+        figure = self.playlists[playlist_name][1]
+
+        figure.clf()
+        exclude = None
+        if curve.data.has_key('manipulated'):
+            exclude = 'raw'
+        elif curve.data.has_key('raw'):
+            exclude = 'manipulated'
+
+        if exclude is not None:
+            #TODO: what is this good for?
+            if not hasattr(self, 'subplot'):
+                axes = figure.add_subplot(111)
+
+            axes.set_title(plot.title)
+            axes.set_xlabel(plot.units[0])
+            axes.set_ylabel(plot.units[1])
+
+            for set_of_plots in curve.data:
+                if set_of_plots != exclude:
+                    plots = curve.data[set_of_plots]
+                    for each_plot in plots:
+                        add_plot(each_plot)
+
+            #TODO: add multiple results support
+            #for fit in curve.fits:
+            if curve.fits.has_key('wlc'):
+                for plot in curve.fits['wlc'].results:
+                    add_plot2(plot)
+                self.panelResults.DisplayResults(curve.fits['wlc'])
+            else:
+                self.panelResults.ClearResults()
+
+            axes.figure.canvas.draw()
+        else:
+            self.AppendToOutput('Not able to plot.')
+
+
+ID_PaneBorderSize = wx.ID_HIGHEST + 1
+ID_SashSize = ID_PaneBorderSize + 1
+ID_CaptionSize = ID_PaneBorderSize + 2
+ID_BackgroundColor = ID_PaneBorderSize + 3
+ID_SashColor = ID_PaneBorderSize + 4
+ID_InactiveCaptionColor =  ID_PaneBorderSize + 5
+ID_InactiveCaptionGradientColor = ID_PaneBorderSize + 6
+ID_InactiveCaptionTextColor = ID_PaneBorderSize + 7
+ID_ActiveCaptionColor = ID_PaneBorderSize + 8
+ID_ActiveCaptionGradientColor = ID_PaneBorderSize + 9
+ID_ActiveCaptionTextColor = ID_PaneBorderSize + 10
+ID_BorderColor = ID_PaneBorderSize + 11
+ID_GripperColor = ID_PaneBorderSize + 12
+
+
+#----------------------------------------------------------------------
+
+if __name__ == '__main__':
+
+    ## now, silence a deprecation warning for py2.3
+    #import warnings
+    #warnings.filterwarnings("ignore", "integer", DeprecationWarning, "wxPython.gdi")
+
+    redirect=True
+    if __debug__:
+        redirect=False
+    app = Hooke(redirect=redirect)
+
+    app.MainLoop()
+
+
index 12c6d26..a7a261e 100644 (file)
-import copy\r
-import os\r
-import os.path\r
-import xml.dom.minidom\r
-\r
-from . import hooke as hooke\r
-from . import libhookecurve as lhc\r
-from . import libhooke as lh\r
-\r
-class Playlist(object):\r
-    def __init__(self, drivers):\r
-        self._saved = False\r
-        self.count = 0\r
-        self.curves = []\r
-        self.drivers = drivers\r
-        self.path = ''\r
-        self.genericsDict = {}\r
-        self.hiddenAttributes = ['curve', 'driver', 'name', 'plots']\r
-        self.index = -1\r
-        self.name = 'Untitled'\r
-        self.plotPanel = None\r
-        self.plotTab = None\r
-        self.xml = None\r
-\r
-    def add_curve(self, path, attributes={}):\r
-        curve = lhc.HookeCurve(path)\r
-        for key,value in attribures.items():\r
-            setattr(curve, key, value)\r
-        curve.identify(self.drivers)\r
-        curve.plots = curve.driver.default_plots()\r
-        self.curves.append(curve)\r
-        self._saved = False\r
-        self.count = len(self.curves)\r
-        return curve\r
-\r
-    def close_curve(self, index):\r
-        if index >= 0 and index < self.count:\r
-            self.curves.remove(index)\r
-\r
-    def filter_curves(self, keeper_fn=labmda curve:True):\r
-        playlist = copy.deepcopy(self)\r
-        for curve in reversed(playlist.curves):\r
-            if not keeper_fn(curve):\r
-                playlist.curves.remove(curve)\r
-        try: # attempt to maintain the same active curve\r
-            playlist.index = playlist.curves.index(self.get_active_curve())\r
-        except ValueError:\r
-            playlist.index = 0\r
-        playlist._saved = False\r
-        playlist.count = len(playlist.curves)\r
-        return playlist\r
-\r
-    def get_active_curve(self):\r
-        return self.curves[self.index]\r
-\r
-    #TODO: do we need this?\r
-    def get_active_plot(self):\r
-        return self.curves[self.index].plots[0]\r
-\r
-    def get_status_string(self):\r
-        if self.has_curves()\r
-            return '%s (%s/%s)' % (self.name, self.index + 1, self.count)\r
-        return 'The file %s does not contain any valid force curve data.' \\r
-            % self.name\r
-\r
-    def has_curves(self):\r
-        if self.count > 0:\r
-            return True\r
-        return False\r
-\r
-    def is_saved(self):\r
-        return self._saved\r
-\r
-    def load(self, path):\r
-        '''\r
-        loads a playlist file\r
-        '''\r
-        self.path = path\r
-        self.name = os.path.basename(path)\r
-        playlist = lh.delete_empty_lines_from_xmlfile(path)\r
-        self.xml = xml.dom.minidom.parse(path)\r
-        # Strip blank spaces:\r
-        self._removeWhitespaceNodes()\r
-\r
-        generics_list = self.xml.getElementsByTagName('generics')\r
-        curve_list = self.xml.getElementsByTagName('curve')\r
-        self._loadGenerics(generics_list)\r
-        self._loadCurves(curve_list)\r
-        self._saved = True\r
-\r
-    def _removeWhitespaceNodes(self, root_node=None):\r
-        if root_node == None:\r
-            root_node = self.xml\r
-        for node in root_node.childNodes:\r
-            if node.nodeType == node.TEXT_NODE and node.data.strip() == '':\r
-                root_node.removeChild(node) # drop this whitespace node\r
-            else:\r
-                _removeWhitespaceNodes(root_node=node) # recurse down a level\r
-\r
-    def _loadGenerics(self, generics_list, clear=True):\r
-        if clear:\r
-            self.genericsDict = {}\r
-        #populate generics\r
-        generics_list = self.xml.getElementsByTagName('generics')\r
-        for generics in generics_list:\r
-            for attribute in generics.attributes.keys():\r
-                self.genericsDict[attribute] = generics_list[0].getAttribute(attribute)\r
-        if self.genericsDict.has_key('pointer'):\r
-            index = int(self.genericsDict['pointer'])\r
-            if index >= 0 and index < len(self.curves):\r
-                self.index = index\r
-            else:\r
-                index = 0\r
-\r
-    def _loadCurves(self, curve_list, clear=True):\r
-        if clear:\r
-            self.curves = []\r
-        #populate playlist with curves\r
-        for curve in curve_list:\r
-            #rebuild a data structure from the xml attributes\r
-            curve_path = lh.get_file_path(element.getAttribute('path'))\r
-            #extract attributes for the single curve\r
-            attributes = dict([(k,curve.getAttribute(k))\r
-                               for k in curve.attributes.keys()])\r
-            attributes.pop('path')\r
-            curve = self.add_curve(os.path.join(path, curve_path), attributes)\r
-            if curve is not None:\r
-                for plot in curve.plots:\r
-                    curve.add_data('raw', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')\r
-                    curve.add_data('raw', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')\r
-\r
-    def next(self):\r
-        self.index += 1\r
-        if self.index > self.count - 1:\r
-            self.index = 0\r
-\r
-    def previous(self):\r
-        self.index -= 1\r
-        if self.index < 0:\r
-            self.index = self.count - 1\r
-\r
-    def reset(self):\r
-        if self.has_curves():\r
-            self.index = 0\r
-        else:\r
-            self.index = None\r
-\r
-    def save(self, path):\r
-        '''\r
-        saves the playlist in a XML file.\r
-        '''\r
-        try:\r
-            output_file = file(path, 'w')\r
-        except IOError, e:\r
-            #TODO: send message\r
-            print 'Cannot save playlist: %s' % e\r
-            return\r
-        self.xml.writexml(output_file, indent='\n')\r
-        output_file.close()\r
-        self._saved = True\r
-\r
-    def set_XML(self):\r
-        '''\r
-        Creates an initial playlist from a list of files.\r
-        A playlist is an XML document with the following syntax:\r
-          <?xml version="1.0" encoding="utf-8"?>\r
-          <playlist>\r
-            <generics pointer="0"/>\r
-            <curve path="/my/file/path/"/ attribute="value" ...>\r
-            <curve path="...">\r
-          </playlist>\r
-        Relative paths are interpreted relative to the location of the\r
-        playlist file.\r
-        '''\r
-        #create the output playlist, a simple XML document\r
-        implementation = xml.dom.minidom.getDOMImplementation()\r
-        #create the document DOM object and the root element\r
-        self.xml = implementation.createDocument(None, 'playlist', None)\r
-        root = self.xml.documentElement\r
-\r
-        #save generics variables\r
-        playlist_generics = self.xml.createElement('generics')\r
-        root.appendChild(playlist_generics)\r
-        self.genericsDict['pointer'] = self.index\r
-        for key in self.genericsDict.keys():\r
-            self.xml.createAttribute(key)\r
-            playlist_generics.setAttribute(key, str(self.genericsDict[key]))\r
-            \r
-        #save curves and their attributes\r
-        for item in self.curves:\r
-            playlist_curve = self.xml.createElement('curve')\r
-            root.appendChild(playlist_curve)\r
-            for key in item.__dict__:\r
-                if not (key in self.hiddenAttributes):\r
-                    self.xml.createAttribute(key)\r
-                    playlist_curve.setAttribute(key, str(item.__dict__[key]))\r
-        self._saved = False\r
+import copy
+import os
+import os.path
+import xml.dom.minidom
+
+from . import hooke as hooke
+from . import libhookecurve as lhc
+from . import libhooke as lh
+
+class Playlist(object):
+    def __init__(self, drivers):
+        self._saved = False
+        self.count = 0
+        self.curves = []
+        self.drivers = drivers
+        self.path = ''
+        self.genericsDict = {}
+        self.hiddenAttributes = ['curve', 'driver', 'name', 'plots']
+        self.index = -1
+        self.name = 'Untitled'
+        self.plotPanel = None
+        self.plotTab = None
+        self.xml = None
+
+    def add_curve(self, path, attributes={}):
+        curve = lhc.HookeCurve(path)
+        for key,value in attribures.items():
+            setattr(curve, key, value)
+        curve.identify(self.drivers)
+        curve.plots = curve.driver.default_plots()
+        self.curves.append(curve)
+        self._saved = False
+        self.count = len(self.curves)
+        return curve
+
+    def close_curve(self, index):
+        if index >= 0 and index < self.count:
+            self.curves.remove(index)
+
+    def filter_curves(self, keeper_fn=labmda curve:True):
+        playlist = copy.deepcopy(self)
+        for curve in reversed(playlist.curves):
+            if not keeper_fn(curve):
+                playlist.curves.remove(curve)
+        try: # attempt to maintain the same active curve
+            playlist.index = playlist.curves.index(self.get_active_curve())
+        except ValueError:
+            playlist.index = 0
+        playlist._saved = False
+        playlist.count = len(playlist.curves)
+        return playlist
+
+    def get_active_curve(self):
+        return self.curves[self.index]
+
+    #TODO: do we need this?
+    def get_active_plot(self):
+        return self.curves[self.index].plots[0]
+
+    def get_status_string(self):
+        if self.has_curves()
+            return '%s (%s/%s)' % (self.name, self.index + 1, self.count)
+        return 'The file %s does not contain any valid force curve data.' \
+            % self.name
+
+    def has_curves(self):
+        if self.count > 0:
+            return True
+        return False
+
+    def is_saved(self):
+        return self._saved
+
+    def load(self, path):
+        '''
+        loads a playlist file
+        '''
+        self.path = path
+        self.name = os.path.basename(path)
+        playlist = lh.delete_empty_lines_from_xmlfile(path)
+        self.xml = xml.dom.minidom.parse(path)
+        # Strip blank spaces:
+        self._removeWhitespaceNodes()
+
+        generics_list = self.xml.getElementsByTagName('generics')
+        curve_list = self.xml.getElementsByTagName('curve')
+        self._loadGenerics(generics_list)
+        self._loadCurves(curve_list)
+        self._saved = True
+
+    def _removeWhitespaceNodes(self, root_node=None):
+        if root_node == None:
+            root_node = self.xml
+        for node in root_node.childNodes:
+            if node.nodeType == node.TEXT_NODE and node.data.strip() == '':
+                root_node.removeChild(node) # drop this whitespace node
+            else:
+                _removeWhitespaceNodes(root_node=node) # recurse down a level
+
+    def _loadGenerics(self, generics_list, clear=True):
+        if clear:
+            self.genericsDict = {}
+        #populate generics
+        generics_list = self.xml.getElementsByTagName('generics')
+        for generics in generics_list:
+            for attribute in generics.attributes.keys():
+                self.genericsDict[attribute] = generics_list[0].getAttribute(attribute)
+        if self.genericsDict.has_key('pointer'):
+            index = int(self.genericsDict['pointer'])
+            if index >= 0 and index < len(self.curves):
+                self.index = index
+            else:
+                index = 0
+
+    def _loadCurves(self, curve_list, clear=True):
+        if clear:
+            self.curves = []
+        #populate playlist with curves
+        for curve in curve_list:
+            #rebuild a data structure from the xml attributes
+            curve_path = lh.get_file_path(element.getAttribute('path'))
+            #extract attributes for the single curve
+            attributes = dict([(k,curve.getAttribute(k))
+                               for k in curve.attributes.keys()])
+            attributes.pop('path')
+            curve = self.add_curve(os.path.join(path, curve_path), attributes)
+            if curve is not None:
+                for plot in curve.plots:
+                    curve.add_data('raw', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')
+                    curve.add_data('raw', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')
+
+    def next(self):
+        self.index += 1
+        if self.index > self.count - 1:
+            self.index = 0
+
+    def previous(self):
+        self.index -= 1
+        if self.index < 0:
+            self.index = self.count - 1
+
+    def reset(self):
+        if self.has_curves():
+            self.index = 0
+        else:
+            self.index = None
+
+    def save(self, path):
+        '''
+        saves the playlist in a XML file.
+        '''
+        try:
+            output_file = file(path, 'w')
+        except IOError, e:
+            #TODO: send message
+            print 'Cannot save playlist: %s' % e
+            return
+        self.xml.writexml(output_file, indent='\n')
+        output_file.close()
+        self._saved = True
+
+    def set_XML(self):
+        '''
+        Creates an initial playlist from a list of files.
+        A playlist is an XML document with the following syntax:
+          <?xml version="1.0" encoding="utf-8"?>
+          <playlist>
+            <generics pointer="0"/>
+            <curve path="/my/file/path/"/ attribute="value" ...>
+            <curve path="...">
+          </playlist>
+        Relative paths are interpreted relative to the location of the
+        playlist file.
+        '''
+        #create the output playlist, a simple XML document
+        implementation = xml.dom.minidom.getDOMImplementation()
+        #create the document DOM object and the root element
+        self.xml = implementation.createDocument(None, 'playlist', None)
+        root = self.xml.documentElement
+
+        #save generics variables
+        playlist_generics = self.xml.createElement('generics')
+        root.appendChild(playlist_generics)
+        self.genericsDict['pointer'] = self.index
+        for key in self.genericsDict.keys():
+            self.xml.createAttribute(key)
+            playlist_generics.setAttribute(key, str(self.genericsDict[key]))
+            
+        #save curves and their attributes
+        for item in self.curves:
+            playlist_curve = self.xml.createElement('curve')
+            root.appendChild(playlist_curve)
+            for key in item.__dict__:
+                if not (key in self.hiddenAttributes):
+                    self.xml.createAttribute(key)
+                    playlist_curve.setAttribute(key, str(item.__dict__[key]))
+        self._saved = False
index bd85e3c..a9f8837 100644 (file)
@@ -1,57 +1,57 @@
-#!/usr/bin/env python\r
-'''\r
-Commands and settings panel for Hooke\r
-\r
-Displays commands and settings for Hooke in a tree control\r
-(c) Dr. Rolf Schmidt, 2009\r
-'''\r
-\r
-from configobj import ConfigObj\r
-import os.path\r
-from validate import Validator\r
-import wx\r
-\r
-import libhooke as lh\r
-\r
-class Commands(wx.Panel):\r
-\r
-    def __init__(self, parent):\r
-        # Use the WANTS_CHARS style so the panel doesn't eat the Return key.\r
-        wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS|wx.NO_BORDER, size=(160, 200))\r
-\r
-        self.CommandsTree = wx.TreeCtrl(self, -1, wx.Point(0, 0), wx.Size(160, 250), wx.TR_DEFAULT_STYLE|wx.NO_BORDER|wx.TR_HIDE_ROOT)\r
-        imglist = wx.ImageList(16, 16, True, 2)\r
-        imglist.Add(wx.ArtProvider.GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, wx.Size(16, 16)))\r
-        imglist.Add(wx.ArtProvider.GetBitmap(wx.ART_EXECUTABLE_FILE, wx.ART_OTHER, wx.Size(16, 16)))\r
-        self.CommandsTree.AssignImageList(imglist)\r
-        self.CommandsTree.AddRoot('Commands and Settings', 0)\r
-\r
-        self.ExecuteButton = wx.Button(self, -1, 'Execute')\r
-\r
-        sizer = wx.BoxSizer(wx.VERTICAL)\r
-        sizer.Add(self.CommandsTree, 1, wx.EXPAND)\r
-        sizer.Add(self.ExecuteButton, 0, wx.EXPAND)\r
-\r
-        self.SetSizer(sizer)\r
-        sizer.Fit(self)\r
-\r
-    def Initialize(self, plugins):\r
-        tree_root = self.CommandsTree.GetRootItem()\r
-        for plugin in plugins:\r
-            filename = ''.join([plugin, '.ini'])\r
-            path = lh.get_file_path(filename, ['plugins'])\r
-            config = ConfigObj()\r
-            if os.path.isfile(path):\r
-                config.filename = path\r
-                config.reload()\r
-                #append the ini file to the plugin\r
-                plugin_root = self.CommandsTree.AppendItem(tree_root, plugin, 0, data=wx.TreeItemData(config))\r
-            else:\r
-                plugin_root = self.CommandsTree.AppendItem(tree_root, plugin, 0)\r
-\r
-            #add all commands to the tree\r
-            for command in plugins[plugin]:\r
-                command_label = command.replace('do_', '')\r
-                #do not add the ini file to the command (we'll access the ini file of the plugin (ie parent) instead, see above)\r
-                self.CommandsTree.AppendItem(plugin_root, command_label, 1)\r
-            self.CommandsTree.Expand(plugin_root)\r
+#!/usr/bin/env python
+'''
+Commands and settings panel for Hooke
+
+Displays commands and settings for Hooke in a tree control
+(c) Dr. Rolf Schmidt, 2009
+'''
+
+from configobj import ConfigObj
+import os.path
+from validate import Validator
+import wx
+
+import libhooke as lh
+
+class Commands(wx.Panel):
+
+    def __init__(self, parent):
+        # Use the WANTS_CHARS style so the panel doesn't eat the Return key.
+        wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS|wx.NO_BORDER, size=(160, 200))
+
+        self.CommandsTree = wx.TreeCtrl(self, -1, wx.Point(0, 0), wx.Size(160, 250), wx.TR_DEFAULT_STYLE|wx.NO_BORDER|wx.TR_HIDE_ROOT)
+        imglist = wx.ImageList(16, 16, True, 2)
+        imglist.Add(wx.ArtProvider.GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, wx.Size(16, 16)))
+        imglist.Add(wx.ArtProvider.GetBitmap(wx.ART_EXECUTABLE_FILE, wx.ART_OTHER, wx.Size(16, 16)))
+        self.CommandsTree.AssignImageList(imglist)
+        self.CommandsTree.AddRoot('Commands and Settings', 0)
+
+        self.ExecuteButton = wx.Button(self, -1, 'Execute')
+
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        sizer.Add(self.CommandsTree, 1, wx.EXPAND)
+        sizer.Add(self.ExecuteButton, 0, wx.EXPAND)
+
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+
+    def Initialize(self, plugins):
+        tree_root = self.CommandsTree.GetRootItem()
+        for plugin in plugins:
+            filename = ''.join([plugin, '.ini'])
+            path = lh.get_file_path(filename, ['plugins'])
+            config = ConfigObj()
+            if os.path.isfile(path):
+                config.filename = path
+                config.reload()
+                #append the ini file to the plugin
+                plugin_root = self.CommandsTree.AppendItem(tree_root, plugin, 0, data=wx.TreeItemData(config))
+            else:
+                plugin_root = self.CommandsTree.AppendItem(tree_root, plugin, 0)
+
+            #add all commands to the tree
+            for command in plugins[plugin]:
+                command_label = command.replace('do_', '')
+                #do not add the ini file to the command (we'll access the ini file of the plugin (ie parent) instead, see above)
+                self.CommandsTree.AppendItem(plugin_root, command_label, 1)
+            self.CommandsTree.Expand(plugin_root)
index a2de586..719f980 100644 (file)
@@ -1,61 +1,61 @@
-#!/usr/bin/env python\r
-\r
-'''\r
-SHOW CONVOLUTED CURVE PLUGIN FOR HOOKE\r
-\r
-This plugin contains a plotmanipulator to show the convoluted curve.\r
-(c) Dr. Rolf Schmidt, 2009\r
-'''\r
-\r
-import libpeakspot\r
-\r
-class showconvolutedCommands:\r
-\r
-    #def _plug_init(self):\r
-        #pass\r
-\r
-    def plotmanip_showconvoluted(self, plot, curve):\r
-        '''\r
-        BEGIN: taken from procplots.py\r
-        might need some tweaking\r
-        '''\r
-        #use only for force spectroscopy experiments!\r
-        if curve.driver.experiment != 'smfs':\r
-            return plot\r
-\r
-        '''\r
-        END: taken from procplots.py\r
-        '''\r
-\r
-        #need to convert the string that contains the list into a list\r
-        #convolution = eval(self.config['convfilt']['convolution']['value'])\r
-        convolution = eval(self.GetStringFromConfig('flatfilts', 'convfilt', 'convolution'))\r
-\r
-        xRet = plot.vectors[1][0]\r
-        yRet = plot.vectors[1][1]\r
-        convoluted = libpeakspot.conv_dx(yRet, convolution)\r
-        #convoluted=libpeakspot.conv_dx(yRet, [-20, -10, -6, 0, 12, 12, 12])\r
-        plot.add_set(xRet, convoluted)\r
-        #plot.vectors[1][1]=[i for i in convoluted]\r
-        #set contact point plot style to 'plot'\r
-        #and the color to red\r
-        plot.styles.append('plot')\r
-        plot.colors.append('black')\r
-        #peak_location, peak_size = self.has_peaks(plot, blindwindow, convolution, minpeaks)\r
-        peak_locations, peak_sizes = self.has_peaks(plot, curve)\r
-\r
-        if peak_locations:\r
-            peak_locations_x = []\r
-            peak_locations_y = []\r
-            for location in peak_locations:\r
-                peak_locations_x.append(xRet[location])\r
-                peak_locations_y.append(yRet[location])\r
-            plot.add_set(peak_locations_x, peak_locations_y)\r
-            plot.styles.append('scatter')\r
-            plot.colors.append('green')\r
-            plot.add_set(peak_locations_x, peak_sizes)\r
-            plot.styles.append('scatter')\r
-            plot.colors.append('magenta')\r
-\r
-        #Return the plot object.\r
-        return plot\r
+#!/usr/bin/env python
+
+'''
+SHOW CONVOLUTED CURVE PLUGIN FOR HOOKE
+
+This plugin contains a plotmanipulator to show the convoluted curve.
+(c) Dr. Rolf Schmidt, 2009
+'''
+
+import libpeakspot
+
+class showconvolutedCommands:
+
+    #def _plug_init(self):
+        #pass
+
+    def plotmanip_showconvoluted(self, plot, curve):
+        '''
+        BEGIN: taken from procplots.py
+        might need some tweaking
+        '''
+        #use only for force spectroscopy experiments!
+        if curve.driver.experiment != 'smfs':
+            return plot
+
+        '''
+        END: taken from procplots.py
+        '''
+
+        #need to convert the string that contains the list into a list
+        #convolution = eval(self.config['convfilt']['convolution']['value'])
+        convolution = eval(self.GetStringFromConfig('flatfilts', 'convfilt', 'convolution'))
+
+        xRet = plot.vectors[1][0]
+        yRet = plot.vectors[1][1]
+        convoluted = libpeakspot.conv_dx(yRet, convolution)
+        #convoluted=libpeakspot.conv_dx(yRet, [-20, -10, -6, 0, 12, 12, 12])
+        plot.add_set(xRet, convoluted)
+        #plot.vectors[1][1]=[i for i in convoluted]
+        #set contact point plot style to 'plot'
+        #and the color to red
+        plot.styles.append('plot')
+        plot.colors.append('black')
+        #peak_location, peak_size = self.has_peaks(plot, blindwindow, convolution, minpeaks)
+        peak_locations, peak_sizes = self.has_peaks(plot, curve)
+
+        if peak_locations:
+            peak_locations_x = []
+            peak_locations_y = []
+            for location in peak_locations:
+                peak_locations_x.append(xRet[location])
+                peak_locations_y.append(yRet[location])
+            plot.add_set(peak_locations_x, peak_locations_y)
+            plot.styles.append('scatter')
+            plot.colors.append('green')
+            plot.add_set(peak_locations_x, peak_sizes)
+            plot.styles.append('scatter')
+            plot.colors.append('magenta')
+
+        #Return the plot object.
+        return plot
index 34b267f..5d731ae 100644 (file)
-#import os\r
-#import os.path\r
-import wx\r
-#import xml.dom.minidom\r
-\r
-class Playlists(wx.Panel):\r
-\r
-    def __init__(self, parent):\r
-        # Use the WANTS_CHARS style so the panel doesn't eat the Return key.\r
-        wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS|wx.NO_BORDER, size=(160, 200))\r
-\r
-        self.PlaylistsTree = wx.TreeCtrl(self, -1, wx.Point(0, 0), wx.Size(160, 250), wx.TR_DEFAULT_STYLE | wx.NO_BORDER | wx.TR_HIDE_ROOT)\r
-        imglist = wx.ImageList(16, 16, True, 2)\r
-        imglist.Add(wx.ArtProvider.GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, wx.Size(16, 16)))\r
-        imglist.Add(wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16)))\r
-        self.PlaylistsTree.AssignImageList(imglist)\r
-        self.PlaylistsTree.AddRoot('Playlists', 0)\r
-        self.PlaylistsTree.Bind(wx.EVT_RIGHT_DOWN , self.OnContextMenu)\r
-\r
-        self.Playlists = {}\r
-\r
-        sizer = wx.BoxSizer(wx.VERTICAL)\r
-        sizer.Add(self.PlaylistsTree, 1, wx.EXPAND)\r
-        self.SetSizer(sizer)\r
-        sizer.Fit(self)\r
-\r
-    #def add_playlist(self, files=[], name='Untitled'):\r
-        ##TODO: change cursor or progressbar (maybe in statusbar)\r
-        ##self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))\r
-        #if files:\r
-            #playlist = hookeplaylist.Playlist(self.drivers)\r
-            #for item in files:\r
-                #playlist.add_curve(item)\r
-        #if playlist.count > 0:\r
-            #playlist_name = name\r
-            #count = 1\r
-            #while playlist_name in self.Playlists:\r
-                #playlist_name = ''.join([name, str(count)])\r
-                #count += 1\r
-            #playlist.name = playlist_name\r
-            #playlist.reset()\r
-            #self.AddToPlaylists(playlist)\r
-\r
-    #def FilterPlaylist(self, curves_to_keep=[]):\r
-        #playlist_active = self.GetActivePlaylist()\r
-        #playlist_new = Playlist(self.drivers)\r
-        #for curve_index in curves_to_keep:\r
-            #playlist_new.curves.append(playlist_active.curves[curve_index])\r
-        #return playlist_new\r
-\r
-    def GetActivePlaylist(self):\r
-        playlist_name = self.GetActivePlaylistName()\r
-        if playlist_name in self.playlists:\r
-            return self.playlists[playlist_name][0]\r
-        else:\r
-            return None\r
-\r
-    def GetActivePlaylistName(self):\r
-        #get the selected item from the tree\r
-        selected_item = self.PlaylistsTree.GetSelection()\r
-        #test if a playlist or a curve was double-clicked\r
-        if self.PlaylistsTree.ItemHasChildren(selected_item):\r
-            playlist_item = selected_item\r
-        else:\r
-            #get the name of the playlist\r
-            playlist_item = self.PlaylistsTree.GetItemParent(selected_item)\r
-        #now we have a playlist\r
-        return self.PlaylistsTree.GetItemText(playlist_item)\r
-\r
-    def OnContextMenu(self, event):\r
-        hit_item, hit_flags = self.PlaylistsTree.HitTest(event.GetPosition())\r
-        if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:\r
-            self.PlaylistsTree.SelectItem(hit_item)\r
-            # only do this part the first time so the events are only bound once\r
-            #\r
-            # Yet another alternate way to do IDs. Some prefer them up top to\r
-            # avoid clutter, some prefer them close to the object of interest\r
-            # for clarity.\r
-            if not hasattr(self, 'ID_popupAdd'):\r
-                self.ID_popupAdd = wx.NewId()\r
-                self.ID_popupClose = wx.NewId()\r
-                self.Bind(wx.EVT_MENU, self.OnPopupAdd, id=self.ID_popupAdd)\r
-                self.Bind(wx.EVT_MENU, self.OnPopupClose, id=self.ID_popupClose)\r
-            # make a menu\r
-            menu = wx.Menu()\r
-            items = [['Add', self.ID_popupAdd] , ['Close', self.ID_popupClose]]\r
-            for item in items:\r
-                menu.Append(item[1], item[0])\r
-            # Popup the menu.  If an item is selected then its handler\r
-            # will be called before PopupMenu returns.\r
-            self.PopupMenu(menu)\r
-            menu.Destroy()\r
-\r
-    def OnPopupAdd(self, event):\r
-        pass\r
-\r
-    def OnPopupClose(self, event):\r
-        item = self.PlaylistsTree.GetSelection()\r
-        if self.PlaylistsTree.ItemHasChildren(item):\r
-            playlist_name = self.PlaylistsTree.GetItemText(item)\r
-            self.Parent.DeletePlotPage(playlist_name)\r
-            #del self.Playlists[playlist_name]\r
-            #TODO: delete playlist, close notebook tab\r
-            #self.Parent.AddToPlaylists()\r
-        else:\r
-            pass\r
+#import os
+#import os.path
+import wx
+#import xml.dom.minidom
+
+class Playlists(wx.Panel):
+
+    def __init__(self, parent):
+        # Use the WANTS_CHARS style so the panel doesn't eat the Return key.
+        wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS|wx.NO_BORDER, size=(160, 200))
+
+        self.PlaylistsTree = wx.TreeCtrl(self, -1, wx.Point(0, 0), wx.Size(160, 250), wx.TR_DEFAULT_STYLE | wx.NO_BORDER | wx.TR_HIDE_ROOT)
+        imglist = wx.ImageList(16, 16, True, 2)
+        imglist.Add(wx.ArtProvider.GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, wx.Size(16, 16)))
+        imglist.Add(wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16)))
+        self.PlaylistsTree.AssignImageList(imglist)
+        self.PlaylistsTree.AddRoot('Playlists', 0)
+        self.PlaylistsTree.Bind(wx.EVT_RIGHT_DOWN , self.OnContextMenu)
+
+        self.Playlists = {}
+
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        sizer.Add(self.PlaylistsTree, 1, wx.EXPAND)
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+
+    #def add_playlist(self, files=[], name='Untitled'):
+        ##TODO: change cursor or progressbar (maybe in statusbar)
+        ##self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
+        #if files:
+            #playlist = hookeplaylist.Playlist(self.drivers)
+            #for item in files:
+                #playlist.add_curve(item)
+        #if playlist.count > 0:
+            #playlist_name = name
+            #count = 1
+            #while playlist_name in self.Playlists:
+                #playlist_name = ''.join([name, str(count)])
+                #count += 1
+            #playlist.name = playlist_name
+            #playlist.reset()
+            #self.AddToPlaylists(playlist)
+
+    #def FilterPlaylist(self, curves_to_keep=[]):
+        #playlist_active = self.GetActivePlaylist()
+        #playlist_new = Playlist(self.drivers)
+        #for curve_index in curves_to_keep:
+            #playlist_new.curves.append(playlist_active.curves[curve_index])
+        #return playlist_new
+
+    def GetActivePlaylist(self):
+        playlist_name = self.GetActivePlaylistName()
+        if playlist_name in self.playlists:
+            return self.playlists[playlist_name][0]
+        else:
+            return None
+
+    def GetActivePlaylistName(self):
+        #get the selected item from the tree
+        selected_item = self.PlaylistsTree.GetSelection()
+        #test if a playlist or a curve was double-clicked
+        if self.PlaylistsTree.ItemHasChildren(selected_item):
+            playlist_item = selected_item
+        else:
+            #get the name of the playlist
+            playlist_item = self.PlaylistsTree.GetItemParent(selected_item)
+        #now we have a playlist
+        return self.PlaylistsTree.GetItemText(playlist_item)
+
+    def OnContextMenu(self, event):
+        hit_item, hit_flags = self.PlaylistsTree.HitTest(event.GetPosition())
+        if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:
+            self.PlaylistsTree.SelectItem(hit_item)
+            # only do this part the first time so the events are only bound once
+            #
+            # Yet another alternate way to do IDs. Some prefer them up top to
+            # avoid clutter, some prefer them close to the object of interest
+            # for clarity.
+            if not hasattr(self, 'ID_popupAdd'):
+                self.ID_popupAdd = wx.NewId()
+                self.ID_popupClose = wx.NewId()
+                self.Bind(wx.EVT_MENU, self.OnPopupAdd, id=self.ID_popupAdd)
+                self.Bind(wx.EVT_MENU, self.OnPopupClose, id=self.ID_popupClose)
+            # make a menu
+            menu = wx.Menu()
+            items = [['Add', self.ID_popupAdd] , ['Close', self.ID_popupClose]]
+            for item in items:
+                menu.Append(item[1], item[0])
+            # Popup the menu.  If an item is selected then its handler
+            # will be called before PopupMenu returns.
+            self.PopupMenu(menu)
+            menu.Destroy()
+
+    def OnPopupAdd(self, event):
+        pass
+
+    def OnPopupClose(self, event):
+        item = self.PlaylistsTree.GetSelection()
+        if self.PlaylistsTree.ItemHasChildren(item):
+            playlist_name = self.PlaylistsTree.GetItemText(item)
+            self.Parent.DeletePlotPage(playlist_name)
+            #del self.Playlists[playlist_name]
+            #TODO: delete playlist, close notebook tab
+            #self.Parent.AddToPlaylists()
+        else:
+            pass
index 2854e36..402b6f3 100644 (file)
-import sys\r
-import time\r
-import math\r
-import os.path\r
-import pdb\r
-\r
-import wx\r
-import wx.propgrid as wxpg\r
-import wx.stc\r
-\r
-\r
-class Display:\r
-    property_descriptor = []\r
-    def __init__(self):\r
-        pass\r
-\r
-\r
-class ValueObject:\r
-    def __init__(self):\r
-        pass\r
-\r
-\r
-class IntProperty2(wxpg.PyProperty):\r
-    """\\r
-    This is a simple re-implementation of wxIntProperty.\r
-    """\r
-    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=0):\r
-        wxpg.PyProperty.__init__(self, label, name)\r
-        self.SetValue(value)\r
-\r
-    def GetClassName(self):\r
-        """\\r
-        This is not 100% necessary and in future is probably going to be\r
-        automated to return class name.\r
-        """\r
-        return "IntProperty2"\r
-\r
-    def GetEditor(self):\r
-        return "TextCtrl"\r
-\r
-    def GetValueAsString(self, flags):\r
-        return str(self.GetValue())\r
-\r
-    def PyStringToValue(self, s, flags):\r
-        try:\r
-            v = int(s)\r
-            if self.GetValue() != v:\r
-                return v\r
-        except TypeError:\r
-            if flags & wxpg.PG_REPORT_ERROR:\r
-                wx.MessageBox("Cannot convert '%s' into a number."%s, "Error")\r
-        return False\r
-\r
-    def PyIntToValue(self, v, flags):\r
-        if (self.GetValue() != v):\r
-            return v\r
-\r
-\r
-class PyFilesProperty(wxpg.PyArrayStringProperty):\r
-    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=[]):\r
-        wxpg.PyArrayStringProperty.__init__(self, label, name, value)\r
-        self.SetValue(value)\r
-\r
-    def OnSetValue(self, v):\r
-        self.value = v\r
-        self.display = ', '.join(self.value)\r
-\r
-    def GetValueAsString(self, argFlags):\r
-        return self.display\r
-\r
-    def PyStringToValue(self, s, flags):\r
-        return [a.strip() for a in text.split(',')]\r
-\r
-    def OnEvent(self, propgrid, ctrl, event):\r
-        if event.GetEventType() == wx.wxEVT_COMMAND_BUTTON_CLICKED:\r
-            # Show dialog to select a string, call DoSetValue and\r
-            # return True, if value changed.\r
-            return True\r
-\r
-        return False\r
-\r
-\r
-class PyObjectPropertyValue:\r
-    """\\r
-    Value type of our sample PyObjectProperty. We keep a simple dash-delimited\r
-    list of string given as argument to constructor.\r
-    """\r
-    def __init__(self, s=None):\r
-        try:\r
-            self.ls = [a.strip() for a in s.split('-')]\r
-        except:\r
-            self.ls = []\r
-\r
-    def __repr__(self):\r
-        return ' - '.join(self.ls)\r
-\r
-\r
-\r
-class PyObjectProperty(wxpg.PyProperty):\r
-    """\\r
-    Another simple example. This time our value is a PyObject (NOTE: we can't\r
-    return an arbitrary python object in DoGetValue. It cannot be a simple\r
-    type such as int, bool, double, or string, nor an array or wxObject based.\r
-    Dictionary, None, or any user-specified Python object is allowed).\r
-    """\r
-    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=None):\r
-        wxpg.PyProperty.__init__(self, label, name)\r
-        self.SetValue(value)\r
-\r
-    def GetClassName(self):\r
-        return self.__class__.__name__\r
-\r
-    def GetEditor(self):\r
-        return "TextCtrl"\r
-\r
-    def GetValueAsString(self, flags):\r
-        return repr(self.GetValue())\r
-\r
-    def PyStringToValue(self, s, flags):\r
-        return PyObjectPropertyValue(s)\r
-\r
-\r
-class ShapeProperty(wxpg.PyEnumProperty):\r
-    """\\r
-    Demonstrates use of OnCustomPaint method.\r
-    """\r
-    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=-1):\r
-        wxpg.PyEnumProperty.__init__(self, label, name, ['Line','Circle','Rectangle'], [0,1,2], value)\r
-\r
-    def OnMeasureImage(self, index):\r
-        return wxpg.DEFAULT_IMAGE_SIZE\r
-\r
-    def OnCustomPaint(self, dc, rect, paint_data):\r
-        """\\r
-        paint_data.m_choiceItem is -1 if we are painting the control,\r
-        in which case we need to get the drawn item using DoGetValue.\r
-        """\r
-        item = paint_data.m_choiceItem\r
-        if item == -1:\r
-            item = self.DoGetValue()\r
-\r
-        dc.SetPen(wx.Pen(wx.BLACK))\r
-        dc.SetBrush(wx.Brush(wx.BLACK))\r
-\r
-        if item == 0:\r
-            dc.DrawLine(rect.x,rect.y,rect.x+rect.width,rect.y+rect.height)\r
-        elif item == 1:\r
-            half_width = rect.width / 2\r
-            dc.DrawCircle(rect.x+half_width,rect.y+half_width,half_width-3)\r
-        elif item == 2:\r
-            dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)\r
-\r
-\r
-class LargeImagePickerCtrl(wx.Window):\r
-    """\\r
-    Control created and used by LargeImageEditor.\r
-    """\r
-    def __init__(self):\r
-        pre = wx.PreWindow()\r
-        self.PostCreate(pre)\r
-\r
-    def Create(self, parent, id_, pos, size, style = 0):\r
-        wx.Window.Create(self, parent, id_, pos, size, style | wx.BORDER_SIMPLE)\r
-        img_spc = size[1]\r
-        self.tc = wx.TextCtrl(self, -1, "", (img_spc,0), (2048,size[1]), wx.BORDER_NONE)\r
-        self.SetBackgroundColour(wx.WHITE)\r
-        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)\r
-        self.property = None\r
-        self.bmp = None\r
-        self.Bind(wx.EVT_PAINT, self.OnPaint)\r
-\r
-    def OnPaint(self, event):\r
-        dc = wx.BufferedPaintDC(self)\r
-\r
-        whiteBrush = wx.Brush(wx.WHITE)\r
-        dc.SetBackground(whiteBrush)\r
-        dc.Clear()\r
-\r
-        bmp = self.bmp\r
-        if bmp:\r
-            dc.DrawBitmap(bmp, 2, 2)\r
-        else:\r
-            dc.SetPen(wx.Pen(wx.BLACK))\r
-            dc.SetBrush(whiteBrush)\r
-            dc.DrawRectangle(2, 2, 64, 64)\r
-\r
-    def RefreshThumbnail(self):\r
-        """\\r
-        We use here very simple image scaling code.\r
-        """\r
-        if not self.property:\r
-            self.bmp = None\r
-            return\r
-\r
-        path = self.property.DoGetValue()\r
-\r
-        if not os.path.isfile(path):\r
-            self.bmp = None\r
-            return\r
-\r
-        image = wx.Image(path)\r
-        image.Rescale(64, 64)\r
-        self.bmp = wx.BitmapFromImage(image)\r
-\r
-    def SetProperty(self, property):\r
-        self.property = property\r
-        self.tc.SetValue(property.GetDisplayedString())\r
-        self.RefreshThumbnail()\r
-\r
-    def SetValue(self, s):\r
-        self.RefreshThumbnail()\r
-        self.tc.SetValue(s)\r
-\r
-    def GetLastPosition(self):\r
-        return self.tc.GetLastPosition()\r
-\r
-\r
-class LargeImageEditor(wxpg.PyEditor):\r
-    """\\r
-    Double-height text-editor with image in front.\r
-    """\r
-    def __init__(self):\r
-        wxpg.PyEditor.__init__(self)\r
-\r
-    def CreateControls(self, propgrid, property, pos, sz):\r
-        try:\r
-            h = 64 + 6\r
-            x = propgrid.GetSplitterPosition()\r
-            x2 = propgrid.GetClientSize().x\r
-            bw = propgrid.GetRowHeight()\r
-            lipc = LargeImagePickerCtrl()\r
-            if sys.platform == 'win32':\r
-                lipc.Hide()\r
-            lipc.Create(propgrid, wxpg.PG_SUBID1, (x,pos[1]), (x2-x-bw,h))\r
-            lipc.SetProperty(property)\r
-            # Hmmm.. how to have two-stage creation without subclassing?\r
-            #btn = wx.PreButton()\r
-            #pre = wx.PreWindow()\r
-            #self.PostCreate(pre)\r
-            #if sys.platform == 'win32':\r
-            #    btn.Hide()\r
-            #btn.Create(propgrid, wxpg.PG_SUBID2, '...', (x2-bw,pos[1]), (bw,h), wx.WANTS_CHARS)\r
-            btn = wx.Button(propgrid, wxpg.PG_SUBID2, '...', (x2-bw,pos[1]), (bw,h), wx.WANTS_CHARS)\r
-            return (lipc, btn)\r
-        except:\r
-            import traceback\r
-            print traceback.print_exc()\r
-\r
-    def UpdateControl(self, property, ctrl):\r
-        ctrl.SetValue(property.GetDisplayedString())\r
-\r
-    def DrawValue(self, dc, property, rect):\r
-        if not (property.GetFlags() & wxpg.PG_PROP_UNSPECIFIED):\r
-            dc.DrawText( property.GetDisplayedString(), rect.x+5, rect.y );\r
-\r
-    def OnEvent(self, propgrid, ctrl, event):\r
-        if not ctrl:\r
-            return False\r
-\r
-        evtType = event.GetEventType()\r
-\r
-        if evtType == wx.wxEVT_COMMAND_TEXT_ENTER:\r
-            if propgrid.IsEditorsValueModified():\r
-                return True\r
-\r
-        elif evtType == wx.wxEVT_COMMAND_TEXT_UPDATED:\r
-            if not property.HasFlag(wxpg.PG_PROP_UNSPECIFIED) or not ctrl or \\r
-               ctrl.GetLastPosition() > 0:\r
-\r
-                # We must check this since an 'empty' text event\r
-                # may be triggered when creating the property.\r
-                PG_FL_IN_SELECT_PROPERTY = 0x00100000\r
-                if not (propgrid.GetInternalFlags() & PG_FL_IN_SELECT_PROPERTY):\r
-                    event.Skip();\r
-                    event.SetId(propGrid.GetId());\r
-\r
-                propgrid.EditorsValueWasModified();\r
-\r
-        return False\r
-\r
-\r
-    def CopyValueFromControl(self, property, ctrl):\r
-        tc = ctrl.tc\r
-        res = property.SetValueFromString(tc.GetValue(),0)\r
-        # Changing unspecified always causes event (returning\r
-        # true here should be enough to trigger it).\r
-        if not res and property.IsFlagSet(wxpg.PG_PROP_UNSPECIFIED):\r
-            res = true\r
-\r
-        return res\r
-\r
-    def SetValueToUnspecified(self, ctrl):\r
-        ctrl.tc.Remove(0,len(ctrl.tc.GetValue()));\r
-\r
-    def SetControlStringValue(self, ctrl, txt):\r
-        ctrl.SetValue(txt)\r
-\r
-    def OnFocus(self, property, ctrl):\r
-        ctrl.tc.SetSelection(-1,-1)\r
-        ctrl.tc.SetFocus()\r
-\r
-\r
-class PropertyEditor(wx.Panel):\r
-\r
-    def __init__(self, parent):\r
-        # Use the WANTS_CHARS style so the panel doesn't eat the Return key.\r
-        wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS, size=(160, 200))\r
-\r
-        sizer = wx.BoxSizer(wx.VERTICAL)\r
-\r
-        self.pg = wxpg.PropertyGrid(self, style=wxpg.PG_SPLITTER_AUTO_CENTER|wxpg.PG_AUTO_SORT)\r
-\r
-        # Show help as tooltips\r
-        self.pg.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS)\r
-\r
-        #pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChange)\r
-        #pg.Bind(wxpg.EVT_PG_SELECTED, self.OnPropGridSelect)\r
-        #self.pg.Bind(wxpg.EVT_PG_RIGHT_CLICK, self.OnPropGridRightClick)\r
-\r
-        # Needed by custom image editor\r
-        wx.InitAllImageHandlers()\r
-\r
-        #\r
-        # Let's create a simple custom editor\r
-        #\r
-        # NOTE: Editor must be registered *before* adding a property that uses it.\r
-        self.pg.RegisterEditor(LargeImageEditor)\r
-\r
-        '''\r
-        #\r
-        # Add properties\r
-        #\r
-\r
-        pg.Append( wxpg.PropertyCategory("1 - Basic Properties") )\r
-        pg.Append( wxpg.StringProperty("String",value="Some Text") )\r
-        pg.Append( wxpg.IntProperty("Int",value=100) )\r
-        pg.Append( wxpg.FloatProperty("Float",value=100.0) )\r
-        pg.Append( wxpg.BoolProperty("Bool",value=True) )\r
-        pg.Append( wxpg.BoolProperty("Bool_with_Checkbox",value=True) )\r
-        pg.SetPropertyAttribute("Bool_with_Checkbox", "UseCheckbox", True)\r
-\r
-        pg.Append( wxpg.PropertyCategory("2 - More Properties") )\r
-        pg.Append( wxpg.LongStringProperty("LongString",value="This is a\\nmulti-line string\\nwith\\ttabs\\nmixed\\tin.") )\r
-        pg.Append( wxpg.DirProperty("Dir",value="C:\\Windows") )\r
-        pg.Append( wxpg.FileProperty("File",value="C:\\Windows\\system.ini") )\r
-        pg.Append( wxpg.ArrayStringProperty("ArrayString",value=['A','B','C']) )\r
-\r
-        pg.Append( wxpg.EnumProperty("Enum","Enum",\r
-                                     ['wxPython Rules','wxPython Rocks','wxPython Is The Best'],\r
-                                     [10,11,12],0) )\r
-        pg.Append( wxpg.EditEnumProperty("EditEnum","EditEnumProperty",['A','B','C'],[0,1,2],"Text Not in List") )\r
-\r
-        pg.Append( wxpg.PropertyCategory("3 - Advanced Properties") )\r
-        pg.Append( wxpg.DateProperty("Date",value=wx.DateTime_Now()) )\r
-        pg.Append( wxpg.FontProperty("Font",value=self.GetFont()) )\r
-        pg.Append( wxpg.ColourProperty("Colour",value=self.GetBackgroundColour()) )\r
-        pg.Append( wxpg.SystemColourProperty("SystemColour") )\r
-        pg.Append( wxpg.ImageFileProperty("ImageFile") )\r
-        pg.Append( wxpg.MultiChoiceProperty("MultiChoice",choices=['wxWidgets','QT','GTK+']) )\r
-\r
-        pg.Append( wxpg.PropertyCategory("4 - Additional Properties") )\r
-        pg.Append( wxpg.PointProperty("Point",value=self.GetPosition()) )\r
-        pg.Append( wxpg.SizeProperty("Size",value=self.GetSize()) )\r
-        pg.Append( wxpg.FontDataProperty("FontData") )\r
-        pg.Append( wxpg.IntProperty("IntWithSpin",value=256) )\r
-        pg.SetPropertyEditor("IntWithSpin","SpinCtrl")\r
-        pg.Append( wxpg.DirsProperty("Dirs",value=['C:/Lib','C:/Bin']) )\r
-        pg.SetPropertyHelpString( "String", "String Property help string!" )\r
-        pg.SetPropertyHelpString( "Dirs", "Dirs Property help string!" )\r
-\r
-        pg.SetPropertyAttribute( "File", wxpg.PG_FILE_SHOW_FULL_PATH, 0 )\r
-        pg.SetPropertyAttribute( "File", wxpg.PG_FILE_INITIAL_PATH, "C:\\Program Files\\Internet Explorer" )\r
-        pg.SetPropertyAttribute( "Date", wxpg.PG_DATE_PICKER_STYLE, wx.DP_DROPDOWN|wx.DP_SHOWCENTURY )\r
-\r
-        pg.Append( wxpg.PropertyCategory("5 - Custom Properties") )\r
-        pg.Append( IntProperty2("IntProperty2", value=1024) )\r
-\r
-        pg.Append( ShapeProperty("ShapeProperty", value=0) )\r
-        pg.Append( PyObjectProperty("PyObjectProperty") )\r
-\r
-        pg.Append( wxpg.ImageFileProperty("ImageFileWithLargeEditor") )\r
-        pg.SetPropertyEditor("ImageFileWithLargeEditor", "LargeImageEditor")\r
-\r
-\r
-        pg.SetPropertyClientData( "Point", 1234 )\r
-        if pg.GetPropertyClientData( "Point" ) != 1234:\r
-            raise ValueError("Set/GetPropertyClientData() failed")\r
-\r
-        # Test setting unicode string\r
-        pg.GetPropertyByName("String").SetValue(u"Some Unicode Text")\r
-\r
-        #\r
-        # Test some code that *should* fail (but not crash)\r
-        #try:\r
-            #a_ = pg.GetPropertyValue( "NotARealProperty" )\r
-            #pg.EnableProperty( "NotAtAllRealProperty", False )\r
-            #pg.SetPropertyHelpString( "AgaintNotARealProperty", "Dummy Help String" )\r
-        #except:\r
-            #pass\r
-            #raise\r
-\r
-        '''\r
-        sizer.Add(self.pg, 1, wx.EXPAND)\r
-        self.SetSizer(sizer)\r
-        sizer.SetSizeHints(self)\r
-\r
-        self.SelectedTreeItem = None\r
-\r
-    def GetPropertyValues(self):\r
-        #return self.pg.GetPropertyValues(as_strings=True)\r
-        return self.pg.GetPropertyValues()\r
-\r
-\r
-    def Initialize(self, properties):\r
-        pg = self.pg\r
-        pg.Clear()\r
-\r
-        if properties:\r
-            for element in properties:\r
-                if element[1]['type'] == 'integer':\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1].as_int('value')\r
-                    else:\r
-                        property_value = element[1].as_int('default')\r
-                    property_control = wxpg.IntProperty(element[0], value=property_value)\r
-                    if 'maximum' in element[1]:\r
-                        property_control.SetAttribute('Max', element[1].as_int('maximum'))\r
-                    if 'minimum' in element[1]:\r
-                        property_control.SetAttribute('Min', element[1].as_int('minimum'))\r
-                    property_control.SetAttribute('Wrap', True)\r
-                    pg.Append(property_control)\r
-                    pg.SetPropertyEditor(element[0], 'SpinCtrl')\r
-\r
-                if element[1]['type'] == 'float':\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1].as_float('value')\r
-                    else:\r
-                        property_value = element[1].as_float('default')\r
-                    property_control = wxpg.FloatProperty(element[0], value=property_value)\r
-                    if 'maximum' in element[1]:\r
-                        property_control.SetAttribute('Max', element[1].as_float('maximum'))\r
-                    if 'minimum' in element[1]:\r
-                        property_control.SetAttribute('Min', element[1].as_float('minimum'))\r
-                    property_control.SetAttribute('Wrap', True)\r
-                    pg.Append(property_control)\r
-                    pg.SetPropertyEditor(element[0], 'SpinCtrl')\r
-\r
-                if element[1]['type'] == 'boolean':\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1].as_bool('value')\r
-                    else:\r
-                        property_value = element[1].as_bool('default')\r
-                    property_control = wxpg.BoolProperty(element[0], value=property_value)\r
-                    pg.Append(property_control)\r
-                    pg.SetPropertyAttribute(element[0], 'UseCheckbox', True)\r
-                if element[1]['type'] == 'string':\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1]['value']\r
-                    else:\r
-                        property_value = element[1]['default']\r
-                    pg.Append(wxpg.StringProperty(element[0], value=property_value))\r
-                if element[1]['type'] == 'folder':\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1]['value']\r
-                    else:\r
-                        property_value = element[1]['default']\r
-                    pg.Append(wxpg.DirProperty(element[0], value=property_value))\r
-                if element[1]['type'] == 'filename':\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1]['value']\r
-                    else:\r
-                        property_value = element[1]['default']\r
-                    pg.Append(wxpg.FileProperty(element[0], value=property_value))\r
-                #if element[0] == 'category':\r
-                    #pg.Append(wxpg.PropertyCategory(element[1]))\r
-                #if element[0] == 'folder':\r
-                    #pg.Append(wxpg.DirProperty(element[1], value=element[2]))\r
-                #if element[0] == 'string':\r
-                    #pg.Append(wxpg.StringProperty(element[1], value=element[2]))\r
-\r
-        pg.Refresh()\r
-\r
-    def OnReserved(self, event):\r
-        pass\r
-\r
-\r
-#---------------------------------------------------------------------------\r
-\r
-\r
-class MemoDialog(wx.Dialog):\r
-    """\\r
-    Dialog for multi-line text editing.\r
-    """\r
-    def __init__(self, parent=None, title='', text='', pos=None, size=(500,500)):\r
-        wx.Dialog.__init__(self, parent, -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)\r
-\r
-        sizer = wx.BoxSizer(wx.VERTICAL)\r
-\r
-        tc = wx.TextCtrl(self, 11, text, style=wx.TE_MULTILINE)\r
-        self.tc = tc\r
-        topsizer.Add(tc,1,wx.EXPAND|wx.ALL,8)\r
-\r
-        rowsizer = wx.BoxSizer( wx.HORIZONTAL )\r
-        rowsizer.Add(wx.Button(self,wx.ID_OK,'Ok'),0,wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL,8)\r
-        rowsizer.Add((0,0),1,wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL,8)\r
-        rowsizer.Add(wx.Button(self,wx.ID_CANCEL,'Cancel'),0,wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL,8)\r
-        topsizer.Add(rowsizer,0,wx.EXPAND|wx.ALL,8)\r
-\r
-        self.SetSizer( topsizer )\r
-        topsizer.Layout()\r
-\r
-        self.SetSize( size )\r
-        if not pos:\r
-            self.CenterOnScreen()\r
-        else:\r
-            self.Move(pos)\r
-\r
-\r
-#---------------------------------------------------------------------------\r
+import sys
+import time
+import math
+import os.path
+import pdb
+
+import wx
+import wx.propgrid as wxpg
+import wx.stc
+
+
+class Display:
+    property_descriptor = []
+    def __init__(self):
+        pass
+
+
+class ValueObject:
+    def __init__(self):
+        pass
+
+
+class IntProperty2(wxpg.PyProperty):
+    """\
+    This is a simple re-implementation of wxIntProperty.
+    """
+    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=0):
+        wxpg.PyProperty.__init__(self, label, name)
+        self.SetValue(value)
+
+    def GetClassName(self):
+        """\
+        This is not 100% necessary and in future is probably going to be
+        automated to return class name.
+        """
+        return "IntProperty2"
+
+    def GetEditor(self):
+        return "TextCtrl"
+
+    def GetValueAsString(self, flags):
+        return str(self.GetValue())
+
+    def PyStringToValue(self, s, flags):
+        try:
+            v = int(s)
+            if self.GetValue() != v:
+                return v
+        except TypeError:
+            if flags & wxpg.PG_REPORT_ERROR:
+                wx.MessageBox("Cannot convert '%s' into a number."%s, "Error")
+        return False
+
+    def PyIntToValue(self, v, flags):
+        if (self.GetValue() != v):
+            return v
+
+
+class PyFilesProperty(wxpg.PyArrayStringProperty):
+    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=[]):
+        wxpg.PyArrayStringProperty.__init__(self, label, name, value)
+        self.SetValue(value)
+
+    def OnSetValue(self, v):
+        self.value = v
+        self.display = ', '.join(self.value)
+
+    def GetValueAsString(self, argFlags):
+        return self.display
+
+    def PyStringToValue(self, s, flags):
+        return [a.strip() for a in text.split(',')]
+
+    def OnEvent(self, propgrid, ctrl, event):
+        if event.GetEventType() == wx.wxEVT_COMMAND_BUTTON_CLICKED:
+            # Show dialog to select a string, call DoSetValue and
+            # return True, if value changed.
+            return True
+
+        return False
+
+
+class PyObjectPropertyValue:
+    """\
+    Value type of our sample PyObjectProperty. We keep a simple dash-delimited
+    list of string given as argument to constructor.
+    """
+    def __init__(self, s=None):
+        try:
+            self.ls = [a.strip() for a in s.split('-')]
+        except:
+            self.ls = []
+
+    def __repr__(self):
+        return ' - '.join(self.ls)
+
+
+
+class PyObjectProperty(wxpg.PyProperty):
+    """\
+    Another simple example. This time our value is a PyObject (NOTE: we can't
+    return an arbitrary python object in DoGetValue. It cannot be a simple
+    type such as int, bool, double, or string, nor an array or wxObject based.
+    Dictionary, None, or any user-specified Python object is allowed).
+    """
+    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=None):
+        wxpg.PyProperty.__init__(self, label, name)
+        self.SetValue(value)
+
+    def GetClassName(self):
+        return self.__class__.__name__
+
+    def GetEditor(self):
+        return "TextCtrl"
+
+    def GetValueAsString(self, flags):
+        return repr(self.GetValue())
+
+    def PyStringToValue(self, s, flags):
+        return PyObjectPropertyValue(s)
+
+
+class ShapeProperty(wxpg.PyEnumProperty):
+    """\
+    Demonstrates use of OnCustomPaint method.
+    """
+    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=-1):
+        wxpg.PyEnumProperty.__init__(self, label, name, ['Line','Circle','Rectangle'], [0,1,2], value)
+
+    def OnMeasureImage(self, index):
+        return wxpg.DEFAULT_IMAGE_SIZE
+
+    def OnCustomPaint(self, dc, rect, paint_data):
+        """\
+        paint_data.m_choiceItem is -1 if we are painting the control,
+        in which case we need to get the drawn item using DoGetValue.
+        """
+        item = paint_data.m_choiceItem
+        if item == -1:
+            item = self.DoGetValue()
+
+        dc.SetPen(wx.Pen(wx.BLACK))
+        dc.SetBrush(wx.Brush(wx.BLACK))
+
+        if item == 0:
+            dc.DrawLine(rect.x,rect.y,rect.x+rect.width,rect.y+rect.height)
+        elif item == 1:
+            half_width = rect.width / 2
+            dc.DrawCircle(rect.x+half_width,rect.y+half_width,half_width-3)
+        elif item == 2:
+            dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
+
+
+class LargeImagePickerCtrl(wx.Window):
+    """\
+    Control created and used by LargeImageEditor.
+    """
+    def __init__(self):
+        pre = wx.PreWindow()
+        self.PostCreate(pre)
+
+    def Create(self, parent, id_, pos, size, style = 0):
+        wx.Window.Create(self, parent, id_, pos, size, style | wx.BORDER_SIMPLE)
+        img_spc = size[1]
+        self.tc = wx.TextCtrl(self, -1, "", (img_spc,0), (2048,size[1]), wx.BORDER_NONE)
+        self.SetBackgroundColour(wx.WHITE)
+        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
+        self.property = None
+        self.bmp = None
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+
+    def OnPaint(self, event):
+        dc = wx.BufferedPaintDC(self)
+
+        whiteBrush = wx.Brush(wx.WHITE)
+        dc.SetBackground(whiteBrush)
+        dc.Clear()
+
+        bmp = self.bmp
+        if bmp:
+            dc.DrawBitmap(bmp, 2, 2)
+        else:
+            dc.SetPen(wx.Pen(wx.BLACK))
+            dc.SetBrush(whiteBrush)
+            dc.DrawRectangle(2, 2, 64, 64)
+
+    def RefreshThumbnail(self):
+        """\
+        We use here very simple image scaling code.
+        """
+        if not self.property:
+            self.bmp = None
+            return
+
+        path = self.property.DoGetValue()
+
+        if not os.path.isfile(path):
+            self.bmp = None
+            return
+
+        image = wx.Image(path)
+        image.Rescale(64, 64)
+        self.bmp = wx.BitmapFromImage(image)
+
+    def SetProperty(self, property):
+        self.property = property
+        self.tc.SetValue(property.GetDisplayedString())
+        self.RefreshThumbnail()
+
+    def SetValue(self, s):
+        self.RefreshThumbnail()
+        self.tc.SetValue(s)
+
+    def GetLastPosition(self):
+        return self.tc.GetLastPosition()
+
+
+class LargeImageEditor(wxpg.PyEditor):
+    """\
+    Double-height text-editor with image in front.
+    """
+    def __init__(self):
+        wxpg.PyEditor.__init__(self)
+
+    def CreateControls(self, propgrid, property, pos, sz):
+        try:
+            h = 64 + 6
+            x = propgrid.GetSplitterPosition()
+            x2 = propgrid.GetClientSize().x
+            bw = propgrid.GetRowHeight()
+            lipc = LargeImagePickerCtrl()
+            if sys.platform == 'win32':
+                lipc.Hide()
+            lipc.Create(propgrid, wxpg.PG_SUBID1, (x,pos[1]), (x2-x-bw,h))
+            lipc.SetProperty(property)
+            # Hmmm.. how to have two-stage creation without subclassing?
+            #btn = wx.PreButton()
+            #pre = wx.PreWindow()
+            #self.PostCreate(pre)
+            #if sys.platform == 'win32':
+            #    btn.Hide()
+            #btn.Create(propgrid, wxpg.PG_SUBID2, '...', (x2-bw,pos[1]), (bw,h), wx.WANTS_CHARS)
+            btn = wx.Button(propgrid, wxpg.PG_SUBID2, '...', (x2-bw,pos[1]), (bw,h), wx.WANTS_CHARS)
+            return (lipc, btn)
+        except:
+            import traceback
+            print traceback.print_exc()
+
+    def UpdateControl(self, property, ctrl):
+        ctrl.SetValue(property.GetDisplayedString())
+
+    def DrawValue(self, dc, property, rect):
+        if not (property.GetFlags() & wxpg.PG_PROP_UNSPECIFIED):
+            dc.DrawText( property.GetDisplayedString(), rect.x+5, rect.y );
+
+    def OnEvent(self, propgrid, ctrl, event):
+        if not ctrl:
+            return False
+
+        evtType = event.GetEventType()
+
+        if evtType == wx.wxEVT_COMMAND_TEXT_ENTER:
+            if propgrid.IsEditorsValueModified():
+                return True
+
+        elif evtType == wx.wxEVT_COMMAND_TEXT_UPDATED:
+            if not property.HasFlag(wxpg.PG_PROP_UNSPECIFIED) or not ctrl or \
+               ctrl.GetLastPosition() > 0:
+
+                # We must check this since an 'empty' text event
+                # may be triggered when creating the property.
+                PG_FL_IN_SELECT_PROPERTY = 0x00100000
+                if not (propgrid.GetInternalFlags() & PG_FL_IN_SELECT_PROPERTY):
+                    event.Skip();
+                    event.SetId(propGrid.GetId());
+
+                propgrid.EditorsValueWasModified();
+
+        return False
+
+
+    def CopyValueFromControl(self, property, ctrl):
+        tc = ctrl.tc
+        res = property.SetValueFromString(tc.GetValue(),0)
+        # Changing unspecified always causes event (returning
+        # true here should be enough to trigger it).
+        if not res and property.IsFlagSet(wxpg.PG_PROP_UNSPECIFIED):
+            res = true
+
+        return res
+
+    def SetValueToUnspecified(self, ctrl):
+        ctrl.tc.Remove(0,len(ctrl.tc.GetValue()));
+
+    def SetControlStringValue(self, ctrl, txt):
+        ctrl.SetValue(txt)
+
+    def OnFocus(self, property, ctrl):
+        ctrl.tc.SetSelection(-1,-1)
+        ctrl.tc.SetFocus()
+
+
+class PropertyEditor(wx.Panel):
+
+    def __init__(self, parent):
+        # Use the WANTS_CHARS style so the panel doesn't eat the Return key.
+        wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS, size=(160, 200))
+
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        self.pg = wxpg.PropertyGrid(self, style=wxpg.PG_SPLITTER_AUTO_CENTER|wxpg.PG_AUTO_SORT)
+
+        # Show help as tooltips
+        self.pg.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS)
+
+        #pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChange)
+        #pg.Bind(wxpg.EVT_PG_SELECTED, self.OnPropGridSelect)
+        #self.pg.Bind(wxpg.EVT_PG_RIGHT_CLICK, self.OnPropGridRightClick)
+
+        # Needed by custom image editor
+        wx.InitAllImageHandlers()
+
+        #
+        # Let's create a simple custom editor
+        #
+        # NOTE: Editor must be registered *before* adding a property that uses it.
+        self.pg.RegisterEditor(LargeImageEditor)
+
+        '''
+        #
+        # Add properties
+        #
+
+        pg.Append( wxpg.PropertyCategory("1 - Basic Properties") )
+        pg.Append( wxpg.StringProperty("String",value="Some Text") )
+        pg.Append( wxpg.IntProperty("Int",value=100) )
+        pg.Append( wxpg.FloatProperty("Float",value=100.0) )
+        pg.Append( wxpg.BoolProperty("Bool",value=True) )
+        pg.Append( wxpg.BoolProperty("Bool_with_Checkbox",value=True) )
+        pg.SetPropertyAttribute("Bool_with_Checkbox", "UseCheckbox", True)
+
+        pg.Append( wxpg.PropertyCategory("2 - More Properties") )
+        pg.Append( wxpg.LongStringProperty("LongString",value="This is a\\nmulti-line string\\nwith\\ttabs\\nmixed\\tin.") )
+        pg.Append( wxpg.DirProperty("Dir",value="C:\\Windows") )
+        pg.Append( wxpg.FileProperty("File",value="C:\\Windows\\system.ini") )
+        pg.Append( wxpg.ArrayStringProperty("ArrayString",value=['A','B','C']) )
+
+        pg.Append( wxpg.EnumProperty("Enum","Enum",
+                                     ['wxPython Rules','wxPython Rocks','wxPython Is The Best'],
+                                     [10,11,12],0) )
+        pg.Append( wxpg.EditEnumProperty("EditEnum","EditEnumProperty",['A','B','C'],[0,1,2],"Text Not in List") )
+
+        pg.Append( wxpg.PropertyCategory("3 - Advanced Properties") )
+        pg.Append( wxpg.DateProperty("Date",value=wx.DateTime_Now()) )
+        pg.Append( wxpg.FontProperty("Font",value=self.GetFont()) )
+        pg.Append( wxpg.ColourProperty("Colour",value=self.GetBackgroundColour()) )
+        pg.Append( wxpg.SystemColourProperty("SystemColour") )
+        pg.Append( wxpg.ImageFileProperty("ImageFile") )
+        pg.Append( wxpg.MultiChoiceProperty("MultiChoice",choices=['wxWidgets','QT','GTK+']) )
+
+        pg.Append( wxpg.PropertyCategory("4 - Additional Properties") )
+        pg.Append( wxpg.PointProperty("Point",value=self.GetPosition()) )
+        pg.Append( wxpg.SizeProperty("Size",value=self.GetSize()) )
+        pg.Append( wxpg.FontDataProperty("FontData") )
+        pg.Append( wxpg.IntProperty("IntWithSpin",value=256) )
+        pg.SetPropertyEditor("IntWithSpin","SpinCtrl")
+        pg.Append( wxpg.DirsProperty("Dirs",value=['C:/Lib','C:/Bin']) )
+        pg.SetPropertyHelpString( "String", "String Property help string!" )
+        pg.SetPropertyHelpString( "Dirs", "Dirs Property help string!" )
+
+        pg.SetPropertyAttribute( "File", wxpg.PG_FILE_SHOW_FULL_PATH, 0 )
+        pg.SetPropertyAttribute( "File", wxpg.PG_FILE_INITIAL_PATH, "C:\\Program Files\\Internet Explorer" )
+        pg.SetPropertyAttribute( "Date", wxpg.PG_DATE_PICKER_STYLE, wx.DP_DROPDOWN|wx.DP_SHOWCENTURY )
+
+        pg.Append( wxpg.PropertyCategory("5 - Custom Properties") )
+        pg.Append( IntProperty2("IntProperty2", value=1024) )
+
+        pg.Append( ShapeProperty("ShapeProperty", value=0) )
+        pg.Append( PyObjectProperty("PyObjectProperty") )
+
+        pg.Append( wxpg.ImageFileProperty("ImageFileWithLargeEditor") )
+        pg.SetPropertyEditor("ImageFileWithLargeEditor", "LargeImageEditor")
+
+
+        pg.SetPropertyClientData( "Point", 1234 )
+        if pg.GetPropertyClientData( "Point" ) != 1234:
+            raise ValueError("Set/GetPropertyClientData() failed")
+
+        # Test setting unicode string
+        pg.GetPropertyByName("String").SetValue(u"Some Unicode Text")
+
+        #
+        # Test some code that *should* fail (but not crash)
+        #try:
+            #a_ = pg.GetPropertyValue( "NotARealProperty" )
+            #pg.EnableProperty( "NotAtAllRealProperty", False )
+            #pg.SetPropertyHelpString( "AgaintNotARealProperty", "Dummy Help String" )
+        #except:
+            #pass
+            #raise
+
+        '''
+        sizer.Add(self.pg, 1, wx.EXPAND)
+        self.SetSizer(sizer)
+        sizer.SetSizeHints(self)
+
+        self.SelectedTreeItem = None
+
+    def GetPropertyValues(self):
+        #return self.pg.GetPropertyValues(as_strings=True)
+        return self.pg.GetPropertyValues()
+
+
+    def Initialize(self, properties):
+        pg = self.pg
+        pg.Clear()
+
+        if properties:
+            for element in properties:
+                if element[1]['type'] == 'integer':
+                    if 'value' in element[1]:
+                        property_value = element[1].as_int('value')
+                    else:
+                        property_value = element[1].as_int('default')
+                    property_control = wxpg.IntProperty(element[0], value=property_value)
+                    if 'maximum' in element[1]:
+                        property_control.SetAttribute('Max', element[1].as_int('maximum'))
+                    if 'minimum' in element[1]:
+                        property_control.SetAttribute('Min', element[1].as_int('minimum'))
+                    property_control.SetAttribute('Wrap', True)
+                    pg.Append(property_control)
+                    pg.SetPropertyEditor(element[0], 'SpinCtrl')
+
+                if element[1]['type'] == 'float':
+                    if 'value' in element[1]:
+                        property_value = element[1].as_float('value')
+                    else:
+                        property_value = element[1].as_float('default')
+                    property_control = wxpg.FloatProperty(element[0], value=property_value)
+                    if 'maximum' in element[1]:
+                        property_control.SetAttribute('Max', element[1].as_float('maximum'))
+                    if 'minimum' in element[1]:
+                        property_control.SetAttribute('Min', element[1].as_float('minimum'))
+                    property_control.SetAttribute('Wrap', True)
+                    pg.Append(property_control)
+                    pg.SetPropertyEditor(element[0], 'SpinCtrl')
+
+                if element[1]['type'] == 'boolean':
+                    if 'value' in element[1]:
+                        property_value = element[1].as_bool('value')
+                    else:
+                        property_value = element[1].as_bool('default')
+                    property_control = wxpg.BoolProperty(element[0], value=property_value)
+                    pg.Append(property_control)
+                    pg.SetPropertyAttribute(element[0], 'UseCheckbox', True)
+                if element[1]['type'] == 'string':
+                    if 'value' in element[1]:
+                        property_value = element[1]['value']
+                    else:
+                        property_value = element[1]['default']
+                    pg.Append(wxpg.StringProperty(element[0], value=property_value))
+                if element[1]['type'] == 'folder':
+                    if 'value' in element[1]:
+                        property_value = element[1]['value']
+                    else:
+                        property_value = element[1]['default']
+                    pg.Append(wxpg.DirProperty(element[0], value=property_value))
+                if element[1]['type'] == 'filename':
+                    if 'value' in element[1]:
+                        property_value = element[1]['value']
+                    else:
+                        property_value = element[1]['default']
+                    pg.Append(wxpg.FileProperty(element[0], value=property_value))
+                #if element[0] == 'category':
+                    #pg.Append(wxpg.PropertyCategory(element[1]))
+                #if element[0] == 'folder':
+                    #pg.Append(wxpg.DirProperty(element[1], value=element[2]))
+                #if element[0] == 'string':
+                    #pg.Append(wxpg.StringProperty(element[1], value=element[2]))
+
+        pg.Refresh()
+
+    def OnReserved(self, event):
+        pass
+
+
+#---------------------------------------------------------------------------
+
+
+class MemoDialog(wx.Dialog):
+    """\
+    Dialog for multi-line text editing.
+    """
+    def __init__(self, parent=None, title='', text='', pos=None, size=(500,500)):
+        wx.Dialog.__init__(self, parent, -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
+
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        tc = wx.TextCtrl(self, 11, text, style=wx.TE_MULTILINE)
+        self.tc = tc
+        topsizer.Add(tc,1,wx.EXPAND|wx.ALL,8)
+
+        rowsizer = wx.BoxSizer( wx.HORIZONTAL )
+        rowsizer.Add(wx.Button(self,wx.ID_OK,'Ok'),0,wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL,8)
+        rowsizer.Add((0,0),1,wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL,8)
+        rowsizer.Add(wx.Button(self,wx.ID_CANCEL,'Cancel'),0,wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL,8)
+        topsizer.Add(rowsizer,0,wx.EXPAND|wx.ALL,8)
+
+        self.SetSizer( topsizer )
+        topsizer.Layout()
+
+        self.SetSize( size )
+        if not pos:
+            self.CenterOnScreen()
+        else:
+            self.Move(pos)
+
+
+#---------------------------------------------------------------------------
index b3f10c9..ecf71f0 100644 (file)
@@ -1,78 +1,78 @@
-#!/usr/bin/env python\r
-\r
-import sys\r
-import wx\r
-from wx.lib.mixins.listctrl import CheckListCtrlMixin\r
-\r
-import prettyformat\r
-\r
-class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin):\r
-    def __init__(self, parent):\r
-        wx.ListCtrl.__init__(self, parent, -1, style=wx.LC_REPORT)\r
-        CheckListCtrlMixin.__init__(self)\r
-        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)\r
-\r
-    def OnItemActivated(self, evt):\r
-        self.ToggleItem(evt.m_itemIndex)\r
-\r
-\r
-class Results(wx.Panel):\r
-    def __init__(self, parent):\r
-        wx.Panel.__init__(self, parent, -1)\r
-        self.results_list = CheckListCtrl(self)\r
-        sizer = wx.BoxSizer()\r
-        sizer.Add(self.results_list, 1, wx.EXPAND)\r
-        self.SetSizer(sizer)\r
-        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.results_list)\r
-        self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected, self.results_list)\r
-        #self.results_list.OnCheckItem = self.OnCheckItem\r
-\r
-    def _GetWidthInPixels(self, text):\r
-        #TODO:\r
-        #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.\r
-        font = self.results_list.GetFont()\r
-        dc = wx.WindowDC(self.results_list)\r
-        dc.SetFont(font)\r
-        width, height = dc.GetTextExtent(text)\r
-        return width\r
-\r
-    def ClearResults(self):\r
-        self.results_list.ClearAll()\r
-\r
-    def DisplayResults(self, results):\r
-        self.ClearResults()\r
-        header = results.header_as_list()\r
-        self.results_list.InsertColumn(0, 'Show')\r
-        for index, column in enumerate(header):\r
-            self.results_list.InsertColumn(index + 1, column, wx.LIST_FORMAT_RIGHT)\r
-\r
-        for result in results.results:\r
-            done = False\r
-            for index, column in enumerate(results.columns):\r
-                value_str = results.get_pretty_value(column, result.result[column])\r
-                if not done:\r
-                    index_col = self.results_list.InsertStringItem(sys.maxint, '')\r
-                    done = True\r
-                column_width = len(self.results_list.GetColumn(index + 1).GetText())\r
-                value_str = value_str.center(column_width)\r
-                self.results_list.SetStringItem(index_col, index + 1, value_str)\r
-                #self.results_list.SetItemData(index, result)\r
-\r
-        for index, result in enumerate(results.results):\r
-            if result.visible:\r
-                #if we use 'CheckItem' then 'UpdatePlot' is called (ie repeated updates)\r
-                self.results_list.SetItemImage(index, 1)\r
-                #self.results_list.CheckItem(index)\r
-        for index in range(self.results_list.GetColumnCount()):\r
-            column_text = self.results_list.GetColumn(index).GetText()\r
-            column_width = self._GetWidthInPixels(column_text)\r
-            self.results_list.SetColumnWidth(index, column_width + 15)\r
-\r
-    def OnItemSelected(self, evt):\r
-        pass\r
-\r
-    def OnItemDeselected(self, evt):\r
-        pass\r
+#!/usr/bin/env python
+
+import sys
+import wx
+from wx.lib.mixins.listctrl import CheckListCtrlMixin
+
+import prettyformat
+
+class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin):
+    def __init__(self, parent):
+        wx.ListCtrl.__init__(self, parent, -1, style=wx.LC_REPORT)
+        CheckListCtrlMixin.__init__(self)
+        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
+
+    def OnItemActivated(self, evt):
+        self.ToggleItem(evt.m_itemIndex)
+
+
+class Results(wx.Panel):
+    def __init__(self, parent):
+        wx.Panel.__init__(self, parent, -1)
+        self.results_list = CheckListCtrl(self)
+        sizer = wx.BoxSizer()
+        sizer.Add(self.results_list, 1, wx.EXPAND)
+        self.SetSizer(sizer)
+        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.results_list)
+        self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected, self.results_list)
+        #self.results_list.OnCheckItem = self.OnCheckItem
+
+    def _GetWidthInPixels(self, text):
+        #TODO:
+        #Returns the width of a string in pixels
+        #Unfortunately, it does not work terribly well (although it should).
+        #Thus, we have to add a bit afterwards.
+        #Annoys the heck out of me.
+        font = self.results_list.GetFont()
+        dc = wx.WindowDC(self.results_list)
+        dc.SetFont(font)
+        width, height = dc.GetTextExtent(text)
+        return width
+
+    def ClearResults(self):
+        self.results_list.ClearAll()
+
+    def DisplayResults(self, results):
+        self.ClearResults()
+        header = results.header_as_list()
+        self.results_list.InsertColumn(0, 'Show')
+        for index, column in enumerate(header):
+            self.results_list.InsertColumn(index + 1, column, wx.LIST_FORMAT_RIGHT)
+
+        for result in results.results:
+            done = False
+            for index, column in enumerate(results.columns):
+                value_str = results.get_pretty_value(column, result.result[column])
+                if not done:
+                    index_col = self.results_list.InsertStringItem(sys.maxint, '')
+                    done = True
+                column_width = len(self.results_list.GetColumn(index + 1).GetText())
+                value_str = value_str.center(column_width)
+                self.results_list.SetStringItem(index_col, index + 1, value_str)
+                #self.results_list.SetItemData(index, result)
+
+        for index, result in enumerate(results.results):
+            if result.visible:
+                #if we use 'CheckItem' then 'UpdatePlot' is called (ie repeated updates)
+                self.results_list.SetItemImage(index, 1)
+                #self.results_list.CheckItem(index)
+        for index in range(self.results_list.GetColumnCount()):
+            column_text = self.results_list.GetColumn(index).GetText()
+            column_width = self._GetWidthInPixels(column_text)
+            self.results_list.SetColumnWidth(index, column_width + 15)
+
+    def OnItemSelected(self, evt):
+        pass
+
+    def OnItemDeselected(self, evt):
+        pass
index 6454ad9..32ee405 100644 (file)
-'''\r
-//\r
-// prettyformat.py - simple Python function to format values with nice prefixes\r
-// Version 1.0.1\r
-//\r
-// History\r
-// 2009 07 16: added negative number support\r
-//             added decimal-formatted output\r
-//\r
-// Copyright (c) 2009 Rolf Schmidt, Montreal\r
-// rschmidt@alcor.concordia.ca\r
-//\r
-// This procedure is released under the GNU General Public License version 2\r
-//\r
-'''\r
-\r
-import math\r
-from numpy import isnan\r
-\r
-def pretty_format(fValue, sUnit='', iDecimals=-1, iMultiplier=1, bLeadingSpaces=False):\r
-    if fValue != 0:\r
-        iLeadingSpaces = 0\r
-        if bLeadingSpaces:\r
-            iLeadingSpaces = 5\r
-        if iMultiplier == 1:\r
-            iMultiplier=get_multiplier(fValue)\r
-        sUnitString = ''\r
-        if sUnit != '':\r
-            sUnitString = ' ' + get_prefix(iMultiplier) + sUnit\r
-        if iDecimals >= 0:\r
-            formatString = '% ' + repr(iLeadingSpaces + iDecimals) + '.' + repr(iDecimals) + 'f'\r
-            return formatString % (fValue / iMultiplier) + sUnitString\r
-        else:\r
-            return str(fValue / iMultiplier) + sUnitString\r
-    else:\r
-        return '0'\r
-    return str(fValue / iMultiplier) + ' ' + get_prefix(fValue / iMultiplier) + sUnit\r
-\r
-def get_multiplier(fValue):\r
-    return pow(10, get_power(fValue))\r
-\r
-def get_power(fValue):\r
-    if fValue != 0 and not isnan(fValue):\r
-        #get the log10 from fValue (make sure the value is not negative)\r
-        dHelp = math.floor(math.log10(math.fabs(fValue)))\r
-        #reduce the log10 to a multiple of 3 and return it\r
-        return dHelp-(dHelp % 3)\r
-    else:\r
-        return 0\r
-\r
-def get_prefix(fValue):\r
-    #set up a dictionary to find the prefix\r
-    prefix = {\r
-        24: lambda: 'Y',\r
-        21: lambda: 'Z',\r
-        18: lambda: 'E',\r
-        15: lambda: 'P',\r
-        12: lambda: 'T',\r
-        9: lambda: 'G',\r
-        6: lambda: 'M',\r
-        3: lambda: 'k',\r
-        0: lambda: '',\r
-        -3: lambda: 'm',\r
-        -6: lambda: u'\u00B5',\r
-        -9: lambda: 'n',\r
-        -12: lambda: 'p',\r
-        -15: lambda: 'f',\r
-        -18: lambda: 'a',\r
-        -21: lambda: 'z',\r
-        -24: lambda: 'y',\r
-    }\r
-    if fValue != 0 and not isnan(fValue):\r
-        #get the log10 from fValue\r
-        dHelp = math.floor(math.log10(math.fabs(fValue)))\r
-    else:\r
-        dHelp = 0\r
-    #reduce the log10 to a multiple of 3 and create the return string\r
-    return prefix.get(dHelp - (dHelp % 3))()\r
-\r
-'''\r
-dTestValue=-2.4115665714484597e-008\r
-print 'Value: '+str(dTestValue)+')'\r
-print 'pretty_format example (value, unit)'\r
-print pretty_format(dTestValue, 'N')\r
-print'-----------------------'\r
-print 'pretty_format example (value, unit, decimals)'\r
-print pretty_format(dTestValue, 'N', 3)\r
-print'-----------------------'\r
-print 'pretty_format example (value, unit, decimals, multiplier)'\r
-print pretty_format(dTestValue, 'N', 5, 0.000001)\r
-print'-----------------------'\r
-print 'pretty_format example (value, unit, decimals, multiplier, leading spaces)'\r
-print pretty_format(0.0166276297705, 'N', 3, 0.001, True)\r
-print pretty_format(0.00750520813323, 'N', 3, 0.001, True)\r
-print pretty_format(0.0136453282825, 'N', 3, 0.001, True)\r
-'''\r
-'''\r
-#example use autoFormatValue\r
-dTestValue=0.00000000567\r
-print 'autoFormatValue example ('+str(dTestValue)+')'\r
-print autoFormatValue(dTestValue, 'N')\r
-#outputs 5.67 nN\r
-'''\r
-'''\r
-#example use of decimalFormatValue(fValue, iDecimals, sUnit):\r
-dTestValue=-2.4115665714484597e-008\r
-iDecimals=3\r
-print 'decimalFormatValue example ('+str(dTestValue)+')'\r
-print decimalFormatValue(dTestValue, iDecimals, 'N')\r
-#outputs -24.116 nN\r
-#change iDecimals to see the effect\r
-'''\r
-'''\r
-#example use formatValue\r
-dTestValue=0.000000000567\r
-print 'formatValue example ('+str(dTestValue)+')'\r
-#find the (common) multiplier\r
-iMultiplier=get_multiplier(dTestValue)\r
-#use the multiplier and a unit to format the value\r
-print formatValue(dTestValue, iMultiplier, 'N')\r
-#outputs 567.0 pN\r
-'''\r
-'''\r
-#to output a scale:\r
-#choose any value on the axis and find the multiplier and prefix for it\r
-#use those to format the rest of the scale\r
-#as values can span several orders of magnitude, you have to decide what units to use\r
-\r
-#tuple of values:\r
-scaleValues=0.000000000985, 0.000000001000, 0.000000001015\r
-#use this element (change to 1 or 2 to see the effect on the scale and label)\r
-iIndex=0\r
-#get the multiplier from the value at iIndex\r
-iMultiplier=get_multiplier(scaleValues[iIndex])\r
-print '\nScale example'\r
-iDecimals=3\r
-#print the scale\r
-for aValue in scaleValues: print decimalFormat(aValue/iMultiplier, iDecimals),\r
-#print the scale label using the value at iIndex\r
-print '\n'+get_prefix(scaleValues[iIndex])+'N'\r
+'''
+//
+// prettyformat.py - simple Python function to format values with nice prefixes
+// Version 1.0.1
+//
+// History
+// 2009 07 16: added negative number support
+//             added decimal-formatted output
+//
+// Copyright (c) 2009 Rolf Schmidt, Montreal
+// rschmidt@alcor.concordia.ca
+//
+// This procedure is released under the GNU General Public License version 2
+//
+'''
+
+import math
+from numpy import isnan
+
+def pretty_format(fValue, sUnit='', iDecimals=-1, iMultiplier=1, bLeadingSpaces=False):
+    if fValue != 0:
+        iLeadingSpaces = 0
+        if bLeadingSpaces:
+            iLeadingSpaces = 5
+        if iMultiplier == 1:
+            iMultiplier=get_multiplier(fValue)
+        sUnitString = ''
+        if sUnit != '':
+            sUnitString = ' ' + get_prefix(iMultiplier) + sUnit
+        if iDecimals >= 0:
+            formatString = '% ' + repr(iLeadingSpaces + iDecimals) + '.' + repr(iDecimals) + 'f'
+            return formatString % (fValue / iMultiplier) + sUnitString
+        else:
+            return str(fValue / iMultiplier) + sUnitString
+    else:
+        return '0'
+    return str(fValue / iMultiplier) + ' ' + get_prefix(fValue / iMultiplier) + sUnit
+
+def get_multiplier(fValue):
+    return pow(10, get_power(fValue))
+
+def get_power(fValue):
+    if fValue != 0 and not isnan(fValue):
+        #get the log10 from fValue (make sure the value is not negative)
+        dHelp = math.floor(math.log10(math.fabs(fValue)))
+        #reduce the log10 to a multiple of 3 and return it
+        return dHelp-(dHelp % 3)
+    else:
+        return 0
+
+def get_prefix(fValue):
+    #set up a dictionary to find the prefix
+    prefix = {
+        24: lambda: 'Y',
+        21: lambda: 'Z',
+        18: lambda: 'E',
+        15: lambda: 'P',
+        12: lambda: 'T',
+        9: lambda: 'G',
+        6: lambda: 'M',
+        3: lambda: 'k',
+        0: lambda: '',
+        -3: lambda: 'm',
+        -6: lambda: u'\u00B5',
+        -9: lambda: 'n',
+        -12: lambda: 'p',
+        -15: lambda: 'f',
+        -18: lambda: 'a',
+        -21: lambda: 'z',
+        -24: lambda: 'y',
+    }
+    if fValue != 0 and not isnan(fValue):
+        #get the log10 from fValue
+        dHelp = math.floor(math.log10(math.fabs(fValue)))
+    else:
+        dHelp = 0
+    #reduce the log10 to a multiple of 3 and create the return string
+    return prefix.get(dHelp - (dHelp % 3))()
+
+'''
+dTestValue=-2.4115665714484597e-008
+print 'Value: '+str(dTestValue)+')'
+print 'pretty_format example (value, unit)'
+print pretty_format(dTestValue, 'N')
+print'-----------------------'
+print 'pretty_format example (value, unit, decimals)'
+print pretty_format(dTestValue, 'N', 3)
+print'-----------------------'
+print 'pretty_format example (value, unit, decimals, multiplier)'
+print pretty_format(dTestValue, 'N', 5, 0.000001)
+print'-----------------------'
+print 'pretty_format example (value, unit, decimals, multiplier, leading spaces)'
+print pretty_format(0.0166276297705, 'N', 3, 0.001, True)
+print pretty_format(0.00750520813323, 'N', 3, 0.001, True)
+print pretty_format(0.0136453282825, 'N', 3, 0.001, True)
+'''
+'''
+#example use autoFormatValue
+dTestValue=0.00000000567
+print 'autoFormatValue example ('+str(dTestValue)+')'
+print autoFormatValue(dTestValue, 'N')
+#outputs 5.67 nN
+'''
+'''
+#example use of decimalFormatValue(fValue, iDecimals, sUnit):
+dTestValue=-2.4115665714484597e-008
+iDecimals=3
+print 'decimalFormatValue example ('+str(dTestValue)+')'
+print decimalFormatValue(dTestValue, iDecimals, 'N')
+#outputs -24.116 nN
+#change iDecimals to see the effect
+'''
+'''
+#example use formatValue
+dTestValue=0.000000000567
+print 'formatValue example ('+str(dTestValue)+')'
+#find the (common) multiplier
+iMultiplier=get_multiplier(dTestValue)
+#use the multiplier and a unit to format the value
+print formatValue(dTestValue, iMultiplier, 'N')
+#outputs 567.0 pN
+'''
+'''
+#to output a scale:
+#choose any value on the axis and find the multiplier and prefix for it
+#use those to format the rest of the scale
+#as values can span several orders of magnitude, you have to decide what units to use
+
+#tuple of values:
+scaleValues=0.000000000985, 0.000000001000, 0.000000001015
+#use this element (change to 1 or 2 to see the effect on the scale and label)
+iIndex=0
+#get the multiplier from the value at iIndex
+iMultiplier=get_multiplier(scaleValues[iIndex])
+print '\nScale example'
+iDecimals=3
+#print the scale
+for aValue in scaleValues: print decimalFormat(aValue/iMultiplier, iDecimals),
+#print the scale label using the value at iIndex
+print '\n'+get_prefix(scaleValues[iIndex])+'N'
 '''
\ No newline at end of file
index 48c549c..c79b3be 100644 (file)
-import prettyformat\r
-\r
-DEFAULT_COLOR = 'orange'\r
-DEFAULT_DECIMAL = 2\r
-DEFAULT_STYLE = 'plot'\r
-\r
-class Result:\r
-    def __init__(self):\r
-        self.color = DEFAULT_COLOR\r
-        self.result = {}\r
-        self.style = DEFAULT_STYLE\r
-        self.visible = True\r
-        self.x = []\r
-        self.y = []\r
-\r
-class Results:\r
-    def __init__(self):\r
-        self.columns = []\r
-        self.decimals = {}\r
-        self.has_multipliers = False\r
-        self.multipliers = {}\r
-        self.results = []\r
-        self.separator='\t'\r
-        self.units = {}\r
-\r
-    def get_pretty_value(self, column, value):\r
-        if self.has_multipliers and self.has_results():\r
-            multiplier = self.multipliers[column]\r
-            decimals = self.decimals[column]\r
-            return prettyformat.pretty_format(value, '', decimals, multiplier, True)\r
-        return str(value)\r
-\r
-    #def get_fit_result(self):\r
-        #if not(self.has_multipliers):\r
-            #self.set_multipliers()\r
-\r
-        #sResult = 'Contour length ['+prettyformat.get_prefix(self.multiplierContourLength) + 'm]' + self.separator\r
-        #sResult += prettyformat.pretty_format(self.contourLength[0], '', self.decimals, self.multiplierContourLength, True) + '\n'\r
-        #sResult += 'Persistence length ['+prettyformat.get_prefix(self.multiplierPersistenceLength) + 'm]' + self.separator\r
-        #sResult += prettyformat.pretty_format(self.persistenceLength[0], '', self.decimals, self.multiplierPersistenceLength, True) + '\n'\r
-        #sResult += 'Rupture force ['+prettyformat.get_prefix(self.multiplierRuptureForce) + 'N]' + self.separator\r
-        #sResult += prettyformat.pretty_format(self.ruptureForces[0], '', self.decimals, self.multiplierRuptureForce, True) + '\n'\r
-        #sResult += 'Loading rate ['+prettyformat.get_prefix(self.multiplierSlope) + 'N/m]' + self.separator\r
-        #sResult += prettyformat.pretty_format(self.slopes[0], '', self.decimals, self.multiplierSlope, True)+'\n'\r
-        #sResult += 'Sigma contour ['+prettyformat.get_prefix(self.multiplierContourLength) + 'm]' + self.separator\r
-        #sResult += prettyformat.pretty_format(self.contourLengthSigma[0], '', self.decimals, self.multiplierContourLength, True) + '\n'\r
-        #sResult += 'Sigma persistence ['+prettyformat.get_prefix(self.multiplierPersistenceLength) + 'm]' + self.separator\r
-        #sResult += prettyformat.pretty_format(self.persistenceLengthSigma[0], '', self.decimals, self.multiplierPersistenceLength, True)\r
-\r
-        #return sResult\r
-\r
-    #def get_fit_results(self, index):\r
-        #if index >= 0 and index < len(self.contourLength):\r
-            #if not(self.has_multipliers):\r
-                #self.set_multipliers()\r
-            #sLine = prettyformat.pretty_format(self.contourLength[index], '', self.decimals, self.multiplierContourLength, True) + self.separator\r
-            #sLine += prettyformat.pretty_format(self.persistenceLength[index], '', self.decimals, self.multiplierPersistenceLength, True) + self.separator\r
-            #sLine += prettyformat.pretty_format(self.ruptureForces[index], '', self.decimals, self.multiplierRuptureForce, True) + self.separator\r
-            #sLine += prettyformat.pretty_format(self.slopes[index], '', self.decimals, self.multiplierSlope, True) + self.separator\r
-            #sLine += prettyformat.pretty_format(self.contourLengthSigma[index], '', self.decimals, self.multiplierContourLength, True) + self.separator\r
-            #sLine += prettyformat.pretty_format(self.persistenceLengthSigma[index], '', self.decimals, self.multiplierPersistenceLength, True)\r
-\r
-            #return sLine\r
-        #else:\r
-            #return ''\r
-\r
-    def has_results(self):\r
-        return self.results\r
-\r
-    def header_as_list(self):\r
-        header = []\r
-        if self.has_results():\r
-            if not self.has_multipliers:\r
-                self.set_multipliers()\r
-            for column in self.columns:\r
-                #result will contain the results dictionary for 'column'\r
-                #result = self.results[0][0][column]\r
-                #result[1] contains the unit\r
-                unit_str = ''.join([prettyformat.get_prefix(self.multipliers[column]), self.units[column]])\r
-                header_str = ''.join([column, ' [', unit_str, ']'])\r
-                header.append(header_str)\r
-        return header\r
-\r
-    #def header_as_str(self):\r
-        #if self.has_results():\r
-            #if not self.has_multipliers:\r
-                #self.set_multipliers()\r
-            #header_str = ''\r
-            #for column in self.columns:\r
-                ##result will contain the results dictionary for 'column'\r
-                #result = self.results[0][0][column]\r
-                ##result[1] contains the unit\r
-                #unit_str = ''.join([prettyformat.get_prefix(self.multipliers[column]), result[1]])\r
-                #header_str = ''.join([header_str, result_str, ' [', unit_str, ']', self.separator])\r
-            #return header_str\r
-        #else:\r
-            #return None\r
-\r
-    def set_decimal(self, column, decimal=DEFAULT_DECIMAL):\r
-        if self.decimals.has_key(name):\r
-            self.decimals[name] = decimal\r
-\r
-    def set_decimals(self, decimals=DEFAULT_DECIMAL):\r
-        if decimals < 0:\r
-            #set default value if necessary\r
-            decimals = DEFAULT_DECIMAL\r
-        for column in self.columns:\r
-            self.decimals[column] = decimals\r
-\r
-    def set_multipliers(self, index=0):\r
-        if self.has_results():\r
-            if index >= 0 and index < len(self.results):\r
-                for column in self.columns:\r
-                    #result will contain the results dictionary at 'index'\r
-                    result = self.results[index][0]\r
-                    #in position 0 of the result we find the value\r
-                    self.multipliers[column] = prettyformat.get_multiplier(result[column][0])\r
-                self.has_multipliers = True\r
-        else:\r
-            self.has_multipliers = False\r
-\r
-\r
-class ResultsWLC(Results):\r
-    def __init__(self):\r
-        Results.__init__(self)\r
-        self.columns = ['Contour length', 'sigma contour length', 'Persistence length', 'sigma persistence length', 'Rupture force', 'Loading rate']\r
-        self.units['Contour length'] = 'm'\r
-        self.units['sigma contour length'] = 'm'\r
-        self.units['Persistence length'] = 'm'\r
-        self.units['sigma persistence length'] = 'm'\r
-        self.units['Rupture force'] = 'N'\r
-        self.units['Loading rate'] = 'N/m'\r
-        self.set_decimals(2)\r
-\r
-    def set_multipliers(self, index=0):\r
-        if self.has_results():\r
-            if index >= 0 and index < len(self.results):\r
-                for column in self.columns:\r
-                    #result will contain the results dictionary at 'index'\r
-                    result = self.results[index].result\r
-                    #in position 0 of the result we find the value\r
-                    if column == 'sigma contour length':\r
-                        self.multipliers[column] = self.multipliers['Contour length']\r
-                    elif column == 'sigma persistence length':\r
-                        self.multipliers[column] = self.multipliers['Persistence length']\r
-                    else:\r
-                        self.multipliers[column] = prettyformat.get_multiplier(result[column])\r
-                self.has_multipliers = True\r
-        else:\r
-            self.has_multipliers = False\r
+import prettyformat
+
+DEFAULT_COLOR = 'orange'
+DEFAULT_DECIMAL = 2
+DEFAULT_STYLE = 'plot'
+
+class Result:
+    def __init__(self):
+        self.color = DEFAULT_COLOR
+        self.result = {}
+        self.style = DEFAULT_STYLE
+        self.visible = True
+        self.x = []
+        self.y = []
+
+class Results:
+    def __init__(self):
+        self.columns = []
+        self.decimals = {}
+        self.has_multipliers = False
+        self.multipliers = {}
+        self.results = []
+        self.separator='\t'
+        self.units = {}
+
+    def get_pretty_value(self, column, value):
+        if self.has_multipliers and self.has_results():
+            multiplier = self.multipliers[column]
+            decimals = self.decimals[column]
+            return prettyformat.pretty_format(value, '', decimals, multiplier, True)
+        return str(value)
+
+    #def get_fit_result(self):
+        #if not(self.has_multipliers):
+            #self.set_multipliers()
+
+        #sResult = 'Contour length ['+prettyformat.get_prefix(self.multiplierContourLength) + 'm]' + self.separator
+        #sResult += prettyformat.pretty_format(self.contourLength[0], '', self.decimals, self.multiplierContourLength, True) + '\n'
+        #sResult += 'Persistence length ['+prettyformat.get_prefix(self.multiplierPersistenceLength) + 'm]' + self.separator
+        #sResult += prettyformat.pretty_format(self.persistenceLength[0], '', self.decimals, self.multiplierPersistenceLength, True) + '\n'
+        #sResult += 'Rupture force ['+prettyformat.get_prefix(self.multiplierRuptureForce) + 'N]' + self.separator
+        #sResult += prettyformat.pretty_format(self.ruptureForces[0], '', self.decimals, self.multiplierRuptureForce, True) + '\n'
+        #sResult += 'Loading rate ['+prettyformat.get_prefix(self.multiplierSlope) + 'N/m]' + self.separator
+        #sResult += prettyformat.pretty_format(self.slopes[0], '', self.decimals, self.multiplierSlope, True)+'\n'
+        #sResult += 'Sigma contour ['+prettyformat.get_prefix(self.multiplierContourLength) + 'm]' + self.separator
+        #sResult += prettyformat.pretty_format(self.contourLengthSigma[0], '', self.decimals, self.multiplierContourLength, True) + '\n'
+        #sResult += 'Sigma persistence ['+prettyformat.get_prefix(self.multiplierPersistenceLength) + 'm]' + self.separator
+        #sResult += prettyformat.pretty_format(self.persistenceLengthSigma[0], '', self.decimals, self.multiplierPersistenceLength, True)
+
+        #return sResult
+
+    #def get_fit_results(self, index):
+        #if index >= 0 and index < len(self.contourLength):
+            #if not(self.has_multipliers):
+                #self.set_multipliers()
+            #sLine = prettyformat.pretty_format(self.contourLength[index], '', self.decimals, self.multiplierContourLength, True) + self.separator
+            #sLine += prettyformat.pretty_format(self.persistenceLength[index], '', self.decimals, self.multiplierPersistenceLength, True) + self.separator
+            #sLine += prettyformat.pretty_format(self.ruptureForces[index], '', self.decimals, self.multiplierRuptureForce, True) + self.separator
+            #sLine += prettyformat.pretty_format(self.slopes[index], '', self.decimals, self.multiplierSlope, True) + self.separator
+            #sLine += prettyformat.pretty_format(self.contourLengthSigma[index], '', self.decimals, self.multiplierContourLength, True) + self.separator
+            #sLine += prettyformat.pretty_format(self.persistenceLengthSigma[index], '', self.decimals, self.multiplierPersistenceLength, True)
+
+            #return sLine
+        #else:
+            #return ''
+
+    def has_results(self):
+        return self.results
+
+    def header_as_list(self):
+        header = []
+        if self.has_results():
+            if not self.has_multipliers:
+                self.set_multipliers()
+            for column in self.columns:
+                #result will contain the results dictionary for 'column'
+                #result = self.results[0][0][column]
+                #result[1] contains the unit
+                unit_str = ''.join([prettyformat.get_prefix(self.multipliers[column]), self.units[column]])
+                header_str = ''.join([column, ' [', unit_str, ']'])
+                header.append(header_str)
+        return header
+
+    #def header_as_str(self):
+        #if self.has_results():
+            #if not self.has_multipliers:
+                #self.set_multipliers()
+            #header_str = ''
+            #for column in self.columns:
+                ##result will contain the results dictionary for 'column'
+                #result = self.results[0][0][column]
+                ##result[1] contains the unit
+                #unit_str = ''.join([prettyformat.get_prefix(self.multipliers[column]), result[1]])
+                #header_str = ''.join([header_str, result_str, ' [', unit_str, ']', self.separator])
+            #return header_str
+        #else:
+            #return None
+
+    def set_decimal(self, column, decimal=DEFAULT_DECIMAL):
+        if self.decimals.has_key(name):
+            self.decimals[name] = decimal
+
+    def set_decimals(self, decimals=DEFAULT_DECIMAL):
+        if decimals < 0:
+            #set default value if necessary
+            decimals = DEFAULT_DECIMAL
+        for column in self.columns:
+            self.decimals[column] = decimals
+
+    def set_multipliers(self, index=0):
+        if self.has_results():
+            if index >= 0 and index < len(self.results):
+                for column in self.columns:
+                    #result will contain the results dictionary at 'index'
+                    result = self.results[index][0]
+                    #in position 0 of the result we find the value
+                    self.multipliers[column] = prettyformat.get_multiplier(result[column][0])
+                self.has_multipliers = True
+        else:
+            self.has_multipliers = False
+
+
+class ResultsWLC(Results):
+    def __init__(self):
+        Results.__init__(self)
+        self.columns = ['Contour length', 'sigma contour length', 'Persistence length', 'sigma persistence length', 'Rupture force', 'Loading rate']
+        self.units['Contour length'] = 'm'
+        self.units['sigma contour length'] = 'm'
+        self.units['Persistence length'] = 'm'
+        self.units['sigma persistence length'] = 'm'
+        self.units['Rupture force'] = 'N'
+        self.units['Loading rate'] = 'N/m'
+        self.set_decimals(2)
+
+    def set_multipliers(self, index=0):
+        if self.has_results():
+            if index >= 0 and index < len(self.results):
+                for column in self.columns:
+                    #result will contain the results dictionary at 'index'
+                    result = self.results[index].result
+                    #in position 0 of the result we find the value
+                    if column == 'sigma contour length':
+                        self.multipliers[column] = self.multipliers['Contour length']
+                    elif column == 'sigma persistence length':
+                        self.multipliers[column] = self.multipliers['Persistence length']
+                    else:
+                        self.multipliers[column] = prettyformat.get_multiplier(result[column])
+                self.has_multipliers = True
+        else:
+            self.has_multipliers = False
index d1234c3..8a3a4ce 100644 (file)
-#pragma rtGlobals=1            // Use modern global access method.\r
-#pragma IgorVersion = 4.0\r
-#pragma version = 0.4\r
-\r
-//\r
-// ExportMFP1D.ipf - A procedure to export force curves from MFP1D to 'hooke'\r
-//\r
-// Copyright (c) 2009 Rolf Schmidt, Montreal\r
-// rschmidt@alcor.concordia.ca\r
-// \r
-// This procedure is released under the GNU General Public License version 2\r
-//\r
-\r
-// History\r
-// 2009 07 24: v0.4\r
-// the wave note is now correctly and fully exported in Igor 4\r
-// 2009 06 29: v0.3\r
-// split functionality into ExportMFP1DFolder and ExportMFP1DWaves\r
-// ExportMFP1DFolder: export individual Igor binary waves file from a folder\r
-// ExportMFP1DWaves: export all currently open waves to a folder\r
-// 2009 06 26: v0.2.1\r
-// added the IgorVersion pragma\r
-// 2009 06 19: v0.2\r
-// changed the filename finding algorithm to work with Igor 4 and up\r
-// Igor 5 users can use the code marked 'the following only works in Igor 5 and up' instead\r
-// the procedure now catches 'Cancel' on NewPath\r
-// added version information\r
-// 2009 05 29: v0.1\r
-// changed the procedure so that it runs in Igor as well as in MFP (ie Igor with MFP plug-in)\r
-\r
-// How to use ExportMFP1D()\r
-// - save all current waves of interest (ExportMFP1D() kills all open waves before exporting files)\r
-// - execute ExportMFP1D() from the command line\r
-// - browse to a folder containing force curves (a 'force curve' consists of two files: one 'deflection' and one 'LVDT' file\r
-// - ExportMFP1D() will now iterate through all the waves in the folder, extract the header information and create four columns:\r
-//   1: approach (x) 2: approach (y) 3: retraction (x) 4; retraction (y)\r
-// - the resulting files are saved in the same folder and the same base name as the original files (ie without 'deflection' and 'LVDT')\r
-// CAUTION:  existing files will be overwritten!\r
-// - these files can then be analyzed with 'hooke'\r
-\r
-Function ExportMFP1DFolder()\r
-\r
-       String sList\r
-       Variable iCount\r
-\r
-       // set the path (used for opening the waves and saving the output files later)\r
-       NewPath /O /Q /M="Choose the folder that contains the waves" PathExport1D\r
-\r
-       KillWaves /A /Z\r
-       \r
-       if(V_flag>=0)\r
-               // get a list of all Igor binary waves in the folder\r
-               sList=IndexedFile(PathExport1D,-1,".ibw")\r
-               // load all waves\r
-               for(iCount=0; iCount<ItemsInList(sList); iCount+=1)\r
-                       LoadWave /P=PathExport1D /Q StringFromList(iCount, sList)\r
-               endfor\r
-               ExportMFP1DWaves()\r
-       endif\r
-       // kill the export path\r
-       KillPath /Z PathExport1D\r
-End\r
-\r
-Function ExportMFP1DWaves()\r
-\r
-       String sFileName1\r
-       String sFileName2\r
-       String sLine\r
-       String sList\r
-       String sNote\r
-       String sWaveCombined\r
-       String sWaveDeflection\r
-       String sWaveLVDT\r
-       Variable iCount\r
-       Variable iLine\r
-       Variable iPoints\r
-       Variable iRefNum\r
-       Wave wApproachX\r
-       Wave wApproachY\r
-       Wave wRetractionX\r
-       Wave wRetractionY\r
-\r
-       // set the path (used for saving the output files later)\r
-       NewPath /O /Q /M="Choose the folder to save the waves" PathExport1D\r
-       \r
-       // get a list of all LVDT waves (could be deflection as well, they always come in a pair)\r
-       sList=WaveList("*LVDT*", ";", "")\r
-       \r
-       // iterate through all the LVDT waves\r
-       for(iCount=0; iCount<ItemsInList(sList); iCount+=1)\r
-               // create the wave names as string\r
-// the following only works in Igor 5 and up\r
-//                     sWaveLVDT=ReplaceString(".ibw",StringFromList(iCount, sList), "")\r
-//                     sWaveDeflection=ReplaceString("LVDT",sWaveLVDT, "deflection")\r
-//                     sWaveCombined=ReplaceString("LVDT",sWaveLVDT, "_")\r
-// END: the following only works in Igor 5 and up\r
-\r
-// the following works in Igor 4 and up\r
-               // treat the filename as a key-value list with '.' as a separator\r
-               // use the first entry (ie 0) as the filename without extension\r
-               sWaveLVDT=StringFromList(0, StringFromList(iCount, sList), ".")\r
-               \r
-               // treat the filename as a key-value list with 'LVDT' as a separator\r
-               // use the first entry (ie 0) as the first part of the filename\r
-               sFileName1=StringFromList(0, sWaveLVDT, "LVDT")\r
-               // getting the second part of the filename is a bit trickier\r
-               // first, we 'remove' the first part of the filename by treating it as a key\r
-               // using 'LVDT' as a separator \r
-               sFileName2=StringByKey(sFileName1, sWaveLVDT, "LVDT")\r
-               // unfortunately, StringByKey only removes the first character of the separator\r
-               // to get the second part of the filename, we use VD as the key and 'T' as the separator\r
-               sFileName2=StringByKey("VD", sFileName2, "T")\r
-               // then we create the wave names as follows:\r
-               sWaveDeflection=sFileName1+"deflection"+sFileName2\r
-               \r
-               sWaveCombined=sFileName1+"_"+sFileName2\r
-               \r
-// END: the following works in Igor 4 and up\r
-\r
-               // create the waves we need\r
-               Wave wLVDT=$sWaveLVDT\r
-               Wave wDeflection=$sWaveDeflection\r
-       \r
-               // open the output text file, add extension\r
-               Open /P=PathExport1D iRefNum as sWaveCombined+".txt"\r
-\r
-               // create the header\r
-               fprintf iRefNum, "Wave:"+sWaveCombined+"\r"\r
-               fprintf iRefNum, "WaveLVDT:"+sWaveLVDT+"\r"\r
-               fprintf iRefNum, "WaveDeflection:"+sWaveDeflection+"\r"\r
-\r
-               // the number of points (use WaveStats to get them) are identical for LVDT and Deflection\r
-               WaveStats /q wLVDT\r
-               iPoints=V_npnts/2\r
-               fprintf iRefNum, "Rows:"+num2str(iPoints)+"\r"\r
-\r
-               // add the note to the file\r
-               // the notes are identical for LVDT and Deflection\r
-// the following only works in Igor 5 and up\r
-//             fprintf iRefNum, note(wDeflection)\r
-// END: the following only works in Igor 5 and up\r
-               sNote=note(wLVDT)\r
-               // in order to get the correct number of lines in the note, we have to specify the EOF as \r\n\r
-               for(iLine=0; iLine<ItemsInList(sNote, "\r\n");iLine+=1)\r
-                       // print every line to the output file\r
-                       fprintf iRefNum, StringFromList(iLine, sNote, "\r\n")\r
-                       // add a CR/LF for every but the last line\r
-                       if(iLine<ItemsInList(sNote, "\r\n")-1)\r
-                               fprintf iRefNum, "\r\n"\r
-                       endif\r
-               endfor\r
-\r
-               // separate the approach from the retraction\r
-               // by simply taking the first half of the points to be the approach\r
-               // and the second half to be the retraction\r
-               // this probably has to be changed for dual pulls\r
-               Duplicate /O /R=[0, iPoints] wLVDT, wApproachX\r
-               Duplicate /O /R=[0, iPoints] wDeflection, wApproachY\r
-               Duplicate /O /R=[iPoints+1] wLVDT, wRetractionX\r
-               Duplicate /O /R=[iPoints+1] wDeflection, wRetractionY\r
-\r
-               // create four columns line by line\r
-               // 1: approach x 2: approach y 3: retraction x 4: retraction y\r
-               for(iLine=0; iLine<iPoints; iLine+=1)\r
-                       sLine=num2str(wApproachX[iLine])+"\t"+num2str(wApproachY[iLine])\r
-                       sLine=sLine+"\t"+num2str(wRetractionX[iLine])+"\t"+num2str(wRetractionY[iLine])\r
-                       // add the line to the file\r
-                       fprintf iRefNum, "\r"+sLine\r
-               endfor\r
-\r
-               // save the text file to disk\r
-               print "Exporting "+sWaveCombined\r
-               Close iRefNum\r
-       endfor\r
-\r
-       // print message\r
-       print "Export completed ("+num2str(ItemsInList(sList))+" files)"\r
-\r
-       // kill the temporary waves used\r
-       // given the names, it is unlikely that this function will interfere with data\r
-       KillWaves /Z wApproachX\r
-       KillWaves /Z wApproachY\r
-       KillWaves /Z wRetractionX\r
-       KillWaves /Z wRetractionY\r
-End\r
+#pragma rtGlobals=1            // Use modern global access method.
+#pragma IgorVersion = 4.0
+#pragma version = 0.4
+
+//
+// ExportMFP1D.ipf - A procedure to export force curves from MFP1D to 'hooke'
+//
+// Copyright (c) 2009 Rolf Schmidt, Montreal
+// rschmidt@alcor.concordia.ca
+// 
+// This procedure is released under the GNU General Public License version 2
+//
+
+// History
+// 2009 07 24: v0.4
+// the wave note is now correctly and fully exported in Igor 4
+// 2009 06 29: v0.3
+// split functionality into ExportMFP1DFolder and ExportMFP1DWaves
+// ExportMFP1DFolder: export individual Igor binary waves file from a folder
+// ExportMFP1DWaves: export all currently open waves to a folder
+// 2009 06 26: v0.2.1
+// added the IgorVersion pragma
+// 2009 06 19: v0.2
+// changed the filename finding algorithm to work with Igor 4 and up
+// Igor 5 users can use the code marked 'the following only works in Igor 5 and up' instead
+// the procedure now catches 'Cancel' on NewPath
+// added version information
+// 2009 05 29: v0.1
+// changed the procedure so that it runs in Igor as well as in MFP (ie Igor with MFP plug-in)
+
+// How to use ExportMFP1D()
+// - save all current waves of interest (ExportMFP1D() kills all open waves before exporting files)
+// - execute ExportMFP1D() from the command line
+// - browse to a folder containing force curves (a 'force curve' consists of two files: one 'deflection' and one 'LVDT' file
+// - ExportMFP1D() will now iterate through all the waves in the folder, extract the header information and create four columns:
+//   1: approach (x) 2: approach (y) 3: retraction (x) 4; retraction (y)
+// - the resulting files are saved in the same folder and the same base name as the original files (ie without 'deflection' and 'LVDT')
+// CAUTION:  existing files will be overwritten!
+// - these files can then be analyzed with 'hooke'
+
+Function ExportMFP1DFolder()
+
+       String sList
+       Variable iCount
+
+       // set the path (used for opening the waves and saving the output files later)
+       NewPath /O /Q /M="Choose the folder that contains the waves" PathExport1D
+
+       KillWaves /A /Z
+       
+       if(V_flag>=0)
+               // get a list of all Igor binary waves in the folder
+               sList=IndexedFile(PathExport1D,-1,".ibw")
+               // load all waves
+               for(iCount=0; iCount<ItemsInList(sList); iCount+=1)
+                       LoadWave /P=PathExport1D /Q StringFromList(iCount, sList)
+               endfor
+               ExportMFP1DWaves()
+       endif
+       // kill the export path
+       KillPath /Z PathExport1D
+End
+
+Function ExportMFP1DWaves()
+
+       String sFileName1
+       String sFileName2
+       String sLine
+       String sList
+       String sNote
+       String sWaveCombined
+       String sWaveDeflection
+       String sWaveLVDT
+       Variable iCount
+       Variable iLine
+       Variable iPoints
+       Variable iRefNum
+       Wave wApproachX
+       Wave wApproachY
+       Wave wRetractionX
+       Wave wRetractionY
+
+       // set the path (used for saving the output files later)
+       NewPath /O /Q /M="Choose the folder to save the waves" PathExport1D
+       
+       // get a list of all LVDT waves (could be deflection as well, they always come in a pair)
+       sList=WaveList("*LVDT*", ";", "")
+       
+       // iterate through all the LVDT waves
+       for(iCount=0; iCount<ItemsInList(sList); iCount+=1)
+               // create the wave names as string
+// the following only works in Igor 5 and up
+//                     sWaveLVDT=ReplaceString(".ibw",StringFromList(iCount, sList), "")
+//                     sWaveDeflection=ReplaceString("LVDT",sWaveLVDT, "deflection")
+//                     sWaveCombined=ReplaceString("LVDT",sWaveLVDT, "_")
+// END: the following only works in Igor 5 and up
+
+// the following works in Igor 4 and up
+               // treat the filename as a key-value list with '.' as a separator
+               // use the first entry (ie 0) as the filename without extension
+               sWaveLVDT=StringFromList(0, StringFromList(iCount, sList), ".")
+               
+               // treat the filename as a key-value list with 'LVDT' as a separator
+               // use the first entry (ie 0) as the first part of the filename
+               sFileName1=StringFromList(0, sWaveLVDT, "LVDT")
+               // getting the second part of the filename is a bit trickier
+               // first, we 'remove' the first part of the filename by treating it as a key
+               // using 'LVDT' as a separator 
+               sFileName2=StringByKey(sFileName1, sWaveLVDT, "LVDT")
+               // unfortunately, StringByKey only removes the first character of the separator
+               // to get the second part of the filename, we use VD as the key and 'T' as the separator
+               sFileName2=StringByKey("VD", sFileName2, "T")
+               // then we create the wave names as follows:
+               sWaveDeflection=sFileName1+"deflection"+sFileName2
+               
+               sWaveCombined=sFileName1+"_"+sFileName2
+               
+// END: the following works in Igor 4 and up
+
+               // create the waves we need
+               Wave wLVDT=$sWaveLVDT
+               Wave wDeflection=$sWaveDeflection
+       
+               // open the output text file, add extension
+               Open /P=PathExport1D iRefNum as sWaveCombined+".txt"
+
+               // create the header
+               fprintf iRefNum, "Wave:"+sWaveCombined+"\r"
+               fprintf iRefNum, "WaveLVDT:"+sWaveLVDT+"\r"
+               fprintf iRefNum, "WaveDeflection:"+sWaveDeflection+"\r"
+
+               // the number of points (use WaveStats to get them) are identical for LVDT and Deflection
+               WaveStats /q wLVDT
+               iPoints=V_npnts/2
+               fprintf iRefNum, "Rows:"+num2str(iPoints)+"\r"
+
+               // add the note to the file
+               // the notes are identical for LVDT and Deflection
+// the following only works in Igor 5 and up
+//             fprintf iRefNum, note(wDeflection)
+// END: the following only works in Igor 5 and up
+               sNote=note(wLVDT)
+               // in order to get the correct number of lines in the note, we have to specify the EOF as \r\n
+               for(iLine=0; iLine<ItemsInList(sNote, "\r\n");iLine+=1)
+                       // print every line to the output file
+                       fprintf iRefNum, StringFromList(iLine, sNote, "\r\n")
+                       // add a CR/LF for every but the last line
+                       if(iLine<ItemsInList(sNote, "\r\n")-1)
+                               fprintf iRefNum, "\r\n"
+                       endif
+               endfor
+
+               // separate the approach from the retraction
+               // by simply taking the first half of the points to be the approach
+               // and the second half to be the retraction
+               // this probably has to be changed for dual pulls
+               Duplicate /O /R=[0, iPoints] wLVDT, wApproachX
+               Duplicate /O /R=[0, iPoints] wDeflection, wApproachY
+               Duplicate /O /R=[iPoints+1] wLVDT, wRetractionX
+               Duplicate /O /R=[iPoints+1] wDeflection, wRetractionY
+
+               // create four columns line by line
+               // 1: approach x 2: approach y 3: retraction x 4: retraction y
+               for(iLine=0; iLine<iPoints; iLine+=1)
+                       sLine=num2str(wApproachX[iLine])+"\t"+num2str(wApproachY[iLine])
+                       sLine=sLine+"\t"+num2str(wRetractionX[iLine])+"\t"+num2str(wRetractionY[iLine])
+                       // add the line to the file
+                       fprintf iRefNum, "\r"+sLine
+               endfor
+
+               // save the text file to disk
+               print "Exporting "+sWaveCombined
+               Close iRefNum
+       endfor
+
+       // print message
+       print "Export completed ("+num2str(ItemsInList(sList))+" files)"
+
+       // kill the temporary waves used
+       // given the names, it is unlikely that this function will interfere with data
+       KillWaves /Z wApproachX
+       KillWaves /Z wApproachY
+       KillWaves /Z wRetractionX
+       KillWaves /Z wRetractionY
+End
index a8cda3b..e13a34e 100644 (file)
@@ -1,4 +1,4 @@
-Menu "hooke"\r
-       "Export folder", ExportMFP1DFolder()\r
-       "Export waves", ExportMFP1DWaves()\r
+Menu "hooke"
+       "Export folder", ExportMFP1DFolder()
+       "Export waves", ExportMFP1DWaves()
 End
\ No newline at end of file