Beginning to rewrite Hooke to decouple from GUI.
[hooke.git] / hooke / hooke.py
1 #!/usr/bin/env python
2
3 '''
4 Hooke - A force spectroscopy review & analysis tool.
5
6 COPYRIGHT
7 '''
8
9 import Queue as queue
10 import multiprocessing
11
12 from . import config as config_mod
13 from .plugin import PLUGINS
14 from .driver import DRIVERS
15
16
17 import copy
18 import cStringIO
19 import os
20 import os.path
21 import sys
22 import glob
23 import time
24
25 #import libhooke as lh
26 #import drivers
27 #import plugins
28 #import hookecommands
29 #import hookeplaylist
30 #import hookepropertyeditor
31 #import hookeresults
32 #import playlist
33
34 class Hooke(object):
35     def __init__(self, config=None, debug=0):
36         self.debug = debug
37         config_mod.DEFAULTS.extend(self.plugin_default_settings())
38         config_mod.DEFAULTS.extend(self.driver_default_settings())
39         if config == None:
40             config = config_mod.HookeConfigParser(
41                 paths=config_mod.DEFAULT_PATHS,
42                 default_settings=config_mod.DEFAULT_SETTINGS)
43             config.read_configs()
44         self.config = config
45         self.load_plugins()
46         self.load_drivers()
47
48     def plugin_default_settings(self):
49         pass
50
51     def driver_default_settings(self):
52         pass
53
54     def load_plugins(self):
55         pass
56
57     def load_drivers(self)
58         pass
59
60     def close(self):
61         if self.config.changed:
62             self.config.write() # Does not preserve original comments
63
64 class HookeFrame(wx.Frame):
65
66     def __init__(self, parent, id=-1, title='', pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE|wx.SUNKEN_BORDER|wx.CLIP_CHILDREN):
67         #call parent constructor
68         wx.Frame.__init__(self, parent, id, title, pos, size, style)
69         self.config = config
70         self.CreateApplicationIcon()
71         #self.configs contains: {the name of the Commands file: corresponding ConfigObj}
72         self.configs = {}
73         ##self.playlists contains: {the name of the playlist: [playlist, tabIndex, plotID]}
74         #self.playlists = {}
75         #self.plugins contains: {the name of the plugin: [caption, function]}
76         self.plugins = {}
77         #self.plotmanipulators list contains: [the name of the plotmanip, function, name of the module]
78         self.plotmanipulators = []
79
80         #tell FrameManager to manage this frame
81         self._mgr = aui.AuiManager()
82         self._mgr.SetManagedWindow(self)
83         #set the gradient style
84         self._mgr.GetArtProvider().SetMetric(aui.AUI_DOCKART_GRADIENT_TYPE, aui.AUI_GRADIENT_NONE)
85         #set transparent drag
86         self._mgr.SetFlags(self._mgr.GetFlags() ^ aui.AUI_MGR_TRANSPARENT_DRAG)
87
88         # set up default notebook style
89         self._notebook_style = aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER
90         self._notebook_theme = 0
91
92         #holds the perspectives: {name, [index, perspectiveStr]}
93         self._perspectives = {}
94
95         # min size for the frame itself isn't completely done.
96         # see the end up FrameManager::Update() for the test
97         # code. For now, just hard code a frame minimum size
98         self.SetMinSize(wx.Size(400, 300))
99         #create panels here
100         self.panelAssistant = self.CreatePanelAssistant()
101         self.panelCommands = self.CreatePanelCommands()
102         self.panelFolders = self.CreatePanelFolders()
103         self.panelPlaylists = self.CreatePanelPlaylists()
104         self.panelProperties = self.CreatePanelProperties()
105         self.panelOutput = self.CreatePanelOutput()
106         self.panelResults = self.CreatePanelResults()
107         self.plotNotebook = self.CreateNotebook()
108         #self.textCtrlCommandLine=self.CreateCommandLine()
109
110         # add panes
111         self._mgr.AddPane(self.panelFolders, aui.AuiPaneInfo().Name('Folders').Caption('Folders').Left().CloseButton(True).MaximizeButton(False))
112         self._mgr.AddPane(self.panelPlaylists, aui.AuiPaneInfo().Name('Playlists').Caption('Playlists').Left().CloseButton(True).MaximizeButton(False))
113         self._mgr.AddPane(self.plotNotebook, aui.AuiPaneInfo().Name('Plots').CenterPane().PaneBorder(False))
114         self._mgr.AddPane(self.panelCommands, aui.AuiPaneInfo().Name('Commands').Caption('Settings and commands').Right().CloseButton(True).MaximizeButton(False))
115         self._mgr.AddPane(self.panelProperties, aui.AuiPaneInfo().Name('Properties').Caption('Properties').Right().CloseButton(True).MaximizeButton(False))
116         self._mgr.AddPane(self.panelAssistant, aui.AuiPaneInfo().Name('Assistant').Caption('Assistant').Right().CloseButton(True).MaximizeButton(False))
117         self._mgr.AddPane(self.panelOutput, aui.AuiPaneInfo().Name('Output').Caption('Output').Bottom().CloseButton(True).MaximizeButton(False))
118         self._mgr.AddPane(self.panelResults, aui.AuiPaneInfo().Name('Results').Caption('Results').Bottom().CloseButton(True).MaximizeButton(False))
119         #self._mgr.AddPane(self.textCtrlCommandLine, aui.AuiPaneInfo().Name('CommandLine').CaptionVisible(False).Fixed().Bottom().Layer(2).CloseButton(False).MaximizeButton(False))
120         #self._mgr.AddPane(panelBottom, aui.AuiPaneInfo().Name("panelCommandLine").Bottom().Position(1).CloseButton(False).MaximizeButton(False))
121
122         # add the toolbars to the manager
123         self.toolbar=self.CreateToolBar()
124         self.toolbarNavigation=self.CreateToolBarNavigation()
125         self._mgr.AddPane(self.toolbar, aui.AuiPaneInfo().Name('toolbar').Caption('Toolbar').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
126         self._mgr.AddPane(self.toolbarNavigation, aui.AuiPaneInfo().Name('toolbarNavigation').Caption('Navigation').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
127         # "commit" all changes made to FrameManager
128         self._mgr.Update()
129         #create the menubar after the panes so that the default perspective
130         #is created with all panes open
131         self.CreateMenuBar()
132         self.statusbar = self.CreateStatusBar()
133         self._BindEvents()
134         #TODO: select item on startup (whatever item)
135         #self.listCtrlCommands.Select(0)
136         #self.OnListboxSelect(None)
137         name = self.config['perspectives']['active']
138         menu_item = self.GetPerspectiveMenuItem(name)
139         self.OnRestorePerspective(menu_item)
140         self.playlists = self.panelPlaylists.Playlists
141         #define the list of active drivers
142         self.drivers = []
143         for driver in self.config['drivers']:
144             if self.config['drivers'][driver]:
145                 #get the corresponding filename and path
146                 filename = ''.join([driver, '.py'])
147                 path = lh.get_file_path(filename, ['drivers'])
148                 #the driver is active for driver[1] == 1
149                 if os.path.isfile(path):
150                     #driver files are located in the 'drivers' subfolder
151                     driver_name = ''.join(['drivers.', driver])
152                     module = __import__(driver_name)
153                     class_file = getattr(drivers, driver)
154                     for command in dir(class_file):
155                         if command.endswith('Driver'):
156                             self.drivers.append(getattr(class_file, command))
157         #import all active plugins and plotmanips
158         #the plotmanip_functions contains: {the name of the plotmanip: [method, class_object]}
159         plotmanip_functions = {}
160         #add 'general.ini' to self.configs (this is not a plugin and thus must be imported seperately)
161         ini_path = lh.get_file_path('general.ini', ['plugins'])
162         plugin_config = ConfigObj(ini_path)
163         #self.config.merge(plugin_config)
164         self.configs['general'] = plugin_config
165         #make sure we execute _plug_init() for every command line plugin we import
166         for plugin in self.config['plugins']:
167             if self.config['plugins'][plugin]:
168                 filename = ''.join([plugin, '.py'])
169                 path = lh.get_file_path(filename, ['plugins'])
170                 if os.path.isfile(path):
171                     #get the corresponding filename and path
172                     plugin_name = ''.join(['plugins.', plugin])
173                     try:
174                         #import the module
175                         module = __import__(plugin_name)
176                         #prepare the ini file for inclusion
177                         ini_path = path.replace('.py', '.ini')
178                         #include ini file
179                         plugin_config = ConfigObj(ini_path)
180                         #self.config.merge(plugin_config)
181                         self.configs[plugin] = plugin_config
182                         #add to plugins
183                         commands = eval('dir(module.' + plugin+ '.' + plugin + 'Commands)')
184                         #keep only commands (ie names that start with 'do_')
185                         commands = [command for command in commands if command.startswith('do_')]
186                         if commands:
187                             self.plugins[plugin] = commands
188                         try:
189                             #initialize the plugin
190                             eval('module.' + plugin+ '.' + plugin + 'Commands._plug_init(self)')
191                         except AttributeError:
192                             pass
193                     except ImportError:
194                         pass
195         #initialize the commands tree
196         commands = dir(HookeFrame)
197         commands = [command for command in commands if command.startswith('do_')]
198         if commands:
199             self.plugins['general'] = commands
200         self.panelCommands.Initialize(self.plugins)
201         for command in dir(self):
202             if command.startswith('plotmanip_'):
203                 plotmanip_functions[command] = [command, getattr(self, command)]
204         for name in self.config['plotmanipulators']['names']:
205             if self.config['plotmanipulators'].as_bool(name):
206                 command_name = ''.join(['plotmanip_', name])
207                 if command_name in plotmanip_functions:
208                     self.plotmanipulators.append(plotmanip_functions[command_name])
209         #load default list, if possible
210         self.do_loadlist(self.config['general']['list'])
211
212     def _BindEvents(self):
213         self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
214         self.Bind(wx.EVT_SIZE, self.OnSize)
215         self.Bind(wx.EVT_CLOSE, self.OnClose)
216         # Show How To Use The Closing Panes Event
217         self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPaneClose)
218         self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnNotebookPageClose)
219         #menu
220         self.Bind(wx.EVT_MENU, self.OnClose, id=wx.ID_EXIT)
221         self.Bind(wx.EVT_MENU, self.OnAbout, id=wx.ID_ABOUT)
222         #view
223         self.Bind(wx.EVT_MENU_RANGE, self.OnView, id=ID_ViewAssistant, id2=ID_ViewResults)
224         #perspectives
225         self.Bind(wx.EVT_MENU, self.OnDeletePerspective, id=ID_DeletePerspective)
226         self.Bind(wx.EVT_MENU, self.OnSavePerspective, id=ID_SavePerspective)
227         self.Bind(wx.EVT_MENU_RANGE, self.OnRestorePerspective, id=ID_FirstPerspective, id2=ID_FirstPerspective+1000)
228         #toolbar
229         self.Bind(wx.EVT_TOOL, self.OnExportImage, id=ID_ExportImage)
230         self.Bind(wx.EVT_TOOL, self.OnNext, id=ID_Next)
231         self.Bind(wx.EVT_TOOL, self.OnPrevious, id=ID_Previous)
232         #self.Bind(.EVT_AUITOOLBAR_TOOL_DROPDOWN, self.OnDropDownToolbarItem, id=ID_DropDownToolbarItem)
233         #dir control
234         treeCtrl = self.panelFolders.GetTreeCtrl()
235         #tree.Bind(wx.EVT_LEFT_UP, self.OnDirCtrl1LeftUp)
236         #tree.Bind(wx.EVT_LEFT_DOWN, self.OnGenericDirCtrl1LeftDown)
237         treeCtrl.Bind(wx.EVT_LEFT_DCLICK, self.OnDirCtrlLeftDclick)
238         #playlist tree
239         self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DOWN, self.OnPlaylistsLeftDown)
240         self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DCLICK, self.OnPlaylistsLeftDclick)
241         #commands tree
242         self.panelCommands.ExecuteButton.Bind(wx.EVT_BUTTON, self.OnExecute)
243         self.panelCommands.CommandsTree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeCtrlCommandsLeftDown)
244         #property editor
245         self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged)
246         self.panelProperties.pg.Bind(wxpg.EVT_PG_SELECTED, self.OnPropGridSelect)
247         #results panel
248         self.panelResults.results_list.OnCheckItem = self.OnResultsCheck
249
250     def _GetActiveCurveIndex(self):
251         playlist = self.GetActivePlaylist()
252         #get the selected item from the tree
253         selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
254         #test if a playlist or a curve was double-clicked
255         if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
256             return -1
257         else:
258             count = 0
259             selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
260             while selected_item.IsOk():
261                 count += 1
262                 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
263             return count
264
265     def _GetActivePlaylistName(self):
266         #get the selected item from the tree
267         selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
268         #test if a playlist or a curve was double-clicked
269         if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
270             playlist_item = selected_item
271         else:
272             #get the name of the playlist
273             playlist_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
274         #now we have a playlist
275         return self.panelPlaylists.PlaylistsTree.GetItemText(playlist_item)
276
277     def _GetPlaylistTab(self, name):
278         for index, page in enumerate(self.plotNotebook._tabs._pages):
279             if page.caption == name:
280                 return index
281         return -1
282
283     def _GetUniquePlaylistName(self, name):
284         playlist_name = name
285         count = 1
286         while playlist_name in self.playlists:
287             playlist_name = ''.join([name, str(count)])
288             count += 1
289         return playlist_name
290
291     def _SavePerspectiveToFile(self, name, perspective):
292         filename = ''.join([name, '.txt'])
293         filename = lh.get_file_path(filename, ['perspectives'])
294         perspectivesFile = open(filename, 'w')
295         perspectivesFile.write(perspective)
296         perspectivesFile.close()
297
298     def AddPlaylist(self, playlist=None, name='Untitled'):
299         #TODO: change cursor or progressbar (maybe in statusbar)
300         #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
301         if playlist and playlist.count > 0:
302             playlist.name = self._GetUniquePlaylistName(name)
303             playlist.reset()
304             self.AddToPlaylists(playlist)
305
306     def AddPlaylistFromFiles(self, files=[], name='Untitled'):
307         #TODO: change cursor or progressbar (maybe in statusbar)
308         #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
309         if files:
310             playlist = Playlist.Playlist(self.drivers)
311             for item in files:
312                 playlist.add_curve(item)
313         if playlist.count > 0:
314             playlist.name = self._GetUniquePlaylistName(name)
315             playlist.reset()
316             self.AddToPlaylists(playlist)
317
318     def AddToPlaylists(self, playlist):
319         if playlist.has_curves:
320             #setup the playlist in the Playlist tree
321             tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
322             playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)
323             #add all curves to the Playlist tree
324             curves = {}
325             for index, curve in enumerate(playlist.curves):
326                 ##remove the extension from the name of the curve
327                 ##TODO: optional?
328                 #item_text, extension = os.path.splitext(curve.name)
329                 #curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, item_text, 1)
330                 curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, curve.name, 1)
331                 if index == playlist.index:
332                     self.panelPlaylists.PlaylistsTree.SelectItem(curve_ID)
333             playlist.reset()
334             #create the plot tab and add playlist to the dictionary
335             plotPanel = wxmpl.PlotPanel(self, ID_FirstPlot + len(self.playlists))
336             notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True)
337             tab_index = self.plotNotebook.GetSelection()
338             figure = plotPanel.get_figure()
339             self.playlists[playlist.name] = [playlist, figure]
340             self.panelPlaylists.PlaylistsTree.Expand(playlist_root)
341             self.statusbar.SetStatusText(playlist.get_status_string(), 0)
342             self.UpdatePlot()
343
344     def AppendToOutput(self, text):
345         self.panelOutput.AppendText(''.join([text, '\n']))
346
347     def CreateApplicationIcon(self):
348         iconFile = 'resources' + os.sep + 'microscope.ico'
349         icon = wx.Icon(iconFile, wx.BITMAP_TYPE_ICO)
350         self.SetIcon(icon)
351
352     def CreateCommandLine(self):
353         return wx.TextCtrl(self, -1, '', style=wx.NO_BORDER|wx.EXPAND)
354
355     def CreatePanelAssistant(self):
356         panel = wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
357         panel.SetEditable(False)
358         return panel
359
360     def CreatePanelCommands(self):
361         return hookecommands.Commands(self)
362
363     def CreatePanelFolders(self):
364         #set file filters
365         filters = self.config['folders']['filters']
366         index = self.config['folders'].as_int('filterindex')
367         #set initial directory
368         folder = self.config['general']['workdir']
369         return wx.GenericDirCtrl(self, -1, dir=folder, size=(200, 250), style=wx.DIRCTRL_SHOW_FILTERS, filter=filters, defaultFilter=index)
370
371     def CreatePanelOutput(self):
372         return wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
373
374     def CreatePanelPlaylists(self):
375         return hookeplaylist.Playlists(self)
376
377     def CreatePanelProperties(self):
378         return hookepropertyeditor.PropertyEditor(self)
379
380     def CreatePanelResults(self):
381         return hookeresults.Results(self)
382
383     def CreatePanelWelcome(self):
384         ctrl = wx.html.HtmlWindow(self, -1, wx.DefaultPosition, wx.Size(400, 300))
385         introStr = '<h1>Welcome to Hooke</h1>' + \
386                  '<h3>Features</h3>' + \
387                  '<ul>' + \
388                  '<li>View, annotate, measure force curves</li>' + \
389                  '<li>Worm-like chain fit of force peaks</li>' + \
390                  '<li>Automatic convolution-based filtering of empty curves</li>' + \
391                  '<li>Automatic fit and measurement of multiple force peaks</li>' + \
392                  '<li>Handles force-clamp force experiments (experimental)</li>' + \
393                  '<li>It is extensible by users by means of plugins and drivers</li>' + \
394                  '</ul>' + \
395                  '<p>See the <a href="/p/hooke/wiki/DocumentationIndex">DocumentationIndex</a> for more information</p>'
396         ctrl.SetPage(introStr)
397         return ctrl
398
399     def CreateMenuBar(self):
400         menu_bar = wx.MenuBar()
401         #file
402         file_menu = wx.Menu()
403         file_menu.Append(wx.ID_OPEN, '&Open playlist\tCtrl-O')
404         file_menu.Append(wx.ID_SAVE, 'Save playlist\tCtrl-S')
405         file_menu.AppendSeparator()
406         file_menu.Append(wx.ID_EXIT, 'Exit\tCtrl-Q')
407         #edit
408         edit_menu = wx.Menu()
409         edit_menu.Append(ID_ExportText, 'Export text...')
410         edit_menu.Append(ID_ExportImage, 'Export image...')
411         edit_menu.AppendSeparator();
412         edit_menu.Append(ID_Config, 'Preferences')
413         #view
414         view_menu = wx.Menu()
415         view_menu.AppendCheckItem(ID_ViewFolders, 'Folders\tF5')
416         view_menu.AppendCheckItem(ID_ViewPlaylists, 'Playlists\tF6')
417         view_menu.AppendCheckItem(ID_ViewCommands, 'Commands\tF7')
418         view_menu.AppendCheckItem(ID_ViewProperties, 'Properties\tF8')
419         view_menu.AppendCheckItem(ID_ViewAssistant, 'Assistant\tF9')
420         view_menu.AppendCheckItem(ID_ViewResults, 'Results\tF10')
421         view_menu.AppendCheckItem(ID_ViewOutput, 'Output\tF11')
422         #perspectives
423         self._perspectives_menu = self.CreatePerspectivesMenu()
424         #help
425         help_menu = wx.Menu()
426         help_menu.Append(wx.ID_ABOUT, 'About Hooke')
427         #put it all together
428         menu_bar.Append(file_menu, 'File')
429         menu_bar.Append(edit_menu, 'Edit')
430         menu_bar.Append(view_menu, 'View')
431         menu_bar.Append(self._perspectives_menu, "Perspectives")
432         menu_bar.Append(help_menu, 'Help')
433
434         self.SetMenuBar(menu_bar)
435
436     def CreateNotebook(self):
437         # create the notebook off-window to avoid flicker
438         client_size = self.GetClientSize()
439         ctrl = aui.AuiNotebook(self, -1, wx.Point(client_size.x, client_size.y), wx.Size(430, 200), self._notebook_style)
440         arts = [aui.AuiDefaultTabArt, aui.AuiSimpleTabArt, aui.VC71TabArt, aui.FF2TabArt, aui.VC8TabArt, aui.ChromeTabArt]
441         art = arts[self._notebook_theme]()
442         ctrl.SetArtProvider(art)
443         #uncomment if we find a nice icon
444         #page_bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))
445         ctrl.AddPage(self.CreatePanelWelcome(), "Welcome", False)
446         return ctrl
447
448     def CreatePerspectivesMenu(self):
449         menu = wx.Menu()
450         menu.Append(ID_SavePerspective, "Save Perspective")
451         menu.Append(ID_DeletePerspective, "Delete Perspective")
452         menu.AppendSeparator()
453         #add perspectives to menubar and _perspectives
454         perspectivesDirectory = os.path.join(lh.hookeDir, 'perspectives')
455         if os.path.isdir(perspectivesDirectory):
456             perspectiveFileNames = os.listdir(perspectivesDirectory)
457             for perspectiveFilename in perspectiveFileNames:
458                 filename = lh.get_file_path(perspectiveFilename, ['perspectives'])
459                 if os.path.isfile(filename):
460                     perspectiveFile = open(filename, 'rU')
461                     perspective = perspectiveFile.readline()
462                     perspectiveFile.close()
463                     if perspective != '':
464                         name, extension = os.path.splitext(perspectiveFilename)
465                         if extension == '.txt':
466                             menuItem = menu.AppendRadioItem(ID_FirstPerspective + len(self._perspectives), name)
467                             self._perspectives[name] = [len(self._perspectives), perspective]
468                             if self.config['perspectives']['active'] == name:
469                                 menuItem.Check()
470         #in case there are no perspectives
471         if not self._perspectives:
472             perspective = self._mgr.SavePerspective()
473             self.config['perspectives']['default'] = 'Default'
474             self._perspectives['Default'] = [0, perspective]
475             menuItem = menu.AppendRadioItem(ID_FirstPerspective, 'Default')
476             menuItem.Check()
477             self._SavePerspectiveToFile('Default', perspective)
478         return menu
479
480     def CreateStatusbar(self):
481         statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
482         statusbar.SetStatusWidths([-2, -3])
483         statusbar.SetStatusText('Ready', 0)
484         welcomeString=u'Welcome to Hooke (version '+__version__+', '+__release_name__+')!'
485         statusbar.SetStatusText(welcomeString, 1)
486         return statusbar
487
488     def CreateToolBar(self):
489         toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)
490         toolbar.SetToolBitmapSize(wx.Size(16,16))
491         toolbar_bmp1 = wx.ArtProvider_GetBitmap(wx.ART_QUESTION, wx.ART_OTHER, wx.Size(16, 16))
492         toolbar_bmpOpen = wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN, wx.ART_OTHER, wx.Size(16, 16))
493         toolbar_bmpSave = wx.ArtProvider_GetBitmap(wx.ART_FILE_SAVE, wx.ART_OTHER, wx.Size(16, 16))
494         toolbar_bmpExportText = wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))
495         toolbar_bmpExportImage = wx.ArtProvider_GetBitmap(wx.ART_MISSING_IMAGE, wx.ART_OTHER, wx.Size(16, 16))
496         toolbar.AddLabelTool(101, 'Open', toolbar_bmpOpen)
497         toolbar.AddLabelTool(102, 'Save', toolbar_bmpSave)
498         toolbar.AddSeparator()
499         toolbar.AddLabelTool(ID_ExportText, 'Export text...', toolbar_bmpExportText)
500         toolbar.AddLabelTool(ID_ExportImage, 'Export image...', toolbar_bmpExportImage)
501         toolbar.Realize()
502         return toolbar
503
504     def CreateToolBarNavigation(self):
505         toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)
506         toolbar.SetToolBitmapSize(wx.Size(16,16))
507         toolbar_bmpBack = wx.ArtProvider_GetBitmap(wx.ART_GO_BACK, wx.ART_OTHER, wx.Size(16, 16))
508         toolbar_bmpForward = wx.ArtProvider_GetBitmap(wx.ART_GO_FORWARD, wx.ART_OTHER, wx.Size(16, 16))
509         toolbar.AddLabelTool(ID_Previous, 'Previous', toolbar_bmpBack, shortHelp='Previous curve')
510         toolbar.AddLabelTool(ID_Next, 'Next', toolbar_bmpForward, shortHelp='Next curve')
511         toolbar.Realize()
512         return toolbar
513
514     def DeleteFromPlaylists(self, name):
515         if name in self.playlists:
516             del self.playlists[name]
517         tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
518         item, cookie = self.panelPlaylists.PlaylistsTree.GetFirstChild(tree_root)
519         while item.IsOk():
520             playlist_name = self.panelPlaylists.PlaylistsTree.GetItemText(item)
521             if playlist_name == name:
522                 try:
523                     self.panelPlaylists.PlaylistsTree.Delete(item)
524                 except:
525                     pass
526             item = self.panelPlaylists.PlaylistsTree.GetNextSibling(item)
527         self.OnPlaylistsLeftDclick(None)
528
529     def DeletePlotPage(self, name):
530         index = self._GetPlaylistTab(name)
531         plot = self.playlists[name][1]
532         plot = None
533         self.plotNotebook.RemovePage(index)
534         self.DeleteFromPlaylists(name)
535
536     def GetActiveCurve(self):
537         playlist = self.GetActivePlaylist()
538         if playlist is not None:
539             return playlist.get_active_curve()
540         return None
541
542     def GetActivePlaylist(self):
543         playlist_name = self._GetActivePlaylistName()
544         if playlist_name in self.playlists:
545             return self.playlists[playlist_name][0]
546         return None
547
548     def GetActivePlot(self):
549         curve = self.GetActiveCurve()
550         if curve is not None:
551             return curve.plots[0]
552         return None
553
554     def GetDockArt(self):
555         return self._mgr.GetArtProvider()
556
557     def GetBoolFromConfig(self, *args):
558         if len(args) == 2:
559             plugin = args[0]
560             section = args[0]
561             key = args[1]
562         elif len(args) == 3:
563             plugin = args[0]
564             section = args[1]
565             key = args[2]
566         if self.configs.has_key(plugin):
567             config = self.configs[plugin]
568             return config[section][key].as_bool('value')
569         return None
570
571     def GetFloatFromConfig(self, *args):
572         if len(args) == 2:
573             plugin = args[0]
574             section = args[0]
575             key = args[1]
576         elif len(args) == 3:
577             plugin = args[0]
578             section = args[1]
579             key = args[2]
580         if self.configs.has_key(plugin):
581             config = self.configs[plugin]
582             return config[section][key].as_float('value')
583         return None
584
585     def GetIntFromConfig(self, *args):
586         if len(args) == 2:
587             plugin = args[0]
588             section = args[0]
589             key = args[1]
590         elif len(args) == 3:
591             plugin = args[0]
592             section = args[1]
593             key = args[2]
594         if self.configs.has_key(plugin):
595             config = self.configs[plugin]
596             return config[section][key].as_int('value')
597         return None
598
599     def GetStringFromConfig(self, *args):
600         if len(args) == 2:
601             plugin = args[0]
602             section = args[0]
603             key = args[1]
604         elif len(args) == 3:
605             plugin = args[0]
606             section = args[1]
607             key = args[2]
608         if self.configs.has_key(plugin):
609             config = self.configs[plugin]
610             return config[section][key]['value']
611         return None
612
613     def GetPerspectiveMenuItem(self, name):
614         index = self._perspectives[name][0]
615         perspective_Id = ID_FirstPerspective + index
616         menu_item = self.MenuBar.FindItemById(perspective_Id)
617         return menu_item
618
619     def HasPlotmanipulator(self, name):
620         '''
621         returns True if the plotmanipulator 'name' is loaded, False otherwise
622         '''
623         for plotmanipulator in self.plotmanipulators:
624             if plotmanipulator[0] == name:
625                 return True
626         return False
627
628     def OnAbout(self, event):
629         msg = 'Hooke\n\n'+\
630             'A free, open source data analysis platform\n'+\
631             '(c) 2006-2008 Massimo Sandal\n\n'+\
632             '(c) 2009 Dr. Rolf Schmidt\n\n'+\
633             'Released under the GNU GPL v2'
634         dialog = wx.MessageDialog(self, msg, "About Hooke", wx.OK | wx.ICON_INFORMATION)
635         dialog.ShowModal()
636         dialog.Destroy()
637
638     def OnClose(self, event):
639         #apply changes
640         self.config['main']['height'] = str(self.GetSize().GetHeight())
641         self.config['main']['left'] = str(self.GetPosition()[0])
642         self.config['main']['top'] = str(self.GetPosition()[1])
643         self.config['main']['width'] = str(self.GetSize().GetWidth())
644         # Writing the configuration file to 'hooke.ini'
645         self.config.write()
646         self._mgr.UnInit()
647         del self._mgr
648         self.Destroy()
649
650     def OnDeletePerspective(self, event):
651         pass
652
653     def OnDirCtrlLeftDclick(self, event):
654         file_path = self.panelFolders.GetPath()
655         if os.path.isfile(file_path):
656             if file_path.endswith('.hkp'):
657                 self.do_loadlist(file_path)
658             else:
659                 pass
660         event.Skip()
661
662     def OnEraseBackground(self, event):
663         event.Skip()
664
665     def OnExecute(self, event):
666         item = self.panelCommands.CommandsTree.GetSelection()
667         if item.IsOk():
668             if self.panelCommands.CommandsTree.ItemHasChildren(item):
669                 pass
670             else:
671                 #get the plugin
672                 parent = self.panelCommands.CommandsTree.GetItemParent(item)
673             if not self.panelCommands.CommandsTree.ItemHasChildren(item):
674                 parent_text = self.panelCommands.CommandsTree.GetItemText(parent)
675                 item_text = self.panelCommands.CommandsTree.GetItemText(item)
676                 if item_text in ['genlist', 'loadlist', 'savelist']:
677                     property_values = self.panelProperties.GetPropertyValues()
678                     arg_str = ''
679                     for item in property_values:
680                         arg_str = ''.join([arg_str, item, '=r"', str(property_values[item]), '", '])
681                     command = ''.join(['self.do_', item_text, '(', arg_str, ')'])
682                 else:
683                     command = ''.join(['self.do_', item_text, '()'])
684                 exec(command)
685         pass
686
687     def OnExit(self, event):
688         self.Close()
689
690     def OnExportImage(self, event):
691         pass
692
693     def OnNext(self, event):
694         '''
695         NEXT
696         Go to the next curve in the playlist.
697         If we are at the last curve, we come back to the first.
698         -----
699         Syntax: next, n
700         '''
701         selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
702         if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
703             #GetFirstChild returns a tuple
704             #we only need the first element
705             next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(selected_item)[0]
706         else:
707             next_item = self.panelPlaylists.PlaylistsTree.GetNextSibling(selected_item)
708             if not next_item.IsOk():
709                 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
710                 #GetFirstChild returns a tuple
711                 #we only need the first element
712                 next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(parent_item)[0]
713         self.panelPlaylists.PlaylistsTree.SelectItem(next_item, True)
714         playlist = self.playlists[self._GetActivePlaylistName()][0]
715         if playlist.count > 1:
716             playlist.next()
717             self.statusbar.SetStatusText(playlist.get_status_string(), 0)
718             self.UpdatePlot()
719
720     def OnNotebookPageClose(self, event):
721         ctrl = event.GetEventObject()
722         playlist_name = ctrl.GetPageText(ctrl._curpage)
723         self.DeleteFromPlaylists(playlist_name)
724
725     def OnPaneClose(self, event):
726         event.Skip()
727
728     def OnPlaylistsLeftDclick(self, event):
729         playlist_name = self._GetActivePlaylistName()
730         #if that playlist already exists
731         #we check if it is the active playlist (ie selected in panelPlaylists)
732         #and switch to it if necessary
733         if playlist_name in self.playlists:
734             index = self.plotNotebook.GetSelection()
735             current_playlist = self.plotNotebook.GetPageText(index)
736             #new_playlist = self.playlists[playlist_name][0]
737             #if current_playlist != new_playlist:
738             if current_playlist != playlist_name:
739                 index = self._GetPlaylistTab(playlist_name)
740                 self.plotNotebook.SetSelection(index)
741             #if a curve was double-clicked
742             item = self.panelPlaylists.PlaylistsTree.GetSelection()
743             #TODO: fix with get_active_curve
744             if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
745                 index = self._GetActiveCurveIndex()
746             else:
747                 index = 0
748             if index >= 0:
749                 playlist = self.playlists[playlist_name][0]
750                 playlist.index = index
751                 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
752                 self.UpdatePlot()
753         #if you uncomment the following line, the tree will collapse/expand as well
754         #event.Skip()
755
756     def OnPlaylistsLeftDown(self, event):
757         hit_item, hit_flags = self.panelPlaylists.PlaylistsTree.HitTest(event.GetPosition())
758         if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:
759             #self.SetFocus()
760             self.panelPlaylists.PlaylistsTree.SelectItem(hit_item)
761             playlist_name = self._GetActivePlaylistName()
762             playlist = self.playlists[playlist_name][0]
763             #if a curve was clicked
764             item = self.panelPlaylists.PlaylistsTree.GetSelection()
765             if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
766                 #TODO: fix with get_active_curve
767                 index = self._GetActiveCurveIndex()
768                 if index >= 0:
769                     #playlist = self.playlists[playlist_name][0]
770                     playlist.index = index
771                     #self.playlists[playlist_name][0].index = index
772             #else:
773                 ##self.playlists[playlist_name][0].index = 0
774                 #playlist.index = index
775             self.playlists[playlist_name][0] = playlist
776         event.Skip()
777
778     def OnPrevious(self, event):
779         '''
780         PREVIOUS
781         Go to the previous curve in the playlist.
782         If we are at the first curve, we jump to the last.
783         -------
784         Syntax: previous, p
785         '''
786         #playlist = self.playlists[self._GetActivePlaylistName()][0]
787         #select the previous curve and tell the user if we wrapped around
788         #self.AppendToOutput(playlist.previous())
789         selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
790         if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
791             previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(selected_item)
792         else:
793             previous_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
794             if not previous_item.IsOk():
795                 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
796                 previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(parent_item)
797         self.panelPlaylists.PlaylistsTree.SelectItem(previous_item, True)
798         playlist = self.playlists[self._GetActivePlaylistName()][0]
799         if playlist.count > 1:
800             playlist.previous()
801             self.statusbar.SetStatusText(playlist.get_status_string(), 0)
802             self.UpdatePlot()
803
804     def OnPropGridChanged (self, event):
805         prop = event.GetProperty()
806         if prop:
807             item_section = self.panelProperties.SelectedTreeItem
808             item_plugin = self.panelCommands.CommandsTree.GetItemParent(item_section)
809             plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
810             config = self.configs[plugin]
811             property_section = self.panelCommands.CommandsTree.GetItemText(item_section)
812             property_key = prop.GetName()
813             property_value = prop.GetValue()
814             config[property_section][property_key]['value'] = property_value
815
816     def OnPropGridSelect(self, event):
817         pass
818
819     def OnRestorePerspective(self, event):
820         name = self.MenuBar.FindItemById(event.GetId()).GetLabel()
821         self._mgr.LoadPerspective(self._perspectives[name][1])
822         self.config['perspectives']['active'] = name
823         self._mgr.Update()
824         all_panes = self._mgr.GetAllPanes()
825         for pane in all_panes:
826             if not pane.name.startswith('toolbar'):
827                 if pane.name == 'Assistant':
828                     self.MenuBar.FindItemById(ID_ViewAssistant).Check(pane.window.IsShown())
829                 if pane.name == 'Folders':
830                     self.MenuBar.FindItemById(ID_ViewFolders).Check(pane.window.IsShown())
831                 if pane.name == 'Playlists':
832                     self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown())
833                 if pane.name == 'Commands':
834                     self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown())
835                 if pane.name == 'Properties':
836                     self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown())
837                 if pane.name == 'Output':
838                     self.MenuBar.FindItemById(ID_ViewOutput).Check(pane.window.IsShown())
839                 if pane.name == 'Results':
840                     self.MenuBar.FindItemById(ID_ViewResults).Check(pane.window.IsShown())
841
842     def OnResultsCheck(self, index, flag):
843         curve = self.GetActiveCurve()
844         result = curve.data['results'][index]['visible'] = flag
845         self.UpdatePlot()
846
847     def OnSavePerspective(self, event):
848         def nameExists(name):
849             for item in self._perspectives_menu.GetMenuItems():
850                 if item.GetText() == name:
851                     return True
852             return False
853
854         done = False
855         while not done:
856             dialog = wx.TextEntryDialog(self, 'Enter a name for the new perspective:', 'Save perspective')
857             dialog.SetValue('New perspective')
858             if dialog.ShowModal() != wx.ID_OK:
859                 return
860             else:
861                 name = dialog.GetValue()
862
863             if nameExists(name):
864                 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)
865                 if dialogConfirm.ShowModal() == wx.ID_YES:
866                     done = True
867             else:
868                 done = True
869
870         perspective = self._mgr.SavePerspective()
871
872         if nameExists(name):
873             #check the corresponding menu item
874             menuItem = self.GetPerspectiveMenuItem(name)
875             #replace the perspectiveStr in _pespectives
876             index = self._perspectives[name][0]
877             self._perspectives[name] = [index, perspective]
878         else:
879             #simply add the new perspective to _perspectives
880             index = len(self._perspectives)
881             self._perspectives[name] = [len(self._perspectives), perspective]
882             menuItem = self._perspectives_menu.AppendRadioItem(ID_FirstPerspective + len(self._perspectives), name)
883
884         menuItem.Check()
885         self._SavePerspectiveToFile(name, perspective)
886         #uncheck all perspective menu items
887         #as these are radio items, one item has to be checked at all times
888         #the line 'menuItem.Check()' above actually checks a second item
889         #but does not toggle
890         #so we need to uncheck all other items afterwards
891         #weirdly enough, menuitem.Toggle() doesn't do this properly either
892         for item in self._perspectives_menu.GetMenuItems():
893             if item.IsCheckable():
894                 if item.GetLabel() != name:
895                     item.Check(False)
896
897     def OnView(self, event):
898         menu_id = event.GetId()
899         menu_item = self.MenuBar.FindItemById(menu_id)
900         menu_label = menu_item.GetLabel()
901
902         pane = self._mgr.GetPane(menu_label)
903         pane.Show(not pane.IsShown())
904         #if we don't do the following, the Folders pane does not resize properly on hide/show
905         if pane.caption == 'Folders' and pane.IsShown() and pane.IsDocked():
906             #folders_size = pane.GetSize()
907             self.panelFolders.Fit()
908         self._mgr.Update()
909
910     def OnSize(self, event):
911         event.Skip()
912
913     def OnTreeCtrlCommandsLeftDown(self, event):
914         hit_item, hit_flags = self.panelCommands.CommandsTree.HitTest(event.GetPosition())
915         if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:
916             self.panelCommands.CommandsTree.SelectItem(hit_item)
917             self.panelProperties.SelectedTreeItem = hit_item
918             #if a command was clicked
919             properties = []
920             if not self.panelCommands.CommandsTree.ItemHasChildren(hit_item):
921                 item_plugin = self.panelCommands.CommandsTree.GetItemParent(hit_item)
922                 plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
923                 if self.configs.has_key(plugin):
924                     #config = self.panelCommands.CommandsTree.GetPyData(item_plugin)
925                     config = self.configs[plugin]
926                     section = self.panelCommands.CommandsTree.GetItemText(hit_item)
927                     #display docstring in help window
928                     doc_string = eval('self.do_' + section + '.__doc__')
929                     if section in config:
930                         for option in config[section]:
931                             properties.append([option, config[section][option]])
932             else:
933                 module = self.panelCommands.CommandsTree.GetItemText(hit_item)
934                 if module != 'general':
935                     doc_string = eval('plugins.' + module + '.' + module + 'Commands.__doc__')
936                 else:
937                     doc_string = 'The module "general" contains Hooke core functionality'
938             if doc_string is not None:
939                 self.panelAssistant.ChangeValue(doc_string)
940             hookepropertyeditor.PropertyEditor.Initialize(self.panelProperties, properties)
941         event.Skip()
942
943     def UpdatePlaylists(self):
944         #setup the playlist in the Playlist tree
945         tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
946         playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)
947         #add all curves to the Playlist tree
948         curves = {}
949         for index, curve in enumerate(playlist.curves):
950             ##remove the extension from the name of the curve
951             ##TODO: optional?
952             #item_text, extension = os.path.splitext(curve.name)
953             #curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, item_text, 1)
954             curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, curve.name, 1)
955             if index == playlist.index:
956                 self.panelPlaylists.PlaylistsTree.SelectItem(curve_ID)
957         #create the plot tab and add playlist to the dictionary
958         plotPanel = wxmpl.PlotPanel(self, ID_FirstPlot + len(self.playlists))
959         notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True)
960         #tab_index = self.plotNotebook.GetSelection()
961         figure = plotPanel.get_figure()
962         #self.playlists[playlist.name] = [playlist, tab_index, figure]
963         self.playlists[playlist.name] = [playlist, figure]
964         self.panelPlaylists.PlaylistsTree.Expand(playlist_root)
965         self.statusbar.SetStatusText(playlist.get_status_string(), 0)
966         self.UpdatePlot()
967
968 #HELPER FUNCTIONS
969 #Everything sending an event should be here
970     def _measure_N_points(self, N, whatset=1):
971         '''
972         general helper function for N-points measures
973         '''
974         wx.PostEvent(self.frame,self.list_of_events['measure_points'](num_of_points=N, set=whatset))
975         while 1:
976             try:
977                 points=self.frame.events_from_gui.get()
978                 break
979             except Empty:
980                 pass
981         return points
982
983     def _clickize(self, xvector, yvector, index):
984         '''
985         returns a ClickedPoint() object from an index and vectors of x, y coordinates
986         '''
987         point = lh.ClickedPoint()
988         point.index = index
989         point.absolute_coords = xvector[index], yvector[index]
990         point.find_graph_coords(xvector, yvector)
991         return point
992
993 #PLAYLIST INTERACTION COMMANDS
994 #-------------------------------
995     def do_genlist(self, folder=lh.hookeDir, filemask='*.*'):
996         '''
997         GENLIST
998         Generates a file playlist.
999         Note it doesn't *save* it: see savelist for this.
1000
1001         If [input files] is a directory, it will use all files in the directory for playlist.
1002         So:
1003         genlist dir
1004         genlist dir/
1005         genlist dir/*.*
1006
1007         are all equivalent syntax.
1008         ------------
1009         Syntax: genlist [input files]
1010         '''
1011         #args list is: input folder, file mask
1012         if os.path.isdir(folder):
1013             path = os.path.join(folder, filemask)
1014             #expanding correctly the input list with the glob module :)
1015             files = glob.glob(path)
1016             files.sort()
1017             #TODO: change cursor or progressbar (maybe in statusbar)
1018             #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
1019             playlist = playlist.Playlist(self.drivers)
1020             for item in files:
1021                 curve = playlist.add_curve(item)
1022                 plot = copy.deepcopy(curve.plots[0])
1023                 #add the 'raw' data
1024                 curve.add_data('raw', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')
1025                 curve.add_data('raw', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')
1026                 #apply all active plotmanipulators and add the 'manipulated' data
1027                 for plotmanipulator in self.plotmanipulators:
1028                     plot = plotmanipulator[1](plot, curve)
1029                     curve.set_data('manipulated', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')
1030                     curve.add_data('manipulated', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')
1031             if playlist.count > 0:
1032                 playlist.name = self._GetUniquePlaylistName(os.path.basename(folder))
1033                 playlist.reset()
1034                 self.AddToPlaylists(playlist)
1035             self.AppendToOutput(playlist.get_status_string())
1036         else:
1037             self.AppendToOutput(''.join(['Cannot find folder ', folder]))
1038
1039     def do_loadlist(self, filename):
1040         '''
1041         LOADLIST
1042         Loads a file playlist
1043         -----------
1044         Syntax: loadlist [playlist file]
1045         '''
1046         #TODO: check for duplicate playlists, ask the user for a unique name
1047         #if self.playlist_name in self.playlists:
1048
1049         #add hkp extension if necessary
1050         if not filename.endswith('.hkp'):
1051             filename = ''.join([filename, '.hkp'])
1052         #prefix with 'hookeDir' if just a filename or a relative path
1053         filename = lh.get_file_path(filename)
1054         if os.path.isfile(filename):
1055             #TODO: change cursor
1056             #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
1057             playlist_new = playlist.Playlist(self.drivers)
1058             playlist_new.load(filename)
1059             if playlist_new.count > 0:
1060                 for curve in playlist_new.curves:
1061                     plot = copy.deepcopy(curve.plots[0])
1062                     for plotmanip in self.plotmanipulators:
1063                         #to_plot = plotmanip[1](to_plot, curve)
1064                         plot = plotmanip[1](plot, curve)
1065                         curve.set_data('manipulated', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')
1066                         curve.add_data('manipulated', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')
1067                 self.AddToPlaylists(playlist_new)
1068             #else:
1069                 ##TODO: display dialog
1070             self.AppendToOutput(playlist_new.get_status_string())
1071             #TODO: change cursor
1072             #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
1073         else:
1074             #TODO: display dialog
1075             self.AppendToOutput(''.join['File ', filename, ' not found.\n'])
1076         pass
1077
1078     def do_savelist(self, filename):
1079         '''
1080         SAVELIST
1081         Saves the current file playlist on disk.
1082         ------------
1083         Syntax: savelist [filename]
1084         '''
1085
1086         #self.playlist_generics['pointer'] = self._GetActiveCurveIndex
1087         pointer = self._GetActiveCurveIndex()
1088         #autocomplete filename if not specified
1089         if not filename.endswith('.hkp'):
1090             filename = filename.join(['.hkp'])
1091
1092         playlist = self.GetActivePlaylist()
1093         playlist.set_XML()
1094         playlist.save(filename)
1095
1096 #PLOT INTERACTION COMMANDS
1097 #-------------------------------
1098     def UpdatePlot(self):
1099         def add_plot(plot):
1100             if plot['visible'] and plot['x'] and plot['y']:
1101                 color = plot['color']
1102                 style = plot['style']
1103                 if style == 'plot':
1104                     axes.plot(plot['x'], plot['y'], color=color, zorder=1)
1105                 if style == 'scatter':
1106                     axes.scatter(plot['x'], plot['y'], color=color, s=0.5, zorder=2)
1107
1108         def add_plot2(plot):
1109             if plot.visible and plot.x and plot.y:
1110                 if plot.style == 'plot':
1111                     axes.plot(plot.x, plot.y, color=plot.color, zorder=1)
1112                 if plot.style == 'scatter':
1113                     axes.scatter(plot.x, plot.y, color=plot.color, s=0.5, zorder=2)
1114
1115         playlist_name = self._GetActivePlaylistName()
1116         index = self._GetActiveCurveIndex()
1117         playlist = self.playlists[playlist_name][0]
1118         curve = playlist.get_active_curve()
1119         plot = playlist.get_active_plot()
1120         figure = self.playlists[playlist_name][1]
1121
1122         figure.clf()
1123         exclude = None
1124         if curve.data.has_key('manipulated'):
1125             exclude = 'raw'
1126         elif curve.data.has_key('raw'):
1127             exclude = 'manipulated'
1128
1129         if exclude is not None:
1130             #TODO: what is this good for?
1131             if not hasattr(self, 'subplot'):
1132                 axes = figure.add_subplot(111)
1133
1134             axes.set_title(plot.title)
1135             axes.set_xlabel(plot.units[0])
1136             axes.set_ylabel(plot.units[1])
1137
1138             for set_of_plots in curve.data:
1139                 if set_of_plots != exclude:
1140                     plots = curve.data[set_of_plots]
1141                     for each_plot in plots:
1142                         add_plot(each_plot)
1143
1144             #TODO: add multiple results support
1145             #for fit in curve.fits:
1146             if curve.fits.has_key('wlc'):
1147                 for plot in curve.fits['wlc'].results:
1148                     add_plot2(plot)
1149                 self.panelResults.DisplayResults(curve.fits['wlc'])
1150             else:
1151                 self.panelResults.ClearResults()
1152
1153             axes.figure.canvas.draw()
1154         else:
1155             self.AppendToOutput('Not able to plot.')
1156
1157
1158 if __name__ == '__main__':
1159     app = Hooke(debug=__debug__)
1160     app.MainLoop()
1161
1162