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