3 """Defines :class:`GUI` providing a wxWindows interface to Hooke.
\r
6 WX_GOOD=['2.6','2.8']
\r
9 wxversion.select(WX_GOOD)
\r
18 import wx.aui as aui
\r
19 import wx.lib.evtmgr as evtmgr
\r
21 # wxPropertyGrid included in wxPython >= 2.9.1, until then, see
\r
22 # http://wxpropgrid.sourceforge.net/cgi-bin/index?page=download
\r
23 # until then, we'll avoid it because of the *nix build problems.
\r
24 #import wx.propgrid as wxpg
\r
26 from matplotlib.ticker import FuncFormatter
\r
28 from ..command import CommandExit, Exit, Command, Argument, StoreValue
\r
29 from ..interaction import Request, BooleanRequest, ReloadUserInterfaceConfig
\r
30 from ..ui import UserInterface, CommandMessage
\r
32 import lib.prettyformat
\r
33 import .panel as panel
\r
37 ID_About = wx.NewId()
\r
38 ID_Next = wx.NewId()
\r
39 ID_Previous = wx.NewId()
\r
41 ID_ViewAssistant = wx.NewId()
\r
42 ID_ViewCommands = wx.NewId()
\r
43 ID_ViewFolders = wx.NewId()
\r
44 ID_ViewNote = wx.NewId()
\r
45 ID_ViewOutput = wx.NewId()
\r
46 ID_ViewPlaylists = wx.NewId()
\r
47 ID_ViewProperties = wx.NewId()
\r
48 ID_ViewResults = wx.NewId()
\r
50 ID_DeletePerspective = wx.NewId()
\r
51 ID_SavePerspective = wx.NewId()
\r
53 ID_FirstPerspective = ID_SavePerspective + 1000
\r
54 #I hope we'll never have more than 1000 perspectives
\r
55 ID_FirstPlot = ID_SavePerspective + 2000
\r
57 class Hooke(wx.App):
\r
60 self.SetAppName('Hooke')
\r
61 self.SetVendorName('')
\r
63 window_height = config['main']['height']
\r
64 window_left= config['main']['left']
\r
65 window_top = config['main']['top']
\r
66 window_width = config['main']['width']
\r
68 #sometimes, the ini file gets confused and sets 'left'
\r
69 #and 'top' to large negative numbers
\r
70 #let's catch and fix this
\r
71 #keep small negative numbers, the user might want those
\r
72 if window_left < -window_width:
\r
74 if window_top < -window_height:
\r
76 window_position = (window_left, window_top)
\r
77 window_size = (window_width, window_height)
\r
79 #setup the splashscreen
\r
80 if config['splashscreen']['show']:
\r
81 filename = lh.get_file_path('hooke.jpg', ['resources'])
\r
82 if os.path.isfile(filename):
\r
83 bitmap = wx.Image(filename).ConvertToBitmap()
\r
84 splashStyle = wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT
\r
85 splashDuration = config['splashscreen']['duration']
\r
86 wx.SplashScreen(bitmap, splashStyle, splashDuration, None, -1)
\r
89 we need for the splash screen to disappear
\r
90 for whatever reason splashDuration and sleep do not correspond to each other
\r
91 at least not on Windows
\r
92 maybe it's because duration is in milliseconds and sleep in seconds
\r
93 thus we need to increase the sleep time a bit
\r
94 a factor of 1.2 seems to work quite well
\r
97 time.sleep(sleepFactor * splashDuration / 1000)
\r
100 for plugin in config['plugins']:
\r
101 if config['plugins'][plugin]:
\r
102 filename = ''.join([plugin, '.py'])
\r
103 path = lh.get_file_path(filename, ['plugins'])
\r
104 if os.path.isfile(path):
\r
105 #get the corresponding filename and path
\r
106 plugin_name = ''.join(['plugins.', plugin])
\r
108 __import__(plugin_name)
\r
109 #get the file that contains the plugin
\r
110 class_file = getattr(plugins, plugin)
\r
111 #get the class that contains the commands
\r
112 class_object = getattr(class_file, plugin + 'Commands')
\r
113 plugin_objects.append(class_object)
\r
115 def make_command_class(*bases):
\r
116 #create metaclass with plugins and plotmanipulators
\r
117 return type(HookeFrame)("HookeFramePlugged", bases + (HookeFrame,), {})
\r
118 frame = make_command_class(*plugin_objects)(parent=None, id=wx.ID_ANY, title='Hooke', pos=window_position, size=window_size)
\r
120 self.SetTopWindow(frame)
\r
128 class HookeFrame(wx.Frame):
\r
130 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
131 #call parent constructor
\r
132 wx.Frame.__init__(self, parent, id, title, pos, size, style)
\r
133 self.config = config
\r
134 self.CreateApplicationIcon()
\r
135 #self.configs contains: {the name of the Commands file: corresponding ConfigObj}
\r
137 #self.displayed_plot holds the currently displayed plot
\r
138 self.displayed_plot = None
\r
139 #self.playlists contains: {the name of the playlist: [playlist, tabIndex, plotID]}
\r
140 self.playlists = {}
\r
141 #list of all plotmanipulators
\r
142 self.plotmanipulators = []
\r
143 #self.plugins contains: {the name of the plugin: [caption, function]}
\r
145 #self.results_str contains the type of results we want to display
\r
146 self.results_str = 'wlc'
\r
148 #tell FrameManager to manage this frame
\r
149 self._mgr = aui.AuiManager()
\r
150 self._mgr.SetManagedWindow(self)
\r
151 #set the gradient style
\r
152 self._mgr.GetArtProvider().SetMetric(aui.AUI_DOCKART_GRADIENT_TYPE, aui.AUI_GRADIENT_NONE)
\r
153 #set transparent drag
\r
154 self._mgr.SetFlags(self._mgr.GetFlags() ^ aui.AUI_MGR_TRANSPARENT_DRAG)
\r
156 # set up default notebook style
\r
157 self._notebook_style = aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER
\r
158 self._notebook_theme = 0
\r
160 #holds the perspectives: {name, perspective_str}
\r
161 self._perspectives = {}
\r
163 # min size for the frame itself isn't completely done.
\r
164 # see the end up FrameManager::Update() for the test
\r
165 # code. For now, just hard code a frame minimum size
\r
166 self.SetMinSize(wx.Size(500, 500))
\r
167 #define the list of active drivers
\r
169 for driver in self.config['drivers']:
\r
170 if self.config['drivers'][driver]:
\r
171 #get the corresponding filename and path
\r
172 filename = ''.join([driver, '.py'])
\r
173 path = lh.get_file_path(filename, ['drivers'])
\r
174 #the driver is active for driver[1] == 1
\r
175 if os.path.isfile(path):
\r
176 #driver files are located in the 'drivers' subfolder
\r
177 driver_name = ''.join(['drivers.', driver])
\r
178 __import__(driver_name)
\r
179 class_file = getattr(drivers, driver)
\r
180 for command in dir(class_file):
\r
181 if command.endswith('Driver'):
\r
182 self.drivers.append(getattr(class_file, command))
\r
183 #import all active plugins and plotmanips
\r
184 #add 'core.ini' to self.configs (this is not a plugin and thus must be imported separately)
\r
185 ini_path = lh.get_file_path('core.ini', ['plugins'])
\r
186 plugin_config = ConfigObj(ini_path)
\r
187 #self.config.merge(plugin_config)
\r
188 self.configs['core'] = plugin_config
\r
189 #existing_commands contains: {command: plugin}
\r
190 existing_commands = {}
\r
191 #make sure we execute _plug_init() for every command line plugin we import
\r
192 for plugin in self.config['plugins']:
\r
193 if self.config['plugins'][plugin]:
\r
194 filename = ''.join([plugin, '.py'])
\r
195 path = lh.get_file_path(filename, ['plugins'])
\r
196 if os.path.isfile(path):
\r
197 #get the corresponding filename and path
\r
198 plugin_name = ''.join(['plugins.', plugin])
\r
201 module = __import__(plugin_name)
\r
202 #prepare the ini file for inclusion
\r
203 ini_path = path.replace('.py', '.ini')
\r
205 plugin_config = ConfigObj(ini_path)
\r
206 #self.config.merge(plugin_config)
\r
207 self.configs[plugin] = plugin_config
\r
209 commands = eval('dir(module.' + plugin+ '.' + plugin + 'Commands)')
\r
210 #keep only commands (ie names that start with 'do_')
\r
211 commands = [command for command in commands if command.startswith('do_')]
\r
213 for command in commands:
\r
214 if existing_commands.has_key(command):
\r
215 message_str = 'Adding "' + command + '" in plugin "' + plugin + '".\n\n'
\r
216 message_str += '"' + command + '" already exists in "' + str(existing_commands[command]) + '".\n\n'
\r
217 message_str += 'Only "' + command + '" in "' + str(existing_commands[command]) + '" will work.\n\n'
\r
218 message_str += 'Please rename one of the commands in the source code and restart Hooke or disable one of the plugins.'
\r
219 dialog = wx.MessageDialog(self, message_str, 'Warning', wx.OK|wx.ICON_WARNING|wx.CENTER)
\r
222 existing_commands[command] = plugin
\r
223 self.plugins[plugin] = commands
\r
225 #initialize the plugin
\r
226 eval('module.' + plugin+ '.' + plugin + 'Commands._plug_init(self)')
\r
227 except AttributeError:
\r
229 except ImportError:
\r
231 #add commands from hooke.py i.e. 'core' commands
\r
232 commands = dir(HookeFrame)
\r
233 commands = [command for command in commands if command.startswith('do_')]
\r
235 self.plugins['core'] = commands
\r
236 #create panels here
\r
237 self.panelAssistant = self.CreatePanelAssistant()
\r
238 self.panelCommands = self.CreatePanelCommands()
\r
239 self.panelFolders = self.CreatePanelFolders()
\r
240 self.panelPlaylists = self.CreatePanelPlaylists()
\r
241 self.panelProperties = self.CreatePanelProperties()
\r
242 self.panelNote = self.CreatePanelNote()
\r
243 self.panelOutput = self.CreatePanelOutput()
\r
244 self.panelResults = self.CreatePanelResults()
\r
245 self.plotNotebook = self.CreateNotebook()
\r
248 self._mgr.AddPane(self.panelFolders, aui.AuiPaneInfo().Name('Folders').Caption('Folders').Left().CloseButton(True).MaximizeButton(False))
\r
249 self._mgr.AddPane(self.panelPlaylists, aui.AuiPaneInfo().Name('Playlists').Caption('Playlists').Left().CloseButton(True).MaximizeButton(False))
\r
250 self._mgr.AddPane(self.panelNote, aui.AuiPaneInfo().Name('Note').Caption('Note').Left().CloseButton(True).MaximizeButton(False))
\r
251 self._mgr.AddPane(self.plotNotebook, aui.AuiPaneInfo().Name('Plots').CenterPane().PaneBorder(False))
\r
252 self._mgr.AddPane(self.panelCommands, aui.AuiPaneInfo().Name('Commands').Caption('Settings and commands').Right().CloseButton(True).MaximizeButton(False))
\r
253 self._mgr.AddPane(self.panelProperties, aui.AuiPaneInfo().Name('Properties').Caption('Properties').Right().CloseButton(True).MaximizeButton(False))
\r
254 self._mgr.AddPane(self.panelAssistant, aui.AuiPaneInfo().Name('Assistant').Caption('Assistant').Right().CloseButton(True).MaximizeButton(False))
\r
255 self._mgr.AddPane(self.panelOutput, aui.AuiPaneInfo().Name('Output').Caption('Output').Bottom().CloseButton(True).MaximizeButton(False))
\r
256 self._mgr.AddPane(self.panelResults, aui.AuiPaneInfo().Name('Results').Caption('Results').Bottom().CloseButton(True).MaximizeButton(False))
\r
257 #self._mgr.AddPane(self.textCtrlCommandLine, aui.AuiPaneInfo().Name('CommandLine').CaptionVisible(False).Fixed().Bottom().Layer(2).CloseButton(False).MaximizeButton(False))
\r
258 #self._mgr.AddPane(panelBottom, aui.AuiPaneInfo().Name("panelCommandLine").Bottom().Position(1).CloseButton(False).MaximizeButton(False))
\r
260 # add the toolbars to the manager
\r
261 #self.toolbar=self.CreateToolBar()
\r
262 self.toolbarNavigation=self.CreateToolBarNavigation()
\r
263 #self._mgr.AddPane(self.toolbar, aui.AuiPaneInfo().Name('toolbar').Caption('Toolbar').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
\r
264 self._mgr.AddPane(self.toolbarNavigation, aui.AuiPaneInfo().Name('toolbarNavigation').Caption('Navigation').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
\r
265 # "commit" all changes made to FrameManager
\r
267 #create the menubar after the panes so that the default perspective
\r
268 #is created with all panes open
\r
269 self.CreateMenuBar()
\r
270 self.statusbar = self.CreateStatusbar()
\r
273 name = self.config['perspectives']['active']
\r
274 menu_item = self.GetPerspectiveMenuItem(name)
\r
275 if menu_item is not None:
\r
276 self.OnRestorePerspective(menu_item)
\r
277 #TODO: config setting to remember playlists from last session
\r
278 self.playlists = self.panelPlaylists.Playlists
\r
279 #initialize the commands tree
\r
280 self.panelCommands.Initialize(self.plugins)
\r
281 for command in dir(self):
\r
282 if command.startswith('plotmanip_'):
\r
283 self.plotmanipulators.append(lib.plotmanipulator.Plotmanipulator(method=getattr(self, command), command=command))
\r
285 #load default list, if possible
\r
286 self.do_loadlist(self.GetStringFromConfig('core', 'preferences', 'playlist'))
\r
288 def _BindEvents(self):
\r
289 #TODO: figure out if we can use the eventManager for menu ranges
\r
290 #and events of 'self' without raising an assertion fail error
\r
291 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
\r
292 self.Bind(wx.EVT_SIZE, self.OnSize)
\r
293 self.Bind(wx.EVT_CLOSE, self.OnClose)
\r
294 # Show How To Use The Closing Panes Event
\r
295 self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPaneClose)
\r
296 self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnNotebookPageClose)
\r
298 evtmgr.eventManager.Register(self.OnAbout, wx.EVT_MENU, win=self, id=wx.ID_ABOUT)
\r
299 evtmgr.eventManager.Register(self.OnClose, wx.EVT_MENU, win=self, id=wx.ID_EXIT)
\r
301 self.Bind(wx.EVT_MENU_RANGE, self.OnView, id=ID_ViewAssistant, id2=ID_ViewResults)
\r
303 self.Bind(wx.EVT_MENU, self.OnDeletePerspective, id=ID_DeletePerspective)
\r
304 self.Bind(wx.EVT_MENU, self.OnSavePerspective, id=ID_SavePerspective)
\r
305 self.Bind(wx.EVT_MENU_RANGE, self.OnRestorePerspective, id=ID_FirstPerspective, id2=ID_FirstPerspective+1000)
\r
307 evtmgr.eventManager.Register(self.OnNext, wx.EVT_TOOL, win=self, id=ID_Next)
\r
308 evtmgr.eventManager.Register(self.OnPrevious, wx.EVT_TOOL, win=self, id=ID_Previous)
\r
309 #self.Bind(.EVT_AUITOOLBAR_TOOL_DROPDOWN, self.OnDropDownToolbarItem, id=ID_DropDownToolbarItem)
\r
311 treeCtrl = self.panelFolders.GetTreeCtrl()
\r
312 #tree.Bind(wx.EVT_LEFT_UP, self.OnDirCtrl1LeftUp)
\r
313 #tree.Bind(wx.EVT_LEFT_DOWN, self.OnGenericDirCtrl1LeftDown)
\r
314 treeCtrl.Bind(wx.EVT_LEFT_DCLICK, self.OnDirCtrlLeftDclick)
\r
316 self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DOWN, self.OnPlaylistsLeftDown)
\r
317 self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DCLICK, self.OnPlaylistsLeftDclick)
\r
319 evtmgr.eventManager.Register(self.OnExecute, wx.EVT_BUTTON, self.panelCommands.ExecuteButton)
\r
320 evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self.panelCommands.CommandsTree)
\r
321 evtmgr.eventManager.Register(self.OnTreeCtrlItemActivated, wx.EVT_TREE_ITEM_ACTIVATED, self.panelCommands.CommandsTree)
\r
322 evtmgr.eventManager.Register(self.OnUpdateNote, wx.EVT_BUTTON, self.panelNote.UpdateButton)
\r
324 self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged)
\r
326 self.panelResults.results_list.OnCheckItem = self.OnResultsCheck
\r
328 def _GetActiveFileIndex(self):
\r
329 lib.playlist.Playlist = self.GetActivePlaylist()
\r
330 #get the selected item from the tree
\r
331 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
332 #test if a playlist or a curve was double-clicked
\r
333 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
337 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
338 while selected_item.IsOk():
\r
340 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
343 def _GetPlaylistTab(self, name):
\r
344 for index, page in enumerate(self.plotNotebook._tabs._pages):
\r
345 if page.caption == name:
\r
349 def _GetUniquePlaylistName(self, name):
\r
350 playlist_name = name
\r
352 while playlist_name in self.playlists:
\r
353 playlist_name = ''.join([name, str(count)])
\r
355 return playlist_name
\r
357 def _RestorePerspective(self, name):
\r
358 self._mgr.LoadPerspective(self._perspectives[name])
\r
359 self.config['perspectives']['active'] = name
\r
361 all_panes = self._mgr.GetAllPanes()
\r
362 for pane in all_panes:
\r
363 if not pane.name.startswith('toolbar'):
\r
364 if pane.name == 'Assistant':
\r
365 self.MenuBar.FindItemById(ID_ViewAssistant).Check(pane.window.IsShown())
\r
366 if pane.name == 'Folders':
\r
367 self.MenuBar.FindItemById(ID_ViewFolders).Check(pane.window.IsShown())
\r
368 if pane.name == 'Playlists':
\r
369 self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown())
\r
370 if pane.name == 'Commands':
\r
371 self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown())
\r
372 if pane.name == 'Note':
\r
373 self.MenuBar.FindItemById(ID_ViewNote).Check(pane.window.IsShown())
\r
374 if pane.name == 'Properties':
\r
375 self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown())
\r
376 if pane.name == 'Output':
\r
377 self.MenuBar.FindItemById(ID_ViewOutput).Check(pane.window.IsShown())
\r
378 if pane.name == 'Results':
\r
379 self.MenuBar.FindItemById(ID_ViewResults).Check(pane.window.IsShown())
\r
381 def _SavePerspectiveToFile(self, name, perspective):
\r
382 filename = ''.join([name, '.txt'])
\r
383 filename = lh.get_file_path(filename, ['perspectives'])
\r
384 perspectivesFile = open(filename, 'w')
\r
385 perspectivesFile.write(perspective)
\r
386 perspectivesFile.close()
\r
388 def _UnbindEvents(self):
\r
390 evtmgr.eventManager.DeregisterListener(self.OnAbout)
\r
391 evtmgr.eventManager.DeregisterListener(self.OnClose)
\r
393 evtmgr.eventManager.DeregisterListener(self.OnNext)
\r
394 evtmgr.eventManager.DeregisterListener(self.OnPrevious)
\r
396 evtmgr.eventManager.DeregisterListener(self.OnExecute)
\r
397 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlCommandsSelectionChanged)
\r
398 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlItemActivated)
\r
399 evtmgr.eventManager.DeregisterListener(self.OnUpdateNote)
\r
401 def AddPlaylist(self, playlist=None, name='Untitled'):
\r
402 if playlist and playlist.count > 0:
\r
403 playlist.name = self._GetUniquePlaylistName(name)
\r
405 self.AddToPlaylists(playlist)
\r
407 def AddPlaylistFromFiles(self, files=[], name='Untitled'):
\r
409 playlist = lib.playlist.Playlist(self, self.drivers)
\r
411 playlist.add_curve(item)
\r
412 if playlist.count > 0:
\r
413 playlist.name = self._GetUniquePlaylistName(name)
\r
415 self.AddTayliss(playlist)
\r
417 def AddToPlaylists(self, playlist):
\r
418 if playlist.count > 0:
\r
419 #setup the playlist in the Playlist tree
\r
420 tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
\r
421 playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)
\r
422 #add all files to the Playlist tree
\r
424 hide_curve_extension = self.GetBoolFromConfig('core', 'preferences', 'hide_curve_extension')
\r
425 for index, file_to_add in enumerate(playlist.files):
\r
426 #optionally remove the extension from the name of the curve
\r
427 if hide_curve_extension:
\r
428 file_to_add.name = lh.remove_extension(file_to_add.name)
\r
429 file_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, file_to_add.name, 1)
\r
430 if index == playlist.index:
\r
431 self.panelPlaylists.PlaylistsTree.SelectItem(file_ID)
\r
433 #create the plot tab and add playlist to the dictionary
\r
434 plotPanel = panels.plot.PlotPanel(self, ID_FirstPlot + len(self.playlists))
\r
435 notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True)
\r
436 #tab_index = self.plotNotebook.GetSelection()
\r
437 playlist.figure = plotPanel.get_figure()
\r
438 self.playlists[playlist.name] = playlist
\r
439 #self.playlists[playlist.name] = [playlist, figure]
\r
440 self.panelPlaylists.PlaylistsTree.Expand(playlist_root)
\r
441 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
445 def AppendToOutput(self, text):
\r
446 self.panelOutput.AppendText(''.join([text, '\n']))
\r
448 def AppliesPlotmanipulator(self, name):
\r
450 Returns True if the plotmanipulator 'name' is applied, False otherwise
\r
451 name does not contain 'plotmanip_', just the name of the plotmanipulator (e.g. 'flatten')
\r
453 return self.GetBoolFromConfig('core', 'plotmanipulators', name)
\r
455 def ApplyPlotmanipulators(self, plot, plot_file):
\r
457 Apply all active plotmanipulators.
\r
459 if plot is not None and plot_file is not None:
\r
460 manipulated_plot = copy.deepcopy(plot)
\r
461 for plotmanipulator in self.plotmanipulators:
\r
462 if self.GetBoolFromConfig('core', 'plotmanipulators', plotmanipulator.name):
\r
463 manipulated_plot = plotmanipulator.method(manipulated_plot, plot_file)
\r
464 return manipulated_plot
\r
466 def CreateApplicationIcon(self):
\r
467 iconFile = 'resources' + os.sep + 'microscope.ico'
\r
468 icon = wx.Icon(iconFile, wx.BITMAP_TYPE_ICO)
\r
471 def CreateCommandLine(self):
\r
472 return wx.TextCtrl(self, -1, '', style=wx.NO_BORDER|wx.EXPAND)
\r
474 def CreatePanelAssistant(self):
\r
475 panel = wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
\r
476 panel.SetEditable(False)
\r
479 def CreatePanelCommands(self):
\r
480 return panels.commands.Commands(self)
\r
482 def CreatePanelFolders(self):
\r
484 filters = self.config['folders']['filters']
\r
485 index = self.config['folders'].as_int('filterindex')
\r
486 #set initial directory
\r
487 folder = self.GetStringFromConfig('core', 'preferences', 'workdir')
\r
488 return wx.GenericDirCtrl(self, -1, dir=folder, size=(200, 250), style=wx.DIRCTRL_SHOW_FILTERS, filter=filters, defaultFilter=index)
\r
490 def CreatePanelNote(self):
\r
491 return panels.note.Note(self)
\r
493 def CreatePanelOutput(self):
\r
494 return wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
\r
496 def CreatePanelPlaylists(self):
\r
497 return panels.playlist.Playlists(self)
\r
499 def CreatePanelProperties(self):
\r
500 return panels.propertyeditor.PropertyEditor(self)
\r
502 def CreatePanelResults(self):
\r
503 return panels.results.Results(self)
\r
505 def CreatePanelWelcome(self):
\r
506 #TODO: move into panels.welcome
\r
507 ctrl = wx.html.HtmlWindow(self, -1, wx.DefaultPosition, wx.Size(400, 300))
\r
508 introStr = '<h1>Welcome to Hooke</h1>' + \
\r
509 '<h3>Features</h3>' + \
\r
511 '<li>View, annotate, measure force files</li>' + \
\r
512 '<li>Worm-like chain fit of force peaks</li>' + \
\r
513 '<li>Automatic convolution-based filtering of empty files</li>' + \
\r
514 '<li>Automatic fit and measurement of multiple force peaks</li>' + \
\r
515 '<li>Handles force-clamp force experiments (experimental)</li>' + \
\r
516 '<li>It is extensible by users by means of plugins and drivers</li>' + \
\r
518 '<p>See the <a href="http://code.google.com/p/hooke/wiki/DocumentationIndex">DocumentationIndex</a> for more information</p>'
\r
519 ctrl.SetPage(introStr)
\r
522 def CreateMenuBar(self):
\r
523 menu_bar = wx.MenuBar()
\r
524 self.SetMenuBar(menu_bar)
\r
526 file_menu = wx.Menu()
\r
527 file_menu.Append(wx.ID_EXIT, 'Exit\tCtrl-Q')
\r
528 # edit_menu.AppendSeparator();
\r
529 # edit_menu.Append(ID_Config, 'Preferences')
\r
531 view_menu = wx.Menu()
\r
532 view_menu.AppendCheckItem(ID_ViewFolders, 'Folders\tF5')
\r
533 view_menu.AppendCheckItem(ID_ViewPlaylists, 'Playlists\tF6')
\r
534 view_menu.AppendCheckItem(ID_ViewCommands, 'Commands\tF7')
\r
535 view_menu.AppendCheckItem(ID_ViewProperties, 'Properties\tF8')
\r
536 view_menu.AppendCheckItem(ID_ViewAssistant, 'Assistant\tF9')
\r
537 view_menu.AppendCheckItem(ID_ViewResults, 'Results\tF10')
\r
538 view_menu.AppendCheckItem(ID_ViewOutput, 'Output\tF11')
\r
539 view_menu.AppendCheckItem(ID_ViewNote, 'Note\tF12')
\r
541 perspectives_menu = wx.Menu()
\r
544 help_menu = wx.Menu()
\r
545 help_menu.Append(wx.ID_ABOUT, 'About Hooke')
\r
546 #put it all together
\r
547 menu_bar.Append(file_menu, 'File')
\r
548 menu_bar.Append(view_menu, 'View')
\r
549 menu_bar.Append(perspectives_menu, "Perspectives")
\r
550 self.UpdatePerspectivesMenu()
\r
551 menu_bar.Append(help_menu, 'Help')
\r
553 def CreateNotebook(self):
\r
554 # create the notebook off-window to avoid flicker
\r
555 client_size = self.GetClientSize()
\r
556 ctrl = aui.AuiNotebook(self, -1, wx.Point(client_size.x, client_size.y), wx.Size(430, 200), self._notebook_style)
\r
557 arts = [aui.AuiDefaultTabArt, aui.AuiSimpleTabArt, aui.VC71TabArt, aui.FF2TabArt, aui.VC8TabArt, aui.ChromeTabArt]
\r
558 art = arts[self._notebook_theme]()
\r
559 ctrl.SetArtProvider(art)
\r
560 #uncomment if we find a nice icon
\r
561 #page_bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))
\r
562 ctrl.AddPage(self.CreatePanelWelcome(), "Welcome", False)
\r
565 def CreateStatusbar(self):
\r
566 statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
\r
567 statusbar.SetStatusWidths([-2, -3])
\r
568 statusbar.SetStatusText('Ready', 0)
\r
569 welcomeString=u'Welcome to Hooke (version '+__version__+', '+__release_name__+')!'
\r
570 statusbar.SetStatusText(welcomeString, 1)
\r
573 def CreateToolBarNavigation(self):
\r
574 toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)
\r
575 toolbar.SetToolBitmapSize(wx.Size(16,16))
\r
576 toolbar_bmpBack = wx.ArtProvider_GetBitmap(wx.ART_GO_BACK, wx.ART_OTHER, wx.Size(16, 16))
\r
577 toolbar_bmpForward = wx.ArtProvider_GetBitmap(wx.ART_GO_FORWARD, wx.ART_OTHER, wx.Size(16, 16))
\r
578 toolbar.AddLabelTool(ID_Previous, 'Previous', toolbar_bmpBack, shortHelp='Previous curve')
\r
579 toolbar.AddLabelTool(ID_Next, 'Next', toolbar_bmpForward, shortHelp='Next curve')
\r
583 def DeleteFromPlaylists(self, name):
\r
584 if name in self.playlists:
\r
585 del self.playlists[name]
\r
586 tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
\r
587 item, cookie = self.panelPlaylists.PlaylistsTree.GetFirstChild(tree_root)
\r
589 playlist_name = self.panelPlaylists.PlaylistsTree.GetItemText(item)
\r
590 if playlist_name == name:
\r
592 self.panelPlaylists.PlaylistsTree.Delete(item)
\r
595 item = self.panelPlaylists.PlaylistsTree.GetNextSibling(item)
\r
597 def GetActiveFigure(self):
\r
598 playlist_name = self.GetActivePlaylistName()
\r
599 figure = self.playlists[playlist_name].figure
\r
600 if figure is not None:
\r
604 def GetActiveFile(self):
\r
605 playlist = self.GetActivePlaylist()
\r
606 if playlist is not None:
\r
607 return playlist.get_active_file()
\r
610 def GetActivePlaylist(self):
\r
611 playlist_name = self.GetActivePlaylistName()
\r
612 if playlist_name in self.playlists:
\r
613 return self.playlists[playlist_name]
\r
616 def GetActivePlaylistName(self):
\r
617 #get the selected item from the tree
\r
618 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
619 #test if a playlist or a curve was double-clicked
\r
620 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
621 playlist_item = selected_item
\r
623 #get the name of the playlist
\r
624 playlist_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
625 #now we have a playlist
\r
626 return self.panelPlaylists.PlaylistsTree.GetItemText(playlist_item)
\r
628 def GetActivePlot(self):
\r
629 playlist = self.GetActivePlaylist()
\r
630 if playlist is not None:
\r
631 return playlist.get_active_file().plot
\r
634 def GetDisplayedPlot(self):
\r
635 plot = copy.deepcopy(self.displayed_plot)
\r
637 #plot.curves = copy.deepcopy(plot.curves)
\r
640 def GetDisplayedPlotCorrected(self):
\r
641 plot = copy.deepcopy(self.displayed_plot)
\r
643 plot.curves = copy.deepcopy(plot.corrected_curves)
\r
646 def GetDisplayedPlotRaw(self):
\r
647 plot = copy.deepcopy(self.displayed_plot)
\r
649 plot.curves = copy.deepcopy(plot.raw_curves)
\r
652 def GetDockArt(self):
\r
653 return self._mgr.GetArtProvider()
\r
655 def GetBoolFromConfig(self, *args):
\r
660 elif len(args) == 3:
\r
664 if self.configs.has_key(plugin):
\r
665 config = self.configs[plugin]
\r
666 return config[section][key].as_bool('value')
\r
669 def GetColorFromConfig(self, *args):
\r
674 elif len(args) == 3:
\r
678 if self.configs.has_key(plugin):
\r
679 config = self.configs[plugin]
\r
680 color_tuple = eval(config[section][key]['value'])
\r
681 color = [value / 255.0 for value in color_tuple]
\r
685 def GetFloatFromConfig(self, *args):
\r
690 elif len(args) == 3:
\r
694 if self.configs.has_key(plugin):
\r
695 config = self.configs[plugin]
\r
696 return config[section][key].as_float('value')
\r
699 def GetIntFromConfig(self, *args):
\r
704 elif len(args) == 3:
\r
708 if self.configs.has_key(plugin):
\r
709 config = self.configs[plugin]
\r
710 return config[section][key].as_int('value')
\r
713 def GetStringFromConfig(self, *args):
\r
718 elif len(args) == 3:
\r
722 if self.configs.has_key(plugin):
\r
723 config = self.configs[plugin]
\r
724 return config[section][key]['value']
\r
727 def GetPlotmanipulator(self, name):
\r
729 Returns a plot manipulator function from its name
\r
731 for plotmanipulator in self.plotmanipulators:
\r
732 if plotmanipulator.name == name:
\r
733 return plotmanipulator
\r
736 def GetPerspectiveMenuItem(self, name):
\r
737 if self._perspectives.has_key(name):
\r
738 perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
739 perspectives_list.sort()
\r
740 index = perspectives_list.index(name)
\r
741 perspective_Id = ID_FirstPerspective + index
\r
742 menu_item = self.MenuBar.FindItemById(perspective_Id)
\r
747 def HasPlotmanipulator(self, name):
\r
749 returns True if the plotmanipulator 'name' is loaded, False otherwise
\r
751 for plotmanipulator in self.plotmanipulators:
\r
752 if plotmanipulator.command == name:
\r
756 def OnAbout(self, event):
\r
757 message = 'Hooke\n\n'+\
\r
758 'A free, open source data analysis platform\n\n'+\
\r
759 'Copyright 2006-2008 by Massimo Sandal\n'+\
\r
760 'Copyright 2010 by Dr. Rolf Schmidt\n\n'+\
\r
761 'Hooke is released under the GNU General Public License version 2.'
\r
762 dialog = wx.MessageDialog(self, message, 'About Hooke', wx.OK | wx.ICON_INFORMATION)
\r
766 def OnClose(self, event):
\r
768 self.config['main']['height'] = str(self.GetSize().GetHeight())
\r
769 self.config['main']['left'] = str(self.GetPosition()[0])
\r
770 self.config['main']['top'] = str(self.GetPosition()[1])
\r
771 self.config['main']['width'] = str(self.GetSize().GetWidth())
\r
772 #save the configuration file to 'config/hooke.ini'
\r
773 self.config.write()
\r
774 #save all plugin config files
\r
775 for config in self.configs:
\r
776 plugin_config = self.configs[config]
\r
777 plugin_config.write()
\r
778 self._UnbindEvents()
\r
783 def OnDeletePerspective(self, event):
\r
784 dialog = panels.perspectives.Perspectives(self, -1, 'Delete perspective(s)')
\r
785 dialog.CenterOnScreen()
\r
788 self.UpdatePerspectivesMenu()
\r
789 #unfortunately, there is a bug in wxWidgets (Ticket #3258) that
\r
790 #makes the radio item indicator in the menu disappear
\r
791 #the code should be fine once this issue is fixed
\r
793 def OnDirCtrlLeftDclick(self, event):
\r
794 file_path = self.panelFolders.GetPath()
\r
795 if os.path.isfile(file_path):
\r
796 if file_path.endswith('.hkp'):
\r
797 self.do_loadlist(file_path)
\r
800 def OnEraseBackground(self, event):
\r
803 def OnExecute(self, event):
\r
804 item = self.panelCommands.CommandsTree.GetSelection()
\r
806 if not self.panelCommands.CommandsTree.ItemHasChildren(item):
\r
807 item_text = self.panelCommands.CommandsTree.GetItemText(item)
\r
808 command = ''.join(['self.do_', item_text, '()'])
\r
809 #self.AppendToOutput(command + '\n')
\r
812 def OnExit(self, event):
\r
815 def OnNext(self, event):
\r
818 Go to the next curve in the playlist.
\r
819 If we are at the last curve, we come back to the first.
\r
823 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
824 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
825 #GetFirstChild returns a tuple
\r
826 #we only need the first element
\r
827 next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(selected_item)[0]
\r
829 next_item = self.panelPlaylists.PlaylistsTree.GetNextSibling(selected_item)
\r
830 if not next_item.IsOk():
\r
831 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
832 #GetFirstChild returns a tuple
\r
833 #we only need the first element
\r
834 next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(parent_item)[0]
\r
835 self.panelPlaylists.PlaylistsTree.SelectItem(next_item, True)
\r
836 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
837 playlist = self.GetActivePlaylist()
\r
838 if playlist.count > 1:
\r
840 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
844 def OnNotebookPageClose(self, event):
\r
845 ctrl = event.GetEventObject()
\r
846 playlist_name = ctrl.GetPageText(ctrl._curpage)
\r
847 self.DeleteFromPlaylists(playlist_name)
\r
849 def OnPaneClose(self, event):
\r
852 def OnPlaylistsLeftDclick(self, event):
\r
853 if self.panelPlaylists.PlaylistsTree.Count > 0:
\r
854 playlist_name = self.GetActivePlaylistName()
\r
855 #if that playlist already exists
\r
856 #we check if it is the active playlist (ie selected in panelPlaylists)
\r
857 #and switch to it if necessary
\r
858 if playlist_name in self.playlists:
\r
859 index = self.plotNotebook.GetSelection()
\r
860 current_playlist = self.plotNotebook.GetPageText(index)
\r
861 if current_playlist != playlist_name:
\r
862 index = self._GetPlaylistTab(playlist_name)
\r
863 self.plotNotebook.SetSelection(index)
\r
864 #if a curve was double-clicked
\r
865 item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
866 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
\r
867 index = self._GetActiveFileIndex()
\r
871 playlist = self.GetActivePlaylist()
\r
872 playlist.index = index
\r
873 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
876 #if you uncomment the following line, the tree will collapse/expand as well
\r
879 def OnPlaylistsLeftDown(self, event):
\r
880 hit_item, hit_flags = self.panelPlaylists.PlaylistsTree.HitTest(event.GetPosition())
\r
881 if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:
\r
882 self.panelPlaylists.PlaylistsTree.SelectItem(hit_item)
\r
883 playlist_name = self.GetActivePlaylistName()
\r
884 playlist = self.GetActivePlaylist()
\r
885 #if a curve was clicked
\r
886 item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
887 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
\r
888 index = self._GetActiveFileIndex()
\r
890 playlist.index = index
\r
891 self.playlists[playlist_name] = playlist
\r
894 def OnPrevious(self, event):
\r
897 Go to the previous curve in the playlist.
\r
898 If we are at the first curve, we jump to the last.
\r
900 Syntax: previous, p
\r
902 #playlist = self.playlists[self.GetActivePlaylistName()][0]
\r
903 #select the previous curve and tell the user if we wrapped around
\r
904 #self.AppendToOutput(playlist.previous())
\r
905 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
906 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
907 previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(selected_item)
\r
909 previous_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
910 if not previous_item.IsOk():
\r
911 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
912 previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(parent_item)
\r
913 self.panelPlaylists.PlaylistsTree.SelectItem(previous_item, True)
\r
914 playlist = self.GetActivePlaylist()
\r
915 if playlist.count > 1:
\r
916 playlist.previous()
\r
917 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
921 def OnPropGridChanged (self, event):
\r
922 prop = event.GetProperty()
\r
924 item_section = self.panelProperties.SelectedTreeItem
\r
925 item_plugin = self.panelCommands.CommandsTree.GetItemParent(item_section)
\r
926 plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
\r
927 config = self.configs[plugin]
\r
928 property_section = self.panelCommands.CommandsTree.GetItemText(item_section)
\r
929 property_key = prop.GetName()
\r
930 property_value = prop.GetDisplayedString()
\r
932 config[property_section][property_key]['value'] = property_value
\r
934 def OnRestorePerspective(self, event):
\r
935 name = self.MenuBar.FindItemById(event.GetId()).GetLabel()
\r
936 self._RestorePerspective(name)
\r
938 def OnResultsCheck(self, index, flag):
\r
939 results = self.GetActivePlot().results
\r
940 if results.has_key(self.results_str):
\r
941 results[self.results_str].results[index].visible = flag
\r
942 results[self.results_str].update()
\r
945 def OnSavePerspective(self, event):
\r
947 def nameExists(name):
\r
948 menu_position = self.MenuBar.FindMenu('Perspectives')
\r
949 menu = self.MenuBar.GetMenu(menu_position)
\r
950 for item in menu.GetMenuItems():
\r
951 if item.GetText() == name:
\r
957 dialog = wx.TextEntryDialog(self, 'Enter a name for the new perspective:', 'Save perspective')
\r
958 dialog.SetValue('New perspective')
\r
959 if dialog.ShowModal() != wx.ID_OK:
\r
962 name = dialog.GetValue()
\r
964 if nameExists(name):
\r
965 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
966 if dialogConfirm.ShowModal() == wx.ID_YES:
\r
971 perspective = self._mgr.SavePerspective()
\r
972 self._SavePerspectiveToFile(name, perspective)
\r
973 self.config['perspectives']['active'] = name
\r
974 self.UpdatePerspectivesMenu()
\r
975 # if nameExists(name):
\r
976 # #check the corresponding menu item
\r
977 # menu_item = self.GetPerspectiveMenuItem(name)
\r
978 # #replace the perspectiveStr in _pespectives
\r
979 # self._perspectives[name] = perspective
\r
981 # #because we deal with radio items, we need to do some extra work
\r
982 # #delete all menu items from the perspectives menu
\r
983 # for item in self._perspectives_menu.GetMenuItems():
\r
984 # self._perspectives_menu.DeleteItem(item)
\r
985 # #recreate the perspectives menu
\r
986 # self._perspectives_menu.Append(ID_SavePerspective, 'Save Perspective')
\r
987 # self._perspectives_menu.Append(ID_DeletePerspective, 'Delete Perspective')
\r
988 # self._perspectives_menu.AppendSeparator()
\r
989 # #convert the perspectives dictionary into a list
\r
990 # # the list contains:
\r
991 # #[0]: name of the perspective
\r
992 # #[1]: perspective
\r
993 # perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
994 # perspectives_list.append(name)
\r
995 # perspectives_list.sort()
\r
996 # #add all previous perspectives
\r
997 # for index, item in enumerate(perspectives_list):
\r
998 # menu_item = self._perspectives_menu.AppendRadioItem(ID_FirstPerspective + index, item)
\r
1000 # menu_item.Check()
\r
1001 # #add the new perspective to _perspectives
\r
1002 # self._perspectives[name] = perspective
\r
1004 def OnSize(self, event):
\r
1007 def OnTreeCtrlCommandsSelectionChanged(self, event):
\r
1008 selected_item = event.GetItem()
\r
1009 if selected_item is not None:
\r
1012 #deregister/register the listener to avoid infinite loop
\r
1013 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlCommandsSelectionChanged)
\r
1014 self.panelCommands.CommandsTree.SelectItem(selected_item)
\r
1015 evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self.panelCommands.CommandsTree)
\r
1016 self.panelProperties.SelectedTreeItem = selected_item
\r
1017 #if a command was clicked
\r
1019 if not self.panelCommands.CommandsTree.ItemHasChildren(selected_item):
\r
1020 item_plugin = self.panelCommands.CommandsTree.GetItemParent(selected_item)
\r
1021 plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
\r
1022 if self.configs.has_key(plugin):
\r
1023 #config = self.panelCommands.CommandsTree.GetPyData(item_plugin)
\r
1024 config = self.configs[plugin]
\r
1025 section = self.panelCommands.CommandsTree.GetItemText(selected_item)
\r
1026 #display docstring in help window
\r
1027 doc_string = eval('self.do_' + section + '.__doc__')
\r
1028 if section in config:
\r
1029 for option in config[section]:
\r
1030 properties.append([option, config[section][option]])
\r
1032 plugin = self.panelCommands.CommandsTree.GetItemText(selected_item)
\r
1033 if plugin != 'core':
\r
1034 doc_string = eval('plugins.' + plugin + '.' + plugin + 'Commands.__doc__')
\r
1036 doc_string = 'The module "core" contains Hooke core functionality'
\r
1037 if doc_string is not None:
\r
1038 self.panelAssistant.ChangeValue(doc_string)
\r
1040 self.panelAssistant.ChangeValue('')
\r
1041 panels.propertyeditor.PropertyEditor.Initialize(self.panelProperties, properties)
\r
1042 #save the currently selected command/plugin to the config file
\r
1043 self.config['command']['command'] = section
\r
1044 self.config['command']['plugin'] = plugin
\r
1046 def OnTreeCtrlItemActivated(self, event):
\r
1047 self.OnExecute(event)
\r
1049 def OnUpdateNote(self, event):
\r
1051 Saves the note to the active file.
\r
1053 active_file = self.GetActiveFile()
\r
1054 active_file.note = self.panelNote.Editor.GetValue()
\r
1056 def OnView(self, event):
\r
1057 menu_id = event.GetId()
\r
1058 menu_item = self.MenuBar.FindItemById(menu_id)
\r
1059 menu_label = menu_item.GetLabel()
\r
1061 pane = self._mgr.GetPane(menu_label)
\r
1062 pane.Show(not pane.IsShown())
\r
1063 #if we don't do the following, the Folders pane does not resize properly on hide/show
\r
1064 if pane.caption == 'Folders' and pane.IsShown() and pane.IsDocked():
\r
1065 #folders_size = pane.GetSize()
\r
1066 self.panelFolders.Fit()
\r
1067 self._mgr.Update()
\r
1069 def _clickize(self, xvector, yvector, index):
\r
1071 Returns a ClickedPoint() object from an index and vectors of x, y coordinates
\r
1073 point = lib.clickedpoint.ClickedPoint()
\r
1074 point.index = index
\r
1075 point.absolute_coords = xvector[index], yvector[index]
\r
1076 point.find_graph_coords(xvector, yvector)
\r
1079 def _delta(self, message='Click 2 points', whatset=lh.RETRACTION):
\r
1081 Calculates the difference between two clicked points
\r
1083 clicked_points = self._measure_N_points(N=2, message=message, whatset=whatset)
\r
1085 plot = self.GetDisplayedPlotCorrected()
\r
1086 curve = plot.curves[whatset]
\r
1088 delta = lib.delta.Delta()
\r
1089 delta.point1.x = clicked_points[0].graph_coords[0]
\r
1090 delta.point1.y = clicked_points[0].graph_coords[1]
\r
1091 delta.point2.x = clicked_points[1].graph_coords[0]
\r
1092 delta.point2.y = clicked_points[1].graph_coords[1]
\r
1093 delta.units.x = curve.units.x
\r
1094 delta.units.y = curve.units.y
\r
1098 def _measure_N_points(self, N, message='', whatset=lh.RETRACTION):
\r
1100 General helper function for N-points measurements
\r
1101 By default, measurements are done on the retraction
\r
1104 dialog = wx.MessageDialog(None, message, 'Info', wx.OK)
\r
1105 dialog.ShowModal()
\r
1107 figure = self.GetActiveFigure()
\r
1109 xvector = self.displayed_plot.curves[whatset].x
\r
1110 yvector = self.displayed_plot.curves[whatset].y
\r
1112 clicked_points = figure.ginput(N, timeout=-1, show_clicks=True)
\r
1115 for clicked_point in clicked_points:
\r
1116 point = lib.clickedpoint.ClickedPoint()
\r
1117 point.absolute_coords = clicked_point[0], clicked_point[1]
\r
1119 #TODO: make this optional?
\r
1120 #so far, the clicked point is taken, not the corresponding data point
\r
1121 point.find_graph_coords(xvector, yvector)
\r
1122 point.is_line_edge = True
\r
1123 point.is_marker = True
\r
1124 points.append(point)
\r
1127 def do_copylog(self):
\r
1129 Copies all files in the current playlist that have a note to the destination folder.
\r
1130 destination: select folder where you want the files to be copied
\r
1131 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
1133 playlist = self.GetActivePlaylist()
\r
1134 if playlist is not None:
\r
1135 destination = self.GetStringFromConfig('core', 'copylog', 'destination')
\r
1136 if not os.path.isdir(destination):
\r
1137 os.makedirs(destination)
\r
1138 for current_file in playlist.files:
\r
1139 if current_file.note:
\r
1140 shutil.copy(current_file.filename, destination)
\r
1141 if current_file.driver.filetype == 'mfp1d':
\r
1142 filename = current_file.filename.replace('deflection', 'LVDT', 1)
\r
1143 path, name = os.path.split(filename)
\r
1144 filename = os.path.join(path, 'lvdt', name)
\r
1145 use_LVDT_folder = self.GetBoolFromConfig('core', 'copylog', 'use_LVDT_folder')
\r
1146 if use_LVDT_folder:
\r
1147 destination = os.path.join(destination, 'LVDT')
\r
1148 shutil.copy(filename, destination)
\r
1150 def do_plotmanipulators(self):
\r
1152 Please select the plotmanipulators you would like to use
\r
1153 and define the order in which they will be applied to the data.
\r
1155 Click 'Execute' to apply your changes.
\r
1159 def do_preferences(self):
\r
1161 Please set general preferences for Hooke here.
\r
1162 hide_curve_extension: hides the extension of the force curve files.
\r
1163 not recommended for 'picoforce' files
\r
1167 def do_test(self):
\r
1169 Use this command for testing purposes. You find do_test in hooke.py.
\r
1173 def do_version(self):
\r
1177 Prints the current version and codename, plus library version. Useful for debugging.
\r
1179 self.AppendToOutput('Hooke ' + __version__ + ' (' + __codename__ + ')')
\r
1180 self.AppendToOutput('Released on: ' + __releasedate__)
\r
1181 self.AppendToOutput('---')
\r
1182 self.AppendToOutput('Python version: ' + python_version)
\r
1183 self.AppendToOutput('WxPython version: ' + wx_version)
\r
1184 self.AppendToOutput('Matplotlib version: ' + mpl_version)
\r
1185 self.AppendToOutput('SciPy version: ' + scipy_version)
\r
1186 self.AppendToOutput('NumPy version: ' + numpy_version)
\r
1187 self.AppendToOutput('ConfigObj version: ' + configobj_version)
\r
1188 self.AppendToOutput('wxPropertyGrid version: ' + '.'.join([str(PROPGRID_MAJOR), str(PROPGRID_MINOR), str(PROPGRID_RELEASE)]))
\r
1189 self.AppendToOutput('---')
\r
1190 self.AppendToOutput('Platform: ' + str(platform.uname()))
\r
1191 self.AppendToOutput('******************************')
\r
1192 self.AppendToOutput('Loaded plugins')
\r
1193 self.AppendToOutput('---')
\r
1195 #sort the plugins into alphabetical order
\r
1196 plugins_list = [key for key, value in self.plugins.iteritems()]
\r
1197 plugins_list.sort()
\r
1198 for plugin in plugins_list:
\r
1199 self.AppendToOutput(plugin)
\r
1201 def UpdateNote(self):
\r
1202 #update the note for the active file
\r
1203 active_file = self.GetActiveFile()
\r
1204 if active_file is not None:
\r
1205 self.panelNote.Editor.SetValue(active_file.note)
\r
1207 def UpdatePerspectivesMenu(self):
\r
1208 #add perspectives to menubar and _perspectives
\r
1209 perspectivesDirectory = os.path.join(lh.hookeDir, 'perspectives')
\r
1210 self._perspectives = {}
\r
1211 if os.path.isdir(perspectivesDirectory):
\r
1212 perspectiveFileNames = os.listdir(perspectivesDirectory)
\r
1213 for perspectiveFilename in perspectiveFileNames:
\r
1214 filename = lh.get_file_path(perspectiveFilename, ['perspectives'])
\r
1215 if os.path.isfile(filename):
\r
1216 perspectiveFile = open(filename, 'rU')
\r
1217 perspective = perspectiveFile.readline()
\r
1218 perspectiveFile.close()
\r
1220 name, extension = os.path.splitext(perspectiveFilename)
\r
1221 if extension == '.txt':
\r
1222 self._perspectives[name] = perspective
\r
1224 #in case there are no perspectives
\r
1225 if not self._perspectives:
\r
1226 perspective = self._mgr.SavePerspective()
\r
1227 self._perspectives['Default'] = perspective
\r
1228 self._SavePerspectiveToFile('Default', perspective)
\r
1230 selected_perspective = self.config['perspectives']['active']
\r
1231 if not self._perspectives.has_key(selected_perspective):
\r
1232 self.config['perspectives']['active'] = 'Default'
\r
1233 selected_perspective = 'Default'
\r
1235 perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
1236 perspectives_list.sort()
\r
1238 #get the Perspectives menu
\r
1239 menu_position = self.MenuBar.FindMenu('Perspectives')
\r
1240 menu = self.MenuBar.GetMenu(menu_position)
\r
1241 #delete all menu items
\r
1242 for item in menu.GetMenuItems():
\r
1243 menu.DeleteItem(item)
\r
1244 #rebuild the menu by adding the standard menu items
\r
1245 menu.Append(ID_SavePerspective, 'Save Perspective')
\r
1246 menu.Append(ID_DeletePerspective, 'Delete Perspective')
\r
1247 menu.AppendSeparator()
\r
1248 #add all previous perspectives
\r
1249 for index, label in enumerate(perspectives_list):
\r
1250 menu_item = menu.AppendRadioItem(ID_FirstPerspective + index, label)
\r
1251 if label == selected_perspective:
\r
1252 self._RestorePerspective(label)
\r
1253 menu_item.Check(True)
\r
1255 def UpdatePlaylistsTreeSelection(self):
\r
1256 playlist = self.GetActivePlaylist()
\r
1257 if playlist is not None:
\r
1258 if playlist.index >= 0:
\r
1259 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
1263 def UpdatePlot(self, plot=None):
\r
1265 def add_to_plot(curve, set_scale=True):
\r
1266 if curve.visible and curve.x and curve.y:
\r
1267 #get the index of the subplot to use as destination
\r
1268 destination = (curve.destination.column - 1) * number_of_rows + curve.destination.row - 1
\r
1269 #set all parameters for the plot
\r
1270 axes_list[destination].set_title(curve.title)
\r
1272 axes_list[destination].set_xlabel(curve.prefix.x + curve.units.x)
\r
1273 axes_list[destination].set_ylabel(curve.prefix.y + curve.units.y)
\r
1274 #set the formatting details for the scale
\r
1275 formatter_x = lib.curve.PrefixFormatter(curve.decimals.x, curve.prefix.x, use_zero)
\r
1276 formatter_y = lib.curve.PrefixFormatter(curve.decimals.y, curve.prefix.y, use_zero)
\r
1277 axes_list[destination].xaxis.set_major_formatter(formatter_x)
\r
1278 axes_list[destination].yaxis.set_major_formatter(formatter_y)
\r
1279 if curve.style == 'plot':
\r
1280 axes_list[destination].plot(curve.x, curve.y, color=curve.color, label=curve.label, lw=curve.linewidth, zorder=1)
\r
1281 if curve.style == 'scatter':
\r
1282 axes_list[destination].scatter(curve.x, curve.y, color=curve.color, label=curve.label, s=curve.size, zorder=2)
\r
1283 #add the legend if necessary
\r
1285 axes_list[destination].legend()
\r
1288 active_file = self.GetActiveFile()
\r
1289 if not active_file.driver:
\r
1290 #the first time we identify a file, the following need to be set
\r
1291 active_file.identify(self.drivers)
\r
1292 for curve in active_file.plot.curves:
\r
1293 curve.decimals.x = self.GetIntFromConfig('core', 'preferences', 'x_decimals')
\r
1294 curve.decimals.y = self.GetIntFromConfig('core', 'preferences', 'y_decimals')
\r
1295 curve.legend = self.GetBoolFromConfig('core', 'preferences', 'legend')
\r
1296 curve.prefix.x = self.GetStringFromConfig('core', 'preferences', 'x_prefix')
\r
1297 curve.prefix.y = self.GetStringFromConfig('core', 'preferences', 'y_prefix')
\r
1298 if active_file.driver is None:
\r
1299 self.AppendToOutput('Invalid file: ' + active_file.filename)
\r
1301 self.displayed_plot = copy.deepcopy(active_file.plot)
\r
1302 #add raw curves to plot
\r
1303 self.displayed_plot.raw_curves = copy.deepcopy(self.displayed_plot.curves)
\r
1304 #apply all active plotmanipulators
\r
1305 self.displayed_plot = self.ApplyPlotmanipulators(self.displayed_plot, active_file)
\r
1306 #add corrected curves to plot
\r
1307 self.displayed_plot.corrected_curves = copy.deepcopy(self.displayed_plot.curves)
\r
1309 active_file = None
\r
1310 self.displayed_plot = copy.deepcopy(plot)
\r
1312 figure = self.GetActiveFigure()
\r
1315 #use '0' instead of e.g. '0.00' for scales
\r
1316 use_zero = self.GetBoolFromConfig('core', 'preferences', 'use_zero')
\r
1317 #optionally remove the extension from the title of the plot
\r
1318 hide_curve_extension = self.GetBoolFromConfig('core', 'preferences', 'hide_curve_extension')
\r
1319 if hide_curve_extension:
\r
1320 title = lh.remove_extension(self.displayed_plot.title)
\r
1322 title = self.displayed_plot.title
\r
1323 figure.suptitle(title, fontsize=14)
\r
1324 #create the list of all axes necessary (rows and columns)
\r
1326 number_of_columns = max([curve.destination.column for curve in self.displayed_plot.curves])
\r
1327 number_of_rows = max([curve.destination.row for curve in self.displayed_plot.curves])
\r
1328 for index in range(number_of_rows * number_of_columns):
\r
1329 axes_list.append(figure.add_subplot(number_of_rows, number_of_columns, index + 1))
\r
1331 #add all curves to the corresponding plots
\r
1332 for curve in self.displayed_plot.curves:
\r
1333 add_to_plot(curve)
\r
1335 #make sure the titles of 'subplots' do not overlap with the axis labels of the 'main plot'
\r
1336 figure.subplots_adjust(hspace=0.3)
\r
1339 self.panelResults.ClearResults()
\r
1340 if self.displayed_plot.results.has_key(self.results_str):
\r
1341 for curve in self.displayed_plot.results[self.results_str].results:
\r
1342 add_to_plot(curve, set_scale=False)
\r
1343 self.panelResults.DisplayResults(self.displayed_plot.results[self.results_str])
\r
1345 self.panelResults.ClearResults()
\r
1347 figure.canvas.draw()
\r
1349 if __name__ == '__main__':
\r
1351 ## now, silence a deprecation warning for py2.3
\r
1353 warnings.filterwarnings("ignore", "integer", DeprecationWarning, "wxPython.gdi")
\r
1359 app = Hooke(redirect=redirect)
\r
1363 from ..command import CommandExit, Exit, Command, Argument, StoreValue
\r
1364 from ..interaction import Request, BooleanRequest, ReloadUserInterfaceConfig
\r
1365 from ..ui import UserInterface, CommandMessage
\r
1366 from ..util.encoding import get_input_encoding, get_output_encoding
\r
1369 class GUI (UserInterface):
\r
1370 """wxWindows graphical user interface.
\r
1372 def __init__(self):
\r
1373 super(GUI, self).__init__(name='gui')
\r
1375 def default_settings(self):
\r
1376 """Return a list of :class:`hooke.config.Setting`\s for any
\r
1377 configurable UI settings.
\r
1379 The suggested section setting is::
\r
1381 Setting(section=self.setting_section, help=self.__doc__)
\r
1385 def reload_config(self):
\r
1388 def run(self, commands, ui_to_command_queue, command_to_ui_queue):
\r
1389 self._initialize()
\r
1390 cmd = self._cmd(commands, ui_to_command_queue, command_to_ui_queue)
\r
1391 cmd.cmdloop(self._splash_text())
\r
1393 def run_lines(self, commands, ui_to_command_queue, command_to_ui_queue,
\r
1395 raise NotImplementedError(
\r
1396 'Use the command line interface for run_lines()')
\r