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
13 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
34 from agw import cubecolourdialog as CCD
\r
35 except ImportError: # if it's not there locally, try the wxPython lib.
\r
36 import wx.lib.agw.cubecolourdialog as CCD
\r
38 #set the Hooke directory
\r
39 lh.hookeDir = os.path.abspath(os.path.dirname(__file__))
\r
40 from config.config import config
\r
43 import lib.plotmanipulator
\r
44 import panels.commands
\r
45 import panels.perspectives
\r
46 import panels.playlist
\r
48 import panels.propertyeditor
\r
49 import panels.results
\r
56 global __releasedate__
\r
57 __version__ = lh.HOOKE_VERSION[0]
\r
58 __codename__ = lh.HOOKE_VERSION[1]
\r
59 __releasedate__ = lh.HOOKE_VERSION[2]
\r
60 __release_name__ = lh.HOOKE_VERSION[1]
\r
62 #TODO: add general preferences to Hooke
\r
63 #this might be useful
\r
64 #ID_Config = wx.NewId()
\r
65 ID_About = wx.NewId()
\r
66 ID_Next = wx.NewId()
\r
67 ID_Previous = wx.NewId()
\r
69 ID_ViewAssistant = wx.NewId()
\r
70 ID_ViewCommands = wx.NewId()
\r
71 ID_ViewFolders = wx.NewId()
\r
72 ID_ViewOutput = wx.NewId()
\r
73 ID_ViewPlaylists = wx.NewId()
\r
74 ID_ViewProperties = wx.NewId()
\r
75 ID_ViewResults = wx.NewId()
\r
77 ID_DeletePerspective = wx.NewId()
\r
78 ID_SavePerspective = wx.NewId()
\r
80 ID_FirstPerspective = ID_SavePerspective + 1000
\r
81 #I hope we'll never have more than 1000 perspectives
\r
82 ID_FirstPlot = ID_SavePerspective + 2000
\r
84 class Hooke(wx.App):
\r
87 self.SetAppName('Hooke')
\r
88 self.SetVendorName('')
\r
90 windowPosition = (config['main']['left'], config['main']['top'])
\r
91 windowSize = (config['main']['width'], config['main']['height'])
\r
93 #setup the splashscreen
\r
94 if config['splashscreen']['show']:
\r
95 filename = lh.get_file_path('hooke.jpg', ['resources'])
\r
96 if os.path.isfile(filename):
\r
97 bitmap = wx.Image(filename).ConvertToBitmap()
\r
98 splashStyle = wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT
\r
99 splashDuration = config['splashscreen']['duration']
\r
100 wx.SplashScreen(bitmap, splashStyle, splashDuration, None, -1)
\r
103 we need for the splash screen to disappear
\r
104 for whatever reason splashDuration and sleep do not correspond to each other
\r
105 at least not on Windows
\r
106 maybe it's because duration is in milliseconds and sleep in seconds
\r
107 thus we need to increase the sleep time a bit
\r
108 a factor of 1.2 seems to work quite well
\r
111 time.sleep(sleepFactor * splashDuration / 1000)
\r
113 plugin_objects = []
\r
114 for plugin in config['plugins']:
\r
115 if config['plugins'][plugin]:
\r
116 filename = ''.join([plugin, '.py'])
\r
117 path = lh.get_file_path(filename, ['plugins'])
\r
118 if os.path.isfile(path):
\r
119 #get the corresponding filename and path
\r
120 plugin_name = ''.join(['plugins.', plugin])
\r
122 __import__(plugin_name)
\r
123 #get the file that contains the plugin
\r
124 class_file = getattr(plugins, plugin)
\r
125 #get the class that contains the commands
\r
126 class_object = getattr(class_file, plugin + 'Commands')
\r
127 plugin_objects.append(class_object)
\r
129 def make_command_class(*bases):
\r
130 #create metaclass with plugins and plotmanipulators
\r
131 return type(HookeFrame)("HookeFramePlugged", bases + (HookeFrame,), {})
\r
132 frame = make_command_class(*plugin_objects)(parent=None, id=wx.ID_ANY, title='Hooke', pos=windowPosition, size=windowSize)
\r
134 self.SetTopWindow(frame)
\r
142 class HookeFrame(wx.Frame):
\r
144 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
145 #call parent constructor
\r
146 wx.Frame.__init__(self, parent, id, title, pos, size, style)
\r
147 self.config = config
\r
148 self.CreateApplicationIcon()
\r
149 #self.configs contains: {the name of the Commands file: corresponding ConfigObj}
\r
151 #self.displayed_plot holds the currently displayed plot
\r
152 self.displayed_plot = None
\r
153 #self.playlists contains: {the name of the playlist: [playlist, tabIndex, plotID]}
\r
154 self.playlists = {}
\r
155 #list of all plotmanipulators
\r
156 self.plotmanipulators = []
\r
157 #self.plugins contains: {the name of the plugin: [caption, function]}
\r
160 #tell FrameManager to manage this frame
\r
161 self._mgr = aui.AuiManager()
\r
162 self._mgr.SetManagedWindow(self)
\r
163 #set the gradient style
\r
164 self._mgr.GetArtProvider().SetMetric(aui.AUI_DOCKART_GRADIENT_TYPE, aui.AUI_GRADIENT_NONE)
\r
165 #set transparent drag
\r
166 self._mgr.SetFlags(self._mgr.GetFlags() ^ aui.AUI_MGR_TRANSPARENT_DRAG)
\r
168 # set up default notebook style
\r
169 self._notebook_style = aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER
\r
170 self._notebook_theme = 0
\r
172 #holds the perspectives: {name, perspective_str}
\r
173 self._perspectives = {}
\r
175 # min size for the frame itself isn't completely done.
\r
176 # see the end up FrameManager::Update() for the test
\r
177 # code. For now, just hard code a frame minimum size
\r
178 self.SetMinSize(wx.Size(500, 500))
\r
179 #create panels here
\r
180 self.panelAssistant = self.CreatePanelAssistant()
\r
181 self.panelCommands = self.CreatePanelCommands()
\r
182 self.panelFolders = self.CreatePanelFolders()
\r
183 self.panelPlaylists = self.CreatePanelPlaylists()
\r
184 self.panelProperties = self.CreatePanelProperties()
\r
185 self.panelOutput = self.CreatePanelOutput()
\r
186 self.panelResults = self.CreatePanelResults()
\r
187 self.plotNotebook = self.CreateNotebook()
\r
188 #self.textCtrlCommandLine=self.CreateCommandLine()
\r
191 self._mgr.AddPane(self.panelFolders, aui.AuiPaneInfo().Name('Folders').Caption('Folders').Left().CloseButton(True).MaximizeButton(False))
\r
192 self._mgr.AddPane(self.panelPlaylists, aui.AuiPaneInfo().Name('Playlists').Caption('Playlists').Left().CloseButton(True).MaximizeButton(False))
\r
193 self._mgr.AddPane(self.plotNotebook, aui.AuiPaneInfo().Name('Plots').CenterPane().PaneBorder(False))
\r
194 self._mgr.AddPane(self.panelCommands, aui.AuiPaneInfo().Name('Commands').Caption('Settings and commands').Right().CloseButton(True).MaximizeButton(False))
\r
195 self._mgr.AddPane(self.panelProperties, aui.AuiPaneInfo().Name('Properties').Caption('Properties').Right().CloseButton(True).MaximizeButton(False))
\r
196 self._mgr.AddPane(self.panelAssistant, aui.AuiPaneInfo().Name('Assistant').Caption('Assistant').Right().CloseButton(True).MaximizeButton(False))
\r
197 self._mgr.AddPane(self.panelOutput, aui.AuiPaneInfo().Name('Output').Caption('Output').Bottom().CloseButton(True).MaximizeButton(False))
\r
198 self._mgr.AddPane(self.panelResults, aui.AuiPaneInfo().Name('Results').Caption('Results').Bottom().CloseButton(True).MaximizeButton(False))
\r
199 #self._mgr.AddPane(self.textCtrlCommandLine, aui.AuiPaneInfo().Name('CommandLine').CaptionVisible(False).Fixed().Bottom().Layer(2).CloseButton(False).MaximizeButton(False))
\r
200 #self._mgr.AddPane(panelBottom, aui.AuiPaneInfo().Name("panelCommandLine").Bottom().Position(1).CloseButton(False).MaximizeButton(False))
\r
202 # add the toolbars to the manager
\r
203 #self.toolbar=self.CreateToolBar()
\r
204 self.toolbarNavigation=self.CreateToolBarNavigation()
\r
205 #self._mgr.AddPane(self.toolbar, aui.AuiPaneInfo().Name('toolbar').Caption('Toolbar').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
\r
206 self._mgr.AddPane(self.toolbarNavigation, aui.AuiPaneInfo().Name('toolbarNavigation').Caption('Navigation').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
\r
207 # "commit" all changes made to FrameManager
\r
209 #create the menubar after the panes so that the default perspective
\r
210 #is created with all panes open
\r
211 self.CreateMenuBar()
\r
212 self.statusbar = self.CreateStatusbar()
\r
215 name = self.config['perspectives']['active']
\r
216 menu_item = self.GetPerspectiveMenuItem(name)
\r
217 if menu_item is not None:
\r
218 self.OnRestorePerspective(menu_item)
\r
219 #TODO: config setting to remember playlists from last session
\r
220 self.playlists = self.panelPlaylists.Playlists
\r
221 #define the list of active drivers
\r
223 for driver in self.config['drivers']:
\r
224 if self.config['drivers'][driver]:
\r
225 #get the corresponding filename and path
\r
226 filename = ''.join([driver, '.py'])
\r
227 path = lh.get_file_path(filename, ['drivers'])
\r
228 #the driver is active for driver[1] == 1
\r
229 if os.path.isfile(path):
\r
230 #driver files are located in the 'drivers' subfolder
\r
231 driver_name = ''.join(['drivers.', driver])
\r
232 __import__(driver_name)
\r
233 class_file = getattr(drivers, driver)
\r
234 for command in dir(class_file):
\r
235 if command.endswith('Driver'):
\r
236 self.drivers.append(getattr(class_file, command))
\r
237 #import all active plugins and plotmanips
\r
238 #add 'core.ini' to self.configs (this is not a plugin and thus must be imported separately)
\r
239 ini_path = lh.get_file_path('core.ini', ['plugins'])
\r
240 plugin_config = ConfigObj(ini_path)
\r
241 #self.config.merge(plugin_config)
\r
242 self.configs['core'] = plugin_config
\r
243 #make sure we execute _plug_init() for every command line plugin we import
\r
244 for plugin in self.config['plugins']:
\r
245 if self.config['plugins'][plugin]:
\r
246 filename = ''.join([plugin, '.py'])
\r
247 path = lh.get_file_path(filename, ['plugins'])
\r
248 if os.path.isfile(path):
\r
249 #get the corresponding filename and path
\r
250 plugin_name = ''.join(['plugins.', plugin])
\r
253 module = __import__(plugin_name)
\r
254 #prepare the ini file for inclusion
\r
255 ini_path = path.replace('.py', '.ini')
\r
257 plugin_config = ConfigObj(ini_path)
\r
258 #self.config.merge(plugin_config)
\r
259 self.configs[plugin] = plugin_config
\r
261 commands = eval('dir(module.' + plugin+ '.' + plugin + 'Commands)')
\r
262 #keep only commands (ie names that start with 'do_')
\r
263 #TODO: check for existing commands and warn the user!
\r
264 commands = [command for command in commands if command.startswith('do_')]
\r
266 self.plugins[plugin] = commands
\r
268 #initialize the plugin
\r
269 eval('module.' + plugin+ '.' + plugin + 'Commands._plug_init(self)')
\r
270 except AttributeError:
\r
272 except ImportError:
\r
274 #initialize the commands tree
\r
275 commands = dir(HookeFrame)
\r
276 commands = [command for command in commands if command.startswith('do_')]
\r
278 self.plugins['core'] = commands
\r
279 self.panelCommands.Initialize(self.plugins)
\r
280 for command in dir(self):
\r
281 if command.startswith('plotmanip_'):
\r
282 self.plotmanipulators.append(lib.plotmanipulator.Plotmanipulator(method=getattr(self, command), command=command))
\r
284 #load default list, if possible
\r
285 self.do_loadlist(self.config['core']['list'])
\r
286 #self.do_loadlist()
\r
288 def _BindEvents(self):
\r
289 #TODO: figure out if we can use the eventManager for menu ranges
\r
290 #and events of 'self' without raising an assertion fail error
\r
291 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
\r
292 self.Bind(wx.EVT_SIZE, self.OnSize)
\r
293 self.Bind(wx.EVT_CLOSE, self.OnClose)
\r
294 # Show How To Use The Closing Panes Event
\r
295 self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPaneClose)
\r
296 self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnNotebookPageClose)
\r
298 evtmgr.eventManager.Register(self.OnAbout, wx.EVT_MENU, win=self, id=wx.ID_ABOUT)
\r
299 evtmgr.eventManager.Register(self.OnClose, wx.EVT_MENU, win=self, id=wx.ID_EXIT)
\r
301 self.Bind(wx.EVT_MENU_RANGE, self.OnView, id=ID_ViewAssistant, id2=ID_ViewResults)
\r
303 self.Bind(wx.EVT_MENU, self.OnDeletePerspective, id=ID_DeletePerspective)
\r
304 self.Bind(wx.EVT_MENU, self.OnSavePerspective, id=ID_SavePerspective)
\r
305 self.Bind(wx.EVT_MENU_RANGE, self.OnRestorePerspective, id=ID_FirstPerspective, id2=ID_FirstPerspective+1000)
\r
307 evtmgr.eventManager.Register(self.OnNext, wx.EVT_TOOL, win=self, id=ID_Next)
\r
308 evtmgr.eventManager.Register(self.OnPrevious, wx.EVT_TOOL, win=self, id=ID_Previous)
\r
309 #self.Bind(.EVT_AUITOOLBAR_TOOL_DROPDOWN, self.OnDropDownToolbarItem, id=ID_DropDownToolbarItem)
\r
311 treeCtrl = self.panelFolders.GetTreeCtrl()
\r
312 #tree.Bind(wx.EVT_LEFT_UP, self.OnDirCtrl1LeftUp)
\r
313 #tree.Bind(wx.EVT_LEFT_DOWN, self.OnGenericDirCtrl1LeftDown)
\r
314 treeCtrl.Bind(wx.EVT_LEFT_DCLICK, self.OnDirCtrlLeftDclick)
\r
316 self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DOWN, self.OnPlaylistsLeftDown)
\r
317 self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DCLICK, self.OnPlaylistsLeftDclick)
\r
319 evtmgr.eventManager.Register(self.OnExecute, wx.EVT_BUTTON, self.panelCommands.ExecuteButton)
\r
320 evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self.panelCommands.CommandsTree)
\r
321 evtmgr.eventManager.Register(self.OnTreeCtrlItemActivated, wx.EVT_TREE_ITEM_ACTIVATED, self.panelCommands.CommandsTree)
\r
323 self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged)
\r
325 self.panelResults.results_list.OnCheckItem = self.OnResultsCheck
\r
327 def _GetActiveFileIndex(self):
\r
328 lib.playlist.Playlist = self.GetActivePlaylist()
\r
329 #get the selected item from the tree
\r
330 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
331 #test if a playlist or a curve was double-clicked
\r
332 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
336 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
337 while selected_item.IsOk():
\r
339 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
342 def _GetPlaylistTab(self, name):
\r
343 for index, page in enumerate(self.plotNotebook._tabs._pages):
\r
344 if page.caption == name:
\r
348 def _GetUniquePlaylistName(self, name):
\r
349 playlist_name = name
\r
351 while playlist_name in self.playlists:
\r
352 playlist_name = ''.join([name, str(count)])
\r
354 return playlist_name
\r
356 def _RestorePerspective(self, name):
\r
357 self._mgr.LoadPerspective(self._perspectives[name])
\r
358 self.config['perspectives']['active'] = name
\r
360 all_panes = self._mgr.GetAllPanes()
\r
361 for pane in all_panes:
\r
362 if not pane.name.startswith('toolbar'):
\r
363 if pane.name == 'Assistant':
\r
364 self.MenuBar.FindItemById(ID_ViewAssistant).Check(pane.window.IsShown())
\r
365 if pane.name == 'Folders':
\r
366 self.MenuBar.FindItemById(ID_ViewFolders).Check(pane.window.IsShown())
\r
367 if pane.name == 'Playlists':
\r
368 self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown())
\r
369 if pane.name == 'Commands':
\r
370 self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown())
\r
371 if pane.name == 'Properties':
\r
372 self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown())
\r
373 if pane.name == 'Output':
\r
374 self.MenuBar.FindItemById(ID_ViewOutput).Check(pane.window.IsShown())
\r
375 if pane.name == 'Results':
\r
376 self.MenuBar.FindItemById(ID_ViewResults).Check(pane.window.IsShown())
\r
378 def _SavePerspectiveToFile(self, name, perspective):
\r
379 filename = ''.join([name, '.txt'])
\r
380 filename = lh.get_file_path(filename, ['perspectives'])
\r
381 perspectivesFile = open(filename, 'w')
\r
382 perspectivesFile.write(perspective)
\r
383 perspectivesFile.close()
\r
385 def _UnbindEvents(self):
\r
387 evtmgr.eventManager.DeregisterListener(self.OnAbout)
\r
388 evtmgr.eventManager.DeregisterListener(self.OnClose)
\r
390 evtmgr.eventManager.DeregisterListener(self.OnNext)
\r
391 evtmgr.eventManager.DeregisterListener(self.OnPrevious)
\r
393 evtmgr.eventManager.DeregisterListener(self.OnExecute)
\r
394 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlCommandsSelectionChanged)
\r
396 def AddPlaylist(self, playlist=None, name='Untitled'):
\r
397 if playlist and playlist.count > 0:
\r
398 playlist.name = self._GetUniquePlaylistName(name)
\r
400 self.AddToPlaylists(playlist)
\r
402 def AddPlaylistFromFiles(self, files=[], name='Untitled'):
\r
404 playlist = lib.playlist.Playlist(self, self.drivers)
\r
406 playlist.add_curve(item)
\r
407 if playlist.count > 0:
\r
408 playlist.name = self._GetUniquePlaylistName(name)
\r
410 self.AddTayliss(playlist)
\r
412 def AddToPlaylists(self, playlist):
\r
413 if playlist.count > 0:
\r
414 #setup the playlist in the Playlist tree
\r
415 tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
\r
416 playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)
\r
417 #add all files to the Playlist tree
\r
419 for index, file_to_add in enumerate(playlist.files):
\r
420 #TODO: optionally remove the extension from the name of the curve
\r
421 #item_text, extension = os.path.splitext(curve.name)
\r
422 #curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, item_text, 1)
\r
423 file_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, file_to_add.name, 1)
\r
424 if index == playlist.index:
\r
425 self.panelPlaylists.PlaylistsTree.SelectItem(file_ID)
\r
427 #create the plot tab and add playlist to the dictionary
\r
428 plotPanel = panels.plot.PlotPanel(self, ID_FirstPlot + len(self.playlists))
\r
429 notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True)
\r
430 #tab_index = self.plotNotebook.GetSelection()
\r
431 playlist.figure = plotPanel.get_figure()
\r
432 self.playlists[playlist.name] = playlist
\r
433 #self.playlists[playlist.name] = [playlist, figure]
\r
434 self.panelPlaylists.PlaylistsTree.Expand(playlist_root)
\r
435 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
438 def AppendToOutput(self, text):
\r
439 self.panelOutput.AppendText(''.join([text, '\n']))
\r
441 def AppliesPlotmanipulator(self, name):
\r
443 returns True if the plotmanipulator 'name' is applied, False otherwise
\r
444 name does not contain 'plotmanip_', just the name of the plotmanipulator (e.g. 'flatten')
\r
446 return self.GetBoolFromConfig('core', 'plotmanipulators', name)
\r
448 def CreateApplicationIcon(self):
\r
449 iconFile = 'resources' + os.sep + 'microscope.ico'
\r
450 icon = wx.Icon(iconFile, wx.BITMAP_TYPE_ICO)
\r
453 def CreateCommandLine(self):
\r
454 return wx.TextCtrl(self, -1, '', style=wx.NO_BORDER|wx.EXPAND)
\r
456 def CreatePanelAssistant(self):
\r
457 panel = wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
\r
458 panel.SetEditable(False)
\r
461 def CreatePanelCommands(self):
\r
462 return panels.commands.Commands(self)
\r
464 def CreatePanelFolders(self):
\r
466 filters = self.config['folders']['filters']
\r
467 index = self.config['folders'].as_int('filterindex')
\r
468 #set initial directory
\r
469 folder = self.config['core']['workdir']
\r
470 return wx.GenericDirCtrl(self, -1, dir=folder, size=(200, 250), style=wx.DIRCTRL_SHOW_FILTERS, filter=filters, defaultFilter=index)
\r
472 def CreatePanelOutput(self):
\r
473 return wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
\r
475 def CreatePanelPlaylists(self):
\r
476 return panels.playlist.Playlists(self)
\r
478 def CreatePanelProperties(self):
\r
479 return panels.propertyeditor.PropertyEditor(self)
\r
481 def CreatePanelResults(self):
\r
482 return panels.results.Results(self)
\r
484 def CreatePanelWelcome(self):
\r
485 #TODO: move into panels.welcome
\r
486 ctrl = wx.html.HtmlWindow(self, -1, wx.DefaultPosition, wx.Size(400, 300))
\r
487 introStr = '<h1>Welcome to Hooke</h1>' + \
\r
488 '<h3>Features</h3>' + \
\r
490 '<li>View, annotate, measure force files</li>' + \
\r
491 '<li>Worm-like chain fit of force peaks</li>' + \
\r
492 '<li>Automatic convolution-based filtering of empty files</li>' + \
\r
493 '<li>Automatic fit and measurement of multiple force peaks</li>' + \
\r
494 '<li>Handles force-clamp force experiments (experimental)</li>' + \
\r
495 '<li>It is extensible by users by means of plugins and drivers</li>' + \
\r
497 '<p>See the <a href="http://code.google.com/p/hooke/wiki/DocumentationIndex">DocumentationIndex</a> for more information</p>'
\r
498 ctrl.SetPage(introStr)
\r
501 def CreateMenuBar(self):
\r
502 menu_bar = wx.MenuBar()
\r
503 self.SetMenuBar(menu_bar)
\r
505 file_menu = wx.Menu()
\r
506 file_menu.Append(wx.ID_EXIT, 'Exit\tCtrl-Q')
\r
507 # edit_menu.AppendSeparator();
\r
508 # edit_menu.Append(ID_Config, 'Preferences')
\r
510 view_menu = wx.Menu()
\r
511 view_menu.AppendCheckItem(ID_ViewFolders, 'Folders\tF5')
\r
512 view_menu.AppendCheckItem(ID_ViewPlaylists, 'Playlists\tF6')
\r
513 view_menu.AppendCheckItem(ID_ViewCommands, 'Commands\tF7')
\r
514 view_menu.AppendCheckItem(ID_ViewProperties, 'Properties\tF8')
\r
515 view_menu.AppendCheckItem(ID_ViewAssistant, 'Assistant\tF9')
\r
516 view_menu.AppendCheckItem(ID_ViewResults, 'Results\tF10')
\r
517 view_menu.AppendCheckItem(ID_ViewOutput, 'Output\tF11')
\r
519 # perspectives_menu = self.CreatePerspectivesMenu()
\r
520 perspectives_menu = wx.Menu()
\r
523 help_menu = wx.Menu()
\r
524 help_menu.Append(wx.ID_ABOUT, 'About Hooke')
\r
525 #put it all together
\r
526 menu_bar.Append(file_menu, 'File')
\r
527 # menu_bar.Append(edit_menu, 'Edit')
\r
528 menu_bar.Append(view_menu, 'View')
\r
529 menu_bar.Append(perspectives_menu, "Perspectives")
\r
530 self.UpdatePerspectivesMenu()
\r
531 menu_bar.Append(help_menu, 'Help')
\r
533 def CreateNotebook(self):
\r
534 # create the notebook off-window to avoid flicker
\r
535 client_size = self.GetClientSize()
\r
536 ctrl = aui.AuiNotebook(self, -1, wx.Point(client_size.x, client_size.y), wx.Size(430, 200), self._notebook_style)
\r
537 arts = [aui.AuiDefaultTabArt, aui.AuiSimpleTabArt, aui.VC71TabArt, aui.FF2TabArt, aui.VC8TabArt, aui.ChromeTabArt]
\r
538 art = arts[self._notebook_theme]()
\r
539 ctrl.SetArtProvider(art)
\r
540 #uncomment if we find a nice icon
\r
541 #page_bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))
\r
542 ctrl.AddPage(self.CreatePanelWelcome(), "Welcome", False)
\r
545 def CreateStatusbar(self):
\r
546 statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
\r
547 statusbar.SetStatusWidths([-2, -3])
\r
548 statusbar.SetStatusText('Ready', 0)
\r
549 welcomeString=u'Welcome to Hooke (version '+__version__+', '+__release_name__+')!'
\r
550 statusbar.SetStatusText(welcomeString, 1)
\r
553 def CreateToolBarNavigation(self):
\r
554 toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)
\r
555 toolbar.SetToolBitmapSize(wx.Size(16,16))
\r
556 toolbar_bmpBack = wx.ArtProvider_GetBitmap(wx.ART_GO_BACK, wx.ART_OTHER, wx.Size(16, 16))
\r
557 toolbar_bmpForward = wx.ArtProvider_GetBitmap(wx.ART_GO_FORWARD, wx.ART_OTHER, wx.Size(16, 16))
\r
558 toolbar.AddLabelTool(ID_Previous, 'Previous', toolbar_bmpBack, shortHelp='Previous curve')
\r
559 toolbar.AddLabelTool(ID_Next, 'Next', toolbar_bmpForward, shortHelp='Next curve')
\r
563 def DeleteFromPlaylists(self, name):
\r
564 if name in self.playlists:
\r
565 del self.playlists[name]
\r
566 tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
\r
567 item, cookie = self.panelPlaylists.PlaylistsTree.GetFirstChild(tree_root)
\r
569 playlist_name = self.panelPlaylists.PlaylistsTree.GetItemText(item)
\r
570 if playlist_name == name:
\r
572 self.panelPlaylists.PlaylistsTree.Delete(item)
\r
575 item = self.panelPlaylists.PlaylistsTree.GetNextSibling(item)
\r
577 def GetActiveFigure(self):
\r
578 playlist_name = self.GetActivePlaylistName()
\r
579 figure = self.playlists[playlist_name].figure
\r
580 if figure is not None:
\r
584 def GetActiveFile(self):
\r
585 playlist = self.GetActivePlaylist()
\r
586 if playlist is not None:
\r
587 return playlist.get_active_file()
\r
590 def GetActivePlaylist(self):
\r
591 playlist_name = self.GetActivePlaylistName()
\r
592 if playlist_name in self.playlists:
\r
593 return self.playlists[playlist_name]
\r
596 def GetActivePlaylistName(self):
\r
597 #get the selected item from the tree
\r
598 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
599 #test if a playlist or a curve was double-clicked
\r
600 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
601 playlist_item = selected_item
\r
603 #get the name of the playlist
\r
604 playlist_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
605 #now we have a playlist
\r
606 return self.panelPlaylists.PlaylistsTree.GetItemText(playlist_item)
\r
608 def GetActivePlot(self):
\r
609 playlist = self.GetActivePlaylist()
\r
610 if playlist is not None:
\r
611 return playlist.get_active_file().plot
\r
614 def GetDisplayedPlot(self):
\r
615 plot = copy.deepcopy(self.displayed_plot)
\r
617 plot.curves = copy.deepcopy(plot.curves)
\r
620 def GetDisplayedPlotCorrected(self):
\r
621 plot = copy.deepcopy(self.displayed_plot)
\r
623 plot.curves = copy.deepcopy(plot.corrected_curves)
\r
626 def GetDisplayedPlotRaw(self):
\r
627 plot = copy.deepcopy(self.displayed_plot)
\r
629 plot.curves = copy.deepcopy(plot.raw_curves)
\r
632 def GetDockArt(self):
\r
633 return self._mgr.GetArtProvider()
\r
635 def GetBoolFromConfig(self, *args):
\r
640 elif len(args) == 3:
\r
644 if self.configs.has_key(plugin):
\r
645 config = self.configs[plugin]
\r
646 return config[section][key].as_bool('value')
\r
649 def GetColorFromConfig(self, *args):
\r
654 elif len(args) == 3:
\r
658 if self.configs.has_key(plugin):
\r
659 config = self.configs[plugin]
\r
660 color_tuple = eval(config[section][key]['value'])
\r
661 color = [value / 255.0 for value in color_tuple]
\r
665 def GetFloatFromConfig(self, *args):
\r
670 elif len(args) == 3:
\r
674 if self.configs.has_key(plugin):
\r
675 config = self.configs[plugin]
\r
676 return config[section][key].as_float('value')
\r
679 def GetIntFromConfig(self, *args):
\r
684 elif len(args) == 3:
\r
688 if self.configs.has_key(plugin):
\r
689 config = self.configs[plugin]
\r
690 return config[section][key].as_int('value')
\r
693 def GetStringFromConfig(self, *args):
\r
698 elif len(args) == 3:
\r
702 if self.configs.has_key(plugin):
\r
703 config = self.configs[plugin]
\r
704 return config[section][key]['value']
\r
707 def GetPerspectiveMenuItem(self, name):
\r
708 if self._perspectives.has_key(name):
\r
709 perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
710 perspectives_list.sort()
\r
711 index = perspectives_list.index(name)
\r
712 perspective_Id = ID_FirstPerspective + index
\r
713 menu_item = self.MenuBar.FindItemById(perspective_Id)
\r
718 def HasPlotmanipulator(self, name):
\r
720 returns True if the plotmanipulator 'name' is loaded, False otherwise
\r
722 for plotmanipulator in self.plotmanipulators:
\r
723 if plotmanipulator.command == name:
\r
727 def OnAbout(self, event):
\r
728 message = 'Hooke\n\n'+\
\r
729 'A free, open source data analysis platform\n\n'+\
\r
730 'Copyright 2006-2008 by Massimo Sandal\n'+\
\r
731 'Copyright 2010 by Dr. Rolf Schmidt\n\n'+\
\r
732 'Hooke is released under the GNU General Public License version 2.'
\r
733 dialog = wx.MessageDialog(self, message, 'About Hooke', wx.OK | wx.ICON_INFORMATION)
\r
737 def OnClose(self, event):
\r
739 self.config['main']['height'] = str(self.GetSize().GetHeight())
\r
740 self.config['main']['left'] = str(self.GetPosition()[0])
\r
741 self.config['main']['top'] = str(self.GetPosition()[1])
\r
742 self.config['main']['width'] = str(self.GetSize().GetWidth())
\r
743 #save the configuration file to 'config/hooke.ini'
\r
744 self.config.write()
\r
745 #save all plugin config files
\r
746 for config in self.configs:
\r
747 plugin_config = self.configs[config]
\r
748 plugin_config.write()
\r
749 self._UnbindEvents()
\r
754 def OnDeletePerspective(self, event):
\r
755 dialog = panels.perspectives.Perspectives(self, -1, 'Delete perspective(s)')
\r
756 dialog.CenterOnScreen()
\r
759 self.UpdatePerspectivesMenu()
\r
760 #unfortunately, there is a bug in wxWidgets (Ticket #3258) that
\r
761 #makes the radio item indicator in the menu disappear
\r
762 #the code should be fine once this issue is fixed
\r
764 def OnDirCtrlLeftDclick(self, event):
\r
765 file_path = self.panelFolders.GetPath()
\r
766 if os.path.isfile(file_path):
\r
767 if file_path.endswith('.hkp'):
\r
768 self.do_loadlist(file_path)
\r
771 def OnEraseBackground(self, event):
\r
774 def OnExecute(self, event):
\r
775 item = self.panelCommands.CommandsTree.GetSelection()
\r
777 if not self.panelCommands.CommandsTree.ItemHasChildren(item):
\r
778 item_text = self.panelCommands.CommandsTree.GetItemText(item)
\r
779 command = ''.join(['self.do_', item_text, '()'])
\r
780 #self.AppendToOutput(command + '\n')
\r
783 def OnExit(self, event):
\r
786 def OnNext(self, event):
\r
789 Go to the next curve in the playlist.
\r
790 If we are at the last curve, we come back to the first.
\r
794 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
795 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
796 #GetFirstChild returns a tuple
\r
797 #we only need the first element
\r
798 next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(selected_item)[0]
\r
800 next_item = self.panelPlaylists.PlaylistsTree.GetNextSibling(selected_item)
\r
801 if not next_item.IsOk():
\r
802 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
803 #GetFirstChild returns a tuple
\r
804 #we only need the first element
\r
805 next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(parent_item)[0]
\r
806 self.panelPlaylists.PlaylistsTree.SelectItem(next_item, True)
\r
807 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
808 playlist = self.GetActivePlaylist()
\r
809 if playlist.count > 1:
\r
811 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
814 def OnNotebookPageClose(self, event):
\r
815 ctrl = event.GetEventObject()
\r
816 playlist_name = ctrl.GetPageText(ctrl._curpage)
\r
817 self.DeleteFromPlaylists(playlist_name)
\r
819 def OnPaneClose(self, event):
\r
822 def OnPlaylistsLeftDclick(self, event):
\r
823 if self.panelPlaylists.PlaylistsTree.Count > 0:
\r
824 playlist_name = self.GetActivePlaylistName()
\r
825 #if that playlist already exists
\r
826 #we check if it is the active playlist (ie selected in panelPlaylists)
\r
827 #and switch to it if necessary
\r
828 if playlist_name in self.playlists:
\r
829 index = self.plotNotebook.GetSelection()
\r
830 current_playlist = self.plotNotebook.GetPageText(index)
\r
831 if current_playlist != playlist_name:
\r
832 index = self._GetPlaylistTab(playlist_name)
\r
833 self.plotNotebook.SetSelection(index)
\r
834 #if a curve was double-clicked
\r
835 item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
836 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
\r
837 index = self._GetActiveFileIndex()
\r
841 playlist = self.GetActivePlaylist()
\r
842 playlist.index = index
\r
843 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
845 #if you uncomment the following line, the tree will collapse/expand as well
\r
848 def OnPlaylistsLeftDown(self, event):
\r
849 hit_item, hit_flags = self.panelPlaylists.PlaylistsTree.HitTest(event.GetPosition())
\r
850 if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:
\r
851 self.panelPlaylists.PlaylistsTree.SelectItem(hit_item)
\r
852 playlist_name = self.GetActivePlaylistName()
\r
853 playlist = self.GetActivePlaylist()
\r
854 #if a curve was clicked
\r
855 item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
856 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
\r
857 index = self._GetActiveFileIndex()
\r
859 playlist.index = index
\r
860 self.playlists[playlist_name] = playlist
\r
863 def OnPrevious(self, event):
\r
866 Go to the previous curve in the playlist.
\r
867 If we are at the first curve, we jump to the last.
\r
869 Syntax: previous, p
\r
871 #playlist = self.playlists[self.GetActivePlaylistName()][0]
\r
872 #select the previous curve and tell the user if we wrapped around
\r
873 #self.AppendToOutput(playlist.previous())
\r
874 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
875 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
876 previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(selected_item)
\r
878 previous_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
879 if not previous_item.IsOk():
\r
880 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
881 previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(parent_item)
\r
882 self.panelPlaylists.PlaylistsTree.SelectItem(previous_item, True)
\r
883 playlist = self.GetActivePlaylist()
\r
884 if playlist.count > 1:
\r
885 playlist.previous()
\r
886 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
889 def OnPropGridChanged (self, event):
\r
890 prop = event.GetProperty()
\r
892 item_section = self.panelProperties.SelectedTreeItem
\r
893 item_plugin = self.panelCommands.CommandsTree.GetItemParent(item_section)
\r
894 plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
\r
895 config = self.configs[plugin]
\r
896 property_section = self.panelCommands.CommandsTree.GetItemText(item_section)
\r
897 property_key = prop.GetName()
\r
898 property_value = prop.GetDisplayedString()
\r
900 config[property_section][property_key]['value'] = property_value
\r
902 def OnRestorePerspective(self, event):
\r
903 name = self.MenuBar.FindItemById(event.GetId()).GetLabel()
\r
904 self._RestorePerspective(name)
\r
905 # self._mgr.LoadPerspective(self._perspectives[name])
\r
906 # self.config['perspectives']['active'] = name
\r
907 # self._mgr.Update()
\r
908 # all_panes = self._mgr.GetAllPanes()
\r
909 # for pane in all_panes:
\r
910 # if not pane.name.startswith('toolbar'):
\r
911 # if pane.name == 'Assistant':
\r
912 # self.MenuBar.FindItemById(ID_ViewAssistant).Check(pane.window.IsShown())
\r
913 # if pane.name == 'Folders':
\r
914 # self.MenuBar.FindItemById(ID_ViewFolders).Check(pane.window.IsShown())
\r
915 # if pane.name == 'Playlists':
\r
916 # self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown())
\r
917 # if pane.name == 'Commands':
\r
918 # self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown())
\r
919 # if pane.name == 'Properties':
\r
920 # self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown())
\r
921 # if pane.name == 'Output':
\r
922 # self.MenuBar.FindItemById(ID_ViewOutput).Check(pane.window.IsShown())
\r
923 # if pane.name == 'Results':
\r
924 # self.MenuBar.FindItemById(ID_ViewResults).Check(pane.window.IsShown())
\r
926 def OnResultsCheck(self, index, flag):
\r
927 #TODO: fix for multiple results
\r
928 results = self.GetActivePlot().results
\r
929 fit_function_str = self.GetStringFromConfig('results', 'show_results', 'fit_function')
\r
930 results[fit_function_str].results[index].visible = flag
\r
933 def OnSavePerspective(self, event):
\r
935 def nameExists(name):
\r
936 menu_position = self.MenuBar.FindMenu('Perspectives')
\r
937 menu = self.MenuBar.GetMenu(menu_position)
\r
938 for item in menu.GetMenuItems():
\r
939 if item.GetText() == name:
\r
945 dialog = wx.TextEntryDialog(self, 'Enter a name for the new perspective:', 'Save perspective')
\r
946 dialog.SetValue('New perspective')
\r
947 if dialog.ShowModal() != wx.ID_OK:
\r
950 name = dialog.GetValue()
\r
952 if nameExists(name):
\r
953 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
954 if dialogConfirm.ShowModal() == wx.ID_YES:
\r
959 perspective = self._mgr.SavePerspective()
\r
960 self._SavePerspectiveToFile(name, perspective)
\r
961 self.config['perspectives']['active'] = name
\r
962 self.UpdatePerspectivesMenu()
\r
963 # if nameExists(name):
\r
964 # #check the corresponding menu item
\r
965 # menu_item = self.GetPerspectiveMenuItem(name)
\r
966 # #replace the perspectiveStr in _pespectives
\r
967 # self._perspectives[name] = perspective
\r
969 # #because we deal with radio items, we need to do some extra work
\r
970 # #delete all menu items from the perspectives menu
\r
971 # for item in self._perspectives_menu.GetMenuItems():
\r
972 # self._perspectives_menu.DeleteItem(item)
\r
973 # #recreate the perspectives menu
\r
974 # self._perspectives_menu.Append(ID_SavePerspective, 'Save Perspective')
\r
975 # self._perspectives_menu.Append(ID_DeletePerspective, 'Delete Perspective')
\r
976 # self._perspectives_menu.AppendSeparator()
\r
977 # #convert the perspectives dictionary into a list
\r
978 # # the list contains:
\r
979 # #[0]: name of the perspective
\r
980 # #[1]: perspective
\r
981 # perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
982 # perspectives_list.append(name)
\r
983 # perspectives_list.sort()
\r
984 # #add all previous perspectives
\r
985 # for index, item in enumerate(perspectives_list):
\r
986 # menu_item = self._perspectives_menu.AppendRadioItem(ID_FirstPerspective + index, item)
\r
988 # menu_item.Check()
\r
989 # #add the new perspective to _perspectives
\r
990 # self._perspectives[name] = perspective
\r
992 def OnSize(self, event):
\r
995 def OnTreeCtrlCommandsSelectionChanged(self, event):
\r
996 selected_item = event.GetItem()
\r
997 if selected_item is not None:
\r
1000 #deregister/register the listener to avoid infinite loop
\r
1001 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlCommandsSelectionChanged)
\r
1002 self.panelCommands.CommandsTree.SelectItem(selected_item)
\r
1003 evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self.panelCommands.CommandsTree)
\r
1004 self.panelProperties.SelectedTreeItem = selected_item
\r
1005 #if a command was clicked
\r
1007 if not self.panelCommands.CommandsTree.ItemHasChildren(selected_item):
\r
1008 item_plugin = self.panelCommands.CommandsTree.GetItemParent(selected_item)
\r
1009 plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
\r
1010 if self.configs.has_key(plugin):
\r
1011 #config = self.panelCommands.CommandsTree.GetPyData(item_plugin)
\r
1012 config = self.configs[plugin]
\r
1013 section = self.panelCommands.CommandsTree.GetItemText(selected_item)
\r
1014 #display docstring in help window
\r
1015 doc_string = eval('self.do_' + section + '.__doc__')
\r
1016 if section in config:
\r
1017 for option in config[section]:
\r
1018 properties.append([option, config[section][option]])
\r
1020 plugin = self.panelCommands.CommandsTree.GetItemText(selected_item)
\r
1021 if plugin != 'core':
\r
1022 doc_string = eval('plugins.' + plugin + '.' + plugin + 'Commands.__doc__')
\r
1024 doc_string = 'The module "core" contains Hooke core functionality'
\r
1025 if doc_string is not None:
\r
1026 self.panelAssistant.ChangeValue(doc_string)
\r
1028 self.panelAssistant.ChangeValue('')
\r
1029 panels.propertyeditor.PropertyEditor.Initialize(self.panelProperties, properties)
\r
1030 #save the currently selected command/plugin to the config file
\r
1031 self.config['command']['command'] = section
\r
1032 self.config['command']['plugin'] = plugin
\r
1034 def OnTreeCtrlItemActivated(self, event):
\r
1035 self.OnExecute(event)
\r
1037 def OnView(self, event):
\r
1038 menu_id = event.GetId()
\r
1039 menu_item = self.MenuBar.FindItemById(menu_id)
\r
1040 menu_label = menu_item.GetLabel()
\r
1042 pane = self._mgr.GetPane(menu_label)
\r
1043 pane.Show(not pane.IsShown())
\r
1044 #if we don't do the following, the Folders pane does not resize properly on hide/show
\r
1045 if pane.caption == 'Folders' and pane.IsShown() and pane.IsDocked():
\r
1046 #folders_size = pane.GetSize()
\r
1047 self.panelFolders.Fit()
\r
1048 self._mgr.Update()
\r
1050 def _measure_N_points(self, N, message='', whatset=lh.RETRACTION):
\r
1052 General helper function for N-points measurements
\r
1053 By default, measurements are done on the retraction
\r
1056 dialog = wx.MessageDialog(None, message, 'Info', wx.OK)
\r
1057 dialog.ShowModal()
\r
1059 figure = self.GetActiveFigure()
\r
1061 xvector = self.displayed_plot.curves[whatset].x
\r
1062 yvector = self.displayed_plot.curves[whatset].y
\r
1064 clicked_points = figure.ginput(N, timeout=-1, show_clicks=True)
\r
1067 for clicked_point in clicked_points:
\r
1068 point = lh.ClickedPoint()
\r
1069 point.absolute_coords = clicked_point[0], clicked_point[1]
\r
1071 #TODO: make this optional?
\r
1072 #so far, the clicked point is taken, not the corresponding data point
\r
1073 point.find_graph_coords(xvector, yvector)
\r
1074 point.is_line_edge = True
\r
1075 point.is_marker = True
\r
1076 points.append(point)
\r
1079 def _clickize(self, xvector, yvector, index):
\r
1081 returns a ClickedPoint() object from an index and vectors of x, y coordinates
\r
1083 point = lh.ClickedPoint()
\r
1084 point.index = index
\r
1085 point.absolute_coords = xvector[index], yvector[index]
\r
1086 point.find_graph_coords(xvector, yvector)
\r
1089 def _delta(self, color='black', message='Click 2 points', show=True, whatset=1):
\r
1091 calculates the difference between two clicked points
\r
1093 clicked_points = self._measure_N_points(N=2, message=message, whatset=whatset)
\r
1094 dx = abs(clicked_points[0].graph_coords[0] - clicked_points[1].graph_coords[0])
\r
1095 dy = abs(clicked_points[0].graph_coords[1] - clicked_points[1].graph_coords[1])
\r
1097 plot = self.GetDisplayedPlotCorrected()
\r
1099 curve = plot.curves[whatset]
\r
1100 unitx = curve.units.x
\r
1101 unity = curve.units.y
\r
1103 #TODO: move this to clicked_points?
\r
1105 for point in clicked_points:
\r
1106 points = copy.deepcopy(curve)
\r
1107 points.x = point.graph_coords[0]
\r
1108 points.y = point.graph_coords[1]
\r
1110 points.color = color
\r
1112 points.style = 'scatter'
\r
1113 plot.curves.append(points)
\r
1115 self.UpdatePlot(plot)
\r
1117 return dx, unitx, dy, unity
\r
1119 def do_plotmanipulators(self):
\r
1121 Please select the plotmanipulators you would like to use
\r
1122 and define the order in which they will be applied to the data.
\r
1124 Click 'Execute' to apply your changes.
\r
1128 def do_test(self):
\r
1130 Use this command for testing purposes. You find do_test in hooke.py.
\r
1134 def do_version(self):
\r
1138 Prints the current version and codename, plus library version. Useful for debugging.
\r
1140 self.AppendToOutput('Hooke ' + __version__ + ' (' + __codename__ + ')')
\r
1141 self.AppendToOutput('Released on: ' + __releasedate__)
\r
1142 self.AppendToOutput('---')
\r
1143 self.AppendToOutput('Python version: ' + python_version)
\r
1144 self.AppendToOutput('WxPython version: ' + wx_version)
\r
1145 self.AppendToOutput('Matplotlib version: ' + mpl_version)
\r
1146 self.AppendToOutput('SciPy version: ' + scipy_version)
\r
1147 self.AppendToOutput('NumPy version: ' + numpy_version)
\r
1148 self.AppendToOutput('---')
\r
1149 self.AppendToOutput('Platform: ' + str(platform.uname()))
\r
1150 #TODO: adapt to 'new' config
\r
1151 #self.AppendToOutput('---')
\r
1152 #self.AppendToOutput('Loaded plugins:', self.config['loaded_plugins'])
\r
1154 def UpdatePerspectivesMenu(self):
\r
1155 #add perspectives to menubar and _perspectives
\r
1156 perspectivesDirectory = os.path.join(lh.hookeDir, 'perspectives')
\r
1157 self._perspectives = {}
\r
1158 if os.path.isdir(perspectivesDirectory):
\r
1159 perspectiveFileNames = os.listdir(perspectivesDirectory)
\r
1160 for perspectiveFilename in perspectiveFileNames:
\r
1161 filename = lh.get_file_path(perspectiveFilename, ['perspectives'])
\r
1162 if os.path.isfile(filename):
\r
1163 perspectiveFile = open(filename, 'rU')
\r
1164 perspective = perspectiveFile.readline()
\r
1165 perspectiveFile.close()
\r
1166 if perspective != '':
\r
1167 name, extension = os.path.splitext(perspectiveFilename)
\r
1168 if extension == '.txt':
\r
1169 self._perspectives[name] = perspective
\r
1171 #in case there are no perspectives
\r
1172 if not self._perspectives:
\r
1173 perspective = self._mgr.SavePerspective()
\r
1174 self._perspectives['Default'] = perspective
\r
1175 self._SavePerspectiveToFile('Default', perspective)
\r
1177 selected_perspective = self.config['perspectives']['active']
\r
1178 if not self._perspectives.has_key(selected_perspective):
\r
1179 self.config['perspectives']['active'] = 'Default'
\r
1180 selected_perspective = 'Default'
\r
1182 perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
1183 perspectives_list.sort()
\r
1185 #get the Perspectives menu
\r
1186 menu_position = self.MenuBar.FindMenu('Perspectives')
\r
1187 menu = self.MenuBar.GetMenu(menu_position)
\r
1188 #delete all menu items
\r
1189 for item in menu.GetMenuItems():
\r
1190 menu.DeleteItem(item)
\r
1191 #rebuild the menu by adding the standard menu items
\r
1192 menu.Append(ID_SavePerspective, 'Save Perspective')
\r
1193 menu.Append(ID_DeletePerspective, 'Delete Perspective')
\r
1194 menu.AppendSeparator()
\r
1195 #add all previous perspectives
\r
1196 for index, label in enumerate(perspectives_list):
\r
1197 menu_item = menu.AppendRadioItem(ID_FirstPerspective + index, label)
\r
1198 if label == selected_perspective:
\r
1199 self._RestorePerspective(label)
\r
1200 menu_item.Check(True)
\r
1202 def UpdatePlaylistsTreeSelection(self):
\r
1203 playlist = self.GetActivePlaylist()
\r
1204 if playlist is not None:
\r
1205 if playlist.index >= 0:
\r
1206 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
1209 def UpdatePlot(self, plot=None):
\r
1211 def add_to_plot(curve):
\r
1212 if curve.visible and curve.x and curve.y:
\r
1213 destination = (curve.destination.column - 1) * number_of_rows + curve.destination.row - 1
\r
1214 axes_list[destination].set_title(curve.title)
\r
1215 axes_list[destination].set_xlabel(curve.units.x)
\r
1216 axes_list[destination].set_ylabel(curve.units.y)
\r
1217 if curve.style == 'plot':
\r
1218 axes_list[destination].plot(curve.x, curve.y, color=curve.color, label=curve.label, zorder=1)
\r
1219 if curve.style == 'scatter':
\r
1220 axes_list[destination].scatter(curve.x, curve.y, color=curve.color, label=curve.label, s=curve.size, zorder=2)
\r
1223 active_file = self.GetActiveFile()
\r
1224 if not active_file.driver:
\r
1225 active_file.identify(self.drivers)
\r
1226 self.displayed_plot = copy.deepcopy(active_file.plot)
\r
1227 #add raw curves to plot
\r
1228 self.displayed_plot.raw_curves = copy.deepcopy(self.displayed_plot.curves)
\r
1229 #apply all active plotmanipulators and add the 'manipulated' data
\r
1230 for plotmanipulator in self.plotmanipulators:
\r
1231 if self.GetBoolFromConfig('core', 'plotmanipulators', plotmanipulator.name):
\r
1232 self.displayed_plot = plotmanipulator.method(self.displayed_plot, active_file)
\r
1233 #add corrected curves to plot
\r
1234 self.displayed_plot.corrected_curves = copy.deepcopy(self.displayed_plot.curves)
\r
1236 active_file = None
\r
1237 self.displayed_plot = copy.deepcopy(plot)
\r
1239 figure = self.GetActiveFigure()
\r
1242 figure.suptitle(self.displayed_plot.title, fontsize=14)
\r
1246 number_of_columns = max([curve.destination.column for curve in self.displayed_plot.curves])
\r
1247 number_of_rows = max([curve.destination.row for curve in self.displayed_plot.curves])
\r
1249 for index in range(number_of_rows * number_of_columns):
\r
1250 axes_list.append(figure.add_subplot(number_of_rows, number_of_columns, index + 1))
\r
1252 for curve in self.displayed_plot.curves:
\r
1253 add_to_plot(curve)
\r
1255 #make sure the titles of 'subplots' do not overlap with the axis labels of the 'main plot'
\r
1256 figure.subplots_adjust(hspace=0.3)
\r
1258 #TODO: add multiple results support to fit in curve.results:
\r
1259 #get the fit_function results to display
\r
1260 fit_function_str = self.GetStringFromConfig('results', 'show_results', 'fit_function')
\r
1261 self.panelResults.ClearResults()
\r
1262 plot = self.GetActivePlot()
\r
1263 if plot is not None:
\r
1264 if plot.results.has_key(fit_function_str):
\r
1265 for curve in plot.results[fit_function_str].results:
\r
1266 add_to_plot(curve)
\r
1267 self.panelResults.DisplayResults(plot.results[fit_function_str])
\r
1269 self.panelResults.ClearResults()
\r
1271 figure.canvas.draw()
\r
1273 for axes in axes_list:
\r
1274 #TODO: add legend as global option or per graph option
\r
1276 axes.figure.canvas.draw()
\r
1279 if __name__ == '__main__':
\r
1281 ## now, silence a deprecation warning for py2.3
\r
1283 warnings.filterwarnings("ignore", "integer", DeprecationWarning, "wxPython.gdi")
\r
1289 app = Hooke(redirect=redirect)
\r