1 #!/usr/bin/env python
\r
4 HOOKE - A force spectroscopy review & analysis tool
\r
6 Copyright 2008 by Massimo Sandal (University of Bologna, Italy)
\r
7 Copyright 2010 by Rolf Schmidt (Concordia University, Canada)
\r
9 This program is released under the GNU General Public License version 2.
\r
12 import lib.libhooke as lh
\r
14 wxversion.select(lh.WX_GOOD)
\r
16 from configobj import ConfigObj
\r
23 import wx.lib.agw.aui as aui
\r
24 import wx.lib.evtmgr as evtmgr
\r
25 import wx.propgrid as wxpg
\r
27 from matplotlib import __version__ as mpl_version
\r
28 from numpy import __version__ as numpy_version
\r
29 from scipy import __version__ as scipy_version
\r
30 from sys import version as python_version
\r
31 from wx import __version__ as wx_version
\r
32 from matplotlib.ticker import FuncFormatter
\r
35 from agw import cubecolourdialog as CCD
\r
36 except ImportError: # if it's not there locally, try the wxPython lib.
\r
37 import wx.lib.agw.cubecolourdialog as CCD
\r
39 #set the Hooke directory
\r
40 lh.hookeDir = os.path.abspath(os.path.dirname(__file__))
\r
41 from config.config import config
\r
45 import lib.plotmanipulator
\r
46 import lib.prettyformat
\r
47 import panels.commands
\r
48 import panels.perspectives
\r
49 import panels.playlist
\r
51 import panels.propertyeditor
\r
52 import panels.results
\r
59 global __releasedate__
\r
60 __version__ = lh.HOOKE_VERSION[0]
\r
61 __codename__ = lh.HOOKE_VERSION[1]
\r
62 __releasedate__ = lh.HOOKE_VERSION[2]
\r
63 __release_name__ = lh.HOOKE_VERSION[1]
\r
65 #TODO: add general preferences to Hooke
\r
66 #this might be useful
\r
67 #ID_Config = wx.NewId()
\r
68 ID_About = wx.NewId()
\r
69 ID_Next = wx.NewId()
\r
70 ID_Previous = wx.NewId()
\r
72 ID_ViewAssistant = wx.NewId()
\r
73 ID_ViewCommands = wx.NewId()
\r
74 ID_ViewFolders = wx.NewId()
\r
75 ID_ViewOutput = wx.NewId()
\r
76 ID_ViewPlaylists = wx.NewId()
\r
77 ID_ViewProperties = wx.NewId()
\r
78 ID_ViewResults = wx.NewId()
\r
80 ID_DeletePerspective = wx.NewId()
\r
81 ID_SavePerspective = wx.NewId()
\r
83 ID_FirstPerspective = ID_SavePerspective + 1000
\r
84 #I hope we'll never have more than 1000 perspectives
\r
85 ID_FirstPlot = ID_SavePerspective + 2000
\r
87 class Hooke(wx.App):
\r
90 self.SetAppName('Hooke')
\r
91 self.SetVendorName('')
\r
93 windowPosition = (config['main']['left'], config['main']['top'])
\r
94 windowSize = (config['main']['width'], config['main']['height'])
\r
96 #setup the splashscreen
\r
97 if config['splashscreen']['show']:
\r
98 filename = lh.get_file_path('hooke.jpg', ['resources'])
\r
99 if os.path.isfile(filename):
\r
100 bitmap = wx.Image(filename).ConvertToBitmap()
\r
101 splashStyle = wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT
\r
102 splashDuration = config['splashscreen']['duration']
\r
103 wx.SplashScreen(bitmap, splashStyle, splashDuration, None, -1)
\r
106 we need for the splash screen to disappear
\r
107 for whatever reason splashDuration and sleep do not correspond to each other
\r
108 at least not on Windows
\r
109 maybe it's because duration is in milliseconds and sleep in seconds
\r
110 thus we need to increase the sleep time a bit
\r
111 a factor of 1.2 seems to work quite well
\r
114 time.sleep(sleepFactor * splashDuration / 1000)
\r
116 plugin_objects = []
\r
117 for plugin in config['plugins']:
\r
118 if config['plugins'][plugin]:
\r
119 filename = ''.join([plugin, '.py'])
\r
120 path = lh.get_file_path(filename, ['plugins'])
\r
121 if os.path.isfile(path):
\r
122 #get the corresponding filename and path
\r
123 plugin_name = ''.join(['plugins.', plugin])
\r
125 __import__(plugin_name)
\r
126 #get the file that contains the plugin
\r
127 class_file = getattr(plugins, plugin)
\r
128 #get the class that contains the commands
\r
129 class_object = getattr(class_file, plugin + 'Commands')
\r
130 plugin_objects.append(class_object)
\r
132 def make_command_class(*bases):
\r
133 #create metaclass with plugins and plotmanipulators
\r
134 return type(HookeFrame)("HookeFramePlugged", bases + (HookeFrame,), {})
\r
135 frame = make_command_class(*plugin_objects)(parent=None, id=wx.ID_ANY, title='Hooke', pos=windowPosition, size=windowSize)
\r
137 self.SetTopWindow(frame)
\r
145 class HookeFrame(wx.Frame):
\r
147 def __init__(self, parent, id=-1, title='', pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE|wx.SUNKEN_BORDER|wx.CLIP_CHILDREN):
\r
148 #call parent constructor
\r
149 wx.Frame.__init__(self, parent, id, title, pos, size, style)
\r
150 self.config = config
\r
151 self.CreateApplicationIcon()
\r
152 #self.configs contains: {the name of the Commands file: corresponding ConfigObj}
\r
154 #self.displayed_plot holds the currently displayed plot
\r
155 self.displayed_plot = None
\r
156 #self.playlists contains: {the name of the playlist: [playlist, tabIndex, plotID]}
\r
157 self.playlists = {}
\r
158 #list of all plotmanipulators
\r
159 self.plotmanipulators = []
\r
160 #self.plugins contains: {the name of the plugin: [caption, function]}
\r
162 #self.results_str contains the type of results we want to display
\r
163 self.results_str = 'wlc'
\r
165 #tell FrameManager to manage this frame
\r
166 self._mgr = aui.AuiManager()
\r
167 self._mgr.SetManagedWindow(self)
\r
168 #set the gradient style
\r
169 self._mgr.GetArtProvider().SetMetric(aui.AUI_DOCKART_GRADIENT_TYPE, aui.AUI_GRADIENT_NONE)
\r
170 #set transparent drag
\r
171 self._mgr.SetFlags(self._mgr.GetFlags() ^ aui.AUI_MGR_TRANSPARENT_DRAG)
\r
173 # set up default notebook style
\r
174 self._notebook_style = aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER
\r
175 self._notebook_theme = 0
\r
177 #holds the perspectives: {name, perspective_str}
\r
178 self._perspectives = {}
\r
180 # min size for the frame itself isn't completely done.
\r
181 # see the end up FrameManager::Update() for the test
\r
182 # code. For now, just hard code a frame minimum size
\r
183 self.SetMinSize(wx.Size(500, 500))
\r
184 #create panels here
\r
185 self.panelAssistant = self.CreatePanelAssistant()
\r
186 self.panelCommands = self.CreatePanelCommands()
\r
187 self.panelFolders = self.CreatePanelFolders()
\r
188 self.panelPlaylists = self.CreatePanelPlaylists()
\r
189 self.panelProperties = self.CreatePanelProperties()
\r
190 self.panelOutput = self.CreatePanelOutput()
\r
191 self.panelResults = self.CreatePanelResults()
\r
192 self.plotNotebook = self.CreateNotebook()
\r
193 #self.textCtrlCommandLine=self.CreateCommandLine()
\r
196 self._mgr.AddPane(self.panelFolders, aui.AuiPaneInfo().Name('Folders').Caption('Folders').Left().CloseButton(True).MaximizeButton(False))
\r
197 self._mgr.AddPane(self.panelPlaylists, aui.AuiPaneInfo().Name('Playlists').Caption('Playlists').Left().CloseButton(True).MaximizeButton(False))
\r
198 self._mgr.AddPane(self.plotNotebook, aui.AuiPaneInfo().Name('Plots').CenterPane().PaneBorder(False))
\r
199 self._mgr.AddPane(self.panelCommands, aui.AuiPaneInfo().Name('Commands').Caption('Settings and commands').Right().CloseButton(True).MaximizeButton(False))
\r
200 self._mgr.AddPane(self.panelProperties, aui.AuiPaneInfo().Name('Properties').Caption('Properties').Right().CloseButton(True).MaximizeButton(False))
\r
201 self._mgr.AddPane(self.panelAssistant, aui.AuiPaneInfo().Name('Assistant').Caption('Assistant').Right().CloseButton(True).MaximizeButton(False))
\r
202 self._mgr.AddPane(self.panelOutput, aui.AuiPaneInfo().Name('Output').Caption('Output').Bottom().CloseButton(True).MaximizeButton(False))
\r
203 self._mgr.AddPane(self.panelResults, aui.AuiPaneInfo().Name('Results').Caption('Results').Bottom().CloseButton(True).MaximizeButton(False))
\r
204 #self._mgr.AddPane(self.textCtrlCommandLine, aui.AuiPaneInfo().Name('CommandLine').CaptionVisible(False).Fixed().Bottom().Layer(2).CloseButton(False).MaximizeButton(False))
\r
205 #self._mgr.AddPane(panelBottom, aui.AuiPaneInfo().Name("panelCommandLine").Bottom().Position(1).CloseButton(False).MaximizeButton(False))
\r
207 # add the toolbars to the manager
\r
208 #self.toolbar=self.CreateToolBar()
\r
209 self.toolbarNavigation=self.CreateToolBarNavigation()
\r
210 #self._mgr.AddPane(self.toolbar, aui.AuiPaneInfo().Name('toolbar').Caption('Toolbar').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
\r
211 self._mgr.AddPane(self.toolbarNavigation, aui.AuiPaneInfo().Name('toolbarNavigation').Caption('Navigation').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
\r
212 # "commit" all changes made to FrameManager
\r
214 #create the menubar after the panes so that the default perspective
\r
215 #is created with all panes open
\r
216 self.CreateMenuBar()
\r
217 self.statusbar = self.CreateStatusbar()
\r
220 name = self.config['perspectives']['active']
\r
221 menu_item = self.GetPerspectiveMenuItem(name)
\r
222 if menu_item is not None:
\r
223 self.OnRestorePerspective(menu_item)
\r
224 #TODO: config setting to remember playlists from last session
\r
225 self.playlists = self.panelPlaylists.Playlists
\r
226 #define the list of active drivers
\r
228 for driver in self.config['drivers']:
\r
229 if self.config['drivers'][driver]:
\r
230 #get the corresponding filename and path
\r
231 filename = ''.join([driver, '.py'])
\r
232 path = lh.get_file_path(filename, ['drivers'])
\r
233 #the driver is active for driver[1] == 1
\r
234 if os.path.isfile(path):
\r
235 #driver files are located in the 'drivers' subfolder
\r
236 driver_name = ''.join(['drivers.', driver])
\r
237 __import__(driver_name)
\r
238 class_file = getattr(drivers, driver)
\r
239 for command in dir(class_file):
\r
240 if command.endswith('Driver'):
\r
241 self.drivers.append(getattr(class_file, command))
\r
242 #import all active plugins and plotmanips
\r
243 #add 'core.ini' to self.configs (this is not a plugin and thus must be imported separately)
\r
244 ini_path = lh.get_file_path('core.ini', ['plugins'])
\r
245 plugin_config = ConfigObj(ini_path)
\r
246 #self.config.merge(plugin_config)
\r
247 self.configs['core'] = plugin_config
\r
248 #make sure we execute _plug_init() for every command line plugin we import
\r
249 for plugin in self.config['plugins']:
\r
250 if self.config['plugins'][plugin]:
\r
251 filename = ''.join([plugin, '.py'])
\r
252 path = lh.get_file_path(filename, ['plugins'])
\r
253 if os.path.isfile(path):
\r
254 #get the corresponding filename and path
\r
255 plugin_name = ''.join(['plugins.', plugin])
\r
258 module = __import__(plugin_name)
\r
259 #prepare the ini file for inclusion
\r
260 ini_path = path.replace('.py', '.ini')
\r
262 plugin_config = ConfigObj(ini_path)
\r
263 #self.config.merge(plugin_config)
\r
264 self.configs[plugin] = plugin_config
\r
266 commands = eval('dir(module.' + plugin+ '.' + plugin + 'Commands)')
\r
267 #keep only commands (ie names that start with 'do_')
\r
268 #TODO: check for existing commands and warn the user!
\r
269 commands = [command for command in commands if command.startswith('do_')]
\r
271 self.plugins[plugin] = commands
\r
273 #initialize the plugin
\r
274 eval('module.' + plugin+ '.' + plugin + 'Commands._plug_init(self)')
\r
275 except AttributeError:
\r
277 except ImportError:
\r
279 #initialize the commands tree
\r
280 commands = dir(HookeFrame)
\r
281 commands = [command for command in commands if command.startswith('do_')]
\r
283 self.plugins['core'] = commands
\r
284 self.panelCommands.Initialize(self.plugins)
\r
285 for command in dir(self):
\r
286 if command.startswith('plotmanip_'):
\r
287 self.plotmanipulators.append(lib.plotmanipulator.Plotmanipulator(method=getattr(self, command), command=command))
\r
289 #load default list, if possible
\r
290 self.do_loadlist(self.config['core']['list'])
\r
291 #self.do_loadlist()
\r
293 def _BindEvents(self):
\r
294 #TODO: figure out if we can use the eventManager for menu ranges
\r
295 #and events of 'self' without raising an assertion fail error
\r
296 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
\r
297 self.Bind(wx.EVT_SIZE, self.OnSize)
\r
298 self.Bind(wx.EVT_CLOSE, self.OnClose)
\r
299 # Show How To Use The Closing Panes Event
\r
300 self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPaneClose)
\r
301 self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnNotebookPageClose)
\r
303 evtmgr.eventManager.Register(self.OnAbout, wx.EVT_MENU, win=self, id=wx.ID_ABOUT)
\r
304 evtmgr.eventManager.Register(self.OnClose, wx.EVT_MENU, win=self, id=wx.ID_EXIT)
\r
306 self.Bind(wx.EVT_MENU_RANGE, self.OnView, id=ID_ViewAssistant, id2=ID_ViewResults)
\r
308 self.Bind(wx.EVT_MENU, self.OnDeletePerspective, id=ID_DeletePerspective)
\r
309 self.Bind(wx.EVT_MENU, self.OnSavePerspective, id=ID_SavePerspective)
\r
310 self.Bind(wx.EVT_MENU_RANGE, self.OnRestorePerspective, id=ID_FirstPerspective, id2=ID_FirstPerspective+1000)
\r
312 evtmgr.eventManager.Register(self.OnNext, wx.EVT_TOOL, win=self, id=ID_Next)
\r
313 evtmgr.eventManager.Register(self.OnPrevious, wx.EVT_TOOL, win=self, id=ID_Previous)
\r
314 #self.Bind(.EVT_AUITOOLBAR_TOOL_DROPDOWN, self.OnDropDownToolbarItem, id=ID_DropDownToolbarItem)
\r
316 treeCtrl = self.panelFolders.GetTreeCtrl()
\r
317 #tree.Bind(wx.EVT_LEFT_UP, self.OnDirCtrl1LeftUp)
\r
318 #tree.Bind(wx.EVT_LEFT_DOWN, self.OnGenericDirCtrl1LeftDown)
\r
319 treeCtrl.Bind(wx.EVT_LEFT_DCLICK, self.OnDirCtrlLeftDclick)
\r
321 self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DOWN, self.OnPlaylistsLeftDown)
\r
322 self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DCLICK, self.OnPlaylistsLeftDclick)
\r
324 evtmgr.eventManager.Register(self.OnExecute, wx.EVT_BUTTON, self.panelCommands.ExecuteButton)
\r
325 evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self.panelCommands.CommandsTree)
\r
326 evtmgr.eventManager.Register(self.OnTreeCtrlItemActivated, wx.EVT_TREE_ITEM_ACTIVATED, self.panelCommands.CommandsTree)
\r
328 self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged)
\r
330 self.panelResults.results_list.OnCheckItem = self.OnResultsCheck
\r
332 def _GetActiveFileIndex(self):
\r
333 lib.playlist.Playlist = self.GetActivePlaylist()
\r
334 #get the selected item from the tree
\r
335 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
336 #test if a playlist or a curve was double-clicked
\r
337 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
341 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
342 while selected_item.IsOk():
\r
344 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
347 def _GetPlaylistTab(self, name):
\r
348 for index, page in enumerate(self.plotNotebook._tabs._pages):
\r
349 if page.caption == name:
\r
353 def _GetUniquePlaylistName(self, name):
\r
354 playlist_name = name
\r
356 while playlist_name in self.playlists:
\r
357 playlist_name = ''.join([name, str(count)])
\r
359 return playlist_name
\r
361 def _RestorePerspective(self, name):
\r
362 self._mgr.LoadPerspective(self._perspectives[name])
\r
363 self.config['perspectives']['active'] = name
\r
365 all_panes = self._mgr.GetAllPanes()
\r
366 for pane in all_panes:
\r
367 if not pane.name.startswith('toolbar'):
\r
368 if pane.name == 'Assistant':
\r
369 self.MenuBar.FindItemById(ID_ViewAssistant).Check(pane.window.IsShown())
\r
370 if pane.name == 'Folders':
\r
371 self.MenuBar.FindItemById(ID_ViewFolders).Check(pane.window.IsShown())
\r
372 if pane.name == 'Playlists':
\r
373 self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown())
\r
374 if pane.name == 'Commands':
\r
375 self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown())
\r
376 if pane.name == 'Properties':
\r
377 self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown())
\r
378 if pane.name == 'Output':
\r
379 self.MenuBar.FindItemById(ID_ViewOutput).Check(pane.window.IsShown())
\r
380 if pane.name == 'Results':
\r
381 self.MenuBar.FindItemById(ID_ViewResults).Check(pane.window.IsShown())
\r
383 def _SavePerspectiveToFile(self, name, perspective):
\r
384 filename = ''.join([name, '.txt'])
\r
385 filename = lh.get_file_path(filename, ['perspectives'])
\r
386 perspectivesFile = open(filename, 'w')
\r
387 perspectivesFile.write(perspective)
\r
388 perspectivesFile.close()
\r
390 def _UnbindEvents(self):
\r
392 evtmgr.eventManager.DeregisterListener(self.OnAbout)
\r
393 evtmgr.eventManager.DeregisterListener(self.OnClose)
\r
395 evtmgr.eventManager.DeregisterListener(self.OnNext)
\r
396 evtmgr.eventManager.DeregisterListener(self.OnPrevious)
\r
398 evtmgr.eventManager.DeregisterListener(self.OnExecute)
\r
399 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlCommandsSelectionChanged)
\r
401 def AddPlaylist(self, playlist=None, name='Untitled'):
\r
402 if playlist and playlist.count > 0:
\r
403 playlist.name = self._GetUniquePlaylistName(name)
\r
405 self.AddToPlaylists(playlist)
\r
407 def AddPlaylistFromFiles(self, files=[], name='Untitled'):
\r
409 playlist = lib.playlist.Playlist(self, self.drivers)
\r
411 playlist.add_curve(item)
\r
412 if playlist.count > 0:
\r
413 playlist.name = self._GetUniquePlaylistName(name)
\r
415 self.AddTayliss(playlist)
\r
417 def AddToPlaylists(self, playlist):
\r
418 if playlist.count > 0:
\r
419 #setup the playlist in the Playlist tree
\r
420 tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
\r
421 playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)
\r
422 #add all files to the Playlist tree
\r
424 for index, file_to_add in enumerate(playlist.files):
\r
425 #TODO: optionally remove the extension from the name of the curve
\r
426 #item_text, extension = os.path.splitext(curve.name)
\r
427 #curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, item_text, 1)
\r
428 file_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, file_to_add.name, 1)
\r
429 if index == playlist.index:
\r
430 self.panelPlaylists.PlaylistsTree.SelectItem(file_ID)
\r
432 #create the plot tab and add playlist to the dictionary
\r
433 plotPanel = panels.plot.PlotPanel(self, ID_FirstPlot + len(self.playlists))
\r
434 notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True)
\r
435 #tab_index = self.plotNotebook.GetSelection()
\r
436 playlist.figure = plotPanel.get_figure()
\r
437 self.playlists[playlist.name] = playlist
\r
438 #self.playlists[playlist.name] = [playlist, figure]
\r
439 self.panelPlaylists.PlaylistsTree.Expand(playlist_root)
\r
440 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
443 def AppendToOutput(self, text):
\r
444 self.panelOutput.AppendText(''.join([text, '\n']))
\r
446 def AppliesPlotmanipulator(self, name):
\r
448 Returns True if the plotmanipulator 'name' is applied, False otherwise
\r
449 name does not contain 'plotmanip_', just the name of the plotmanipulator (e.g. 'flatten')
\r
451 return self.GetBoolFromConfig('core', 'plotmanipulators', name)
\r
453 def ApplyPlotmanipulators(self, plot, plot_file):
\r
455 Apply all active plotmanipulators.
\r
457 if plot is not None and plot_file is not None:
\r
458 manipulated_plot = copy.deepcopy(plot)
\r
459 for plotmanipulator in self.plotmanipulators:
\r
460 if self.GetBoolFromConfig('core', 'plotmanipulators', plotmanipulator.name):
\r
461 manipulated_plot = plotmanipulator.method(manipulated_plot, plot_file)
\r
462 return manipulated_plot
\r
464 def CreateApplicationIcon(self):
\r
465 iconFile = 'resources' + os.sep + 'microscope.ico'
\r
466 icon = wx.Icon(iconFile, wx.BITMAP_TYPE_ICO)
\r
469 def CreateCommandLine(self):
\r
470 return wx.TextCtrl(self, -1, '', style=wx.NO_BORDER|wx.EXPAND)
\r
472 def CreatePanelAssistant(self):
\r
473 panel = wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
\r
474 panel.SetEditable(False)
\r
477 def CreatePanelCommands(self):
\r
478 return panels.commands.Commands(self)
\r
480 def CreatePanelFolders(self):
\r
482 filters = self.config['folders']['filters']
\r
483 index = self.config['folders'].as_int('filterindex')
\r
484 #set initial directory
\r
485 folder = self.config['core']['workdir']
\r
486 return wx.GenericDirCtrl(self, -1, dir=folder, size=(200, 250), style=wx.DIRCTRL_SHOW_FILTERS, filter=filters, defaultFilter=index)
\r
488 def CreatePanelOutput(self):
\r
489 return wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
\r
491 def CreatePanelPlaylists(self):
\r
492 return panels.playlist.Playlists(self)
\r
494 def CreatePanelProperties(self):
\r
495 return panels.propertyeditor.PropertyEditor(self)
\r
497 def CreatePanelResults(self):
\r
498 return panels.results.Results(self)
\r
500 def CreatePanelWelcome(self):
\r
501 #TODO: move into panels.welcome
\r
502 ctrl = wx.html.HtmlWindow(self, -1, wx.DefaultPosition, wx.Size(400, 300))
\r
503 introStr = '<h1>Welcome to Hooke</h1>' + \
\r
504 '<h3>Features</h3>' + \
\r
506 '<li>View, annotate, measure force files</li>' + \
\r
507 '<li>Worm-like chain fit of force peaks</li>' + \
\r
508 '<li>Automatic convolution-based filtering of empty files</li>' + \
\r
509 '<li>Automatic fit and measurement of multiple force peaks</li>' + \
\r
510 '<li>Handles force-clamp force experiments (experimental)</li>' + \
\r
511 '<li>It is extensible by users by means of plugins and drivers</li>' + \
\r
513 '<p>See the <a href="http://code.google.com/p/hooke/wiki/DocumentationIndex">DocumentationIndex</a> for more information</p>'
\r
514 ctrl.SetPage(introStr)
\r
517 def CreateMenuBar(self):
\r
518 menu_bar = wx.MenuBar()
\r
519 self.SetMenuBar(menu_bar)
\r
521 file_menu = wx.Menu()
\r
522 file_menu.Append(wx.ID_EXIT, 'Exit\tCtrl-Q')
\r
523 # edit_menu.AppendSeparator();
\r
524 # edit_menu.Append(ID_Config, 'Preferences')
\r
526 view_menu = wx.Menu()
\r
527 view_menu.AppendCheckItem(ID_ViewFolders, 'Folders\tF5')
\r
528 view_menu.AppendCheckItem(ID_ViewPlaylists, 'Playlists\tF6')
\r
529 view_menu.AppendCheckItem(ID_ViewCommands, 'Commands\tF7')
\r
530 view_menu.AppendCheckItem(ID_ViewProperties, 'Properties\tF8')
\r
531 view_menu.AppendCheckItem(ID_ViewAssistant, 'Assistant\tF9')
\r
532 view_menu.AppendCheckItem(ID_ViewResults, 'Results\tF10')
\r
533 view_menu.AppendCheckItem(ID_ViewOutput, 'Output\tF11')
\r
535 # perspectives_menu = self.CreatePerspectivesMenu()
\r
536 perspectives_menu = wx.Menu()
\r
539 help_menu = wx.Menu()
\r
540 help_menu.Append(wx.ID_ABOUT, 'About Hooke')
\r
541 #put it all together
\r
542 menu_bar.Append(file_menu, 'File')
\r
543 # menu_bar.Append(edit_menu, 'Edit')
\r
544 menu_bar.Append(view_menu, 'View')
\r
545 menu_bar.Append(perspectives_menu, "Perspectives")
\r
546 self.UpdatePerspectivesMenu()
\r
547 menu_bar.Append(help_menu, 'Help')
\r
549 def CreateNotebook(self):
\r
550 # create the notebook off-window to avoid flicker
\r
551 client_size = self.GetClientSize()
\r
552 ctrl = aui.AuiNotebook(self, -1, wx.Point(client_size.x, client_size.y), wx.Size(430, 200), self._notebook_style)
\r
553 arts = [aui.AuiDefaultTabArt, aui.AuiSimpleTabArt, aui.VC71TabArt, aui.FF2TabArt, aui.VC8TabArt, aui.ChromeTabArt]
\r
554 art = arts[self._notebook_theme]()
\r
555 ctrl.SetArtProvider(art)
\r
556 #uncomment if we find a nice icon
\r
557 #page_bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))
\r
558 ctrl.AddPage(self.CreatePanelWelcome(), "Welcome", False)
\r
561 def CreateStatusbar(self):
\r
562 statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
\r
563 statusbar.SetStatusWidths([-2, -3])
\r
564 statusbar.SetStatusText('Ready', 0)
\r
565 welcomeString=u'Welcome to Hooke (version '+__version__+', '+__release_name__+')!'
\r
566 statusbar.SetStatusText(welcomeString, 1)
\r
569 def CreateToolBarNavigation(self):
\r
570 toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)
\r
571 toolbar.SetToolBitmapSize(wx.Size(16,16))
\r
572 toolbar_bmpBack = wx.ArtProvider_GetBitmap(wx.ART_GO_BACK, wx.ART_OTHER, wx.Size(16, 16))
\r
573 toolbar_bmpForward = wx.ArtProvider_GetBitmap(wx.ART_GO_FORWARD, wx.ART_OTHER, wx.Size(16, 16))
\r
574 toolbar.AddLabelTool(ID_Previous, 'Previous', toolbar_bmpBack, shortHelp='Previous curve')
\r
575 toolbar.AddLabelTool(ID_Next, 'Next', toolbar_bmpForward, shortHelp='Next curve')
\r
579 def DeleteFromPlaylists(self, name):
\r
580 if name in self.playlists:
\r
581 del self.playlists[name]
\r
582 tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
\r
583 item, cookie = self.panelPlaylists.PlaylistsTree.GetFirstChild(tree_root)
\r
585 playlist_name = self.panelPlaylists.PlaylistsTree.GetItemText(item)
\r
586 if playlist_name == name:
\r
588 self.panelPlaylists.PlaylistsTree.Delete(item)
\r
591 item = self.panelPlaylists.PlaylistsTree.GetNextSibling(item)
\r
593 def GetActiveFigure(self):
\r
594 playlist_name = self.GetActivePlaylistName()
\r
595 figure = self.playlists[playlist_name].figure
\r
596 if figure is not None:
\r
600 def GetActiveFile(self):
\r
601 playlist = self.GetActivePlaylist()
\r
602 if playlist is not None:
\r
603 return playlist.get_active_file()
\r
606 def GetActivePlaylist(self):
\r
607 playlist_name = self.GetActivePlaylistName()
\r
608 if playlist_name in self.playlists:
\r
609 return self.playlists[playlist_name]
\r
612 def GetActivePlaylistName(self):
\r
613 #get the selected item from the tree
\r
614 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
615 #test if a playlist or a curve was double-clicked
\r
616 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
617 playlist_item = selected_item
\r
619 #get the name of the playlist
\r
620 playlist_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
621 #now we have a playlist
\r
622 return self.panelPlaylists.PlaylistsTree.GetItemText(playlist_item)
\r
624 def GetActivePlot(self):
\r
625 playlist = self.GetActivePlaylist()
\r
626 if playlist is not None:
\r
627 return playlist.get_active_file().plot
\r
630 def GetDisplayedPlot(self):
\r
631 plot = copy.deepcopy(self.displayed_plot)
\r
633 #plot.curves = copy.deepcopy(plot.curves)
\r
636 def GetDisplayedPlotCorrected(self):
\r
637 plot = copy.deepcopy(self.displayed_plot)
\r
639 plot.curves = copy.deepcopy(plot.corrected_curves)
\r
642 def GetDisplayedPlotRaw(self):
\r
643 plot = copy.deepcopy(self.displayed_plot)
\r
645 plot.curves = copy.deepcopy(plot.raw_curves)
\r
648 def GetDockArt(self):
\r
649 return self._mgr.GetArtProvider()
\r
651 def GetBoolFromConfig(self, *args):
\r
656 elif len(args) == 3:
\r
660 if self.configs.has_key(plugin):
\r
661 config = self.configs[plugin]
\r
662 return config[section][key].as_bool('value')
\r
665 def GetColorFromConfig(self, *args):
\r
670 elif len(args) == 3:
\r
674 if self.configs.has_key(plugin):
\r
675 config = self.configs[plugin]
\r
676 color_tuple = eval(config[section][key]['value'])
\r
677 color = [value / 255.0 for value in color_tuple]
\r
681 def GetFloatFromConfig(self, *args):
\r
686 elif len(args) == 3:
\r
690 if self.configs.has_key(plugin):
\r
691 config = self.configs[plugin]
\r
692 return config[section][key].as_float('value')
\r
695 def GetIntFromConfig(self, *args):
\r
700 elif len(args) == 3:
\r
704 if self.configs.has_key(plugin):
\r
705 config = self.configs[plugin]
\r
706 return config[section][key].as_int('value')
\r
709 def GetStringFromConfig(self, *args):
\r
714 elif len(args) == 3:
\r
718 if self.configs.has_key(plugin):
\r
719 config = self.configs[plugin]
\r
720 return config[section][key]['value']
\r
723 def GetPlotmanipulator(self, name):
\r
725 Returns a plot manipulator function from its name
\r
727 for plotmanipulator in self.plotmanipulators:
\r
728 if plotmanipulator.name == name:
\r
729 return plotmanipulator
\r
732 def GetPerspectiveMenuItem(self, name):
\r
733 if self._perspectives.has_key(name):
\r
734 perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
735 perspectives_list.sort()
\r
736 index = perspectives_list.index(name)
\r
737 perspective_Id = ID_FirstPerspective + index
\r
738 menu_item = self.MenuBar.FindItemById(perspective_Id)
\r
743 def HasPlotmanipulator(self, name):
\r
745 returns True if the plotmanipulator 'name' is loaded, False otherwise
\r
747 for plotmanipulator in self.plotmanipulators:
\r
748 if plotmanipulator.command == name:
\r
752 def OnAbout(self, event):
\r
753 message = 'Hooke\n\n'+\
\r
754 'A free, open source data analysis platform\n\n'+\
\r
755 'Copyright 2006-2008 by Massimo Sandal\n'+\
\r
756 'Copyright 2010 by Dr. Rolf Schmidt\n\n'+\
\r
757 'Hooke is released under the GNU General Public License version 2.'
\r
758 dialog = wx.MessageDialog(self, message, 'About Hooke', wx.OK | wx.ICON_INFORMATION)
\r
762 def OnClose(self, event):
\r
764 self.config['main']['height'] = str(self.GetSize().GetHeight())
\r
765 self.config['main']['left'] = str(self.GetPosition()[0])
\r
766 self.config['main']['top'] = str(self.GetPosition()[1])
\r
767 self.config['main']['width'] = str(self.GetSize().GetWidth())
\r
768 #save the configuration file to 'config/hooke.ini'
\r
769 self.config.write()
\r
770 #save all plugin config files
\r
771 for config in self.configs:
\r
772 plugin_config = self.configs[config]
\r
773 plugin_config.write()
\r
774 self._UnbindEvents()
\r
779 def OnDeletePerspective(self, event):
\r
780 dialog = panels.perspectives.Perspectives(self, -1, 'Delete perspective(s)')
\r
781 dialog.CenterOnScreen()
\r
784 self.UpdatePerspectivesMenu()
\r
785 #unfortunately, there is a bug in wxWidgets (Ticket #3258) that
\r
786 #makes the radio item indicator in the menu disappear
\r
787 #the code should be fine once this issue is fixed
\r
789 def OnDirCtrlLeftDclick(self, event):
\r
790 file_path = self.panelFolders.GetPath()
\r
791 if os.path.isfile(file_path):
\r
792 if file_path.endswith('.hkp'):
\r
793 self.do_loadlist(file_path)
\r
796 def OnEraseBackground(self, event):
\r
799 def OnExecute(self, event):
\r
800 item = self.panelCommands.CommandsTree.GetSelection()
\r
802 if not self.panelCommands.CommandsTree.ItemHasChildren(item):
\r
803 item_text = self.panelCommands.CommandsTree.GetItemText(item)
\r
804 command = ''.join(['self.do_', item_text, '()'])
\r
805 #self.AppendToOutput(command + '\n')
\r
808 def OnExit(self, event):
\r
811 def OnNext(self, event):
\r
814 Go to the next curve in the playlist.
\r
815 If we are at the last curve, we come back to the first.
\r
819 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
820 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
821 #GetFirstChild returns a tuple
\r
822 #we only need the first element
\r
823 next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(selected_item)[0]
\r
825 next_item = self.panelPlaylists.PlaylistsTree.GetNextSibling(selected_item)
\r
826 if not next_item.IsOk():
\r
827 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
828 #GetFirstChild returns a tuple
\r
829 #we only need the first element
\r
830 next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(parent_item)[0]
\r
831 self.panelPlaylists.PlaylistsTree.SelectItem(next_item, True)
\r
832 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
833 playlist = self.GetActivePlaylist()
\r
834 if playlist.count > 1:
\r
836 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
839 def OnNotebookPageClose(self, event):
\r
840 ctrl = event.GetEventObject()
\r
841 playlist_name = ctrl.GetPageText(ctrl._curpage)
\r
842 self.DeleteFromPlaylists(playlist_name)
\r
844 def OnPaneClose(self, event):
\r
847 def OnPlaylistsLeftDclick(self, event):
\r
848 if self.panelPlaylists.PlaylistsTree.Count > 0:
\r
849 playlist_name = self.GetActivePlaylistName()
\r
850 #if that playlist already exists
\r
851 #we check if it is the active playlist (ie selected in panelPlaylists)
\r
852 #and switch to it if necessary
\r
853 if playlist_name in self.playlists:
\r
854 index = self.plotNotebook.GetSelection()
\r
855 current_playlist = self.plotNotebook.GetPageText(index)
\r
856 if current_playlist != playlist_name:
\r
857 index = self._GetPlaylistTab(playlist_name)
\r
858 self.plotNotebook.SetSelection(index)
\r
859 #if a curve was double-clicked
\r
860 item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
861 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
\r
862 index = self._GetActiveFileIndex()
\r
866 playlist = self.GetActivePlaylist()
\r
867 playlist.index = index
\r
868 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
870 #if you uncomment the following line, the tree will collapse/expand as well
\r
873 def OnPlaylistsLeftDown(self, event):
\r
874 hit_item, hit_flags = self.panelPlaylists.PlaylistsTree.HitTest(event.GetPosition())
\r
875 if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:
\r
876 self.panelPlaylists.PlaylistsTree.SelectItem(hit_item)
\r
877 playlist_name = self.GetActivePlaylistName()
\r
878 playlist = self.GetActivePlaylist()
\r
879 #if a curve was clicked
\r
880 item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
881 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
\r
882 index = self._GetActiveFileIndex()
\r
884 playlist.index = index
\r
885 self.playlists[playlist_name] = playlist
\r
888 def OnPrevious(self, event):
\r
891 Go to the previous curve in the playlist.
\r
892 If we are at the first curve, we jump to the last.
\r
894 Syntax: previous, p
\r
896 #playlist = self.playlists[self.GetActivePlaylistName()][0]
\r
897 #select the previous curve and tell the user if we wrapped around
\r
898 #self.AppendToOutput(playlist.previous())
\r
899 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
900 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
901 previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(selected_item)
\r
903 previous_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
904 if not previous_item.IsOk():
\r
905 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
906 previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(parent_item)
\r
907 self.panelPlaylists.PlaylistsTree.SelectItem(previous_item, True)
\r
908 playlist = self.GetActivePlaylist()
\r
909 if playlist.count > 1:
\r
910 playlist.previous()
\r
911 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
914 def OnPropGridChanged (self, event):
\r
915 prop = event.GetProperty()
\r
917 item_section = self.panelProperties.SelectedTreeItem
\r
918 item_plugin = self.panelCommands.CommandsTree.GetItemParent(item_section)
\r
919 plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
\r
920 config = self.configs[plugin]
\r
921 property_section = self.panelCommands.CommandsTree.GetItemText(item_section)
\r
922 property_key = prop.GetName()
\r
923 property_value = prop.GetDisplayedString()
\r
925 config[property_section][property_key]['value'] = property_value
\r
927 def OnRestorePerspective(self, event):
\r
928 name = self.MenuBar.FindItemById(event.GetId()).GetLabel()
\r
929 self._RestorePerspective(name)
\r
930 # self._mgr.LoadPerspective(self._perspectives[name])
\r
931 # self.config['perspectives']['active'] = name
\r
932 # self._mgr.Update()
\r
933 # all_panes = self._mgr.GetAllPanes()
\r
934 # for pane in all_panes:
\r
935 # if not pane.name.startswith('toolbar'):
\r
936 # if pane.name == 'Assistant':
\r
937 # self.MenuBar.FindItemById(ID_ViewAssistant).Check(pane.window.IsShown())
\r
938 # if pane.name == 'Folders':
\r
939 # self.MenuBar.FindItemById(ID_ViewFolders).Check(pane.window.IsShown())
\r
940 # if pane.name == 'Playlists':
\r
941 # self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown())
\r
942 # if pane.name == 'Commands':
\r
943 # self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown())
\r
944 # if pane.name == 'Properties':
\r
945 # self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown())
\r
946 # if pane.name == 'Output':
\r
947 # self.MenuBar.FindItemById(ID_ViewOutput).Check(pane.window.IsShown())
\r
948 # if pane.name == 'Results':
\r
949 # self.MenuBar.FindItemById(ID_ViewResults).Check(pane.window.IsShown())
\r
951 def OnResultsCheck(self, index, flag):
\r
952 #TODO: fix for multiple results
\r
953 results = self.GetActivePlot().results
\r
954 if results.has_key(self.results_str):
\r
955 results[self.results_str].results[index].visible = flag
\r
956 results[self.results_str].update()
\r
959 def OnSavePerspective(self, event):
\r
961 def nameExists(name):
\r
962 menu_position = self.MenuBar.FindMenu('Perspectives')
\r
963 menu = self.MenuBar.GetMenu(menu_position)
\r
964 for item in menu.GetMenuItems():
\r
965 if item.GetText() == name:
\r
971 dialog = wx.TextEntryDialog(self, 'Enter a name for the new perspective:', 'Save perspective')
\r
972 dialog.SetValue('New perspective')
\r
973 if dialog.ShowModal() != wx.ID_OK:
\r
976 name = dialog.GetValue()
\r
978 if nameExists(name):
\r
979 dialogConfirm = wx.MessageDialog(self, 'A file with this name already exists.\n\nDo you want to replace it?', 'Confirm', wx.YES_NO|wx.ICON_QUESTION|wx.CENTER)
\r
980 if dialogConfirm.ShowModal() == wx.ID_YES:
\r
985 perspective = self._mgr.SavePerspective()
\r
986 self._SavePerspectiveToFile(name, perspective)
\r
987 self.config['perspectives']['active'] = name
\r
988 self.UpdatePerspectivesMenu()
\r
989 # if nameExists(name):
\r
990 # #check the corresponding menu item
\r
991 # menu_item = self.GetPerspectiveMenuItem(name)
\r
992 # #replace the perspectiveStr in _pespectives
\r
993 # self._perspectives[name] = perspective
\r
995 # #because we deal with radio items, we need to do some extra work
\r
996 # #delete all menu items from the perspectives menu
\r
997 # for item in self._perspectives_menu.GetMenuItems():
\r
998 # self._perspectives_menu.DeleteItem(item)
\r
999 # #recreate the perspectives menu
\r
1000 # self._perspectives_menu.Append(ID_SavePerspective, 'Save Perspective')
\r
1001 # self._perspectives_menu.Append(ID_DeletePerspective, 'Delete Perspective')
\r
1002 # self._perspectives_menu.AppendSeparator()
\r
1003 # #convert the perspectives dictionary into a list
\r
1004 # # the list contains:
\r
1005 # #[0]: name of the perspective
\r
1006 # #[1]: perspective
\r
1007 # perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
1008 # perspectives_list.append(name)
\r
1009 # perspectives_list.sort()
\r
1010 # #add all previous perspectives
\r
1011 # for index, item in enumerate(perspectives_list):
\r
1012 # menu_item = self._perspectives_menu.AppendRadioItem(ID_FirstPerspective + index, item)
\r
1013 # if item == name:
\r
1014 # menu_item.Check()
\r
1015 # #add the new perspective to _perspectives
\r
1016 # self._perspectives[name] = perspective
\r
1018 def OnSize(self, event):
\r
1021 def OnTreeCtrlCommandsSelectionChanged(self, event):
\r
1022 selected_item = event.GetItem()
\r
1023 if selected_item is not None:
\r
1026 #deregister/register the listener to avoid infinite loop
\r
1027 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlCommandsSelectionChanged)
\r
1028 self.panelCommands.CommandsTree.SelectItem(selected_item)
\r
1029 evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self.panelCommands.CommandsTree)
\r
1030 self.panelProperties.SelectedTreeItem = selected_item
\r
1031 #if a command was clicked
\r
1033 if not self.panelCommands.CommandsTree.ItemHasChildren(selected_item):
\r
1034 item_plugin = self.panelCommands.CommandsTree.GetItemParent(selected_item)
\r
1035 plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
\r
1036 if self.configs.has_key(plugin):
\r
1037 #config = self.panelCommands.CommandsTree.GetPyData(item_plugin)
\r
1038 config = self.configs[plugin]
\r
1039 section = self.panelCommands.CommandsTree.GetItemText(selected_item)
\r
1040 #display docstring in help window
\r
1041 doc_string = eval('self.do_' + section + '.__doc__')
\r
1042 if section in config:
\r
1043 for option in config[section]:
\r
1044 properties.append([option, config[section][option]])
\r
1046 plugin = self.panelCommands.CommandsTree.GetItemText(selected_item)
\r
1047 if plugin != 'core':
\r
1048 doc_string = eval('plugins.' + plugin + '.' + plugin + 'Commands.__doc__')
\r
1050 doc_string = 'The module "core" contains Hooke core functionality'
\r
1051 if doc_string is not None:
\r
1052 self.panelAssistant.ChangeValue(doc_string)
\r
1054 self.panelAssistant.ChangeValue('')
\r
1055 panels.propertyeditor.PropertyEditor.Initialize(self.panelProperties, properties)
\r
1056 #save the currently selected command/plugin to the config file
\r
1057 self.config['command']['command'] = section
\r
1058 self.config['command']['plugin'] = plugin
\r
1060 def OnTreeCtrlItemActivated(self, event):
\r
1061 self.OnExecute(event)
\r
1063 def OnView(self, event):
\r
1064 menu_id = event.GetId()
\r
1065 menu_item = self.MenuBar.FindItemById(menu_id)
\r
1066 menu_label = menu_item.GetLabel()
\r
1068 pane = self._mgr.GetPane(menu_label)
\r
1069 pane.Show(not pane.IsShown())
\r
1070 #if we don't do the following, the Folders pane does not resize properly on hide/show
\r
1071 if pane.caption == 'Folders' and pane.IsShown() and pane.IsDocked():
\r
1072 #folders_size = pane.GetSize()
\r
1073 self.panelFolders.Fit()
\r
1074 self._mgr.Update()
\r
1076 def _clickize(self, xvector, yvector, index):
\r
1078 returns a ClickedPoint() object from an index and vectors of x, y coordinates
\r
1080 point = lh.ClickedPoint()
\r
1081 point.index = index
\r
1082 point.absolute_coords = xvector[index], yvector[index]
\r
1083 point.find_graph_coords(xvector, yvector)
\r
1086 def _delta(self, message='Click 2 points', whatset=lh.RETRACTION):
\r
1088 calculates the difference between two clicked points
\r
1090 clicked_points = self._measure_N_points(N=2, message=message, whatset=whatset)
\r
1092 plot = self.GetDisplayedPlotCorrected()
\r
1093 curve = plot.curves[whatset]
\r
1095 delta = lib.delta.Delta()
\r
1096 delta.point1.x = clicked_points[0].graph_coords[0]
\r
1097 delta.point1.y = clicked_points[0].graph_coords[1]
\r
1098 delta.point2.x = clicked_points[1].graph_coords[0]
\r
1099 delta.point2.y = clicked_points[1].graph_coords[1]
\r
1100 delta.units.x = curve.units.x
\r
1101 delta.units.y = curve.units.y
\r
1105 def _measure_N_points(self, N, message='', whatset=lh.RETRACTION):
\r
1107 General helper function for N-points measurements
\r
1108 By default, measurements are done on the retraction
\r
1111 dialog = wx.MessageDialog(None, message, 'Info', wx.OK)
\r
1112 dialog.ShowModal()
\r
1114 figure = self.GetActiveFigure()
\r
1116 xvector = self.displayed_plot.curves[whatset].x
\r
1117 yvector = self.displayed_plot.curves[whatset].y
\r
1119 clicked_points = figure.ginput(N, timeout=-1, show_clicks=True)
\r
1122 for clicked_point in clicked_points:
\r
1123 point = lh.ClickedPoint()
\r
1124 point.absolute_coords = clicked_point[0], clicked_point[1]
\r
1126 #TODO: make this optional?
\r
1127 #so far, the clicked point is taken, not the corresponding data point
\r
1128 point.find_graph_coords(xvector, yvector)
\r
1129 point.is_line_edge = True
\r
1130 point.is_marker = True
\r
1131 points.append(point)
\r
1134 def do_plotmanipulators(self):
\r
1136 Please select the plotmanipulators you would like to use
\r
1137 and define the order in which they will be applied to the data.
\r
1139 Click 'Execute' to apply your changes.
\r
1143 def do_test(self):
\r
1145 Use this command for testing purposes. You find do_test in hooke.py.
\r
1149 def do_version(self):
\r
1153 Prints the current version and codename, plus library version. Useful for debugging.
\r
1155 self.AppendToOutput('Hooke ' + __version__ + ' (' + __codename__ + ')')
\r
1156 self.AppendToOutput('Released on: ' + __releasedate__)
\r
1157 self.AppendToOutput('---')
\r
1158 self.AppendToOutput('Python version: ' + python_version)
\r
1159 self.AppendToOutput('WxPython version: ' + wx_version)
\r
1160 self.AppendToOutput('Matplotlib version: ' + mpl_version)
\r
1161 self.AppendToOutput('SciPy version: ' + scipy_version)
\r
1162 self.AppendToOutput('NumPy version: ' + numpy_version)
\r
1163 self.AppendToOutput('---')
\r
1164 self.AppendToOutput('Platform: ' + str(platform.uname()))
\r
1165 #TODO: adapt to 'new' config
\r
1166 #self.AppendToOutput('---')
\r
1167 #self.AppendToOutput('Loaded plugins:', self.config['loaded_plugins'])
\r
1169 def UpdatePerspectivesMenu(self):
\r
1170 #add perspectives to menubar and _perspectives
\r
1171 perspectivesDirectory = os.path.join(lh.hookeDir, 'perspectives')
\r
1172 self._perspectives = {}
\r
1173 if os.path.isdir(perspectivesDirectory):
\r
1174 perspectiveFileNames = os.listdir(perspectivesDirectory)
\r
1175 for perspectiveFilename in perspectiveFileNames:
\r
1176 filename = lh.get_file_path(perspectiveFilename, ['perspectives'])
\r
1177 if os.path.isfile(filename):
\r
1178 perspectiveFile = open(filename, 'rU')
\r
1179 perspective = perspectiveFile.readline()
\r
1180 perspectiveFile.close()
\r
1181 if perspective != '':
\r
1182 name, extension = os.path.splitext(perspectiveFilename)
\r
1183 if extension == '.txt':
\r
1184 self._perspectives[name] = perspective
\r
1186 #in case there are no perspectives
\r
1187 if not self._perspectives:
\r
1188 perspective = self._mgr.SavePerspective()
\r
1189 self._perspectives['Default'] = perspective
\r
1190 self._SavePerspectiveToFile('Default', perspective)
\r
1192 selected_perspective = self.config['perspectives']['active']
\r
1193 if not self._perspectives.has_key(selected_perspective):
\r
1194 self.config['perspectives']['active'] = 'Default'
\r
1195 selected_perspective = 'Default'
\r
1197 perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
1198 perspectives_list.sort()
\r
1200 #get the Perspectives menu
\r
1201 menu_position = self.MenuBar.FindMenu('Perspectives')
\r
1202 menu = self.MenuBar.GetMenu(menu_position)
\r
1203 #delete all menu items
\r
1204 for item in menu.GetMenuItems():
\r
1205 menu.DeleteItem(item)
\r
1206 #rebuild the menu by adding the standard menu items
\r
1207 menu.Append(ID_SavePerspective, 'Save Perspective')
\r
1208 menu.Append(ID_DeletePerspective, 'Delete Perspective')
\r
1209 menu.AppendSeparator()
\r
1210 #add all previous perspectives
\r
1211 for index, label in enumerate(perspectives_list):
\r
1212 menu_item = menu.AppendRadioItem(ID_FirstPerspective + index, label)
\r
1213 if label == selected_perspective:
\r
1214 self._RestorePerspective(label)
\r
1215 menu_item.Check(True)
\r
1217 def UpdatePlaylistsTreeSelection(self):
\r
1218 playlist = self.GetActivePlaylist()
\r
1219 if playlist is not None:
\r
1220 if playlist.index >= 0:
\r
1221 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
1224 def UpdatePlot(self, plot=None):
\r
1226 def add_to_plot(curve):
\r
1227 if curve.visible and curve.x and curve.y:
\r
1228 destination = (curve.destination.column - 1) * number_of_rows + curve.destination.row - 1
\r
1229 axes_list[destination].set_title(curve.title)
\r
1230 axes_list[destination].set_xlabel(multiplier_x + curve.units.x)
\r
1231 axes_list[destination].set_ylabel(multiplier_y + curve.units.y)
\r
1232 if curve.style == 'plot':
\r
1233 axes_list[destination].plot(curve.x, curve.y, color=curve.color, label=curve.label, lw=curve.linewidth, zorder=1)
\r
1234 if curve.style == 'scatter':
\r
1235 axes_list[destination].scatter(curve.x, curve.y, color=curve.color, label=curve.label, s=curve.size, zorder=2)
\r
1237 def get_format_x(x, pos):
\r
1238 'The two args are the value and tick position'
\r
1239 multiplier = lib.prettyformat.get_exponent(multiplier_x)
\r
1240 decimals_str = '%.' + str(decimals_x) + 'f'
\r
1241 return decimals_str % (x/(10 ** multiplier))
\r
1243 def get_format_y(x, pos):
\r
1244 'The two args are the value and tick position'
\r
1245 multiplier = lib.prettyformat.get_exponent(multiplier_y)
\r
1246 decimals_str = '%.' + str(decimals_y) + 'f'
\r
1247 return decimals_str % (x/(10 ** multiplier))
\r
1249 decimals_x = self.GetIntFromConfig('plot', 'x_decimals')
\r
1250 decimals_y = self.GetIntFromConfig('plot', 'y_decimals')
\r
1251 multiplier_x = self.GetStringFromConfig('plot', 'x_multiplier')
\r
1252 multiplier_y = self.GetStringFromConfig('plot', 'y_multiplier')
\r
1255 active_file = self.GetActiveFile()
\r
1256 if not active_file.driver:
\r
1257 active_file.identify(self.drivers)
\r
1258 self.displayed_plot = copy.deepcopy(active_file.plot)
\r
1259 #add raw curves to plot
\r
1260 self.displayed_plot.raw_curves = copy.deepcopy(self.displayed_plot.curves)
\r
1261 #apply all active plotmanipulators
\r
1262 self.displayed_plot = self.ApplyPlotmanipulators(self.displayed_plot, active_file)
\r
1263 #add corrected curves to plot
\r
1264 self.displayed_plot.corrected_curves = copy.deepcopy(self.displayed_plot.curves)
\r
1266 active_file = None
\r
1267 self.displayed_plot = copy.deepcopy(plot)
\r
1269 figure = self.GetActiveFigure()
\r
1272 figure.suptitle(self.displayed_plot.title, fontsize=14)
\r
1276 number_of_columns = max([curve.destination.column for curve in self.displayed_plot.curves])
\r
1277 number_of_rows = max([curve.destination.row for curve in self.displayed_plot.curves])
\r
1279 for index in range(number_of_rows * number_of_columns):
\r
1280 axes_list.append(figure.add_subplot(number_of_rows, number_of_columns, index + 1))
\r
1282 for axes in axes_list:
\r
1283 formatter_x = FuncFormatter(get_format_x)
\r
1284 formatter_y = FuncFormatter(get_format_y)
\r
1285 axes.xaxis.set_major_formatter(formatter_x)
\r
1286 axes.yaxis.set_major_formatter(formatter_y)
\r
1288 for curve in self.displayed_plot.curves:
\r
1289 add_to_plot(curve)
\r
1291 #make sure the titles of 'subplots' do not overlap with the axis labels of the 'main plot'
\r
1292 figure.subplots_adjust(hspace=0.3)
\r
1294 #TODO: add multiple results support to fit in curve.results:
\r
1296 self.panelResults.ClearResults()
\r
1297 if self.displayed_plot.results.has_key(self.results_str):
\r
1298 for curve in self.displayed_plot.results[self.results_str].results:
\r
1299 add_to_plot(curve)
\r
1300 self.panelResults.DisplayResults(self.displayed_plot.results[self.results_str])
\r
1302 self.panelResults.ClearResults()
\r
1304 legend = self.GetBoolFromConfig('plot', 'legend')
\r
1305 for axes in axes_list:
\r
1308 figure.canvas.draw()
\r
1310 if __name__ == '__main__':
\r
1312 ## now, silence a deprecation warning for py2.3
\r
1314 warnings.filterwarnings("ignore", "integer", DeprecationWarning, "wxPython.gdi")
\r
1320 app = Hooke(redirect=redirect)
\r