3 """Defines :class:`GUI` providing a wxWindows interface to Hooke.
\r
6 import lib.libhooke as lh
\r
8 wxversion.select(lh.WX_GOOD)
\r
10 from configobj import ConfigObj
\r
18 import wx.lib.agw.aui as aui
\r
19 import wx.lib.evtmgr as evtmgr
\r
20 import wx.propgrid as wxpg
\r
22 from matplotlib.ticker import FuncFormatter
\r
24 from configobj import __version__ as configobj_version
\r
25 from matplotlib import __version__ as mpl_version
\r
26 from numpy import __version__ as numpy_version
\r
27 from scipy import __version__ as scipy_version
\r
28 from sys import version as python_version
\r
29 from wx import __version__ as wx_version
\r
30 from wx.propgrid import PROPGRID_MAJOR
\r
31 from wx.propgrid import PROPGRID_MINOR
\r
32 from wx.propgrid import PROPGRID_RELEASE
\r
35 from agw import cubecolourdialog as CCD
\r
36 except ImportError: # if it's not there locally, try the wxPython lib.
\r
37 import wx.lib.agw.cubecolourdialog as CCD
\r
39 #set the Hooke directory
\r
40 lh.hookeDir = os.path.abspath(os.path.dirname(__file__))
\r
41 from config.config import config
\r
43 import lib.clickedpoint
\r
47 import lib.plotmanipulator
\r
48 import lib.prettyformat
\r
49 import panels.commands
\r
51 import panels.perspectives
\r
52 import panels.playlist
\r
54 import panels.propertyeditor
\r
55 import panels.results
\r
60 global __releasedate__
\r
61 __version__ = lh.HOOKE_VERSION[0]
\r
62 __codename__ = lh.HOOKE_VERSION[1]
\r
63 __releasedate__ = lh.HOOKE_VERSION[2]
\r
64 __release_name__ = lh.HOOKE_VERSION[1]
\r
66 ID_About = wx.NewId()
\r
67 ID_Next = wx.NewId()
\r
68 ID_Previous = wx.NewId()
\r
70 ID_ViewAssistant = wx.NewId()
\r
71 ID_ViewCommands = wx.NewId()
\r
72 ID_ViewFolders = wx.NewId()
\r
73 ID_ViewNote = wx.NewId()
\r
74 ID_ViewOutput = wx.NewId()
\r
75 ID_ViewPlaylists = wx.NewId()
\r
76 ID_ViewProperties = wx.NewId()
\r
77 ID_ViewResults = wx.NewId()
\r
79 ID_DeletePerspective = wx.NewId()
\r
80 ID_SavePerspective = wx.NewId()
\r
82 ID_FirstPerspective = ID_SavePerspective + 1000
\r
83 #I hope we'll never have more than 1000 perspectives
\r
84 ID_FirstPlot = ID_SavePerspective + 2000
\r
86 class Hooke(wx.App):
\r
89 self.SetAppName('Hooke')
\r
90 self.SetVendorName('')
\r
92 window_height = config['main']['height']
\r
93 window_left= config['main']['left']
\r
94 window_top = config['main']['top']
\r
95 window_width = config['main']['width']
\r
97 #sometimes, the ini file gets confused and sets 'left'
\r
98 #and 'top' to large negative numbers
\r
99 #let's catch and fix this
\r
100 #keep small negative numbers, the user might want those
\r
101 if window_left < -window_width:
\r
103 if window_top < -window_height:
\r
105 window_position = (window_left, window_top)
\r
106 window_size = (window_width, window_height)
\r
108 #setup the splashscreen
\r
109 if config['splashscreen']['show']:
\r
110 filename = lh.get_file_path('hooke.jpg', ['resources'])
\r
111 if os.path.isfile(filename):
\r
112 bitmap = wx.Image(filename).ConvertToBitmap()
\r
113 splashStyle = wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT
\r
114 splashDuration = config['splashscreen']['duration']
\r
115 wx.SplashScreen(bitmap, splashStyle, splashDuration, None, -1)
\r
118 we need for the splash screen to disappear
\r
119 for whatever reason splashDuration and sleep do not correspond to each other
\r
120 at least not on Windows
\r
121 maybe it's because duration is in milliseconds and sleep in seconds
\r
122 thus we need to increase the sleep time a bit
\r
123 a factor of 1.2 seems to work quite well
\r
126 time.sleep(sleepFactor * splashDuration / 1000)
\r
128 plugin_objects = []
\r
129 for plugin in config['plugins']:
\r
130 if config['plugins'][plugin]:
\r
131 filename = ''.join([plugin, '.py'])
\r
132 path = lh.get_file_path(filename, ['plugins'])
\r
133 if os.path.isfile(path):
\r
134 #get the corresponding filename and path
\r
135 plugin_name = ''.join(['plugins.', plugin])
\r
137 __import__(plugin_name)
\r
138 #get the file that contains the plugin
\r
139 class_file = getattr(plugins, plugin)
\r
140 #get the class that contains the commands
\r
141 class_object = getattr(class_file, plugin + 'Commands')
\r
142 plugin_objects.append(class_object)
\r
144 def make_command_class(*bases):
\r
145 #create metaclass with plugins and plotmanipulators
\r
146 return type(HookeFrame)("HookeFramePlugged", bases + (HookeFrame,), {})
\r
147 frame = make_command_class(*plugin_objects)(parent=None, id=wx.ID_ANY, title='Hooke', pos=window_position, size=window_size)
\r
149 self.SetTopWindow(frame)
\r
157 class HookeFrame(wx.Frame):
\r
159 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
160 #call parent constructor
\r
161 wx.Frame.__init__(self, parent, id, title, pos, size, style)
\r
162 self.config = config
\r
163 self.CreateApplicationIcon()
\r
164 #self.configs contains: {the name of the Commands file: corresponding ConfigObj}
\r
166 #self.displayed_plot holds the currently displayed plot
\r
167 self.displayed_plot = None
\r
168 #self.playlists contains: {the name of the playlist: [playlist, tabIndex, plotID]}
\r
169 self.playlists = {}
\r
170 #list of all plotmanipulators
\r
171 self.plotmanipulators = []
\r
172 #self.plugins contains: {the name of the plugin: [caption, function]}
\r
174 #self.results_str contains the type of results we want to display
\r
175 self.results_str = 'wlc'
\r
177 #tell FrameManager to manage this frame
\r
178 self._mgr = aui.AuiManager()
\r
179 self._mgr.SetManagedWindow(self)
\r
180 #set the gradient style
\r
181 self._mgr.GetArtProvider().SetMetric(aui.AUI_DOCKART_GRADIENT_TYPE, aui.AUI_GRADIENT_NONE)
\r
182 #set transparent drag
\r
183 self._mgr.SetFlags(self._mgr.GetFlags() ^ aui.AUI_MGR_TRANSPARENT_DRAG)
\r
185 # set up default notebook style
\r
186 self._notebook_style = aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER
\r
187 self._notebook_theme = 0
\r
189 #holds the perspectives: {name, perspective_str}
\r
190 self._perspectives = {}
\r
192 # min size for the frame itself isn't completely done.
\r
193 # see the end up FrameManager::Update() for the test
\r
194 # code. For now, just hard code a frame minimum size
\r
195 self.SetMinSize(wx.Size(500, 500))
\r
196 #define the list of active drivers
\r
198 for driver in self.config['drivers']:
\r
199 if self.config['drivers'][driver]:
\r
200 #get the corresponding filename and path
\r
201 filename = ''.join([driver, '.py'])
\r
202 path = lh.get_file_path(filename, ['drivers'])
\r
203 #the driver is active for driver[1] == 1
\r
204 if os.path.isfile(path):
\r
205 #driver files are located in the 'drivers' subfolder
\r
206 driver_name = ''.join(['drivers.', driver])
\r
207 __import__(driver_name)
\r
208 class_file = getattr(drivers, driver)
\r
209 for command in dir(class_file):
\r
210 if command.endswith('Driver'):
\r
211 self.drivers.append(getattr(class_file, command))
\r
212 #import all active plugins and plotmanips
\r
213 #add 'core.ini' to self.configs (this is not a plugin and thus must be imported separately)
\r
214 ini_path = lh.get_file_path('core.ini', ['plugins'])
\r
215 plugin_config = ConfigObj(ini_path)
\r
216 #self.config.merge(plugin_config)
\r
217 self.configs['core'] = plugin_config
\r
218 #existing_commands contains: {command: plugin}
\r
219 existing_commands = {}
\r
220 #make sure we execute _plug_init() for every command line plugin we import
\r
221 for plugin in self.config['plugins']:
\r
222 if self.config['plugins'][plugin]:
\r
223 filename = ''.join([plugin, '.py'])
\r
224 path = lh.get_file_path(filename, ['plugins'])
\r
225 if os.path.isfile(path):
\r
226 #get the corresponding filename and path
\r
227 plugin_name = ''.join(['plugins.', plugin])
\r
230 module = __import__(plugin_name)
\r
231 #prepare the ini file for inclusion
\r
232 ini_path = path.replace('.py', '.ini')
\r
234 plugin_config = ConfigObj(ini_path)
\r
235 #self.config.merge(plugin_config)
\r
236 self.configs[plugin] = plugin_config
\r
238 commands = eval('dir(module.' + plugin+ '.' + plugin + 'Commands)')
\r
239 #keep only commands (ie names that start with 'do_')
\r
240 commands = [command for command in commands if command.startswith('do_')]
\r
242 for command in commands:
\r
243 if existing_commands.has_key(command):
\r
244 message_str = 'Adding "' + command + '" in plugin "' + plugin + '".\n\n'
\r
245 message_str += '"' + command + '" already exists in "' + str(existing_commands[command]) + '".\n\n'
\r
246 message_str += 'Only "' + command + '" in "' + str(existing_commands[command]) + '" will work.\n\n'
\r
247 message_str += 'Please rename one of the commands in the source code and restart Hooke or disable one of the plugins.'
\r
248 dialog = wx.MessageDialog(self, message_str, 'Warning', wx.OK|wx.ICON_WARNING|wx.CENTER)
\r
251 existing_commands[command] = plugin
\r
252 self.plugins[plugin] = commands
\r
254 #initialize the plugin
\r
255 eval('module.' + plugin+ '.' + plugin + 'Commands._plug_init(self)')
\r
256 except AttributeError:
\r
258 except ImportError:
\r
260 #add commands from hooke.py i.e. 'core' commands
\r
261 commands = dir(HookeFrame)
\r
262 commands = [command for command in commands if command.startswith('do_')]
\r
264 self.plugins['core'] = commands
\r
265 #create panels here
\r
266 self.panelAssistant = self.CreatePanelAssistant()
\r
267 self.panelCommands = self.CreatePanelCommands()
\r
268 self.panelFolders = self.CreatePanelFolders()
\r
269 self.panelPlaylists = self.CreatePanelPlaylists()
\r
270 self.panelProperties = self.CreatePanelProperties()
\r
271 self.panelNote = self.CreatePanelNote()
\r
272 self.panelOutput = self.CreatePanelOutput()
\r
273 self.panelResults = self.CreatePanelResults()
\r
274 self.plotNotebook = self.CreateNotebook()
\r
277 self._mgr.AddPane(self.panelFolders, aui.AuiPaneInfo().Name('Folders').Caption('Folders').Left().CloseButton(True).MaximizeButton(False))
\r
278 self._mgr.AddPane(self.panelPlaylists, aui.AuiPaneInfo().Name('Playlists').Caption('Playlists').Left().CloseButton(True).MaximizeButton(False))
\r
279 self._mgr.AddPane(self.panelNote, aui.AuiPaneInfo().Name('Note').Caption('Note').Left().CloseButton(True).MaximizeButton(False))
\r
280 self._mgr.AddPane(self.plotNotebook, aui.AuiPaneInfo().Name('Plots').CenterPane().PaneBorder(False))
\r
281 self._mgr.AddPane(self.panelCommands, aui.AuiPaneInfo().Name('Commands').Caption('Settings and commands').Right().CloseButton(True).MaximizeButton(False))
\r
282 self._mgr.AddPane(self.panelProperties, aui.AuiPaneInfo().Name('Properties').Caption('Properties').Right().CloseButton(True).MaximizeButton(False))
\r
283 self._mgr.AddPane(self.panelAssistant, aui.AuiPaneInfo().Name('Assistant').Caption('Assistant').Right().CloseButton(True).MaximizeButton(False))
\r
284 self._mgr.AddPane(self.panelOutput, aui.AuiPaneInfo().Name('Output').Caption('Output').Bottom().CloseButton(True).MaximizeButton(False))
\r
285 self._mgr.AddPane(self.panelResults, aui.AuiPaneInfo().Name('Results').Caption('Results').Bottom().CloseButton(True).MaximizeButton(False))
\r
286 #self._mgr.AddPane(self.textCtrlCommandLine, aui.AuiPaneInfo().Name('CommandLine').CaptionVisible(False).Fixed().Bottom().Layer(2).CloseButton(False).MaximizeButton(False))
\r
287 #self._mgr.AddPane(panelBottom, aui.AuiPaneInfo().Name("panelCommandLine").Bottom().Position(1).CloseButton(False).MaximizeButton(False))
\r
289 # add the toolbars to the manager
\r
290 #self.toolbar=self.CreateToolBar()
\r
291 self.toolbarNavigation=self.CreateToolBarNavigation()
\r
292 #self._mgr.AddPane(self.toolbar, aui.AuiPaneInfo().Name('toolbar').Caption('Toolbar').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
\r
293 self._mgr.AddPane(self.toolbarNavigation, aui.AuiPaneInfo().Name('toolbarNavigation').Caption('Navigation').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))
\r
294 # "commit" all changes made to FrameManager
\r
296 #create the menubar after the panes so that the default perspective
\r
297 #is created with all panes open
\r
298 self.CreateMenuBar()
\r
299 self.statusbar = self.CreateStatusbar()
\r
302 name = self.config['perspectives']['active']
\r
303 menu_item = self.GetPerspectiveMenuItem(name)
\r
304 if menu_item is not None:
\r
305 self.OnRestorePerspective(menu_item)
\r
306 #TODO: config setting to remember playlists from last session
\r
307 self.playlists = self.panelPlaylists.Playlists
\r
308 #initialize the commands tree
\r
309 self.panelCommands.Initialize(self.plugins)
\r
310 for command in dir(self):
\r
311 if command.startswith('plotmanip_'):
\r
312 self.plotmanipulators.append(lib.plotmanipulator.Plotmanipulator(method=getattr(self, command), command=command))
\r
314 #load default list, if possible
\r
315 self.do_loadlist(self.GetStringFromConfig('core', 'preferences', 'playlist'))
\r
317 def _BindEvents(self):
\r
318 #TODO: figure out if we can use the eventManager for menu ranges
\r
319 #and events of 'self' without raising an assertion fail error
\r
320 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
\r
321 self.Bind(wx.EVT_SIZE, self.OnSize)
\r
322 self.Bind(wx.EVT_CLOSE, self.OnClose)
\r
323 # Show How To Use The Closing Panes Event
\r
324 self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPaneClose)
\r
325 self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnNotebookPageClose)
\r
327 evtmgr.eventManager.Register(self.OnAbout, wx.EVT_MENU, win=self, id=wx.ID_ABOUT)
\r
328 evtmgr.eventManager.Register(self.OnClose, wx.EVT_MENU, win=self, id=wx.ID_EXIT)
\r
330 self.Bind(wx.EVT_MENU_RANGE, self.OnView, id=ID_ViewAssistant, id2=ID_ViewResults)
\r
332 self.Bind(wx.EVT_MENU, self.OnDeletePerspective, id=ID_DeletePerspective)
\r
333 self.Bind(wx.EVT_MENU, self.OnSavePerspective, id=ID_SavePerspective)
\r
334 self.Bind(wx.EVT_MENU_RANGE, self.OnRestorePerspective, id=ID_FirstPerspective, id2=ID_FirstPerspective+1000)
\r
336 evtmgr.eventManager.Register(self.OnNext, wx.EVT_TOOL, win=self, id=ID_Next)
\r
337 evtmgr.eventManager.Register(self.OnPrevious, wx.EVT_TOOL, win=self, id=ID_Previous)
\r
338 #self.Bind(.EVT_AUITOOLBAR_TOOL_DROPDOWN, self.OnDropDownToolbarItem, id=ID_DropDownToolbarItem)
\r
340 treeCtrl = self.panelFolders.GetTreeCtrl()
\r
341 #tree.Bind(wx.EVT_LEFT_UP, self.OnDirCtrl1LeftUp)
\r
342 #tree.Bind(wx.EVT_LEFT_DOWN, self.OnGenericDirCtrl1LeftDown)
\r
343 treeCtrl.Bind(wx.EVT_LEFT_DCLICK, self.OnDirCtrlLeftDclick)
\r
345 self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DOWN, self.OnPlaylistsLeftDown)
\r
346 self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DCLICK, self.OnPlaylistsLeftDclick)
\r
348 evtmgr.eventManager.Register(self.OnExecute, wx.EVT_BUTTON, self.panelCommands.ExecuteButton)
\r
349 evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self.panelCommands.CommandsTree)
\r
350 evtmgr.eventManager.Register(self.OnTreeCtrlItemActivated, wx.EVT_TREE_ITEM_ACTIVATED, self.panelCommands.CommandsTree)
\r
351 evtmgr.eventManager.Register(self.OnUpdateNote, wx.EVT_BUTTON, self.panelNote.UpdateButton)
\r
353 self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged)
\r
355 self.panelResults.results_list.OnCheckItem = self.OnResultsCheck
\r
357 def _GetActiveFileIndex(self):
\r
358 lib.playlist.Playlist = self.GetActivePlaylist()
\r
359 #get the selected item from the tree
\r
360 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
361 #test if a playlist or a curve was double-clicked
\r
362 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
366 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
367 while selected_item.IsOk():
\r
369 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
372 def _GetPlaylistTab(self, name):
\r
373 for index, page in enumerate(self.plotNotebook._tabs._pages):
\r
374 if page.caption == name:
\r
378 def _GetUniquePlaylistName(self, name):
\r
379 playlist_name = name
\r
381 while playlist_name in self.playlists:
\r
382 playlist_name = ''.join([name, str(count)])
\r
384 return playlist_name
\r
386 def _RestorePerspective(self, name):
\r
387 self._mgr.LoadPerspective(self._perspectives[name])
\r
388 self.config['perspectives']['active'] = name
\r
390 all_panes = self._mgr.GetAllPanes()
\r
391 for pane in all_panes:
\r
392 if not pane.name.startswith('toolbar'):
\r
393 if pane.name == 'Assistant':
\r
394 self.MenuBar.FindItemById(ID_ViewAssistant).Check(pane.window.IsShown())
\r
395 if pane.name == 'Folders':
\r
396 self.MenuBar.FindItemById(ID_ViewFolders).Check(pane.window.IsShown())
\r
397 if pane.name == 'Playlists':
\r
398 self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown())
\r
399 if pane.name == 'Commands':
\r
400 self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown())
\r
401 if pane.name == 'Note':
\r
402 self.MenuBar.FindItemById(ID_ViewNote).Check(pane.window.IsShown())
\r
403 if pane.name == 'Properties':
\r
404 self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown())
\r
405 if pane.name == 'Output':
\r
406 self.MenuBar.FindItemById(ID_ViewOutput).Check(pane.window.IsShown())
\r
407 if pane.name == 'Results':
\r
408 self.MenuBar.FindItemById(ID_ViewResults).Check(pane.window.IsShown())
\r
410 def _SavePerspectiveToFile(self, name, perspective):
\r
411 filename = ''.join([name, '.txt'])
\r
412 filename = lh.get_file_path(filename, ['perspectives'])
\r
413 perspectivesFile = open(filename, 'w')
\r
414 perspectivesFile.write(perspective)
\r
415 perspectivesFile.close()
\r
417 def _UnbindEvents(self):
\r
419 evtmgr.eventManager.DeregisterListener(self.OnAbout)
\r
420 evtmgr.eventManager.DeregisterListener(self.OnClose)
\r
422 evtmgr.eventManager.DeregisterListener(self.OnNext)
\r
423 evtmgr.eventManager.DeregisterListener(self.OnPrevious)
\r
425 evtmgr.eventManager.DeregisterListener(self.OnExecute)
\r
426 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlCommandsSelectionChanged)
\r
427 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlItemActivated)
\r
428 evtmgr.eventManager.DeregisterListener(self.OnUpdateNote)
\r
430 def AddPlaylist(self, playlist=None, name='Untitled'):
\r
431 if playlist and playlist.count > 0:
\r
432 playlist.name = self._GetUniquePlaylistName(name)
\r
434 self.AddToPlaylists(playlist)
\r
436 def AddPlaylistFromFiles(self, files=[], name='Untitled'):
\r
438 playlist = lib.playlist.Playlist(self, self.drivers)
\r
440 playlist.add_curve(item)
\r
441 if playlist.count > 0:
\r
442 playlist.name = self._GetUniquePlaylistName(name)
\r
444 self.AddTayliss(playlist)
\r
446 def AddToPlaylists(self, playlist):
\r
447 if playlist.count > 0:
\r
448 #setup the playlist in the Playlist tree
\r
449 tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
\r
450 playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)
\r
451 #add all files to the Playlist tree
\r
453 hide_curve_extension = self.GetBoolFromConfig('core', 'preferences', 'hide_curve_extension')
\r
454 for index, file_to_add in enumerate(playlist.files):
\r
455 #optionally remove the extension from the name of the curve
\r
456 if hide_curve_extension:
\r
457 file_to_add.name = lh.remove_extension(file_to_add.name)
\r
458 file_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, file_to_add.name, 1)
\r
459 if index == playlist.index:
\r
460 self.panelPlaylists.PlaylistsTree.SelectItem(file_ID)
\r
462 #create the plot tab and add playlist to the dictionary
\r
463 plotPanel = panels.plot.PlotPanel(self, ID_FirstPlot + len(self.playlists))
\r
464 notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True)
\r
465 #tab_index = self.plotNotebook.GetSelection()
\r
466 playlist.figure = plotPanel.get_figure()
\r
467 self.playlists[playlist.name] = playlist
\r
468 #self.playlists[playlist.name] = [playlist, figure]
\r
469 self.panelPlaylists.PlaylistsTree.Expand(playlist_root)
\r
470 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
474 def AppendToOutput(self, text):
\r
475 self.panelOutput.AppendText(''.join([text, '\n']))
\r
477 def AppliesPlotmanipulator(self, name):
\r
479 Returns True if the plotmanipulator 'name' is applied, False otherwise
\r
480 name does not contain 'plotmanip_', just the name of the plotmanipulator (e.g. 'flatten')
\r
482 return self.GetBoolFromConfig('core', 'plotmanipulators', name)
\r
484 def ApplyPlotmanipulators(self, plot, plot_file):
\r
486 Apply all active plotmanipulators.
\r
488 if plot is not None and plot_file is not None:
\r
489 manipulated_plot = copy.deepcopy(plot)
\r
490 for plotmanipulator in self.plotmanipulators:
\r
491 if self.GetBoolFromConfig('core', 'plotmanipulators', plotmanipulator.name):
\r
492 manipulated_plot = plotmanipulator.method(manipulated_plot, plot_file)
\r
493 return manipulated_plot
\r
495 def CreateApplicationIcon(self):
\r
496 iconFile = 'resources' + os.sep + 'microscope.ico'
\r
497 icon = wx.Icon(iconFile, wx.BITMAP_TYPE_ICO)
\r
500 def CreateCommandLine(self):
\r
501 return wx.TextCtrl(self, -1, '', style=wx.NO_BORDER|wx.EXPAND)
\r
503 def CreatePanelAssistant(self):
\r
504 panel = wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
\r
505 panel.SetEditable(False)
\r
508 def CreatePanelCommands(self):
\r
509 return panels.commands.Commands(self)
\r
511 def CreatePanelFolders(self):
\r
513 filters = self.config['folders']['filters']
\r
514 index = self.config['folders'].as_int('filterindex')
\r
515 #set initial directory
\r
516 folder = self.GetStringFromConfig('core', 'preferences', 'workdir')
\r
517 return wx.GenericDirCtrl(self, -1, dir=folder, size=(200, 250), style=wx.DIRCTRL_SHOW_FILTERS, filter=filters, defaultFilter=index)
\r
519 def CreatePanelNote(self):
\r
520 return panels.note.Note(self)
\r
522 def CreatePanelOutput(self):
\r
523 return wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)
\r
525 def CreatePanelPlaylists(self):
\r
526 return panels.playlist.Playlists(self)
\r
528 def CreatePanelProperties(self):
\r
529 return panels.propertyeditor.PropertyEditor(self)
\r
531 def CreatePanelResults(self):
\r
532 return panels.results.Results(self)
\r
534 def CreatePanelWelcome(self):
\r
535 #TODO: move into panels.welcome
\r
536 ctrl = wx.html.HtmlWindow(self, -1, wx.DefaultPosition, wx.Size(400, 300))
\r
537 introStr = '<h1>Welcome to Hooke</h1>' + \
\r
538 '<h3>Features</h3>' + \
\r
540 '<li>View, annotate, measure force files</li>' + \
\r
541 '<li>Worm-like chain fit of force peaks</li>' + \
\r
542 '<li>Automatic convolution-based filtering of empty files</li>' + \
\r
543 '<li>Automatic fit and measurement of multiple force peaks</li>' + \
\r
544 '<li>Handles force-clamp force experiments (experimental)</li>' + \
\r
545 '<li>It is extensible by users by means of plugins and drivers</li>' + \
\r
547 '<p>See the <a href="http://code.google.com/p/hooke/wiki/DocumentationIndex">DocumentationIndex</a> for more information</p>'
\r
548 ctrl.SetPage(introStr)
\r
551 def CreateMenuBar(self):
\r
552 menu_bar = wx.MenuBar()
\r
553 self.SetMenuBar(menu_bar)
\r
555 file_menu = wx.Menu()
\r
556 file_menu.Append(wx.ID_EXIT, 'Exit\tCtrl-Q')
\r
557 # edit_menu.AppendSeparator();
\r
558 # edit_menu.Append(ID_Config, 'Preferences')
\r
560 view_menu = wx.Menu()
\r
561 view_menu.AppendCheckItem(ID_ViewFolders, 'Folders\tF5')
\r
562 view_menu.AppendCheckItem(ID_ViewPlaylists, 'Playlists\tF6')
\r
563 view_menu.AppendCheckItem(ID_ViewCommands, 'Commands\tF7')
\r
564 view_menu.AppendCheckItem(ID_ViewProperties, 'Properties\tF8')
\r
565 view_menu.AppendCheckItem(ID_ViewAssistant, 'Assistant\tF9')
\r
566 view_menu.AppendCheckItem(ID_ViewResults, 'Results\tF10')
\r
567 view_menu.AppendCheckItem(ID_ViewOutput, 'Output\tF11')
\r
568 view_menu.AppendCheckItem(ID_ViewNote, 'Note\tF12')
\r
570 perspectives_menu = wx.Menu()
\r
573 help_menu = wx.Menu()
\r
574 help_menu.Append(wx.ID_ABOUT, 'About Hooke')
\r
575 #put it all together
\r
576 menu_bar.Append(file_menu, 'File')
\r
577 menu_bar.Append(view_menu, 'View')
\r
578 menu_bar.Append(perspectives_menu, "Perspectives")
\r
579 self.UpdatePerspectivesMenu()
\r
580 menu_bar.Append(help_menu, 'Help')
\r
582 def CreateNotebook(self):
\r
583 # create the notebook off-window to avoid flicker
\r
584 client_size = self.GetClientSize()
\r
585 ctrl = aui.AuiNotebook(self, -1, wx.Point(client_size.x, client_size.y), wx.Size(430, 200), self._notebook_style)
\r
586 arts = [aui.AuiDefaultTabArt, aui.AuiSimpleTabArt, aui.VC71TabArt, aui.FF2TabArt, aui.VC8TabArt, aui.ChromeTabArt]
\r
587 art = arts[self._notebook_theme]()
\r
588 ctrl.SetArtProvider(art)
\r
589 #uncomment if we find a nice icon
\r
590 #page_bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))
\r
591 ctrl.AddPage(self.CreatePanelWelcome(), "Welcome", False)
\r
594 def CreateStatusbar(self):
\r
595 statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
\r
596 statusbar.SetStatusWidths([-2, -3])
\r
597 statusbar.SetStatusText('Ready', 0)
\r
598 welcomeString=u'Welcome to Hooke (version '+__version__+', '+__release_name__+')!'
\r
599 statusbar.SetStatusText(welcomeString, 1)
\r
602 def CreateToolBarNavigation(self):
\r
603 toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)
\r
604 toolbar.SetToolBitmapSize(wx.Size(16,16))
\r
605 toolbar_bmpBack = wx.ArtProvider_GetBitmap(wx.ART_GO_BACK, wx.ART_OTHER, wx.Size(16, 16))
\r
606 toolbar_bmpForward = wx.ArtProvider_GetBitmap(wx.ART_GO_FORWARD, wx.ART_OTHER, wx.Size(16, 16))
\r
607 toolbar.AddLabelTool(ID_Previous, 'Previous', toolbar_bmpBack, shortHelp='Previous curve')
\r
608 toolbar.AddLabelTool(ID_Next, 'Next', toolbar_bmpForward, shortHelp='Next curve')
\r
612 def DeleteFromPlaylists(self, name):
\r
613 if name in self.playlists:
\r
614 del self.playlists[name]
\r
615 tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()
\r
616 item, cookie = self.panelPlaylists.PlaylistsTree.GetFirstChild(tree_root)
\r
618 playlist_name = self.panelPlaylists.PlaylistsTree.GetItemText(item)
\r
619 if playlist_name == name:
\r
621 self.panelPlaylists.PlaylistsTree.Delete(item)
\r
624 item = self.panelPlaylists.PlaylistsTree.GetNextSibling(item)
\r
626 def GetActiveFigure(self):
\r
627 playlist_name = self.GetActivePlaylistName()
\r
628 figure = self.playlists[playlist_name].figure
\r
629 if figure is not None:
\r
633 def GetActiveFile(self):
\r
634 playlist = self.GetActivePlaylist()
\r
635 if playlist is not None:
\r
636 return playlist.get_active_file()
\r
639 def GetActivePlaylist(self):
\r
640 playlist_name = self.GetActivePlaylistName()
\r
641 if playlist_name in self.playlists:
\r
642 return self.playlists[playlist_name]
\r
645 def GetActivePlaylistName(self):
\r
646 #get the selected item from the tree
\r
647 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
648 #test if a playlist or a curve was double-clicked
\r
649 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
650 playlist_item = selected_item
\r
652 #get the name of the playlist
\r
653 playlist_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
654 #now we have a playlist
\r
655 return self.panelPlaylists.PlaylistsTree.GetItemText(playlist_item)
\r
657 def GetActivePlot(self):
\r
658 playlist = self.GetActivePlaylist()
\r
659 if playlist is not None:
\r
660 return playlist.get_active_file().plot
\r
663 def GetDisplayedPlot(self):
\r
664 plot = copy.deepcopy(self.displayed_plot)
\r
666 #plot.curves = copy.deepcopy(plot.curves)
\r
669 def GetDisplayedPlotCorrected(self):
\r
670 plot = copy.deepcopy(self.displayed_plot)
\r
672 plot.curves = copy.deepcopy(plot.corrected_curves)
\r
675 def GetDisplayedPlotRaw(self):
\r
676 plot = copy.deepcopy(self.displayed_plot)
\r
678 plot.curves = copy.deepcopy(plot.raw_curves)
\r
681 def GetDockArt(self):
\r
682 return self._mgr.GetArtProvider()
\r
684 def GetBoolFromConfig(self, *args):
\r
689 elif len(args) == 3:
\r
693 if self.configs.has_key(plugin):
\r
694 config = self.configs[plugin]
\r
695 return config[section][key].as_bool('value')
\r
698 def GetColorFromConfig(self, *args):
\r
703 elif len(args) == 3:
\r
707 if self.configs.has_key(plugin):
\r
708 config = self.configs[plugin]
\r
709 color_tuple = eval(config[section][key]['value'])
\r
710 color = [value / 255.0 for value in color_tuple]
\r
714 def GetFloatFromConfig(self, *args):
\r
719 elif len(args) == 3:
\r
723 if self.configs.has_key(plugin):
\r
724 config = self.configs[plugin]
\r
725 return config[section][key].as_float('value')
\r
728 def GetIntFromConfig(self, *args):
\r
733 elif len(args) == 3:
\r
737 if self.configs.has_key(plugin):
\r
738 config = self.configs[plugin]
\r
739 return config[section][key].as_int('value')
\r
742 def GetStringFromConfig(self, *args):
\r
747 elif len(args) == 3:
\r
751 if self.configs.has_key(plugin):
\r
752 config = self.configs[plugin]
\r
753 return config[section][key]['value']
\r
756 def GetPlotmanipulator(self, name):
\r
758 Returns a plot manipulator function from its name
\r
760 for plotmanipulator in self.plotmanipulators:
\r
761 if plotmanipulator.name == name:
\r
762 return plotmanipulator
\r
765 def GetPerspectiveMenuItem(self, name):
\r
766 if self._perspectives.has_key(name):
\r
767 perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
768 perspectives_list.sort()
\r
769 index = perspectives_list.index(name)
\r
770 perspective_Id = ID_FirstPerspective + index
\r
771 menu_item = self.MenuBar.FindItemById(perspective_Id)
\r
776 def HasPlotmanipulator(self, name):
\r
778 returns True if the plotmanipulator 'name' is loaded, False otherwise
\r
780 for plotmanipulator in self.plotmanipulators:
\r
781 if plotmanipulator.command == name:
\r
785 def OnAbout(self, event):
\r
786 message = 'Hooke\n\n'+\
\r
787 'A free, open source data analysis platform\n\n'+\
\r
788 'Copyright 2006-2008 by Massimo Sandal\n'+\
\r
789 'Copyright 2010 by Dr. Rolf Schmidt\n\n'+\
\r
790 'Hooke is released under the GNU General Public License version 2.'
\r
791 dialog = wx.MessageDialog(self, message, 'About Hooke', wx.OK | wx.ICON_INFORMATION)
\r
795 def OnClose(self, event):
\r
797 self.config['main']['height'] = str(self.GetSize().GetHeight())
\r
798 self.config['main']['left'] = str(self.GetPosition()[0])
\r
799 self.config['main']['top'] = str(self.GetPosition()[1])
\r
800 self.config['main']['width'] = str(self.GetSize().GetWidth())
\r
801 #save the configuration file to 'config/hooke.ini'
\r
802 self.config.write()
\r
803 #save all plugin config files
\r
804 for config in self.configs:
\r
805 plugin_config = self.configs[config]
\r
806 plugin_config.write()
\r
807 self._UnbindEvents()
\r
812 def OnDeletePerspective(self, event):
\r
813 dialog = panels.perspectives.Perspectives(self, -1, 'Delete perspective(s)')
\r
814 dialog.CenterOnScreen()
\r
817 self.UpdatePerspectivesMenu()
\r
818 #unfortunately, there is a bug in wxWidgets (Ticket #3258) that
\r
819 #makes the radio item indicator in the menu disappear
\r
820 #the code should be fine once this issue is fixed
\r
822 def OnDirCtrlLeftDclick(self, event):
\r
823 file_path = self.panelFolders.GetPath()
\r
824 if os.path.isfile(file_path):
\r
825 if file_path.endswith('.hkp'):
\r
826 self.do_loadlist(file_path)
\r
829 def OnEraseBackground(self, event):
\r
832 def OnExecute(self, event):
\r
833 item = self.panelCommands.CommandsTree.GetSelection()
\r
835 if not self.panelCommands.CommandsTree.ItemHasChildren(item):
\r
836 item_text = self.panelCommands.CommandsTree.GetItemText(item)
\r
837 command = ''.join(['self.do_', item_text, '()'])
\r
838 #self.AppendToOutput(command + '\n')
\r
841 def OnExit(self, event):
\r
844 def OnNext(self, event):
\r
847 Go to the next curve in the playlist.
\r
848 If we are at the last curve, we come back to the first.
\r
852 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
853 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
854 #GetFirstChild returns a tuple
\r
855 #we only need the first element
\r
856 next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(selected_item)[0]
\r
858 next_item = self.panelPlaylists.PlaylistsTree.GetNextSibling(selected_item)
\r
859 if not next_item.IsOk():
\r
860 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
861 #GetFirstChild returns a tuple
\r
862 #we only need the first element
\r
863 next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(parent_item)[0]
\r
864 self.panelPlaylists.PlaylistsTree.SelectItem(next_item, True)
\r
865 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
866 playlist = self.GetActivePlaylist()
\r
867 if playlist.count > 1:
\r
869 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
873 def OnNotebookPageClose(self, event):
\r
874 ctrl = event.GetEventObject()
\r
875 playlist_name = ctrl.GetPageText(ctrl._curpage)
\r
876 self.DeleteFromPlaylists(playlist_name)
\r
878 def OnPaneClose(self, event):
\r
881 def OnPlaylistsLeftDclick(self, event):
\r
882 if self.panelPlaylists.PlaylistsTree.Count > 0:
\r
883 playlist_name = self.GetActivePlaylistName()
\r
884 #if that playlist already exists
\r
885 #we check if it is the active playlist (ie selected in panelPlaylists)
\r
886 #and switch to it if necessary
\r
887 if playlist_name in self.playlists:
\r
888 index = self.plotNotebook.GetSelection()
\r
889 current_playlist = self.plotNotebook.GetPageText(index)
\r
890 if current_playlist != playlist_name:
\r
891 index = self._GetPlaylistTab(playlist_name)
\r
892 self.plotNotebook.SetSelection(index)
\r
893 #if a curve was double-clicked
\r
894 item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
895 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
\r
896 index = self._GetActiveFileIndex()
\r
900 playlist = self.GetActivePlaylist()
\r
901 playlist.index = index
\r
902 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
905 #if you uncomment the following line, the tree will collapse/expand as well
\r
908 def OnPlaylistsLeftDown(self, event):
\r
909 hit_item, hit_flags = self.panelPlaylists.PlaylistsTree.HitTest(event.GetPosition())
\r
910 if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:
\r
911 self.panelPlaylists.PlaylistsTree.SelectItem(hit_item)
\r
912 playlist_name = self.GetActivePlaylistName()
\r
913 playlist = self.GetActivePlaylist()
\r
914 #if a curve was clicked
\r
915 item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
916 if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):
\r
917 index = self._GetActiveFileIndex()
\r
919 playlist.index = index
\r
920 self.playlists[playlist_name] = playlist
\r
923 def OnPrevious(self, event):
\r
926 Go to the previous curve in the playlist.
\r
927 If we are at the first curve, we jump to the last.
\r
929 Syntax: previous, p
\r
931 #playlist = self.playlists[self.GetActivePlaylistName()][0]
\r
932 #select the previous curve and tell the user if we wrapped around
\r
933 #self.AppendToOutput(playlist.previous())
\r
934 selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()
\r
935 if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):
\r
936 previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(selected_item)
\r
938 previous_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)
\r
939 if not previous_item.IsOk():
\r
940 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)
\r
941 previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(parent_item)
\r
942 self.panelPlaylists.PlaylistsTree.SelectItem(previous_item, True)
\r
943 playlist = self.GetActivePlaylist()
\r
944 if playlist.count > 1:
\r
945 playlist.previous()
\r
946 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
950 def OnPropGridChanged (self, event):
\r
951 prop = event.GetProperty()
\r
953 item_section = self.panelProperties.SelectedTreeItem
\r
954 item_plugin = self.panelCommands.CommandsTree.GetItemParent(item_section)
\r
955 plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
\r
956 config = self.configs[plugin]
\r
957 property_section = self.panelCommands.CommandsTree.GetItemText(item_section)
\r
958 property_key = prop.GetName()
\r
959 property_value = prop.GetDisplayedString()
\r
961 config[property_section][property_key]['value'] = property_value
\r
963 def OnRestorePerspective(self, event):
\r
964 name = self.MenuBar.FindItemById(event.GetId()).GetLabel()
\r
965 self._RestorePerspective(name)
\r
967 def OnResultsCheck(self, index, flag):
\r
968 results = self.GetActivePlot().results
\r
969 if results.has_key(self.results_str):
\r
970 results[self.results_str].results[index].visible = flag
\r
971 results[self.results_str].update()
\r
974 def OnSavePerspective(self, event):
\r
976 def nameExists(name):
\r
977 menu_position = self.MenuBar.FindMenu('Perspectives')
\r
978 menu = self.MenuBar.GetMenu(menu_position)
\r
979 for item in menu.GetMenuItems():
\r
980 if item.GetText() == name:
\r
986 dialog = wx.TextEntryDialog(self, 'Enter a name for the new perspective:', 'Save perspective')
\r
987 dialog.SetValue('New perspective')
\r
988 if dialog.ShowModal() != wx.ID_OK:
\r
991 name = dialog.GetValue()
\r
993 if nameExists(name):
\r
994 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
995 if dialogConfirm.ShowModal() == wx.ID_YES:
\r
1000 perspective = self._mgr.SavePerspective()
\r
1001 self._SavePerspectiveToFile(name, perspective)
\r
1002 self.config['perspectives']['active'] = name
\r
1003 self.UpdatePerspectivesMenu()
\r
1004 # if nameExists(name):
\r
1005 # #check the corresponding menu item
\r
1006 # menu_item = self.GetPerspectiveMenuItem(name)
\r
1007 # #replace the perspectiveStr in _pespectives
\r
1008 # self._perspectives[name] = perspective
\r
1010 # #because we deal with radio items, we need to do some extra work
\r
1011 # #delete all menu items from the perspectives menu
\r
1012 # for item in self._perspectives_menu.GetMenuItems():
\r
1013 # self._perspectives_menu.DeleteItem(item)
\r
1014 # #recreate the perspectives menu
\r
1015 # self._perspectives_menu.Append(ID_SavePerspective, 'Save Perspective')
\r
1016 # self._perspectives_menu.Append(ID_DeletePerspective, 'Delete Perspective')
\r
1017 # self._perspectives_menu.AppendSeparator()
\r
1018 # #convert the perspectives dictionary into a list
\r
1019 # # the list contains:
\r
1020 # #[0]: name of the perspective
\r
1021 # #[1]: perspective
\r
1022 # perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
1023 # perspectives_list.append(name)
\r
1024 # perspectives_list.sort()
\r
1025 # #add all previous perspectives
\r
1026 # for index, item in enumerate(perspectives_list):
\r
1027 # menu_item = self._perspectives_menu.AppendRadioItem(ID_FirstPerspective + index, item)
\r
1028 # if item == name:
\r
1029 # menu_item.Check()
\r
1030 # #add the new perspective to _perspectives
\r
1031 # self._perspectives[name] = perspective
\r
1033 def OnSize(self, event):
\r
1036 def OnTreeCtrlCommandsSelectionChanged(self, event):
\r
1037 selected_item = event.GetItem()
\r
1038 if selected_item is not None:
\r
1041 #deregister/register the listener to avoid infinite loop
\r
1042 evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlCommandsSelectionChanged)
\r
1043 self.panelCommands.CommandsTree.SelectItem(selected_item)
\r
1044 evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self.panelCommands.CommandsTree)
\r
1045 self.panelProperties.SelectedTreeItem = selected_item
\r
1046 #if a command was clicked
\r
1048 if not self.panelCommands.CommandsTree.ItemHasChildren(selected_item):
\r
1049 item_plugin = self.panelCommands.CommandsTree.GetItemParent(selected_item)
\r
1050 plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)
\r
1051 if self.configs.has_key(plugin):
\r
1052 #config = self.panelCommands.CommandsTree.GetPyData(item_plugin)
\r
1053 config = self.configs[plugin]
\r
1054 section = self.panelCommands.CommandsTree.GetItemText(selected_item)
\r
1055 #display docstring in help window
\r
1056 doc_string = eval('self.do_' + section + '.__doc__')
\r
1057 if section in config:
\r
1058 for option in config[section]:
\r
1059 properties.append([option, config[section][option]])
\r
1061 plugin = self.panelCommands.CommandsTree.GetItemText(selected_item)
\r
1062 if plugin != 'core':
\r
1063 doc_string = eval('plugins.' + plugin + '.' + plugin + 'Commands.__doc__')
\r
1065 doc_string = 'The module "core" contains Hooke core functionality'
\r
1066 if doc_string is not None:
\r
1067 self.panelAssistant.ChangeValue(doc_string)
\r
1069 self.panelAssistant.ChangeValue('')
\r
1070 panels.propertyeditor.PropertyEditor.Initialize(self.panelProperties, properties)
\r
1071 #save the currently selected command/plugin to the config file
\r
1072 self.config['command']['command'] = section
\r
1073 self.config['command']['plugin'] = plugin
\r
1075 def OnTreeCtrlItemActivated(self, event):
\r
1076 self.OnExecute(event)
\r
1078 def OnUpdateNote(self, event):
\r
1080 Saves the note to the active file.
\r
1082 active_file = self.GetActiveFile()
\r
1083 active_file.note = self.panelNote.Editor.GetValue()
\r
1085 def OnView(self, event):
\r
1086 menu_id = event.GetId()
\r
1087 menu_item = self.MenuBar.FindItemById(menu_id)
\r
1088 menu_label = menu_item.GetLabel()
\r
1090 pane = self._mgr.GetPane(menu_label)
\r
1091 pane.Show(not pane.IsShown())
\r
1092 #if we don't do the following, the Folders pane does not resize properly on hide/show
\r
1093 if pane.caption == 'Folders' and pane.IsShown() and pane.IsDocked():
\r
1094 #folders_size = pane.GetSize()
\r
1095 self.panelFolders.Fit()
\r
1096 self._mgr.Update()
\r
1098 def _clickize(self, xvector, yvector, index):
\r
1100 Returns a ClickedPoint() object from an index and vectors of x, y coordinates
\r
1102 point = lib.clickedpoint.ClickedPoint()
\r
1103 point.index = index
\r
1104 point.absolute_coords = xvector[index], yvector[index]
\r
1105 point.find_graph_coords(xvector, yvector)
\r
1108 def _delta(self, message='Click 2 points', whatset=lh.RETRACTION):
\r
1110 Calculates the difference between two clicked points
\r
1112 clicked_points = self._measure_N_points(N=2, message=message, whatset=whatset)
\r
1114 plot = self.GetDisplayedPlotCorrected()
\r
1115 curve = plot.curves[whatset]
\r
1117 delta = lib.delta.Delta()
\r
1118 delta.point1.x = clicked_points[0].graph_coords[0]
\r
1119 delta.point1.y = clicked_points[0].graph_coords[1]
\r
1120 delta.point2.x = clicked_points[1].graph_coords[0]
\r
1121 delta.point2.y = clicked_points[1].graph_coords[1]
\r
1122 delta.units.x = curve.units.x
\r
1123 delta.units.y = curve.units.y
\r
1127 def _measure_N_points(self, N, message='', whatset=lh.RETRACTION):
\r
1129 General helper function for N-points measurements
\r
1130 By default, measurements are done on the retraction
\r
1133 dialog = wx.MessageDialog(None, message, 'Info', wx.OK)
\r
1134 dialog.ShowModal()
\r
1136 figure = self.GetActiveFigure()
\r
1138 xvector = self.displayed_plot.curves[whatset].x
\r
1139 yvector = self.displayed_plot.curves[whatset].y
\r
1141 clicked_points = figure.ginput(N, timeout=-1, show_clicks=True)
\r
1144 for clicked_point in clicked_points:
\r
1145 point = lib.clickedpoint.ClickedPoint()
\r
1146 point.absolute_coords = clicked_point[0], clicked_point[1]
\r
1148 #TODO: make this optional?
\r
1149 #so far, the clicked point is taken, not the corresponding data point
\r
1150 point.find_graph_coords(xvector, yvector)
\r
1151 point.is_line_edge = True
\r
1152 point.is_marker = True
\r
1153 points.append(point)
\r
1156 def do_copylog(self):
\r
1158 Copies all files in the current playlist that have a note to the destination folder.
\r
1159 destination: select folder where you want the files to be copied
\r
1160 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
1162 playlist = self.GetActivePlaylist()
\r
1163 if playlist is not None:
\r
1164 destination = self.GetStringFromConfig('core', 'copylog', 'destination')
\r
1165 if not os.path.isdir(destination):
\r
1166 os.makedirs(destination)
\r
1167 for current_file in playlist.files:
\r
1168 if current_file.note:
\r
1169 shutil.copy(current_file.filename, destination)
\r
1170 if current_file.driver.filetype == 'mfp1d':
\r
1171 filename = current_file.filename.replace('deflection', 'LVDT', 1)
\r
1172 path, name = os.path.split(filename)
\r
1173 filename = os.path.join(path, 'lvdt', name)
\r
1174 use_LVDT_folder = self.GetBoolFromConfig('core', 'copylog', 'use_LVDT_folder')
\r
1175 if use_LVDT_folder:
\r
1176 destination = os.path.join(destination, 'LVDT')
\r
1177 shutil.copy(filename, destination)
\r
1179 def do_plotmanipulators(self):
\r
1181 Please select the plotmanipulators you would like to use
\r
1182 and define the order in which they will be applied to the data.
\r
1184 Click 'Execute' to apply your changes.
\r
1188 def do_preferences(self):
\r
1190 Please set general preferences for Hooke here.
\r
1191 hide_curve_extension: hides the extension of the force curve files.
\r
1192 not recommended for 'picoforce' files
\r
1196 def do_test(self):
\r
1198 Use this command for testing purposes. You find do_test in hooke.py.
\r
1202 def do_version(self):
\r
1206 Prints the current version and codename, plus library version. Useful for debugging.
\r
1208 self.AppendToOutput('Hooke ' + __version__ + ' (' + __codename__ + ')')
\r
1209 self.AppendToOutput('Released on: ' + __releasedate__)
\r
1210 self.AppendToOutput('---')
\r
1211 self.AppendToOutput('Python version: ' + python_version)
\r
1212 self.AppendToOutput('WxPython version: ' + wx_version)
\r
1213 self.AppendToOutput('Matplotlib version: ' + mpl_version)
\r
1214 self.AppendToOutput('SciPy version: ' + scipy_version)
\r
1215 self.AppendToOutput('NumPy version: ' + numpy_version)
\r
1216 self.AppendToOutput('ConfigObj version: ' + configobj_version)
\r
1217 self.AppendToOutput('wxPropertyGrid version: ' + '.'.join([str(PROPGRID_MAJOR), str(PROPGRID_MINOR), str(PROPGRID_RELEASE)]))
\r
1218 self.AppendToOutput('---')
\r
1219 self.AppendToOutput('Platform: ' + str(platform.uname()))
\r
1220 self.AppendToOutput('******************************')
\r
1221 self.AppendToOutput('Loaded plugins')
\r
1222 self.AppendToOutput('---')
\r
1224 #sort the plugins into alphabetical order
\r
1225 plugins_list = [key for key, value in self.plugins.iteritems()]
\r
1226 plugins_list.sort()
\r
1227 for plugin in plugins_list:
\r
1228 self.AppendToOutput(plugin)
\r
1230 def UpdateNote(self):
\r
1231 #update the note for the active file
\r
1232 active_file = self.GetActiveFile()
\r
1233 if active_file is not None:
\r
1234 self.panelNote.Editor.SetValue(active_file.note)
\r
1236 def UpdatePerspectivesMenu(self):
\r
1237 #add perspectives to menubar and _perspectives
\r
1238 perspectivesDirectory = os.path.join(lh.hookeDir, 'perspectives')
\r
1239 self._perspectives = {}
\r
1240 if os.path.isdir(perspectivesDirectory):
\r
1241 perspectiveFileNames = os.listdir(perspectivesDirectory)
\r
1242 for perspectiveFilename in perspectiveFileNames:
\r
1243 filename = lh.get_file_path(perspectiveFilename, ['perspectives'])
\r
1244 if os.path.isfile(filename):
\r
1245 perspectiveFile = open(filename, 'rU')
\r
1246 perspective = perspectiveFile.readline()
\r
1247 perspectiveFile.close()
\r
1249 name, extension = os.path.splitext(perspectiveFilename)
\r
1250 if extension == '.txt':
\r
1251 self._perspectives[name] = perspective
\r
1253 #in case there are no perspectives
\r
1254 if not self._perspectives:
\r
1255 perspective = self._mgr.SavePerspective()
\r
1256 self._perspectives['Default'] = perspective
\r
1257 self._SavePerspectiveToFile('Default', perspective)
\r
1259 selected_perspective = self.config['perspectives']['active']
\r
1260 if not self._perspectives.has_key(selected_perspective):
\r
1261 self.config['perspectives']['active'] = 'Default'
\r
1262 selected_perspective = 'Default'
\r
1264 perspectives_list = [key for key, value in self._perspectives.iteritems()]
\r
1265 perspectives_list.sort()
\r
1267 #get the Perspectives menu
\r
1268 menu_position = self.MenuBar.FindMenu('Perspectives')
\r
1269 menu = self.MenuBar.GetMenu(menu_position)
\r
1270 #delete all menu items
\r
1271 for item in menu.GetMenuItems():
\r
1272 menu.DeleteItem(item)
\r
1273 #rebuild the menu by adding the standard menu items
\r
1274 menu.Append(ID_SavePerspective, 'Save Perspective')
\r
1275 menu.Append(ID_DeletePerspective, 'Delete Perspective')
\r
1276 menu.AppendSeparator()
\r
1277 #add all previous perspectives
\r
1278 for index, label in enumerate(perspectives_list):
\r
1279 menu_item = menu.AppendRadioItem(ID_FirstPerspective + index, label)
\r
1280 if label == selected_perspective:
\r
1281 self._RestorePerspective(label)
\r
1282 menu_item.Check(True)
\r
1284 def UpdatePlaylistsTreeSelection(self):
\r
1285 playlist = self.GetActivePlaylist()
\r
1286 if playlist is not None:
\r
1287 if playlist.index >= 0:
\r
1288 self.statusbar.SetStatusText(playlist.get_status_string(), 0)
\r
1292 def UpdatePlot(self, plot=None):
\r
1294 def add_to_plot(curve, set_scale=True):
\r
1295 if curve.visible and curve.x and curve.y:
\r
1296 #get the index of the subplot to use as destination
\r
1297 destination = (curve.destination.column - 1) * number_of_rows + curve.destination.row - 1
\r
1298 #set all parameters for the plot
\r
1299 axes_list[destination].set_title(curve.title)
\r
1301 axes_list[destination].set_xlabel(curve.prefix.x + curve.units.x)
\r
1302 axes_list[destination].set_ylabel(curve.prefix.y + curve.units.y)
\r
1303 #set the formatting details for the scale
\r
1304 formatter_x = lib.curve.PrefixFormatter(curve.decimals.x, curve.prefix.x, use_zero)
\r
1305 formatter_y = lib.curve.PrefixFormatter(curve.decimals.y, curve.prefix.y, use_zero)
\r
1306 axes_list[destination].xaxis.set_major_formatter(formatter_x)
\r
1307 axes_list[destination].yaxis.set_major_formatter(formatter_y)
\r
1308 if curve.style == 'plot':
\r
1309 axes_list[destination].plot(curve.x, curve.y, color=curve.color, label=curve.label, lw=curve.linewidth, zorder=1)
\r
1310 if curve.style == 'scatter':
\r
1311 axes_list[destination].scatter(curve.x, curve.y, color=curve.color, label=curve.label, s=curve.size, zorder=2)
\r
1312 #add the legend if necessary
\r
1314 axes_list[destination].legend()
\r
1317 active_file = self.GetActiveFile()
\r
1318 if not active_file.driver:
\r
1319 #the first time we identify a file, the following need to be set
\r
1320 active_file.identify(self.drivers)
\r
1321 for curve in active_file.plot.curves:
\r
1322 curve.decimals.x = self.GetIntFromConfig('core', 'preferences', 'x_decimals')
\r
1323 curve.decimals.y = self.GetIntFromConfig('core', 'preferences', 'y_decimals')
\r
1324 curve.legend = self.GetBoolFromConfig('core', 'preferences', 'legend')
\r
1325 curve.prefix.x = self.GetStringFromConfig('core', 'preferences', 'x_prefix')
\r
1326 curve.prefix.y = self.GetStringFromConfig('core', 'preferences', 'y_prefix')
\r
1327 if active_file.driver is None:
\r
1328 self.AppendToOutput('Invalid file: ' + active_file.filename)
\r
1330 self.displayed_plot = copy.deepcopy(active_file.plot)
\r
1331 #add raw curves to plot
\r
1332 self.displayed_plot.raw_curves = copy.deepcopy(self.displayed_plot.curves)
\r
1333 #apply all active plotmanipulators
\r
1334 self.displayed_plot = self.ApplyPlotmanipulators(self.displayed_plot, active_file)
\r
1335 #add corrected curves to plot
\r
1336 self.displayed_plot.corrected_curves = copy.deepcopy(self.displayed_plot.curves)
\r
1338 active_file = None
\r
1339 self.displayed_plot = copy.deepcopy(plot)
\r
1341 figure = self.GetActiveFigure()
\r
1344 #use '0' instead of e.g. '0.00' for scales
\r
1345 use_zero = self.GetBoolFromConfig('core', 'preferences', 'use_zero')
\r
1346 #optionally remove the extension from the title of the plot
\r
1347 hide_curve_extension = self.GetBoolFromConfig('core', 'preferences', 'hide_curve_extension')
\r
1348 if hide_curve_extension:
\r
1349 title = lh.remove_extension(self.displayed_plot.title)
\r
1351 title = self.displayed_plot.title
\r
1352 figure.suptitle(title, fontsize=14)
\r
1353 #create the list of all axes necessary (rows and columns)
\r
1355 number_of_columns = max([curve.destination.column for curve in self.displayed_plot.curves])
\r
1356 number_of_rows = max([curve.destination.row for curve in self.displayed_plot.curves])
\r
1357 for index in range(number_of_rows * number_of_columns):
\r
1358 axes_list.append(figure.add_subplot(number_of_rows, number_of_columns, index + 1))
\r
1360 #add all curves to the corresponding plots
\r
1361 for curve in self.displayed_plot.curves:
\r
1362 add_to_plot(curve)
\r
1364 #make sure the titles of 'subplots' do not overlap with the axis labels of the 'main plot'
\r
1365 figure.subplots_adjust(hspace=0.3)
\r
1368 self.panelResults.ClearResults()
\r
1369 if self.displayed_plot.results.has_key(self.results_str):
\r
1370 for curve in self.displayed_plot.results[self.results_str].results:
\r
1371 add_to_plot(curve, set_scale=False)
\r
1372 self.panelResults.DisplayResults(self.displayed_plot.results[self.results_str])
\r
1374 self.panelResults.ClearResults()
\r
1376 figure.canvas.draw()
\r
1378 if __name__ == '__main__':
\r
1380 ## now, silence a deprecation warning for py2.3
\r
1382 warnings.filterwarnings("ignore", "integer", DeprecationWarning, "wxPython.gdi")
\r
1388 app = Hooke(redirect=redirect)
\r
1392 from ..command import CommandExit, Exit, Command, Argument, StoreValue
\r
1393 from ..interaction import Request, BooleanRequest, ReloadUserInterfaceConfig
\r
1394 from ..ui import UserInterface, CommandMessage
\r
1395 from ..util.encoding import get_input_encoding, get_output_encoding
\r
1398 class GUI (UserInterface):
\r
1399 """wxWindows graphical user interface.
\r
1401 def __init__(self):
\r
1402 super(GUI, self).__init__(name='gui')
\r
1404 def default_settings(self):
\r
1405 """Return a list of :class:`hooke.config.Setting`\s for any
\r
1406 configurable UI settings.
\r
1408 The suggested section setting is::
\r
1410 Setting(section=self.setting_section, help=self.__doc__)
\r
1414 def reload_config(self):
\r
1417 def run(self, commands, ui_to_command_queue, command_to_ui_queue):
\r
1418 self._initialize()
\r
1419 cmd = self._cmd(commands, ui_to_command_queue, command_to_ui_queue)
\r
1420 cmd.cmdloop(self._splash_text())
\r
1422 def run_lines(self, commands, ui_to_command_queue, command_to_ui_queue,
\r
1424 raise NotImplementedError(
\r
1425 'Use the command line interface for run_lines()')
\r