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
54 global __releasedate__
\r
55 __version__ = lh.HOOKE_VERSION[0]
\r
56 __codename__ = lh.HOOKE_VERSION[1]
\r
57 __releasedate__ = lh.HOOKE_VERSION[2]
\r
58 __release_name__ = lh.HOOKE_VERSION[1]
\r
60 #TODO: add general preferences to Hooke
\r
61 #this might be useful
\r
62 #ID_Config = wx.NewId()
\r
63 ID_About = wx.NewId()
\r
64 ID_Next = wx.NewId()
\r
65 ID_Previous = wx.NewId()
\r
67 ID_ViewAssistant = wx.NewId()
\r
68 ID_ViewCommands = wx.NewId()
\r
69 ID_ViewFolders = wx.NewId()
\r
70 ID_ViewOutput = wx.NewId()
\r
71 ID_ViewPlaylists = wx.NewId()
\r
72 ID_ViewProperties = wx.NewId()
\r
73 ID_ViewResults = wx.NewId()
\r
75 ID_DeletePerspective = wx.NewId()
\r
76 ID_SavePerspective = wx.NewId()
\r
78 ID_FirstPerspective = ID_SavePerspective + 1000
\r
79 #I hope we'll never have more than 1000 perspectives
\r
80 ID_FirstPlot = ID_SavePerspective + 2000
\r
82 class Hooke(wx.App):
\r
85 self.SetAppName('Hooke')
\r
86 self.SetVendorName('')
\r
88 windowPosition = (config['main']['left'], config['main']['top'])
\r
89 windowSize = (config['main']['width'], config['main']['height'])
\r
91 #setup the splashscreen
\r
92 if config['splashscreen']['show']:
\r
93 filename = lh.get_file_path('hooke.jpg', ['resources'])
\r
94 if os.path.isfile(filename):
\r
95 bitmap = wx.Image(filename).ConvertToBitmap()
\r
96 splashStyle = wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT
\r
97 splashDuration = config['splashscreen']['duration']
\r
98 wx.SplashScreen(bitmap, splashStyle, splashDuration, None, -1)
\r
101 we need for the splash screen to disappear
\r
102 for whatever reason splashDuration and sleep do not correspond to each other
\r
103 at least not on Windows
\r
104 maybe it's because duration is in milliseconds and sleep in seconds
\r
105 thus we need to increase the sleep time a bit
\r
106 a factor of 1.2 seems to work quite well
\r
109 time.sleep(sleepFactor * splashDuration / 1000)
\r
111 plugin_objects = []
\r
112 for plugin in config['plugins']:
\r
113 if config['plugins'][plugin]:
\r
114 filename = ''.join([plugin, '.py'])
\r
115 path = lh.get_file_path(filename, ['plugins'])
\r
116 if os.path.isfile(path):
\r
117 #get the corresponding filename and path
\r
118 plugin_name = ''.join(['plugins.', plugin])
\r
120 __import__(plugin_name)
\r
121 #get the file that contains the plugin
\r
122 class_file = getattr(plugins, plugin)
\r
123 #get the class that contains the commands
\r
124 class_object = getattr(class_file, plugin + 'Commands')
\r
125 plugin_objects.append(class_object)
\r
127 def make_command_class(*bases):
\r
128 #create metaclass with plugins and plotmanipulators
\r
129 return type(HookeFrame)("HookeFramePlugged", bases + (HookeFrame,), {})
\r
130 frame = make_command_class(*plugin_objects)(parent=None, id=wx.ID_ANY, title='Hooke', pos=windowPosition, size=windowSize)
\r
132 self.SetTopWindow(frame)
\r
140 class HookeFrame(wx.Frame):
\r
142 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
143 #call parent constructor
\r
144 wx.Frame.__init__(self, parent, id, title, pos, size, style)
\r
145 self.config = config
\r
146 self.CreateApplicationIcon()
\r
147 #self.configs contains: {the name of the Commands file: corresponding ConfigObj}
\r
149 #self.displayed_plot holds the currently displayed plot
\r
150 self.displayed_plot = None
\r
151 #self.playlists contains: {the name of the playlist: [playlist, tabIndex, plotID]}
\r
152 self.playlists = {}
\r
153 #list of all plotmanipulators
\r
154 self.plotmanipulators = []
\r
155 #self.plugins contains: {the name of the plugin: [caption, function]}
\r
158 #tell FrameManager to manage this frame
\r
159 self._mgr = aui.AuiManager()
\r
160 self._mgr.SetManagedWindow(self)
\r
161 #set the gradient style
\r
162 self._mgr.GetArtProvider().SetMetric(aui.AUI_DOCKART_GRADIENT_TYPE, aui.AUI_GRADIENT_NONE)
\r
163 #set transparent drag
\r
164 self._mgr.SetFlags(self._mgr.GetFlags() ^ aui.AUI_MGR_TRANSPARENT_DRAG)
\r
166 # set up default notebook style
\r
167 self._notebook_style = aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER
\r
168 self._notebook_theme = 0
\r
170 #holds the perspectives: {name, perspective_str}
\r
171 self._perspectives = {}
\r
173 # min size for the frame itself isn't completely done.
\r
174 # see the end up FrameManager::Update() for the test
\r
175 # code. For now, just hard code a frame minimum size
\r
176 self.SetMinSize(wx.Size(500, 500))
\r
177 #create panels here
\r
178 self.panelAssistant = self.CreatePanelAssistant()
\r
179 self.panelCommands = self.CreatePanelCommands()
\r
180 self.panelFolders = self.CreatePanelFolders()
\r
181 self.panelPlaylists = self.CreatePanelPlaylists()
\r
182 self.panelProperties = self.CreatePanelProperties()
\r
183 self.panelOutput = self.CreatePanelOutput()
\r
184 self.panelResults = self.CreatePanelResults()
\r
185 self.plotNotebook = self.CreateNotebook()
\r
186 #self.textCtrlCommandLine=self.CreateCommandLine()
\r
189 self._mgr.AddPane(self.panelFolders, aui.AuiPaneInfo().Name('Folders').Caption('Folders').Left().CloseButton(True).MaximizeButton(False))
\r
190 self._mgr.AddPane(self.panelPlaylists, aui.AuiPaneInfo().Name('Playlists').Caption('Playlists').Left().CloseButton(True).MaximizeButton(False))
\r
191 self._mgr.AddPane(self.plotNotebook, aui.AuiPaneInfo().Name('Plots').CenterPane().PaneBorder(False))
\r
192 self._mgr.AddPane(self.panelCommands, aui.AuiPaneInfo().Name('Commands').Caption('Settings and commands').Right().CloseButton(True).MaximizeButton(False))
\r
193 self._mgr.AddPane(self.panelProperties, aui.AuiPaneInfo().Name('Properties').Caption('Properties').Right().CloseButton(True).MaximizeButton(False))
\r
194 self._mgr.AddPane(self.panelAssistant, aui.AuiPaneInfo().Name('Assistant').Caption('Assistant').Right().CloseButton(True).MaximizeButton(False))
\r
195 self._mgr.AddPane(self.panelOutput, aui.AuiPaneInfo().Name('Output').Caption('Output').Bottom().CloseButton(True).MaximizeButton(False))
\r
196 self._mgr.AddPane(self.panelResults, aui.AuiPaneInfo().Name('Results').Caption('Results').Bottom().CloseButton(True).MaximizeButton(False))
\r
197 #self._mgr.AddPane(self.textCtrlCommandLine, aui.AuiPaneInfo().Name('CommandLine').CaptionVisible(False).Fixed().Bottom().Layer(2).CloseButton(False).MaximizeButton(False))
\r
198 #self._mgr.AddPane(panelBottom, aui.AuiPaneInfo().Name("panelCommandLine").Bottom().Position(1).CloseButton(False).MaximizeButton(False))
\r
200 # add the toolbars to the manager
\r
201 #self.toolbar=self.CreateToolBar()
\r
202 self.toolbarNavigation=self.CreateToolBarNavigation()
\r
203 #self._mgr.AddPane(self.toolbar, aui.AuiPaneInfo().Name('toolbar').Caption('Toolbar').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
\r
204 self._mgr.AddPane(self.toolbarNavigation, aui.AuiPaneInfo().Name('toolbarNavigation').Caption('Navigation').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
\r
205 # "commit" all changes made to FrameManager
\r
207 #create the menubar after the panes so that the default perspective
\r
208 #is created with all panes open
\r
209 self.CreateMenuBar()
\r
210 self.statusbar = self.CreateStatusbar()
\r
213 name = self.config['perspectives']['active']
\r
214 menu_item = self.GetPerspectiveMenuItem(name)
\r
215 if menu_item is not None:
\r
216 self.OnRestorePerspective(menu_item)
\r
217 #TODO: config setting to remember playlists from last session
\r
218 self.playlists = self.panelPlaylists.Playlists
\r
219 #define the list of active drivers
\r
221 for driver in self.config['drivers']:
\r
222 if self.config['drivers'][driver]:
\r
223 #get the corresponding filename and path
\r
224 filename = ''.join([driver, '.py'])
\r
225 path = lh.get_file_path(filename, ['drivers'])
\r
226 #the driver is active for driver[1] == 1
\r
227 if os.path.isfile(path):
\r
228 #driver files are located in the 'drivers' subfolder
\r
229 driver_name = ''.join(['drivers.', driver])
\r
230 __import__(driver_name)
\r
231 class_file = getattr(drivers, driver)
\r
232 for command in dir(class_file):
\r
233 if command.endswith('Driver'):
\r
234 self.drivers.append(getattr(class_file, command))
\r
235 #import all active plugins and plotmanips
\r
236 #add 'core.ini' to self.configs (this is not a plugin and thus must be imported separately)
\r
237 ini_path = lh.get_file_path('core.ini', ['plugins'])
\r
238 plugin_config = ConfigObj(ini_path)
\r
239 #self.config.merge(plugin_config)
\r
240 self.configs['core'] = plugin_config
\r
241 #make sure we execute _plug_init() for every command line plugin we import
\r
242 for plugin in self.config['plugins']:
\r
243 if self.config['plugins'][plugin]:
\r
244 filename = ''.join([plugin, '.py'])
\r
245 path = lh.get_file_path(filename, ['plugins'])
\r
246 if os.path.isfile(path):
\r
247 #get the corresponding filename and path
\r
248 plugin_name = ''.join(['plugins.', plugin])
\r
251 module = __import__(plugin_name)
\r
252 #prepare the ini file for inclusion
\r
253 ini_path = path.replace('.py', '.ini')
\r
255 plugin_config = ConfigObj(ini_path)
\r
256 #self.config.merge(plugin_config)
\r
257 self.configs[plugin] = plugin_config
\r
259 commands = eval('dir(module.' + plugin+ '.' + plugin + 'Commands)')
\r
260 #keep only commands (ie names that start with 'do_')
\r
261 #TODO: check for existing commands and warn the user!
\r
262 commands = [command for command in commands if command.startswith('do_')]
\r
264 self.plugins[plugin] = commands
\r
266 #initialize the plugin
\r
267 eval('module.' + plugin+ '.' + plugin + 'Commands._plug_init(self)')
\r
268 except AttributeError:
\r
270 except ImportError:
\r
272 #initialize the commands tree
\r
273 commands = dir(HookeFrame)
\r
274 commands = [command for command in commands if command.startswith('do_')]
\r
276 self.plugins['core'] = commands
\r
277 self.panelCommands.Initialize(self.plugins)
\r
278 for command in dir(self):
\r
279 if command.startswith('plotmanip_'):
\r
280 self.plotmanipulators.append(lib.plotmanipulator.Plotmanipulator(method=getattr(self, command), command=command))
\r
282 #load default list, if possible
\r
283 self.do_loadlist(self.config['core']['list'])
\r
284 #self.do_loadlist()
\r
286 def _BindEvents(self):
\r
287 #TODO: figure out if we can use the eventManager for menu ranges
\r
288 #and events of 'self' without raising an assertion fail error
\r
289 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
\r
290 self.Bind(wx.EVT_SIZE, self.OnSize)
\r
291 self.Bind(wx.EVT_CLOSE, self.OnClose)
\r
292 # Show How To Use The Closing Panes Event
\r
293 self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPaneClose)
\r
294 self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnNotebookPageClose)
\r
296 evtmgr.eventManager.Register(self.OnAbout, wx.EVT_MENU, win=self, id=wx.ID_ABOUT)
\r
297 evtmgr.eventManager.Register(self.OnClose, wx.EVT_MENU, win=self, id=wx.ID_EXIT)
\r
299 self.Bind(wx.EVT_MENU_RANGE, self.OnView, id=ID_ViewAssistant, id2=ID_ViewResults)
\r
301 self.Bind(wx.EVT_MENU, self.OnDeletePerspective, id=ID_DeletePerspective)
\r
302 self.Bind(wx.EVT_MENU, self.OnSavePerspective, id=ID_SavePerspective)
\r
303 self.Bind(wx.EVT_MENU_RANGE, self.OnRestorePerspective, id=ID_FirstPerspective, id2=ID_FirstPerspective+1000)
\r
305 evtmgr.eventManager.Register(self.OnNext, wx.EVT_TOOL, win=self, id=ID_Next)
\r
306 evtmgr.eventManager.Register(self.OnPrevious, wx.EVT_TOOL, win=self, id=ID_Previous)
\r
307 #self.Bind(.EVT_AUITOOLBAR_TOOL_DROPDOWN, self.OnDropDownToolbarItem, id=ID_DropDownToolbarItem)
\r
309 treeCtrl = self.panelFolders.GetTreeCtrl()
\r
310 #tree.Bind(wx.EVT_LEFT_UP, self.OnDirCtrl1LeftUp)
\r
311 #tree.Bind(wx.EVT_LEFT_DOWN, self.OnGenericDirCtrl1LeftDown)
\r
312 treeCtrl.Bind(wx.EVT_LEFT_DCLICK, self.OnDirCtrlLeftDclick)
\r
314 self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DOWN, self.OnPlaylistsLeftDown)
\r
315 self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DCLICK, self.OnPlaylistsLeftDclick)
\r
317 evtmgr.eventManager.Register(self.OnExecute, wx.EVT_BUTTON, self.panelCommands.ExecuteButton)
\r
318 evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self.panelCommands.CommandsTree)
\r
319 evtmgr.eventManager.Register(self.OnTreeCtrlItemActivated, wx.EVT_TREE_ITEM_ACTIVATED, self.panelCommands.CommandsTree)
\r
321 self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged)
\r
323 self.panelResults.results_list.OnCheckItem = self.OnResultsCheck
\r
325 def _GetActiveFileIndex(self):
\r
326 lib.playlist.Playlist = self.GetActivePlaylist()
\r
327 #get the selected item from the tree
\r
328 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
329 #test if a playlist or a curve was double-clicked
\r
330 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
334 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
335 while selected_item.IsOk():
\r
337 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
340 def _GetPlaylistTab(self, name):
\r
341 for index, page in enumerate(self.plotNotebook._tabs._pages):
\r
342 if page.caption == name:
\r
346 def _GetUniquePlaylistName(self, name):
\r
347 playlist_name = name
\r
349 while playlist_name in self.playlists:
\r
350 playlist_name = ''.join([name, str(count)])
\r
352 return playlist_name
\r
354 def _RestorePerspective(self, name):
\r
355 self._mgr.LoadPerspective(self._perspectives[name])
\r
356 self.config['perspectives']['active'] = name
\r
358 all_panes = self._mgr.GetAllPanes()
\r
359 for pane in all_panes:
\r
360 if not pane.name.startswith('toolbar'):
\r
361 if pane.name == 'Assistant':
\r
362 self.MenuBar.FindItemById(ID_ViewAssistant).Check(pane.window.IsShown())
\r
363 if pane.name == 'Folders':
\r
364 self.MenuBar.FindItemById(ID_ViewFolders).Check(pane.window.IsShown())
\r
365 if pane.name == 'Playlists':
\r
366 self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown())
\r
367 if pane.name == 'Commands':
\r
368 self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown())
\r
369 if pane.name == 'Properties':
\r
370 self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown())
\r
371 if pane.name == 'Output':
\r
372 self.MenuBar.FindItemById(ID_ViewOutput).Check(pane.window.IsShown())
\r
373 if pane.name == 'Results':
\r
374 self.MenuBar.FindItemById(ID_ViewResults).Check(pane.window.IsShown())
\r
376 def _SavePerspectiveToFile(self, name, perspective):
\r
377 filename = ''.join([name, '.txt'])
\r
378 filename = lh.get_file_path(filename, ['perspectives'])
\r
379 perspectivesFile = open(filename, 'w')
\r
380 perspectivesFile.write(perspective)
\r
381 perspectivesFile.close()
\r
383 def _UnbindEvents(self):
\r
385 evtmgr.eventManager.DeregisterListener(self.OnAbout)
\r
386 evtmgr.eventManager.DeregisterListener(self.OnClose)
\r
388 evtmgr.eventManager.DeregisterListener(self.OnNext)
\r
389 evtmgr.eventManager.DeregisterListener(self.OnPrevious)
\r
391 evtmgr.eventManager.DeregisterListener(self.OnExecute)
\r
392 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlCommandsSelectionChanged)
\r
394 def AddPlaylist(self, playlist=None, name='Untitled'):
\r
395 if playlist and playlist.count > 0:
\r
396 playlist.name = self._GetUniquePlaylistName(name)
\r
398 self.AddToPlaylists(playlist)
\r
400 def AddPlaylistFromFiles(self, files=[], name='Untitled'):
\r
402 playlist = lib.playlist.Playlist(self, self.drivers)
\r
404 playlist.add_curve(item)
\r
405 if playlist.count > 0:
\r
406 playlist.name = self._GetUniquePlaylistName(name)
\r
408 self.AddTayliss(playlist)
\r
410 def AddToPlaylists(self, playlist):
\r
411 if playlist.count > 0:
\r
412 #setup the playlist in the Playlist tree
\r
413 tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
\r
414 playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)
\r
415 #add all files to the Playlist tree
\r
417 for index, file_to_add in enumerate(playlist.files):
\r
418 #TODO: optionally remove the extension from the name of the curve
\r
419 #item_text, extension = os.path.splitext(curve.name)
\r
420 #curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, item_text, 1)
\r
421 file_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, file_to_add.name, 1)
\r
422 if index == playlist.index:
\r
423 self.panelPlaylists.PlaylistsTree.SelectItem(file_ID)
\r
425 #create the plot tab and add playlist to the dictionary
\r
426 plotPanel = panels.plot.PlotPanel(self, ID_FirstPlot + len(self.playlists))
\r
427 notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True)
\r
428 #tab_index = self.plotNotebook.GetSelection()
\r
429 playlist.figure = plotPanel.get_figure()
\r
430 self.playlists[playlist.name] = playlist
\r
431 #self.playlists[playlist.name] = [playlist, figure]
\r
432 self.panelPlaylists.PlaylistsTree.Expand(playlist_root)
\r
433 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
436 def AppendToOutput(self, text):
\r
437 self.panelOutput.AppendText(''.join([text, '\n']))
\r
439 def AppliesPlotmanipulator(self, name):
\r
441 returns True if the plotmanipulator 'name' is applied, False otherwise
\r
442 name does not contain 'plotmanip_', just the name of the plotmanipulator (e.g. 'flatten')
\r
444 return self.GetBoolFromConfig('core', 'plotmanipulators', name)
\r
446 def CreateApplicationIcon(self):
\r
447 iconFile = 'resources' + os.sep + 'microscope.ico'
\r
448 icon = wx.Icon(iconFile, wx.BITMAP_TYPE_ICO)
\r
451 def CreateCommandLine(self):
\r
452 return wx.TextCtrl(self, -1, '', style=wx.NO_BORDER|wx.EXPAND)
\r
454 def CreatePanelAssistant(self):
\r
455 panel = wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
\r
456 panel.SetEditable(False)
\r
459 def CreatePanelCommands(self):
\r
460 return panels.commands.Commands(self)
\r
462 def CreatePanelFolders(self):
\r
464 filters = self.config['folders']['filters']
\r
465 index = self.config['folders'].as_int('filterindex')
\r
466 #set initial directory
\r
467 folder = self.config['core']['workdir']
\r
468 return wx.GenericDirCtrl(self, -1, dir=folder, size=(200, 250), style=wx.DIRCTRL_SHOW_FILTERS, filter=filters, defaultFilter=index)
\r
470 def CreatePanelOutput(self):
\r
471 return wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
\r
473 def CreatePanelPlaylists(self):
\r
474 return panels.playlist.Playlists(self)
\r
476 def CreatePanelProperties(self):
\r
477 return panels.propertyeditor.PropertyEditor(self)
\r
479 def CreatePanelResults(self):
\r
480 return panels.results.Results(self)
\r
482 def CreatePanelWelcome(self):
\r
483 #TODO: move into panels.welcome
\r
484 ctrl = wx.html.HtmlWindow(self, -1, wx.DefaultPosition, wx.Size(400, 300))
\r
485 introStr = '<h1>Welcome to Hooke</h1>' + \
\r
486 '<h3>Features</h3>' + \
\r
488 '<li>View, annotate, measure force files</li>' + \
\r
489 '<li>Worm-like chain fit of force peaks</li>' + \
\r
490 '<li>Automatic convolution-based filtering of empty files</li>' + \
\r
491 '<li>Automatic fit and measurement of multiple force peaks</li>' + \
\r
492 '<li>Handles force-clamp force experiments (experimental)</li>' + \
\r
493 '<li>It is extensible by users by means of plugins and drivers</li>' + \
\r
495 '<p>See the <a href="http://code.google.com/p/hooke/wiki/DocumentationIndex">DocumentationIndex</a> for more information</p>'
\r
496 ctrl.SetPage(introStr)
\r
499 def CreateMenuBar(self):
\r
500 menu_bar = wx.MenuBar()
\r
501 self.SetMenuBar(menu_bar)
\r
503 file_menu = wx.Menu()
\r
504 file_menu.Append(wx.ID_EXIT, 'Exit\tCtrl-Q')
\r
505 # edit_menu.AppendSeparator();
\r
506 # edit_menu.Append(ID_Config, 'Preferences')
\r
508 view_menu = wx.Menu()
\r
509 view_menu.AppendCheckItem(ID_ViewFolders, 'Folders\tF5')
\r
510 view_menu.AppendCheckItem(ID_ViewPlaylists, 'Playlists\tF6')
\r
511 view_menu.AppendCheckItem(ID_ViewCommands, 'Commands\tF7')
\r
512 view_menu.AppendCheckItem(ID_ViewProperties, 'Properties\tF8')
\r
513 view_menu.AppendCheckItem(ID_ViewAssistant, 'Assistant\tF9')
\r
514 view_menu.AppendCheckItem(ID_ViewResults, 'Results\tF10')
\r
515 view_menu.AppendCheckItem(ID_ViewOutput, 'Output\tF11')
\r
517 # perspectives_menu = self.CreatePerspectivesMenu()
\r
518 perspectives_menu = wx.Menu()
\r
521 help_menu = wx.Menu()
\r
522 help_menu.Append(wx.ID_ABOUT, 'About Hooke')
\r
523 #put it all together
\r
524 menu_bar.Append(file_menu, 'File')
\r
525 # menu_bar.Append(edit_menu, 'Edit')
\r
526 menu_bar.Append(view_menu, 'View')
\r
527 menu_bar.Append(perspectives_menu, "Perspectives")
\r
528 self.UpdatePerspectivesMenu()
\r
529 menu_bar.Append(help_menu, 'Help')
\r
531 def CreateNotebook(self):
\r
532 # create the notebook off-window to avoid flicker
\r
533 client_size = self.GetClientSize()
\r
534 ctrl = aui.AuiNotebook(self, -1, wx.Point(client_size.x, client_size.y), wx.Size(430, 200), self._notebook_style)
\r
535 arts = [aui.AuiDefaultTabArt, aui.AuiSimpleTabArt, aui.VC71TabArt, aui.FF2TabArt, aui.VC8TabArt, aui.ChromeTabArt]
\r
536 art = arts[self._notebook_theme]()
\r
537 ctrl.SetArtProvider(art)
\r
538 #uncomment if we find a nice icon
\r
539 #page_bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))
\r
540 ctrl.AddPage(self.CreatePanelWelcome(), "Welcome", False)
\r
543 def CreateStatusbar(self):
\r
544 statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
\r
545 statusbar.SetStatusWidths([-2, -3])
\r
546 statusbar.SetStatusText('Ready', 0)
\r
547 welcomeString=u'Welcome to Hooke (version '+__version__+', '+__release_name__+')!'
\r
548 statusbar.SetStatusText(welcomeString, 1)
\r
551 def CreateToolBarNavigation(self):
\r
552 toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)
\r
553 toolbar.SetToolBitmapSize(wx.Size(16,16))
\r
554 toolbar_bmpBack = wx.ArtProvider_GetBitmap(wx.ART_GO_BACK, wx.ART_OTHER, wx.Size(16, 16))
\r
555 toolbar_bmpForward = wx.ArtProvider_GetBitmap(wx.ART_GO_FORWARD, wx.ART_OTHER, wx.Size(16, 16))
\r
556 toolbar.AddLabelTool(ID_Previous, 'Previous', toolbar_bmpBack, shortHelp='Previous curve')
\r
557 toolbar.AddLabelTool(ID_Next, 'Next', toolbar_bmpForward, shortHelp='Next curve')
\r
561 def DeleteFromPlaylists(self, name):
\r
562 if name in self.playlists:
\r
563 del self.playlists[name]
\r
564 tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
\r
565 item, cookie = self.panelPlaylists.PlaylistsTree.GetFirstChild(tree_root)
\r
567 playlist_name = self.panelPlaylists.PlaylistsTree.GetItemText(item)
\r
568 if playlist_name == name:
\r
570 self.panelPlaylists.PlaylistsTree.Delete(item)
\r
573 item = self.panelPlaylists.PlaylistsTree.GetNextSibling(item)
\r
575 def GetActiveFigure(self):
\r
576 playlist_name = self.GetActivePlaylistName()
\r
577 figure = self.playlists[playlist_name].figure
\r
578 if figure is not None:
\r
582 def GetActiveFile(self):
\r
583 playlist = self.GetActivePlaylist()
\r
584 if playlist is not None:
\r
585 return playlist.get_active_file()
\r
588 def GetActivePlaylist(self):
\r
589 playlist_name = self.GetActivePlaylistName()
\r
590 if playlist_name in self.playlists:
\r
591 return self.playlists[playlist_name]
\r
594 def GetActivePlaylistName(self):
\r
595 #get the selected item from the tree
\r
596 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
597 #test if a playlist or a curve was double-clicked
\r
598 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
599 playlist_item = selected_item
\r
601 #get the name of the playlist
\r
602 playlist_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
603 #now we have a playlist
\r
604 return self.panelPlaylists.PlaylistsTree.GetItemText(playlist_item)
\r
606 def GetActivePlot(self):
\r
607 playlist = self.GetActivePlaylist()
\r
608 if playlist is not None:
\r
609 return playlist.get_active_file().plot
\r
612 def GetDisplayedPlot(self):
\r
613 plot = copy.deepcopy(self.displayed_plot)
\r
615 plot.curves = copy.deepcopy(plot.curves)
\r
618 def GetDisplayedPlotCorrected(self):
\r
619 plot = copy.deepcopy(self.displayed_plot)
\r
621 plot.curves = copy.deepcopy(plot.corrected_curves)
\r
624 def GetDisplayedPlotRaw(self):
\r
625 plot = copy.deepcopy(self.displayed_plot)
\r
627 plot.curves = copy.deepcopy(plot.raw_curves)
\r
630 def GetDockArt(self):
\r
631 return self._mgr.GetArtProvider()
\r
633 def GetBoolFromConfig(self, *args):
\r
638 elif len(args) == 3:
\r
642 if self.configs.has_key(plugin):
\r
643 config = self.configs[plugin]
\r
644 return config[section][key].as_bool('value')
\r
647 def GetColorFromConfig(self, *args):
\r
652 elif len(args) == 3:
\r
656 if self.configs.has_key(plugin):
\r
657 config = self.configs[plugin]
\r
658 color_tuple = eval(config[section][key]['value'])
\r
659 color = [value / 255.0 for value in color_tuple]
\r
663 def GetFloatFromConfig(self, *args):
\r
668 elif len(args) == 3:
\r
672 if self.configs.has_key(plugin):
\r
673 config = self.configs[plugin]
\r
674 return config[section][key].as_float('value')
\r
677 def GetIntFromConfig(self, *args):
\r
682 elif len(args) == 3:
\r
686 if self.configs.has_key(plugin):
\r
687 config = self.configs[plugin]
\r
688 return config[section][key].as_int('value')
\r
691 def GetStringFromConfig(self, *args):
\r
696 elif len(args) == 3:
\r
700 if self.configs.has_key(plugin):
\r
701 config = self.configs[plugin]
\r
702 return config[section][key]['value']
\r
705 def GetPerspectiveMenuItem(self, name):
\r
706 if self._perspectives.has_key(name):
\r
707 perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
708 perspectives_list.sort()
\r
709 index = perspectives_list.index(name)
\r
710 perspective_Id = ID_FirstPerspective + index
\r
711 menu_item = self.MenuBar.FindItemById(perspective_Id)
\r
716 def HasPlotmanipulator(self, name):
\r
718 returns True if the plotmanipulator 'name' is loaded, False otherwise
\r
720 for plotmanipulator in self.plotmanipulators:
\r
721 if plotmanipulator.command == name:
\r
725 def OnAbout(self, event):
\r
726 message = 'Hooke\n\n'+\
\r
727 'A free, open source data analysis platform\n\n'+\
\r
728 'Copyright 2006-2008 by Massimo Sandal\n'+\
\r
729 'Copyright 2010 by Dr. Rolf Schmidt\n\n'+\
\r
730 'Hooke is released under the GNU General Public License version 2.'
\r
731 dialog = wx.MessageDialog(self, message, 'About Hooke', wx.OK | wx.ICON_INFORMATION)
\r
735 def OnClose(self, event):
\r
737 self.config['main']['height'] = str(self.GetSize().GetHeight())
\r
738 self.config['main']['left'] = str(self.GetPosition()[0])
\r
739 self.config['main']['top'] = str(self.GetPosition()[1])
\r
740 self.config['main']['width'] = str(self.GetSize().GetWidth())
\r
741 #save the configuration file to 'config/hooke.ini'
\r
742 self.config.write()
\r
743 #save all plugin config files
\r
744 for config in self.configs:
\r
745 plugin_config = self.configs[config]
\r
746 plugin_config.write()
\r
747 self._UnbindEvents()
\r
752 def OnDeletePerspective(self, event):
\r
753 dialog = panels.perspectives.Perspectives(self, -1, 'Delete perspective(s)')
\r
754 dialog.CenterOnScreen()
\r
757 self.UpdatePerspectivesMenu()
\r
758 #unfortunately, there is a bug in wxWidgets (Ticket #3258) that
\r
759 #makes the radio item indicator in the menu disappear
\r
760 #the code should be fine once this issue is fixed
\r
762 def OnDirCtrlLeftDclick(self, event):
\r
763 file_path = self.panelFolders.GetPath()
\r
764 if os.path.isfile(file_path):
\r
765 if file_path.endswith('.hkp'):
\r
766 self.do_loadlist(file_path)
\r
769 def OnEraseBackground(self, event):
\r
772 def OnExecute(self, event):
\r
773 item = self.panelCommands.CommandsTree.GetSelection()
\r
775 if not self.panelCommands.CommandsTree.ItemHasChildren(item):
\r
776 item_text = self.panelCommands.CommandsTree.GetItemText(item)
\r
777 command = ''.join(['self.do_', item_text, '()'])
\r
778 #self.AppendToOutput(command + '\n')
\r
781 def OnExit(self, event):
\r
784 def OnNext(self, event):
\r
787 Go to the next curve in the playlist.
\r
788 If we are at the last curve, we come back to the first.
\r
792 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
793 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
794 #GetFirstChild returns a tuple
\r
795 #we only need the first element
\r
796 next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(selected_item)[0]
\r
798 next_item = self.panelPlaylists.PlaylistsTree.GetNextSibling(selected_item)
\r
799 if not next_item.IsOk():
\r
800 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
801 #GetFirstChild returns a tuple
\r
802 #we only need the first element
\r
803 next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(parent_item)[0]
\r
804 self.panelPlaylists.PlaylistsTree.SelectItem(next_item, True)
\r
805 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
806 playlist = self.GetActivePlaylist()
\r
807 if playlist.count > 1:
\r
809 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
812 def OnNotebookPageClose(self, event):
\r
813 ctrl = event.GetEventObject()
\r
814 playlist_name = ctrl.GetPageText(ctrl._curpage)
\r
815 self.DeleteFromPlaylists(playlist_name)
\r
817 def OnPaneClose(self, event):
\r
820 def OnPlaylistsLeftDclick(self, event):
\r
821 if self.panelPlaylists.PlaylistsTree.Count > 0:
\r
822 playlist_name = self.GetActivePlaylistName()
\r
823 #if that playlist already exists
\r
824 #we check if it is the active playlist (ie selected in panelPlaylists)
\r
825 #and switch to it if necessary
\r
826 if playlist_name in self.playlists:
\r
827 index = self.plotNotebook.GetSelection()
\r
828 current_playlist = self.plotNotebook.GetPageText(index)
\r
829 if current_playlist != playlist_name:
\r
830 index = self._GetPlaylistTab(playlist_name)
\r
831 self.plotNotebook.SetSelection(index)
\r
832 #if a curve was double-clicked
\r
833 item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
834 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
\r
835 index = self._GetActiveFileIndex()
\r
839 playlist = self.GetActivePlaylist()
\r
840 playlist.index = index
\r
841 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
843 #if you uncomment the following line, the tree will collapse/expand as well
\r
846 def OnPlaylistsLeftDown(self, event):
\r
847 hit_item, hit_flags = self.panelPlaylists.PlaylistsTree.HitTest(event.GetPosition())
\r
848 if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:
\r
849 self.panelPlaylists.PlaylistsTree.SelectItem(hit_item)
\r
850 playlist_name = self.GetActivePlaylistName()
\r
851 playlist = self.GetActivePlaylist()
\r
852 #if a curve was clicked
\r
853 item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
854 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
\r
855 index = self._GetActiveFileIndex()
\r
857 playlist.index = index
\r
858 self.playlists[playlist_name] = playlist
\r
861 def OnPrevious(self, event):
\r
864 Go to the previous curve in the playlist.
\r
865 If we are at the first curve, we jump to the last.
\r
867 Syntax: previous, p
\r
869 #playlist = self.playlists[self.GetActivePlaylistName()][0]
\r
870 #select the previous curve and tell the user if we wrapped around
\r
871 #self.AppendToOutput(playlist.previous())
\r
872 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
873 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
874 previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(selected_item)
\r
876 previous_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
877 if not previous_item.IsOk():
\r
878 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
879 previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(parent_item)
\r
880 self.panelPlaylists.PlaylistsTree.SelectItem(previous_item, True)
\r
881 playlist = self.GetActivePlaylist()
\r
882 if playlist.count > 1:
\r
883 playlist.previous()
\r
884 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
887 def OnPropGridChanged (self, event):
\r
888 prop = event.GetProperty()
\r
890 item_section = self.panelProperties.SelectedTreeItem
\r
891 item_plugin = self.panelCommands.CommandsTree.GetItemParent(item_section)
\r
892 plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
\r
893 config = self.configs[plugin]
\r
894 property_section = self.panelCommands.CommandsTree.GetItemText(item_section)
\r
895 property_key = prop.GetName()
\r
896 property_value = prop.GetDisplayedString()
\r
898 config[property_section][property_key]['value'] = property_value
\r
900 def OnRestorePerspective(self, event):
\r
901 name = self.MenuBar.FindItemById(event.GetId()).GetLabel()
\r
902 self._RestorePerspective(name)
\r
903 # self._mgr.LoadPerspective(self._perspectives[name])
\r
904 # self.config['perspectives']['active'] = name
\r
905 # self._mgr.Update()
\r
906 # all_panes = self._mgr.GetAllPanes()
\r
907 # for pane in all_panes:
\r
908 # if not pane.name.startswith('toolbar'):
\r
909 # if pane.name == 'Assistant':
\r
910 # self.MenuBar.FindItemById(ID_ViewAssistant).Check(pane.window.IsShown())
\r
911 # if pane.name == 'Folders':
\r
912 # self.MenuBar.FindItemById(ID_ViewFolders).Check(pane.window.IsShown())
\r
913 # if pane.name == 'Playlists':
\r
914 # self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown())
\r
915 # if pane.name == 'Commands':
\r
916 # self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown())
\r
917 # if pane.name == 'Properties':
\r
918 # self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown())
\r
919 # if pane.name == 'Output':
\r
920 # self.MenuBar.FindItemById(ID_ViewOutput).Check(pane.window.IsShown())
\r
921 # if pane.name == 'Results':
\r
922 # self.MenuBar.FindItemById(ID_ViewResults).Check(pane.window.IsShown())
\r
924 def OnResultsCheck(self, index, flag):
\r
925 #TODO: fix for multiple results
\r
926 results = self.GetActivePlot().results
\r
927 fit_function_str = self.GetStringFromConfig('results', 'show_results', 'fit_function')
\r
928 results[fit_function_str].results[index].visible = flag
\r
931 def OnSavePerspective(self, event):
\r
933 def nameExists(name):
\r
934 menu_position = self.MenuBar.FindMenu('Perspectives')
\r
935 menu = self.MenuBar.GetMenu(menu_position)
\r
936 for item in menu.GetMenuItems():
\r
937 if item.GetText() == name:
\r
943 dialog = wx.TextEntryDialog(self, 'Enter a name for the new perspective:', 'Save perspective')
\r
944 dialog.SetValue('New perspective')
\r
945 if dialog.ShowModal() != wx.ID_OK:
\r
948 name = dialog.GetValue()
\r
950 if nameExists(name):
\r
951 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
952 if dialogConfirm.ShowModal() == wx.ID_YES:
\r
957 perspective = self._mgr.SavePerspective()
\r
958 self._SavePerspectiveToFile(name, perspective)
\r
959 self.config['perspectives']['active'] = name
\r
960 self.UpdatePerspectivesMenu()
\r
961 # if nameExists(name):
\r
962 # #check the corresponding menu item
\r
963 # menu_item = self.GetPerspectiveMenuItem(name)
\r
964 # #replace the perspectiveStr in _pespectives
\r
965 # self._perspectives[name] = perspective
\r
967 # #because we deal with radio items, we need to do some extra work
\r
968 # #delete all menu items from the perspectives menu
\r
969 # for item in self._perspectives_menu.GetMenuItems():
\r
970 # self._perspectives_menu.DeleteItem(item)
\r
971 # #recreate the perspectives menu
\r
972 # self._perspectives_menu.Append(ID_SavePerspective, 'Save Perspective')
\r
973 # self._perspectives_menu.Append(ID_DeletePerspective, 'Delete Perspective')
\r
974 # self._perspectives_menu.AppendSeparator()
\r
975 # #convert the perspectives dictionary into a list
\r
976 # # the list contains:
\r
977 # #[0]: name of the perspective
\r
978 # #[1]: perspective
\r
979 # perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
980 # perspectives_list.append(name)
\r
981 # perspectives_list.sort()
\r
982 # #add all previous perspectives
\r
983 # for index, item in enumerate(perspectives_list):
\r
984 # menu_item = self._perspectives_menu.AppendRadioItem(ID_FirstPerspective + index, item)
\r
986 # menu_item.Check()
\r
987 # #add the new perspective to _perspectives
\r
988 # self._perspectives[name] = perspective
\r
990 def OnSize(self, event):
\r
993 def OnTreeCtrlCommandsSelectionChanged(self, event):
\r
994 selected_item = event.GetItem()
\r
995 if selected_item is not None:
\r
998 #deregister/register the listener to avoid infinite loop
\r
999 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlCommandsSelectionChanged)
\r
1000 self.panelCommands.CommandsTree.SelectItem(selected_item)
\r
1001 evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self.panelCommands.CommandsTree)
\r
1002 self.panelProperties.SelectedTreeItem = selected_item
\r
1003 #if a command was clicked
\r
1005 if not self.panelCommands.CommandsTree.ItemHasChildren(selected_item):
\r
1006 item_plugin = self.panelCommands.CommandsTree.GetItemParent(selected_item)
\r
1007 plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
\r
1008 if self.configs.has_key(plugin):
\r
1009 #config = self.panelCommands.CommandsTree.GetPyData(item_plugin)
\r
1010 config = self.configs[plugin]
\r
1011 section = self.panelCommands.CommandsTree.GetItemText(selected_item)
\r
1012 #display docstring in help window
\r
1013 doc_string = eval('self.do_' + section + '.__doc__')
\r
1014 if section in config:
\r
1015 for option in config[section]:
\r
1016 properties.append([option, config[section][option]])
\r
1018 plugin = self.panelCommands.CommandsTree.GetItemText(selected_item)
\r
1019 if plugin != 'core':
\r
1020 doc_string = eval('plugins.' + plugin + '.' + plugin + 'Commands.__doc__')
\r
1022 doc_string = 'The module "core" contains Hooke core functionality'
\r
1023 if doc_string is not None:
\r
1024 self.panelAssistant.ChangeValue(doc_string)
\r
1026 self.panelAssistant.ChangeValue('')
\r
1027 panels.propertyeditor.PropertyEditor.Initialize(self.panelProperties, properties)
\r
1028 #save the currently selected command/plugin to the config file
\r
1029 self.config['command']['command'] = section
\r
1030 self.config['command']['plugin'] = plugin
\r
1032 def OnTreeCtrlItemActivated(self, event):
\r
1033 self.OnExecute(event)
\r
1035 def OnView(self, event):
\r
1036 menu_id = event.GetId()
\r
1037 menu_item = self.MenuBar.FindItemById(menu_id)
\r
1038 menu_label = menu_item.GetLabel()
\r
1040 pane = self._mgr.GetPane(menu_label)
\r
1041 pane.Show(not pane.IsShown())
\r
1042 #if we don't do the following, the Folders pane does not resize properly on hide/show
\r
1043 if pane.caption == 'Folders' and pane.IsShown() and pane.IsDocked():
\r
1044 #folders_size = pane.GetSize()
\r
1045 self.panelFolders.Fit()
\r
1046 self._mgr.Update()
\r
1048 def _measure_N_points(self, N, message='', whatset=lh.RETRACTION):
\r
1050 General helper function for N-points measurements
\r
1051 By default, measurements are done on the retraction
\r
1054 dialog = wx.MessageDialog(None, message, 'Info', wx.OK)
\r
1055 dialog.ShowModal()
\r
1057 figure = self.GetActiveFigure()
\r
1059 xvector = self.displayed_plot.curves[whatset].x
\r
1060 yvector = self.displayed_plot.curves[whatset].y
\r
1062 clicked_points = figure.ginput(N, timeout=-1, show_clicks=True)
\r
1065 for clicked_point in clicked_points:
\r
1066 point = lh.ClickedPoint()
\r
1067 point.absolute_coords = clicked_point[0], clicked_point[1]
\r
1069 #TODO: make this optional?
\r
1070 #so far, the clicked point is taken, not the corresponding data point
\r
1071 point.find_graph_coords(xvector, yvector)
\r
1072 point.is_line_edge = True
\r
1073 point.is_marker = True
\r
1074 points.append(point)
\r
1077 def _clickize(self, xvector, yvector, index):
\r
1079 returns a ClickedPoint() object from an index and vectors of x, y coordinates
\r
1081 point = lh.ClickedPoint()
\r
1082 point.index = index
\r
1083 point.absolute_coords = xvector[index], yvector[index]
\r
1084 point.find_graph_coords(xvector, yvector)
\r
1087 def _delta(self, color='black', message='Click 2 points', show=True, whatset=1):
\r
1089 calculates the difference between two clicked points
\r
1091 clicked_points = self._measure_N_points(N=2, message=message, whatset=whatset)
\r
1092 dx = abs(clicked_points[0].graph_coords[0] - clicked_points[1].graph_coords[0])
\r
1093 dy = abs(clicked_points[0].graph_coords[1] - clicked_points[1].graph_coords[1])
\r
1095 plot = self.GetDisplayedPlotCorrected()
\r
1097 curve = plot.curves[whatset]
\r
1098 unitx = curve.units.x
\r
1099 unity = curve.units.y
\r
1101 #TODO: move this to clicked_points?
\r
1103 for point in clicked_points:
\r
1104 points = copy.deepcopy(curve)
\r
1105 points.x = point.graph_coords[0]
\r
1106 points.y = point.graph_coords[1]
\r
1108 points.color = color
\r
1110 points.style = 'scatter'
\r
1111 plot.curves.append(points)
\r
1113 self.UpdatePlot(plot)
\r
1115 return dx, unitx, dy, unity
\r
1117 def do_plotmanipulators(self):
\r
1119 Please select the plotmanipulators you would like to use
\r
1120 and define the order in which they will be applied to the data.
\r
1122 Click 'Execute' to apply your changes.
\r
1126 def do_test(self):
\r
1127 self.AppendToOutput(self.config['perspectives']['active'])
\r
1130 def do_version(self):
\r
1134 Prints the current version and codename, plus library version. Useful for debugging.
\r
1136 self.AppendToOutput('Hooke ' + __version__ + ' (' + __codename__ + ')')
\r
1137 self.AppendToOutput('Released on: ' + __releasedate__)
\r
1138 self.AppendToOutput('---')
\r
1139 self.AppendToOutput('Python version: ' + python_version)
\r
1140 self.AppendToOutput('WxPython version: ' + wx_version)
\r
1141 self.AppendToOutput('Matplotlib version: ' + mpl_version)
\r
1142 self.AppendToOutput('SciPy version: ' + scipy_version)
\r
1143 self.AppendToOutput('NumPy version: ' + numpy_version)
\r
1144 self.AppendToOutput('---')
\r
1145 self.AppendToOutput('Platform: ' + str(platform.uname()))
\r
1146 #TODO: adapt to 'new' config
\r
1147 #self.AppendToOutput('---')
\r
1148 #self.AppendToOutput('Loaded plugins:', self.config['loaded_plugins'])
\r
1150 def UpdatePerspectivesMenu(self):
\r
1151 #add perspectives to menubar and _perspectives
\r
1152 perspectivesDirectory = os.path.join(lh.hookeDir, 'perspectives')
\r
1153 self._perspectives = {}
\r
1154 if os.path.isdir(perspectivesDirectory):
\r
1155 perspectiveFileNames = os.listdir(perspectivesDirectory)
\r
1156 for perspectiveFilename in perspectiveFileNames:
\r
1157 filename = lh.get_file_path(perspectiveFilename, ['perspectives'])
\r
1158 if os.path.isfile(filename):
\r
1159 perspectiveFile = open(filename, 'rU')
\r
1160 perspective = perspectiveFile.readline()
\r
1161 perspectiveFile.close()
\r
1162 if perspective != '':
\r
1163 name, extension = os.path.splitext(perspectiveFilename)
\r
1164 if extension == '.txt':
\r
1165 self._perspectives[name] = perspective
\r
1167 #in case there are no perspectives
\r
1168 if not self._perspectives:
\r
1169 perspective = self._mgr.SavePerspective()
\r
1170 self._perspectives['Default'] = perspective
\r
1171 self._SavePerspectiveToFile('Default', perspective)
\r
1173 selected_perspective = self.config['perspectives']['active']
\r
1174 if not self._perspectives.has_key(selected_perspective):
\r
1175 self.config['perspectives']['active'] = 'Default'
\r
1176 selected_perspective = 'Default'
\r
1178 perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
1179 perspectives_list.sort()
\r
1181 #get the Perspectives menu
\r
1182 menu_position = self.MenuBar.FindMenu('Perspectives')
\r
1183 menu = self.MenuBar.GetMenu(menu_position)
\r
1184 #delete all menu items
\r
1185 for item in menu.GetMenuItems():
\r
1186 menu.DeleteItem(item)
\r
1187 #rebuild the menu by adding the standard menu items
\r
1188 menu.Append(ID_SavePerspective, 'Save Perspective')
\r
1189 menu.Append(ID_DeletePerspective, 'Delete Perspective')
\r
1190 menu.AppendSeparator()
\r
1191 #add all previous perspectives
\r
1192 for index, label in enumerate(perspectives_list):
\r
1193 menu_item = menu.AppendRadioItem(ID_FirstPerspective + index, label)
\r
1194 if label == selected_perspective:
\r
1195 self._RestorePerspective(label)
\r
1196 menu_item.Check(True)
\r
1198 def UpdatePlaylistsTreeSelection(self):
\r
1199 playlist = self.GetActivePlaylist()
\r
1200 if playlist is not None:
\r
1201 if playlist.index >= 0:
\r
1202 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
1205 def UpdatePlot(self, plot=None):
\r
1207 def add_to_plot(curve):
\r
1208 if curve.visible and curve.x and curve.y:
\r
1209 destination = (curve.destination.column - 1) * number_of_rows + curve.destination.row - 1
\r
1210 axes_list[destination].set_title(curve.title)
\r
1211 axes_list[destination].set_xlabel(curve.units.x)
\r
1212 axes_list[destination].set_ylabel(curve.units.y)
\r
1213 if curve.style == 'plot':
\r
1214 axes_list[destination].plot(curve.x, curve.y, color=curve.color, label=curve.label, zorder=1)
\r
1215 if curve.style == 'scatter':
\r
1216 axes_list[destination].scatter(curve.x, curve.y, color=curve.color, label=curve.label, s=curve.size, zorder=2)
\r
1219 active_file = self.GetActiveFile()
\r
1220 if not active_file.driver:
\r
1221 active_file.identify(self.drivers)
\r
1222 self.displayed_plot = copy.deepcopy(active_file.plot)
\r
1223 #add raw curves to plot
\r
1224 self.displayed_plot.raw_curves = copy.deepcopy(self.displayed_plot.curves)
\r
1225 #apply all active plotmanipulators and add the 'manipulated' data
\r
1226 for plotmanipulator in self.plotmanipulators:
\r
1227 if self.GetBoolFromConfig('core', 'plotmanipulators', plotmanipulator.name):
\r
1228 self.displayed_plot = plotmanipulator.method(self.displayed_plot, active_file)
\r
1229 #add corrected curves to plot
\r
1230 self.displayed_plot.corrected_curves = copy.deepcopy(self.displayed_plot.curves)
\r
1232 active_file = None
\r
1233 self.displayed_plot = copy.deepcopy(plot)
\r
1235 figure = self.GetActiveFigure()
\r
1238 figure.suptitle(self.displayed_plot.title, fontsize=14)
\r
1242 number_of_columns = max([curve.destination.column for curve in self.displayed_plot.curves])
\r
1243 number_of_rows = max([curve.destination.row for curve in self.displayed_plot.curves])
\r
1245 for index in range(number_of_rows * number_of_columns):
\r
1246 axes_list.append(figure.add_subplot(number_of_rows, number_of_columns, index + 1))
\r
1248 for curve in self.displayed_plot.curves:
\r
1249 add_to_plot(curve)
\r
1251 #make sure the titles of 'subplots' do not overlap with the axis labels of the 'main plot'
\r
1252 figure.subplots_adjust(hspace=0.3)
\r
1254 #TODO: add multiple results support to fit in curve.results:
\r
1255 #get the fit_function results to display
\r
1256 fit_function_str = self.GetStringFromConfig('results', 'show_results', 'fit_function')
\r
1257 self.panelResults.ClearResults()
\r
1258 plot = self.GetActivePlot()
\r
1259 if plot is not None:
\r
1260 if plot.results.has_key(fit_function_str):
\r
1261 for curve in plot.results[fit_function_str].results:
\r
1262 add_to_plot(curve)
\r
1263 self.panelResults.DisplayResults(plot.results[fit_function_str])
\r
1265 self.panelResults.ClearResults()
\r
1267 figure.canvas.draw()
\r
1269 for axes in axes_list:
\r
1270 #TODO: add legend as global option or per graph option
\r
1272 axes.figure.canvas.draw()
\r
1275 if __name__ == '__main__':
\r
1277 ## now, silence a deprecation warning for py2.3
\r
1279 warnings.filterwarnings("ignore", "integer", DeprecationWarning, "wxPython.gdi")
\r
1285 app = Hooke(redirect=redirect)
\r