1 #!/usr/bin/env python
\r
4 HOOKE - A force spectroscopy review & analysis tool
\r
6 Copyright 2008 by Massimo Sandal (University of Bologna, Italy)
\r
7 Copyright 2010 by Rolf Schmidt (Concordia University, Canada)
\r
9 This program is released under the GNU General Public License version 2.
\r
12 import lib.libhooke as lh
\r
14 wxversion.select(lh.WX_GOOD)
\r
16 from configobj import ConfigObj
\r
24 import wx.lib.agw.aui as aui
\r
25 import wx.lib.evtmgr as evtmgr
\r
26 import wx.propgrid as wxpg
\r
28 from matplotlib.ticker import FuncFormatter
\r
30 from configobj import __version__ as configobj_version
\r
31 from matplotlib import __version__ as mpl_version
\r
32 from numpy import __version__ as numpy_version
\r
33 from scipy import __version__ as scipy_version
\r
34 from sys import version as python_version
\r
35 from wx import __version__ as wx_version
\r
36 from wx.propgrid import PROPGRID_MAJOR
\r
37 from wx.propgrid import PROPGRID_MINOR
\r
38 from wx.propgrid import PROPGRID_RELEASE
\r
41 from agw import cubecolourdialog as CCD
\r
42 except ImportError: # if it's not there locally, try the wxPython lib.
\r
43 import wx.lib.agw.cubecolourdialog as CCD
\r
45 #set the Hooke directory
\r
46 lh.hookeDir = os.path.abspath(os.path.dirname(__file__))
\r
47 from config.config import config
\r
49 import lib.clickedpoint
\r
53 import lib.plotmanipulator
\r
54 import lib.prettyformat
\r
55 import panels.commands
\r
57 import panels.perspectives
\r
58 import panels.playlist
\r
60 import panels.propertyeditor
\r
61 import panels.results
\r
66 global __releasedate__
\r
67 __version__ = lh.HOOKE_VERSION[0]
\r
68 __codename__ = lh.HOOKE_VERSION[1]
\r
69 __releasedate__ = lh.HOOKE_VERSION[2]
\r
70 __release_name__ = lh.HOOKE_VERSION[1]
\r
72 ID_About = wx.NewId()
\r
73 ID_Next = wx.NewId()
\r
74 ID_Previous = wx.NewId()
\r
76 ID_ViewAssistant = wx.NewId()
\r
77 ID_ViewCommands = wx.NewId()
\r
78 ID_ViewFolders = wx.NewId()
\r
79 ID_ViewNote = wx.NewId()
\r
80 ID_ViewOutput = wx.NewId()
\r
81 ID_ViewPlaylists = wx.NewId()
\r
82 ID_ViewProperties = wx.NewId()
\r
83 ID_ViewResults = wx.NewId()
\r
85 ID_DeletePerspective = wx.NewId()
\r
86 ID_SavePerspective = wx.NewId()
\r
88 ID_FirstPerspective = ID_SavePerspective + 1000
\r
89 #I hope we'll never have more than 1000 perspectives
\r
90 ID_FirstPlot = ID_SavePerspective + 2000
\r
92 class Hooke(wx.App):
\r
95 self.SetAppName('Hooke')
\r
96 self.SetVendorName('')
\r
98 window_height = config['main']['height']
\r
99 window_left= config['main']['left']
\r
100 window_top = config['main']['top']
\r
101 window_width = config['main']['width']
\r
103 #sometimes, the ini file gets confused and sets 'left'
\r
104 #and 'top' to large negative numbers
\r
105 #let's catch and fix this
\r
106 #keep small negative numbers, the user might want those
\r
107 if window_left < -window_width:
\r
109 if window_top < -window_height:
\r
111 window_position = (window_left, window_top)
\r
112 window_size = (window_width, window_height)
\r
114 #setup the splashscreen
\r
115 if config['splashscreen']['show']:
\r
116 filename = lh.get_file_path('hooke.jpg', ['resources'])
\r
117 if os.path.isfile(filename):
\r
118 bitmap = wx.Image(filename).ConvertToBitmap()
\r
119 splashStyle = wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT
\r
120 splashDuration = config['splashscreen']['duration']
\r
121 wx.SplashScreen(bitmap, splashStyle, splashDuration, None, -1)
\r
124 we need for the splash screen to disappear
\r
125 for whatever reason splashDuration and sleep do not correspond to each other
\r
126 at least not on Windows
\r
127 maybe it's because duration is in milliseconds and sleep in seconds
\r
128 thus we need to increase the sleep time a bit
\r
129 a factor of 1.2 seems to work quite well
\r
132 time.sleep(sleepFactor * splashDuration / 1000)
\r
134 plugin_objects = []
\r
135 for plugin in config['plugins']:
\r
136 if config['plugins'][plugin]:
\r
137 filename = ''.join([plugin, '.py'])
\r
138 path = lh.get_file_path(filename, ['plugins'])
\r
139 if os.path.isfile(path):
\r
140 #get the corresponding filename and path
\r
141 plugin_name = ''.join(['plugins.', plugin])
\r
143 __import__(plugin_name)
\r
144 #get the file that contains the plugin
\r
145 class_file = getattr(plugins, plugin)
\r
146 #get the class that contains the commands
\r
147 class_object = getattr(class_file, plugin + 'Commands')
\r
148 plugin_objects.append(class_object)
\r
150 def make_command_class(*bases):
\r
151 #create metaclass with plugins and plotmanipulators
\r
152 return type(HookeFrame)("HookeFramePlugged", bases + (HookeFrame,), {})
\r
153 frame = make_command_class(*plugin_objects)(parent=None, id=wx.ID_ANY, title='Hooke', pos=window_position, size=window_size)
\r
155 self.SetTopWindow(frame)
\r
163 class HookeFrame(wx.Frame):
\r
165 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
166 #call parent constructor
\r
167 wx.Frame.__init__(self, parent, id, title, pos, size, style)
\r
168 self.config = config
\r
169 self.CreateApplicationIcon()
\r
170 #self.configs contains: {the name of the Commands file: corresponding ConfigObj}
\r
172 #self.displayed_plot holds the currently displayed plot
\r
173 self.displayed_plot = None
\r
174 #self.playlists contains: {the name of the playlist: [playlist, tabIndex, plotID]}
\r
175 self.playlists = {}
\r
176 #list of all plotmanipulators
\r
177 self.plotmanipulators = []
\r
178 #self.plugins contains: {the name of the plugin: [caption, function]}
\r
180 #self.results_str contains the type of results we want to display
\r
181 self.results_str = 'wlc'
\r
183 #tell FrameManager to manage this frame
\r
184 self._mgr = aui.AuiManager()
\r
185 self._mgr.SetManagedWindow(self)
\r
186 #set the gradient style
\r
187 self._mgr.GetArtProvider().SetMetric(aui.AUI_DOCKART_GRADIENT_TYPE, aui.AUI_GRADIENT_NONE)
\r
188 #set transparent drag
\r
189 self._mgr.SetFlags(self._mgr.GetFlags() ^ aui.AUI_MGR_TRANSPARENT_DRAG)
\r
191 # set up default notebook style
\r
192 self._notebook_style = aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER
\r
193 self._notebook_theme = 0
\r
195 #holds the perspectives: {name, perspective_str}
\r
196 self._perspectives = {}
\r
198 # min size for the frame itself isn't completely done.
\r
199 # see the end up FrameManager::Update() for the test
\r
200 # code. For now, just hard code a frame minimum size
\r
201 self.SetMinSize(wx.Size(500, 500))
\r
202 #define the list of active drivers
\r
204 for driver in self.config['drivers']:
\r
205 if self.config['drivers'][driver]:
\r
206 #get the corresponding filename and path
\r
207 filename = ''.join([driver, '.py'])
\r
208 path = lh.get_file_path(filename, ['drivers'])
\r
209 #the driver is active for driver[1] == 1
\r
210 if os.path.isfile(path):
\r
211 #driver files are located in the 'drivers' subfolder
\r
212 driver_name = ''.join(['drivers.', driver])
\r
213 __import__(driver_name)
\r
214 class_file = getattr(drivers, driver)
\r
215 for command in dir(class_file):
\r
216 if command.endswith('Driver'):
\r
217 self.drivers.append(getattr(class_file, command))
\r
218 #import all active plugins and plotmanips
\r
219 #add 'core.ini' to self.configs (this is not a plugin and thus must be imported separately)
\r
220 ini_path = lh.get_file_path('core.ini', ['plugins'])
\r
221 plugin_config = ConfigObj(ini_path)
\r
222 #self.config.merge(plugin_config)
\r
223 self.configs['core'] = plugin_config
\r
224 #existing_commands contains: {command: plugin}
\r
225 existing_commands = {}
\r
226 #make sure we execute _plug_init() for every command line plugin we import
\r
227 for plugin in self.config['plugins']:
\r
228 if self.config['plugins'][plugin]:
\r
229 filename = ''.join([plugin, '.py'])
\r
230 path = lh.get_file_path(filename, ['plugins'])
\r
231 if os.path.isfile(path):
\r
232 #get the corresponding filename and path
\r
233 plugin_name = ''.join(['plugins.', plugin])
\r
236 module = __import__(plugin_name)
\r
237 #prepare the ini file for inclusion
\r
238 ini_path = path.replace('.py', '.ini')
\r
240 plugin_config = ConfigObj(ini_path)
\r
241 #self.config.merge(plugin_config)
\r
242 self.configs[plugin] = plugin_config
\r
244 commands = eval('dir(module.' + plugin+ '.' + plugin + 'Commands)')
\r
245 #keep only commands (ie names that start with 'do_')
\r
246 commands = [command for command in commands if command.startswith('do_')]
\r
248 for command in commands:
\r
249 if existing_commands.has_key(command):
\r
250 message_str = 'Adding "' + command + '" in plugin "' + plugin + '".\n\n'
\r
251 message_str += '"' + command + '" already exists in "' + str(existing_commands[command]) + '".\n\n'
\r
252 message_str += 'Only "' + command + '" in "' + str(existing_commands[command]) + '" will work.\n\n'
\r
253 message_str += 'Please rename one of the commands in the source code and restart Hooke or disable one of the plugins.'
\r
254 dialog = wx.MessageDialog(self, message_str, 'Warning', wx.OK|wx.ICON_WARNING|wx.CENTER)
\r
257 existing_commands[command] = plugin
\r
258 self.plugins[plugin] = commands
\r
260 #initialize the plugin
\r
261 eval('module.' + plugin+ '.' + plugin + 'Commands._plug_init(self)')
\r
262 except AttributeError:
\r
264 except ImportError:
\r
266 #add commands from hooke.py i.e. 'core' commands
\r
267 commands = dir(HookeFrame)
\r
268 commands = [command for command in commands if command.startswith('do_')]
\r
270 self.plugins['core'] = commands
\r
271 #create panels here
\r
272 self.panelAssistant = self.CreatePanelAssistant()
\r
273 self.panelCommands = self.CreatePanelCommands()
\r
274 self.panelFolders = self.CreatePanelFolders()
\r
275 self.panelPlaylists = self.CreatePanelPlaylists()
\r
276 self.panelProperties = self.CreatePanelProperties()
\r
277 self.panelNote = self.CreatePanelNote()
\r
278 self.panelOutput = self.CreatePanelOutput()
\r
279 self.panelResults = self.CreatePanelResults()
\r
280 self.plotNotebook = self.CreateNotebook()
\r
283 self._mgr.AddPane(self.panelFolders, aui.AuiPaneInfo().Name('Folders').Caption('Folders').Left().CloseButton(True).MaximizeButton(False))
\r
284 self._mgr.AddPane(self.panelPlaylists, aui.AuiPaneInfo().Name('Playlists').Caption('Playlists').Left().CloseButton(True).MaximizeButton(False))
\r
285 self._mgr.AddPane(self.panelNote, aui.AuiPaneInfo().Name('Note').Caption('Note').Left().CloseButton(True).MaximizeButton(False))
\r
286 self._mgr.AddPane(self.plotNotebook, aui.AuiPaneInfo().Name('Plots').CenterPane().PaneBorder(False))
\r
287 self._mgr.AddPane(self.panelCommands, aui.AuiPaneInfo().Name('Commands').Caption('Settings and commands').Right().CloseButton(True).MaximizeButton(False))
\r
288 self._mgr.AddPane(self.panelProperties, aui.AuiPaneInfo().Name('Properties').Caption('Properties').Right().CloseButton(True).MaximizeButton(False))
\r
289 self._mgr.AddPane(self.panelAssistant, aui.AuiPaneInfo().Name('Assistant').Caption('Assistant').Right().CloseButton(True).MaximizeButton(False))
\r
290 self._mgr.AddPane(self.panelOutput, aui.AuiPaneInfo().Name('Output').Caption('Output').Bottom().CloseButton(True).MaximizeButton(False))
\r
291 self._mgr.AddPane(self.panelResults, aui.AuiPaneInfo().Name('Results').Caption('Results').Bottom().CloseButton(True).MaximizeButton(False))
\r
292 #self._mgr.AddPane(self.textCtrlCommandLine, aui.AuiPaneInfo().Name('CommandLine').CaptionVisible(False).Fixed().Bottom().Layer(2).CloseButton(False).MaximizeButton(False))
\r
293 #self._mgr.AddPane(panelBottom, aui.AuiPaneInfo().Name("panelCommandLine").Bottom().Position(1).CloseButton(False).MaximizeButton(False))
\r
295 # add the toolbars to the manager
\r
296 #self.toolbar=self.CreateToolBar()
\r
297 self.toolbarNavigation=self.CreateToolBarNavigation()
\r
298 #self._mgr.AddPane(self.toolbar, aui.AuiPaneInfo().Name('toolbar').Caption('Toolbar').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
\r
299 self._mgr.AddPane(self.toolbarNavigation, aui.AuiPaneInfo().Name('toolbarNavigation').Caption('Navigation').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
\r
300 # "commit" all changes made to FrameManager
\r
302 #create the menubar after the panes so that the default perspective
\r
303 #is created with all panes open
\r
304 self.CreateMenuBar()
\r
305 self.statusbar = self.CreateStatusbar()
\r
308 name = self.config['perspectives']['active']
\r
309 menu_item = self.GetPerspectiveMenuItem(name)
\r
310 if menu_item is not None:
\r
311 self.OnRestorePerspective(menu_item)
\r
312 #TODO: config setting to remember playlists from last session
\r
313 self.playlists = self.panelPlaylists.Playlists
\r
314 #initialize the commands tree
\r
315 self.panelCommands.Initialize(self.plugins)
\r
316 for command in dir(self):
\r
317 if command.startswith('plotmanip_'):
\r
318 self.plotmanipulators.append(lib.plotmanipulator.Plotmanipulator(method=getattr(self, command), command=command))
\r
320 #load default list, if possible
\r
321 self.do_loadlist(self.GetStringFromConfig('core', 'preferences', 'playlist'))
\r
323 def _BindEvents(self):
\r
324 #TODO: figure out if we can use the eventManager for menu ranges
\r
325 #and events of 'self' without raising an assertion fail error
\r
326 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
\r
327 self.Bind(wx.EVT_SIZE, self.OnSize)
\r
328 self.Bind(wx.EVT_CLOSE, self.OnClose)
\r
329 # Show How To Use The Closing Panes Event
\r
330 self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPaneClose)
\r
331 self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnNotebookPageClose)
\r
333 evtmgr.eventManager.Register(self.OnAbout, wx.EVT_MENU, win=self, id=wx.ID_ABOUT)
\r
334 evtmgr.eventManager.Register(self.OnClose, wx.EVT_MENU, win=self, id=wx.ID_EXIT)
\r
336 self.Bind(wx.EVT_MENU_RANGE, self.OnView, id=ID_ViewAssistant, id2=ID_ViewResults)
\r
338 self.Bind(wx.EVT_MENU, self.OnDeletePerspective, id=ID_DeletePerspective)
\r
339 self.Bind(wx.EVT_MENU, self.OnSavePerspective, id=ID_SavePerspective)
\r
340 self.Bind(wx.EVT_MENU_RANGE, self.OnRestorePerspective, id=ID_FirstPerspective, id2=ID_FirstPerspective+1000)
\r
342 evtmgr.eventManager.Register(self.OnNext, wx.EVT_TOOL, win=self, id=ID_Next)
\r
343 evtmgr.eventManager.Register(self.OnPrevious, wx.EVT_TOOL, win=self, id=ID_Previous)
\r
344 #self.Bind(.EVT_AUITOOLBAR_TOOL_DROPDOWN, self.OnDropDownToolbarItem, id=ID_DropDownToolbarItem)
\r
346 treeCtrl = self.panelFolders.GetTreeCtrl()
\r
347 #tree.Bind(wx.EVT_LEFT_UP, self.OnDirCtrl1LeftUp)
\r
348 #tree.Bind(wx.EVT_LEFT_DOWN, self.OnGenericDirCtrl1LeftDown)
\r
349 treeCtrl.Bind(wx.EVT_LEFT_DCLICK, self.OnDirCtrlLeftDclick)
\r
351 self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DOWN, self.OnPlaylistsLeftDown)
\r
352 self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DCLICK, self.OnPlaylistsLeftDclick)
\r
354 evtmgr.eventManager.Register(self.OnExecute, wx.EVT_BUTTON, self.panelCommands.ExecuteButton)
\r
355 evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self.panelCommands.CommandsTree)
\r
356 evtmgr.eventManager.Register(self.OnTreeCtrlItemActivated, wx.EVT_TREE_ITEM_ACTIVATED, self.panelCommands.CommandsTree)
\r
357 evtmgr.eventManager.Register(self.OnUpdateNote, wx.EVT_BUTTON, self.panelNote.UpdateButton)
\r
359 self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged)
\r
361 self.panelResults.results_list.OnCheckItem = self.OnResultsCheck
\r
363 def _GetActiveFileIndex(self):
\r
364 lib.playlist.Playlist = self.GetActivePlaylist()
\r
365 #get the selected item from the tree
\r
366 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
367 #test if a playlist or a curve was double-clicked
\r
368 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
372 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
373 while selected_item.IsOk():
\r
375 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
378 def _GetPlaylistTab(self, name):
\r
379 for index, page in enumerate(self.plotNotebook._tabs._pages):
\r
380 if page.caption == name:
\r
384 def _GetUniquePlaylistName(self, name):
\r
385 playlist_name = name
\r
387 while playlist_name in self.playlists:
\r
388 playlist_name = ''.join([name, str(count)])
\r
390 return playlist_name
\r
392 def _RestorePerspective(self, name):
\r
393 self._mgr.LoadPerspective(self._perspectives[name])
\r
394 self.config['perspectives']['active'] = name
\r
396 all_panes = self._mgr.GetAllPanes()
\r
397 for pane in all_panes:
\r
398 if not pane.name.startswith('toolbar'):
\r
399 if pane.name == 'Assistant':
\r
400 self.MenuBar.FindItemById(ID_ViewAssistant).Check(pane.window.IsShown())
\r
401 if pane.name == 'Folders':
\r
402 self.MenuBar.FindItemById(ID_ViewFolders).Check(pane.window.IsShown())
\r
403 if pane.name == 'Playlists':
\r
404 self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown())
\r
405 if pane.name == 'Commands':
\r
406 self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown())
\r
407 if pane.name == 'Note':
\r
408 self.MenuBar.FindItemById(ID_ViewNote).Check(pane.window.IsShown())
\r
409 if pane.name == 'Properties':
\r
410 self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown())
\r
411 if pane.name == 'Output':
\r
412 self.MenuBar.FindItemById(ID_ViewOutput).Check(pane.window.IsShown())
\r
413 if pane.name == 'Results':
\r
414 self.MenuBar.FindItemById(ID_ViewResults).Check(pane.window.IsShown())
\r
416 def _SavePerspectiveToFile(self, name, perspective):
\r
417 filename = ''.join([name, '.txt'])
\r
418 filename = lh.get_file_path(filename, ['perspectives'])
\r
419 perspectivesFile = open(filename, 'w')
\r
420 perspectivesFile.write(perspective)
\r
421 perspectivesFile.close()
\r
423 def _UnbindEvents(self):
\r
425 evtmgr.eventManager.DeregisterListener(self.OnAbout)
\r
426 evtmgr.eventManager.DeregisterListener(self.OnClose)
\r
428 evtmgr.eventManager.DeregisterListener(self.OnNext)
\r
429 evtmgr.eventManager.DeregisterListener(self.OnPrevious)
\r
431 evtmgr.eventManager.DeregisterListener(self.OnExecute)
\r
432 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlCommandsSelectionChanged)
\r
433 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlItemActivated)
\r
434 evtmgr.eventManager.DeregisterListener(self.OnUpdateNote)
\r
436 def AddPlaylist(self, playlist=None, name='Untitled'):
\r
437 if playlist and playlist.count > 0:
\r
438 playlist.name = self._GetUniquePlaylistName(name)
\r
440 self.AddToPlaylists(playlist)
\r
442 def AddPlaylistFromFiles(self, files=[], name='Untitled'):
\r
444 playlist = lib.playlist.Playlist(self, self.drivers)
\r
446 playlist.add_curve(item)
\r
447 if playlist.count > 0:
\r
448 playlist.name = self._GetUniquePlaylistName(name)
\r
450 self.AddTayliss(playlist)
\r
452 def AddToPlaylists(self, playlist):
\r
453 if playlist.count > 0:
\r
454 #setup the playlist in the Playlist tree
\r
455 tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
\r
456 playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)
\r
457 #add all files to the Playlist tree
\r
459 hide_curve_extension = self.GetBoolFromConfig('core', 'preferences', 'hide_curve_extension')
\r
460 for index, file_to_add in enumerate(playlist.files):
\r
461 #optionally remove the extension from the name of the curve
\r
462 if hide_curve_extension:
\r
463 file_to_add.name = lh.remove_extension(file_to_add.name)
\r
464 file_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, file_to_add.name, 1)
\r
465 if index == playlist.index:
\r
466 self.panelPlaylists.PlaylistsTree.SelectItem(file_ID)
\r
468 #create the plot tab and add playlist to the dictionary
\r
469 plotPanel = panels.plot.PlotPanel(self, ID_FirstPlot + len(self.playlists))
\r
470 notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True)
\r
471 #tab_index = self.plotNotebook.GetSelection()
\r
472 playlist.figure = plotPanel.get_figure()
\r
473 self.playlists[playlist.name] = playlist
\r
474 #self.playlists[playlist.name] = [playlist, figure]
\r
475 self.panelPlaylists.PlaylistsTree.Expand(playlist_root)
\r
476 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
480 def AppendToOutput(self, text):
\r
481 self.panelOutput.AppendText(''.join([text, '\n']))
\r
483 def AppliesPlotmanipulator(self, name):
\r
485 Returns True if the plotmanipulator 'name' is applied, False otherwise
\r
486 name does not contain 'plotmanip_', just the name of the plotmanipulator (e.g. 'flatten')
\r
488 return self.GetBoolFromConfig('core', 'plotmanipulators', name)
\r
490 def ApplyPlotmanipulators(self, plot, plot_file):
\r
492 Apply all active plotmanipulators.
\r
494 if plot is not None and plot_file is not None:
\r
495 manipulated_plot = copy.deepcopy(plot)
\r
496 for plotmanipulator in self.plotmanipulators:
\r
497 if self.GetBoolFromConfig('core', 'plotmanipulators', plotmanipulator.name):
\r
498 manipulated_plot = plotmanipulator.method(manipulated_plot, plot_file)
\r
499 return manipulated_plot
\r
501 def CreateApplicationIcon(self):
\r
502 iconFile = 'resources' + os.sep + 'microscope.ico'
\r
503 icon = wx.Icon(iconFile, wx.BITMAP_TYPE_ICO)
\r
506 def CreateCommandLine(self):
\r
507 return wx.TextCtrl(self, -1, '', style=wx.NO_BORDER|wx.EXPAND)
\r
509 def CreatePanelAssistant(self):
\r
510 panel = wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
\r
511 panel.SetEditable(False)
\r
514 def CreatePanelCommands(self):
\r
515 return panels.commands.Commands(self)
\r
517 def CreatePanelFolders(self):
\r
519 filters = self.config['folders']['filters']
\r
520 index = self.config['folders'].as_int('filterindex')
\r
521 #set initial directory
\r
522 folder = self.GetStringFromConfig('core', 'preferences', 'workdir')
\r
523 return wx.GenericDirCtrl(self, -1, dir=folder, size=(200, 250), style=wx.DIRCTRL_SHOW_FILTERS, filter=filters, defaultFilter=index)
\r
525 def CreatePanelNote(self):
\r
526 return panels.note.Note(self)
\r
528 def CreatePanelOutput(self):
\r
529 return wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
\r
531 def CreatePanelPlaylists(self):
\r
532 return panels.playlist.Playlists(self)
\r
534 def CreatePanelProperties(self):
\r
535 return panels.propertyeditor.PropertyEditor(self)
\r
537 def CreatePanelResults(self):
\r
538 return panels.results.Results(self)
\r
540 def CreatePanelWelcome(self):
\r
541 #TODO: move into panels.welcome
\r
542 ctrl = wx.html.HtmlWindow(self, -1, wx.DefaultPosition, wx.Size(400, 300))
\r
543 introStr = '<h1>Welcome to Hooke</h1>' + \
\r
544 '<h3>Features</h3>' + \
\r
546 '<li>View, annotate, measure force files</li>' + \
\r
547 '<li>Worm-like chain fit of force peaks</li>' + \
\r
548 '<li>Automatic convolution-based filtering of empty files</li>' + \
\r
549 '<li>Automatic fit and measurement of multiple force peaks</li>' + \
\r
550 '<li>Handles force-clamp force experiments (experimental)</li>' + \
\r
551 '<li>It is extensible by users by means of plugins and drivers</li>' + \
\r
553 '<p>See the <a href="http://code.google.com/p/hooke/wiki/DocumentationIndex">DocumentationIndex</a> for more information</p>'
\r
554 ctrl.SetPage(introStr)
\r
557 def CreateMenuBar(self):
\r
558 menu_bar = wx.MenuBar()
\r
559 self.SetMenuBar(menu_bar)
\r
561 file_menu = wx.Menu()
\r
562 file_menu.Append(wx.ID_EXIT, 'Exit\tCtrl-Q')
\r
563 # edit_menu.AppendSeparator();
\r
564 # edit_menu.Append(ID_Config, 'Preferences')
\r
566 view_menu = wx.Menu()
\r
567 view_menu.AppendCheckItem(ID_ViewFolders, 'Folders\tF5')
\r
568 view_menu.AppendCheckItem(ID_ViewPlaylists, 'Playlists\tF6')
\r
569 view_menu.AppendCheckItem(ID_ViewCommands, 'Commands\tF7')
\r
570 view_menu.AppendCheckItem(ID_ViewProperties, 'Properties\tF8')
\r
571 view_menu.AppendCheckItem(ID_ViewAssistant, 'Assistant\tF9')
\r
572 view_menu.AppendCheckItem(ID_ViewResults, 'Results\tF10')
\r
573 view_menu.AppendCheckItem(ID_ViewOutput, 'Output\tF11')
\r
574 view_menu.AppendCheckItem(ID_ViewNote, 'Note\tF12')
\r
576 perspectives_menu = wx.Menu()
\r
579 help_menu = wx.Menu()
\r
580 help_menu.Append(wx.ID_ABOUT, 'About Hooke')
\r
581 #put it all together
\r
582 menu_bar.Append(file_menu, 'File')
\r
583 menu_bar.Append(view_menu, 'View')
\r
584 menu_bar.Append(perspectives_menu, "Perspectives")
\r
585 self.UpdatePerspectivesMenu()
\r
586 menu_bar.Append(help_menu, 'Help')
\r
588 def CreateNotebook(self):
\r
589 # create the notebook off-window to avoid flicker
\r
590 client_size = self.GetClientSize()
\r
591 ctrl = aui.AuiNotebook(self, -1, wx.Point(client_size.x, client_size.y), wx.Size(430, 200), self._notebook_style)
\r
592 arts = [aui.AuiDefaultTabArt, aui.AuiSimpleTabArt, aui.VC71TabArt, aui.FF2TabArt, aui.VC8TabArt, aui.ChromeTabArt]
\r
593 art = arts[self._notebook_theme]()
\r
594 ctrl.SetArtProvider(art)
\r
595 #uncomment if we find a nice icon
\r
596 #page_bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))
\r
597 ctrl.AddPage(self.CreatePanelWelcome(), "Welcome", False)
\r
600 def CreateStatusbar(self):
\r
601 statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
\r
602 statusbar.SetStatusWidths([-2, -3])
\r
603 statusbar.SetStatusText('Ready', 0)
\r
604 welcomeString=u'Welcome to Hooke (version '+__version__+', '+__release_name__+')!'
\r
605 statusbar.SetStatusText(welcomeString, 1)
\r
608 def CreateToolBarNavigation(self):
\r
609 toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)
\r
610 toolbar.SetToolBitmapSize(wx.Size(16,16))
\r
611 toolbar_bmpBack = wx.ArtProvider_GetBitmap(wx.ART_GO_BACK, wx.ART_OTHER, wx.Size(16, 16))
\r
612 toolbar_bmpForward = wx.ArtProvider_GetBitmap(wx.ART_GO_FORWARD, wx.ART_OTHER, wx.Size(16, 16))
\r
613 toolbar.AddLabelTool(ID_Previous, 'Previous', toolbar_bmpBack, shortHelp='Previous curve')
\r
614 toolbar.AddLabelTool(ID_Next, 'Next', toolbar_bmpForward, shortHelp='Next curve')
\r
618 def DeleteFromPlaylists(self, name):
\r
619 if name in self.playlists:
\r
620 del self.playlists[name]
\r
621 tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
\r
622 item, cookie = self.panelPlaylists.PlaylistsTree.GetFirstChild(tree_root)
\r
624 playlist_name = self.panelPlaylists.PlaylistsTree.GetItemText(item)
\r
625 if playlist_name == name:
\r
627 self.panelPlaylists.PlaylistsTree.Delete(item)
\r
630 item = self.panelPlaylists.PlaylistsTree.GetNextSibling(item)
\r
632 def GetActiveFigure(self):
\r
633 playlist_name = self.GetActivePlaylistName()
\r
634 figure = self.playlists[playlist_name].figure
\r
635 if figure is not None:
\r
639 def GetActiveFile(self):
\r
640 playlist = self.GetActivePlaylist()
\r
641 if playlist is not None:
\r
642 return playlist.get_active_file()
\r
645 def GetActivePlaylist(self):
\r
646 playlist_name = self.GetActivePlaylistName()
\r
647 if playlist_name in self.playlists:
\r
648 return self.playlists[playlist_name]
\r
651 def GetActivePlaylistName(self):
\r
652 #get the selected item from the tree
\r
653 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
654 #test if a playlist or a curve was double-clicked
\r
655 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
656 playlist_item = selected_item
\r
658 #get the name of the playlist
\r
659 playlist_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
660 #now we have a playlist
\r
661 return self.panelPlaylists.PlaylistsTree.GetItemText(playlist_item)
\r
663 def GetActivePlot(self):
\r
664 playlist = self.GetActivePlaylist()
\r
665 if playlist is not None:
\r
666 return playlist.get_active_file().plot
\r
669 def GetDisplayedPlot(self):
\r
670 plot = copy.deepcopy(self.displayed_plot)
\r
672 #plot.curves = copy.deepcopy(plot.curves)
\r
675 def GetDisplayedPlotCorrected(self):
\r
676 plot = copy.deepcopy(self.displayed_plot)
\r
678 plot.curves = copy.deepcopy(plot.corrected_curves)
\r
681 def GetDisplayedPlotRaw(self):
\r
682 plot = copy.deepcopy(self.displayed_plot)
\r
684 plot.curves = copy.deepcopy(plot.raw_curves)
\r
687 def GetDockArt(self):
\r
688 return self._mgr.GetArtProvider()
\r
690 def GetBoolFromConfig(self, *args):
\r
695 elif len(args) == 3:
\r
699 if self.configs.has_key(plugin):
\r
700 config = self.configs[plugin]
\r
701 return config[section][key].as_bool('value')
\r
704 def GetColorFromConfig(self, *args):
\r
709 elif len(args) == 3:
\r
713 if self.configs.has_key(plugin):
\r
714 config = self.configs[plugin]
\r
715 color_tuple = eval(config[section][key]['value'])
\r
716 color = [value / 255.0 for value in color_tuple]
\r
720 def GetFloatFromConfig(self, *args):
\r
725 elif len(args) == 3:
\r
729 if self.configs.has_key(plugin):
\r
730 config = self.configs[plugin]
\r
731 return config[section][key].as_float('value')
\r
734 def GetIntFromConfig(self, *args):
\r
739 elif len(args) == 3:
\r
743 if self.configs.has_key(plugin):
\r
744 config = self.configs[plugin]
\r
745 return config[section][key].as_int('value')
\r
748 def GetStringFromConfig(self, *args):
\r
753 elif len(args) == 3:
\r
757 if self.configs.has_key(plugin):
\r
758 config = self.configs[plugin]
\r
759 return config[section][key]['value']
\r
762 def GetPlotmanipulator(self, name):
\r
764 Returns a plot manipulator function from its name
\r
766 for plotmanipulator in self.plotmanipulators:
\r
767 if plotmanipulator.name == name:
\r
768 return plotmanipulator
\r
771 def GetPerspectiveMenuItem(self, name):
\r
772 if self._perspectives.has_key(name):
\r
773 perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
774 perspectives_list.sort()
\r
775 index = perspectives_list.index(name)
\r
776 perspective_Id = ID_FirstPerspective + index
\r
777 menu_item = self.MenuBar.FindItemById(perspective_Id)
\r
782 def HasPlotmanipulator(self, name):
\r
784 returns True if the plotmanipulator 'name' is loaded, False otherwise
\r
786 for plotmanipulator in self.plotmanipulators:
\r
787 if plotmanipulator.command == name:
\r
791 def OnAbout(self, event):
\r
792 message = 'Hooke\n\n'+\
\r
793 'A free, open source data analysis platform\n\n'+\
\r
794 'Copyright 2006-2008 by Massimo Sandal\n'+\
\r
795 'Copyright 2010 by Dr. Rolf Schmidt\n\n'+\
\r
796 'Hooke is released under the GNU General Public License version 2.'
\r
797 dialog = wx.MessageDialog(self, message, 'About Hooke', wx.OK | wx.ICON_INFORMATION)
\r
801 def OnClose(self, event):
\r
803 self.config['main']['height'] = str(self.GetSize().GetHeight())
\r
804 self.config['main']['left'] = str(self.GetPosition()[0])
\r
805 self.config['main']['top'] = str(self.GetPosition()[1])
\r
806 self.config['main']['width'] = str(self.GetSize().GetWidth())
\r
807 #save the configuration file to 'config/hooke.ini'
\r
808 self.config.write()
\r
809 #save all plugin config files
\r
810 for config in self.configs:
\r
811 plugin_config = self.configs[config]
\r
812 plugin_config.write()
\r
813 self._UnbindEvents()
\r
818 def OnDeletePerspective(self, event):
\r
819 dialog = panels.perspectives.Perspectives(self, -1, 'Delete perspective(s)')
\r
820 dialog.CenterOnScreen()
\r
823 self.UpdatePerspectivesMenu()
\r
824 #unfortunately, there is a bug in wxWidgets (Ticket #3258) that
\r
825 #makes the radio item indicator in the menu disappear
\r
826 #the code should be fine once this issue is fixed
\r
828 def OnDirCtrlLeftDclick(self, event):
\r
829 file_path = self.panelFolders.GetPath()
\r
830 if os.path.isfile(file_path):
\r
831 if file_path.endswith('.hkp'):
\r
832 self.do_loadlist(file_path)
\r
835 def OnEraseBackground(self, event):
\r
838 def OnExecute(self, event):
\r
839 item = self.panelCommands.CommandsTree.GetSelection()
\r
841 if not self.panelCommands.CommandsTree.ItemHasChildren(item):
\r
842 item_text = self.panelCommands.CommandsTree.GetItemText(item)
\r
843 command = ''.join(['self.do_', item_text, '()'])
\r
844 #self.AppendToOutput(command + '\n')
\r
847 def OnExit(self, event):
\r
850 def OnNext(self, event):
\r
853 Go to the next curve in the playlist.
\r
854 If we are at the last curve, we come back to the first.
\r
858 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
859 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
860 #GetFirstChild returns a tuple
\r
861 #we only need the first element
\r
862 next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(selected_item)[0]
\r
864 next_item = self.panelPlaylists.PlaylistsTree.GetNextSibling(selected_item)
\r
865 if not next_item.IsOk():
\r
866 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
867 #GetFirstChild returns a tuple
\r
868 #we only need the first element
\r
869 next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(parent_item)[0]
\r
870 self.panelPlaylists.PlaylistsTree.SelectItem(next_item, True)
\r
871 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
872 playlist = self.GetActivePlaylist()
\r
873 if playlist.count > 1:
\r
875 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
879 def OnNotebookPageClose(self, event):
\r
880 ctrl = event.GetEventObject()
\r
881 playlist_name = ctrl.GetPageText(ctrl._curpage)
\r
882 self.DeleteFromPlaylists(playlist_name)
\r
884 def OnPaneClose(self, event):
\r
887 def OnPlaylistsLeftDclick(self, event):
\r
888 if self.panelPlaylists.PlaylistsTree.Count > 0:
\r
889 playlist_name = self.GetActivePlaylistName()
\r
890 #if that playlist already exists
\r
891 #we check if it is the active playlist (ie selected in panelPlaylists)
\r
892 #and switch to it if necessary
\r
893 if playlist_name in self.playlists:
\r
894 index = self.plotNotebook.GetSelection()
\r
895 current_playlist = self.plotNotebook.GetPageText(index)
\r
896 if current_playlist != playlist_name:
\r
897 index = self._GetPlaylistTab(playlist_name)
\r
898 self.plotNotebook.SetSelection(index)
\r
899 #if a curve was double-clicked
\r
900 item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
901 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
\r
902 index = self._GetActiveFileIndex()
\r
906 playlist = self.GetActivePlaylist()
\r
907 playlist.index = index
\r
908 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
911 #if you uncomment the following line, the tree will collapse/expand as well
\r
914 def OnPlaylistsLeftDown(self, event):
\r
915 hit_item, hit_flags = self.panelPlaylists.PlaylistsTree.HitTest(event.GetPosition())
\r
916 if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:
\r
917 self.panelPlaylists.PlaylistsTree.SelectItem(hit_item)
\r
918 playlist_name = self.GetActivePlaylistName()
\r
919 playlist = self.GetActivePlaylist()
\r
920 #if a curve was clicked
\r
921 item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
922 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
\r
923 index = self._GetActiveFileIndex()
\r
925 playlist.index = index
\r
926 self.playlists[playlist_name] = playlist
\r
929 def OnPrevious(self, event):
\r
932 Go to the previous curve in the playlist.
\r
933 If we are at the first curve, we jump to the last.
\r
935 Syntax: previous, p
\r
937 #playlist = self.playlists[self.GetActivePlaylistName()][0]
\r
938 #select the previous curve and tell the user if we wrapped around
\r
939 #self.AppendToOutput(playlist.previous())
\r
940 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
941 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
942 previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(selected_item)
\r
944 previous_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
945 if not previous_item.IsOk():
\r
946 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
947 previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(parent_item)
\r
948 self.panelPlaylists.PlaylistsTree.SelectItem(previous_item, True)
\r
949 playlist = self.GetActivePlaylist()
\r
950 if playlist.count > 1:
\r
951 playlist.previous()
\r
952 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
956 def OnPropGridChanged (self, event):
\r
957 prop = event.GetProperty()
\r
959 item_section = self.panelProperties.SelectedTreeItem
\r
960 item_plugin = self.panelCommands.CommandsTree.GetItemParent(item_section)
\r
961 plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
\r
962 config = self.configs[plugin]
\r
963 property_section = self.panelCommands.CommandsTree.GetItemText(item_section)
\r
964 property_key = prop.GetName()
\r
965 property_value = prop.GetDisplayedString()
\r
967 config[property_section][property_key]['value'] = property_value
\r
969 def OnRestorePerspective(self, event):
\r
970 name = self.MenuBar.FindItemById(event.GetId()).GetLabel()
\r
971 self._RestorePerspective(name)
\r
973 def OnResultsCheck(self, index, flag):
\r
974 results = self.GetActivePlot().results
\r
975 if results.has_key(self.results_str):
\r
976 results[self.results_str].results[index].visible = flag
\r
977 results[self.results_str].update()
\r
980 def OnSavePerspective(self, event):
\r
982 def nameExists(name):
\r
983 menu_position = self.MenuBar.FindMenu('Perspectives')
\r
984 menu = self.MenuBar.GetMenu(menu_position)
\r
985 for item in menu.GetMenuItems():
\r
986 if item.GetText() == name:
\r
992 dialog = wx.TextEntryDialog(self, 'Enter a name for the new perspective:', 'Save perspective')
\r
993 dialog.SetValue('New perspective')
\r
994 if dialog.ShowModal() != wx.ID_OK:
\r
997 name = dialog.GetValue()
\r
999 if nameExists(name):
\r
1000 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
1001 if dialogConfirm.ShowModal() == wx.ID_YES:
\r
1006 perspective = self._mgr.SavePerspective()
\r
1007 self._SavePerspectiveToFile(name, perspective)
\r
1008 self.config['perspectives']['active'] = name
\r
1009 self.UpdatePerspectivesMenu()
\r
1010 # if nameExists(name):
\r
1011 # #check the corresponding menu item
\r
1012 # menu_item = self.GetPerspectiveMenuItem(name)
\r
1013 # #replace the perspectiveStr in _pespectives
\r
1014 # self._perspectives[name] = perspective
\r
1016 # #because we deal with radio items, we need to do some extra work
\r
1017 # #delete all menu items from the perspectives menu
\r
1018 # for item in self._perspectives_menu.GetMenuItems():
\r
1019 # self._perspectives_menu.DeleteItem(item)
\r
1020 # #recreate the perspectives menu
\r
1021 # self._perspectives_menu.Append(ID_SavePerspective, 'Save Perspective')
\r
1022 # self._perspectives_menu.Append(ID_DeletePerspective, 'Delete Perspective')
\r
1023 # self._perspectives_menu.AppendSeparator()
\r
1024 # #convert the perspectives dictionary into a list
\r
1025 # # the list contains:
\r
1026 # #[0]: name of the perspective
\r
1027 # #[1]: perspective
\r
1028 # perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
1029 # perspectives_list.append(name)
\r
1030 # perspectives_list.sort()
\r
1031 # #add all previous perspectives
\r
1032 # for index, item in enumerate(perspectives_list):
\r
1033 # menu_item = self._perspectives_menu.AppendRadioItem(ID_FirstPerspective + index, item)
\r
1034 # if item == name:
\r
1035 # menu_item.Check()
\r
1036 # #add the new perspective to _perspectives
\r
1037 # self._perspectives[name] = perspective
\r
1039 def OnSize(self, event):
\r
1042 def OnTreeCtrlCommandsSelectionChanged(self, event):
\r
1043 selected_item = event.GetItem()
\r
1044 if selected_item is not None:
\r
1047 #deregister/register the listener to avoid infinite loop
\r
1048 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlCommandsSelectionChanged)
\r
1049 self.panelCommands.CommandsTree.SelectItem(selected_item)
\r
1050 evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self.panelCommands.CommandsTree)
\r
1051 self.panelProperties.SelectedTreeItem = selected_item
\r
1052 #if a command was clicked
\r
1054 if not self.panelCommands.CommandsTree.ItemHasChildren(selected_item):
\r
1055 item_plugin = self.panelCommands.CommandsTree.GetItemParent(selected_item)
\r
1056 plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
\r
1057 if self.configs.has_key(plugin):
\r
1058 #config = self.panelCommands.CommandsTree.GetPyData(item_plugin)
\r
1059 config = self.configs[plugin]
\r
1060 section = self.panelCommands.CommandsTree.GetItemText(selected_item)
\r
1061 #display docstring in help window
\r
1062 doc_string = eval('self.do_' + section + '.__doc__')
\r
1063 if section in config:
\r
1064 for option in config[section]:
\r
1065 properties.append([option, config[section][option]])
\r
1067 plugin = self.panelCommands.CommandsTree.GetItemText(selected_item)
\r
1068 if plugin != 'core':
\r
1069 doc_string = eval('plugins.' + plugin + '.' + plugin + 'Commands.__doc__')
\r
1071 doc_string = 'The module "core" contains Hooke core functionality'
\r
1072 if doc_string is not None:
\r
1073 self.panelAssistant.ChangeValue(doc_string)
\r
1075 self.panelAssistant.ChangeValue('')
\r
1076 panels.propertyeditor.PropertyEditor.Initialize(self.panelProperties, properties)
\r
1077 #save the currently selected command/plugin to the config file
\r
1078 self.config['command']['command'] = section
\r
1079 self.config['command']['plugin'] = plugin
\r
1081 def OnTreeCtrlItemActivated(self, event):
\r
1082 self.OnExecute(event)
\r
1084 def OnUpdateNote(self, event):
\r
1086 Saves the note to the active file.
\r
1088 active_file = self.GetActiveFile()
\r
1089 active_file.note = self.panelNote.Editor.GetValue()
\r
1091 def OnView(self, event):
\r
1092 menu_id = event.GetId()
\r
1093 menu_item = self.MenuBar.FindItemById(menu_id)
\r
1094 menu_label = menu_item.GetLabel()
\r
1096 pane = self._mgr.GetPane(menu_label)
\r
1097 pane.Show(not pane.IsShown())
\r
1098 #if we don't do the following, the Folders pane does not resize properly on hide/show
\r
1099 if pane.caption == 'Folders' and pane.IsShown() and pane.IsDocked():
\r
1100 #folders_size = pane.GetSize()
\r
1101 self.panelFolders.Fit()
\r
1102 self._mgr.Update()
\r
1104 def _clickize(self, xvector, yvector, index):
\r
1106 Returns a ClickedPoint() object from an index and vectors of x, y coordinates
\r
1108 point = lib.clickedpoint.ClickedPoint()
\r
1109 point.index = index
\r
1110 point.absolute_coords = xvector[index], yvector[index]
\r
1111 point.find_graph_coords(xvector, yvector)
\r
1114 def _delta(self, message='Click 2 points', whatset=lh.RETRACTION):
\r
1116 Calculates the difference between two clicked points
\r
1118 clicked_points = self._measure_N_points(N=2, message=message, whatset=whatset)
\r
1120 plot = self.GetDisplayedPlotCorrected()
\r
1121 curve = plot.curves[whatset]
\r
1123 delta = lib.delta.Delta()
\r
1124 delta.point1.x = clicked_points[0].graph_coords[0]
\r
1125 delta.point1.y = clicked_points[0].graph_coords[1]
\r
1126 delta.point2.x = clicked_points[1].graph_coords[0]
\r
1127 delta.point2.y = clicked_points[1].graph_coords[1]
\r
1128 delta.units.x = curve.units.x
\r
1129 delta.units.y = curve.units.y
\r
1133 def _measure_N_points(self, N, message='', whatset=lh.RETRACTION):
\r
1135 General helper function for N-points measurements
\r
1136 By default, measurements are done on the retraction
\r
1139 dialog = wx.MessageDialog(None, message, 'Info', wx.OK)
\r
1140 dialog.ShowModal()
\r
1142 figure = self.GetActiveFigure()
\r
1144 xvector = self.displayed_plot.curves[whatset].x
\r
1145 yvector = self.displayed_plot.curves[whatset].y
\r
1147 clicked_points = figure.ginput(N, timeout=-1, show_clicks=True)
\r
1150 for clicked_point in clicked_points:
\r
1151 point = lib.clickedpoint.ClickedPoint()
\r
1152 point.absolute_coords = clicked_point[0], clicked_point[1]
\r
1154 #TODO: make this optional?
\r
1155 #so far, the clicked point is taken, not the corresponding data point
\r
1156 point.find_graph_coords(xvector, yvector)
\r
1157 point.is_line_edge = True
\r
1158 point.is_marker = True
\r
1159 points.append(point)
\r
1162 def do_copylog(self):
\r
1164 Copies all files in the current playlist that have a note to the destination folder.
\r
1165 destination: select folder where you want the files to be copied
\r
1166 use_LVDT_folder: when checked, the files will be copied to a folder called 'LVDT' in the destination folder (for MFP-1D files only)
\r
1168 playlist = self.GetActivePlaylist()
\r
1169 if playlist is not None:
\r
1170 destination = self.GetStringFromConfig('core', 'copylog', 'destination')
\r
1171 if not os.path.isdir(destination):
\r
1172 os.makedirs(destination)
\r
1173 for current_file in playlist.files:
\r
1174 if current_file.note:
\r
1175 shutil.copy(current_file.filename, destination)
\r
1176 if current_file.driver.filetype == 'mfp1d':
\r
1177 filename = current_file.filename.replace('deflection', 'LVDT', 1)
\r
1178 path, name = os.path.split(filename)
\r
1179 filename = os.path.join(path, 'lvdt', name)
\r
1180 use_LVDT_folder = self.GetBoolFromConfig('core', 'copylog', 'use_LVDT_folder')
\r
1181 if use_LVDT_folder:
\r
1182 destination = os.path.join(destination, 'LVDT')
\r
1183 shutil.copy(filename, destination)
\r
1185 def do_plotmanipulators(self):
\r
1187 Please select the plotmanipulators you would like to use
\r
1188 and define the order in which they will be applied to the data.
\r
1190 Click 'Execute' to apply your changes.
\r
1194 def do_preferences(self):
\r
1196 Please set general preferences for Hooke here.
\r
1197 hide_curve_extension: hides the extension of the force curve files.
\r
1198 not recommended for 'picoforce' files
\r
1202 def do_test(self):
\r
1204 Use this command for testing purposes. You find do_test in hooke.py.
\r
1208 def do_version(self):
\r
1212 Prints the current version and codename, plus library version. Useful for debugging.
\r
1214 self.AppendToOutput('Hooke ' + __version__ + ' (' + __codename__ + ')')
\r
1215 self.AppendToOutput('Released on: ' + __releasedate__)
\r
1216 self.AppendToOutput('---')
\r
1217 self.AppendToOutput('Python version: ' + python_version)
\r
1218 self.AppendToOutput('WxPython version: ' + wx_version)
\r
1219 self.AppendToOutput('Matplotlib version: ' + mpl_version)
\r
1220 self.AppendToOutput('SciPy version: ' + scipy_version)
\r
1221 self.AppendToOutput('NumPy version: ' + numpy_version)
\r
1222 self.AppendToOutput('ConfigObj version: ' + configobj_version)
\r
1223 self.AppendToOutput('wxPropertyGrid version: ' + '.'.join([str(PROPGRID_MAJOR), str(PROPGRID_MINOR), str(PROPGRID_RELEASE)]))
\r
1224 self.AppendToOutput('---')
\r
1225 self.AppendToOutput('Platform: ' + str(platform.uname()))
\r
1226 self.AppendToOutput('******************************')
\r
1227 self.AppendToOutput('Loaded plugins')
\r
1228 self.AppendToOutput('---')
\r
1230 #sort the plugins into alphabetical order
\r
1231 plugins_list = [key for key, value in self.plugins.iteritems()]
\r
1232 plugins_list.sort()
\r
1233 for plugin in plugins_list:
\r
1234 self.AppendToOutput(plugin)
\r
1236 def UpdateNote(self):
\r
1237 #update the note for the active file
\r
1238 active_file = self.GetActiveFile()
\r
1239 if active_file is not None:
\r
1240 self.panelNote.Editor.SetValue(active_file.note)
\r
1242 def UpdatePerspectivesMenu(self):
\r
1243 #add perspectives to menubar and _perspectives
\r
1244 perspectivesDirectory = os.path.join(lh.hookeDir, 'perspectives')
\r
1245 self._perspectives = {}
\r
1246 if os.path.isdir(perspectivesDirectory):
\r
1247 perspectiveFileNames = os.listdir(perspectivesDirectory)
\r
1248 for perspectiveFilename in perspectiveFileNames:
\r
1249 filename = lh.get_file_path(perspectiveFilename, ['perspectives'])
\r
1250 if os.path.isfile(filename):
\r
1251 perspectiveFile = open(filename, 'rU')
\r
1252 perspective = perspectiveFile.readline()
\r
1253 perspectiveFile.close()
\r
1255 name, extension = os.path.splitext(perspectiveFilename)
\r
1256 if extension == '.txt':
\r
1257 self._perspectives[name] = perspective
\r
1259 #in case there are no perspectives
\r
1260 if not self._perspectives:
\r
1261 perspective = self._mgr.SavePerspective()
\r
1262 self._perspectives['Default'] = perspective
\r
1263 self._SavePerspectiveToFile('Default', perspective)
\r
1265 selected_perspective = self.config['perspectives']['active']
\r
1266 if not self._perspectives.has_key(selected_perspective):
\r
1267 self.config['perspectives']['active'] = 'Default'
\r
1268 selected_perspective = 'Default'
\r
1270 perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
1271 perspectives_list.sort()
\r
1273 #get the Perspectives menu
\r
1274 menu_position = self.MenuBar.FindMenu('Perspectives')
\r
1275 menu = self.MenuBar.GetMenu(menu_position)
\r
1276 #delete all menu items
\r
1277 for item in menu.GetMenuItems():
\r
1278 menu.DeleteItem(item)
\r
1279 #rebuild the menu by adding the standard menu items
\r
1280 menu.Append(ID_SavePerspective, 'Save Perspective')
\r
1281 menu.Append(ID_DeletePerspective, 'Delete Perspective')
\r
1282 menu.AppendSeparator()
\r
1283 #add all previous perspectives
\r
1284 for index, label in enumerate(perspectives_list):
\r
1285 menu_item = menu.AppendRadioItem(ID_FirstPerspective + index, label)
\r
1286 if label == selected_perspective:
\r
1287 self._RestorePerspective(label)
\r
1288 menu_item.Check(True)
\r
1290 def UpdatePlaylistsTreeSelection(self):
\r
1291 playlist = self.GetActivePlaylist()
\r
1292 if playlist is not None:
\r
1293 if playlist.index >= 0:
\r
1294 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
1298 def UpdatePlot(self, plot=None):
\r
1300 def add_to_plot(curve, set_scale=True):
\r
1301 if curve.visible and curve.x and curve.y:
\r
1302 #get the index of the subplot to use as destination
\r
1303 destination = (curve.destination.column - 1) * number_of_rows + curve.destination.row - 1
\r
1304 #set all parameters for the plot
\r
1305 axes_list[destination].set_title(curve.title)
\r
1307 axes_list[destination].set_xlabel(curve.prefix.x + curve.units.x)
\r
1308 axes_list[destination].set_ylabel(curve.prefix.y + curve.units.y)
\r
1309 #set the formatting details for the scale
\r
1310 formatter_x = lib.curve.PrefixFormatter(curve.decimals.x, curve.prefix.x, use_zero)
\r
1311 formatter_y = lib.curve.PrefixFormatter(curve.decimals.y, curve.prefix.y, use_zero)
\r
1312 axes_list[destination].xaxis.set_major_formatter(formatter_x)
\r
1313 axes_list[destination].yaxis.set_major_formatter(formatter_y)
\r
1314 if curve.style == 'plot':
\r
1315 axes_list[destination].plot(curve.x, curve.y, color=curve.color, label=curve.label, lw=curve.linewidth, zorder=1)
\r
1316 if curve.style == 'scatter':
\r
1317 axes_list[destination].scatter(curve.x, curve.y, color=curve.color, label=curve.label, s=curve.size, zorder=2)
\r
1318 #add the legend if necessary
\r
1320 axes_list[destination].legend()
\r
1323 active_file = self.GetActiveFile()
\r
1324 if not active_file.driver:
\r
1325 #the first time we identify a file, the following need to be set
\r
1326 active_file.identify(self.drivers)
\r
1327 for curve in active_file.plot.curves:
\r
1328 curve.decimals.x = self.GetIntFromConfig('core', 'preferences', 'x_decimals')
\r
1329 curve.decimals.y = self.GetIntFromConfig('core', 'preferences', 'y_decimals')
\r
1330 curve.legend = self.GetBoolFromConfig('core', 'preferences', 'legend')
\r
1331 curve.prefix.x = self.GetStringFromConfig('core', 'preferences', 'x_prefix')
\r
1332 curve.prefix.y = self.GetStringFromConfig('core', 'preferences', 'y_prefix')
\r
1333 if active_file.driver is None:
\r
1334 self.AppendToOutput('Invalid file: ' + active_file.filename)
\r
1336 self.displayed_plot = copy.deepcopy(active_file.plot)
\r
1337 #add raw curves to plot
\r
1338 self.displayed_plot.raw_curves = copy.deepcopy(self.displayed_plot.curves)
\r
1339 #apply all active plotmanipulators
\r
1340 self.displayed_plot = self.ApplyPlotmanipulators(self.displayed_plot, active_file)
\r
1341 #add corrected curves to plot
\r
1342 self.displayed_plot.corrected_curves = copy.deepcopy(self.displayed_plot.curves)
\r
1344 active_file = None
\r
1345 self.displayed_plot = copy.deepcopy(plot)
\r
1347 figure = self.GetActiveFigure()
\r
1350 #use '0' instead of e.g. '0.00' for scales
\r
1351 use_zero = self.GetBoolFromConfig('core', 'preferences', 'use_zero')
\r
1352 #optionally remove the extension from the title of the plot
\r
1353 hide_curve_extension = self.GetBoolFromConfig('core', 'preferences', 'hide_curve_extension')
\r
1354 if hide_curve_extension:
\r
1355 title = lh.remove_extension(self.displayed_plot.title)
\r
1357 title = self.displayed_plot.title
\r
1358 figure.suptitle(title, fontsize=14)
\r
1359 #create the list of all axes necessary (rows and columns)
\r
1361 number_of_columns = max([curve.destination.column for curve in self.displayed_plot.curves])
\r
1362 number_of_rows = max([curve.destination.row for curve in self.displayed_plot.curves])
\r
1363 for index in range(number_of_rows * number_of_columns):
\r
1364 axes_list.append(figure.add_subplot(number_of_rows, number_of_columns, index + 1))
\r
1366 #add all curves to the corresponding plots
\r
1367 for curve in self.displayed_plot.curves:
\r
1368 add_to_plot(curve)
\r
1370 #make sure the titles of 'subplots' do not overlap with the axis labels of the 'main plot'
\r
1371 figure.subplots_adjust(hspace=0.3)
\r
1374 self.panelResults.ClearResults()
\r
1375 if self.displayed_plot.results.has_key(self.results_str):
\r
1376 for curve in self.displayed_plot.results[self.results_str].results:
\r
1377 add_to_plot(curve, set_scale=False)
\r
1378 self.panelResults.DisplayResults(self.displayed_plot.results[self.results_str])
\r
1380 self.panelResults.ClearResults()
\r
1382 figure.canvas.draw()
\r
1384 if __name__ == '__main__':
\r
1386 ## now, silence a deprecation warning for py2.3
\r
1388 warnings.filterwarnings("ignore", "integer", DeprecationWarning, "wxPython.gdi")
\r
1394 app = Hooke(redirect=redirect)
\r