#!/usr/bin/env python ''' Hooke - A force spectroscopy review & analysis tool. COPYRIGHT ''' import Queue as queue import multiprocessing from . import config as config_mod from . import plugin as plugin_mod from . import driver as driver_mod import copy import cStringIO import os import os.path import sys import glob import time #import libhooke as lh #import drivers #import plugins #import hookecommands #import hookeplaylist #import hookepropertyeditor #import hookeresults #import playlist class Hooke(object): def __init__(self, config=None, debug=0): self.debug = debug config_mod.DEFAULTS.extend(plugin_mod.default_settings()) config_mod.DEFAULTS.extend(driver_mod.default_settings()) if config == None: config = config_mod.HookeConfigParser( paths=config_mod.DEFAULT_PATHS, default_settings=config_mod.DEFAULT_SETTINGS) config.read_configs() self.config = config self.load_plugins() self.load_drivers() def driver_default_settings(self): settings = [] for driver in driver_mod.drivers: settings.extend(driver_mod.default_settings(driver)) return settings def load_plugins(self): for plugin,value in self.config[plugins].items(): def load_drivers(self) pass def close(self): if self.config.changed: self.config.write() # Does not preserve original comments class HookeFrame(wx.Frame): def __init__(self, parent, id=-1, title='', pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE|wx.SUNKEN_BORDER|wx.CLIP_CHILDREN): #call parent constructor wx.Frame.__init__(self, parent, id, title, pos, size, style) self.config = config self.CreateApplicationIcon() #self.configs contains: {the name of the Commands file: corresponding ConfigObj} self.configs = {} ##self.playlists contains: {the name of the playlist: [playlist, tabIndex, plotID]} #self.playlists = {} #self.plugins contains: {the name of the plugin: [caption, function]} self.plugins = {} #self.plotmanipulators list contains: [the name of the plotmanip, function, name of the module] self.plotmanipulators = [] #tell FrameManager to manage this frame self._mgr = aui.AuiManager() self._mgr.SetManagedWindow(self) #set the gradient style self._mgr.GetArtProvider().SetMetric(aui.AUI_DOCKART_GRADIENT_TYPE, aui.AUI_GRADIENT_NONE) #set transparent drag self._mgr.SetFlags(self._mgr.GetFlags() ^ aui.AUI_MGR_TRANSPARENT_DRAG) # set up default notebook style self._notebook_style = aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER self._notebook_theme = 0 #holds the perspectives: {name, [index, perspectiveStr]} self._perspectives = {} # min size for the frame itself isn't completely done. # see the end up FrameManager::Update() for the test # code. For now, just hard code a frame minimum size self.SetMinSize(wx.Size(400, 300)) #create panels here self.panelAssistant = self.CreatePanelAssistant() self.panelCommands = self.CreatePanelCommands() self.panelFolders = self.CreatePanelFolders() self.panelPlaylists = self.CreatePanelPlaylists() self.panelProperties = self.CreatePanelProperties() self.panelOutput = self.CreatePanelOutput() self.panelResults = self.CreatePanelResults() self.plotNotebook = self.CreateNotebook() #self.textCtrlCommandLine=self.CreateCommandLine() # add panes self._mgr.AddPane(self.panelFolders, aui.AuiPaneInfo().Name('Folders').Caption('Folders').Left().CloseButton(True).MaximizeButton(False)) self._mgr.AddPane(self.panelPlaylists, aui.AuiPaneInfo().Name('Playlists').Caption('Playlists').Left().CloseButton(True).MaximizeButton(False)) self._mgr.AddPane(self.plotNotebook, aui.AuiPaneInfo().Name('Plots').CenterPane().PaneBorder(False)) self._mgr.AddPane(self.panelCommands, aui.AuiPaneInfo().Name('Commands').Caption('Settings and commands').Right().CloseButton(True).MaximizeButton(False)) self._mgr.AddPane(self.panelProperties, aui.AuiPaneInfo().Name('Properties').Caption('Properties').Right().CloseButton(True).MaximizeButton(False)) self._mgr.AddPane(self.panelAssistant, aui.AuiPaneInfo().Name('Assistant').Caption('Assistant').Right().CloseButton(True).MaximizeButton(False)) self._mgr.AddPane(self.panelOutput, aui.AuiPaneInfo().Name('Output').Caption('Output').Bottom().CloseButton(True).MaximizeButton(False)) self._mgr.AddPane(self.panelResults, aui.AuiPaneInfo().Name('Results').Caption('Results').Bottom().CloseButton(True).MaximizeButton(False)) #self._mgr.AddPane(self.textCtrlCommandLine, aui.AuiPaneInfo().Name('CommandLine').CaptionVisible(False).Fixed().Bottom().Layer(2).CloseButton(False).MaximizeButton(False)) #self._mgr.AddPane(panelBottom, aui.AuiPaneInfo().Name("panelCommandLine").Bottom().Position(1).CloseButton(False).MaximizeButton(False)) # add the toolbars to the manager self.toolbar=self.CreateToolBar() self.toolbarNavigation=self.CreateToolBarNavigation() self._mgr.AddPane(self.toolbar, aui.AuiPaneInfo().Name('toolbar').Caption('Toolbar').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False)) self._mgr.AddPane(self.toolbarNavigation, aui.AuiPaneInfo().Name('toolbarNavigation').Caption('Navigation').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False)) # "commit" all changes made to FrameManager self._mgr.Update() #create the menubar after the panes so that the default perspective #is created with all panes open self.CreateMenuBar() self.statusbar = self.CreateStatusBar() self._BindEvents() #TODO: select item on startup (whatever item) #self.listCtrlCommands.Select(0) #self.OnListboxSelect(None) name = self.config['perspectives']['active'] menu_item = self.GetPerspectiveMenuItem(name) self.OnRestorePerspective(menu_item) self.playlists = self.panelPlaylists.Playlists #define the list of active drivers self.drivers = [] for driver in self.config['drivers']: if self.config['drivers'][driver]: #get the corresponding filename and path filename = ''.join([driver, '.py']) path = lh.get_file_path(filename, ['drivers']) #the driver is active for driver[1] == 1 if os.path.isfile(path): #driver files are located in the 'drivers' subfolder driver_name = ''.join(['drivers.', driver]) module = __import__(driver_name) class_file = getattr(drivers, driver) for command in dir(class_file): if command.endswith('Driver'): self.drivers.append(getattr(class_file, command)) #import all active plugins and plotmanips #the plotmanip_functions contains: {the name of the plotmanip: [method, class_object]} plotmanip_functions = {} #add 'general.ini' to self.configs (this is not a plugin and thus must be imported seperately) ini_path = lh.get_file_path('general.ini', ['plugins']) plugin_config = ConfigObj(ini_path) #self.config.merge(plugin_config) self.configs['general'] = plugin_config #make sure we execute _plug_init() for every command line plugin we import for plugin in self.config['plugins']: if self.config['plugins'][plugin]: filename = ''.join([plugin, '.py']) path = lh.get_file_path(filename, ['plugins']) if os.path.isfile(path): #get the corresponding filename and path plugin_name = ''.join(['plugins.', plugin]) try: #import the module module = __import__(plugin_name) #prepare the ini file for inclusion ini_path = path.replace('.py', '.ini') #include ini file plugin_config = ConfigObj(ini_path) #self.config.merge(plugin_config) self.configs[plugin] = plugin_config #add to plugins commands = eval('dir(module.' + plugin+ '.' + plugin + 'Commands)') #keep only commands (ie names that start with 'do_') commands = [command for command in commands if command.startswith('do_')] if commands: self.plugins[plugin] = commands try: #initialize the plugin eval('module.' + plugin+ '.' + plugin + 'Commands._plug_init(self)') except AttributeError: pass except ImportError: pass #initialize the commands tree commands = dir(HookeFrame) commands = [command for command in commands if command.startswith('do_')] if commands: self.plugins['general'] = commands self.panelCommands.Initialize(self.plugins) for command in dir(self): if command.startswith('plotmanip_'): plotmanip_functions[command] = [command, getattr(self, command)] for name in self.config['plotmanipulators']['names']: if self.config['plotmanipulators'].as_bool(name): command_name = ''.join(['plotmanip_', name]) if command_name in plotmanip_functions: self.plotmanipulators.append(plotmanip_functions[command_name]) #load default list, if possible self.do_loadlist(self.config['general']['list']) def _BindEvents(self): self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_SIZE, self.OnSize) self.Bind(wx.EVT_CLOSE, self.OnClose) # Show How To Use The Closing Panes Event self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPaneClose) self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnNotebookPageClose) #menu self.Bind(wx.EVT_MENU, self.OnClose, id=wx.ID_EXIT) self.Bind(wx.EVT_MENU, self.OnAbout, id=wx.ID_ABOUT) #view self.Bind(wx.EVT_MENU_RANGE, self.OnView, id=ID_ViewAssistant, id2=ID_ViewResults) #perspectives self.Bind(wx.EVT_MENU, self.OnDeletePerspective, id=ID_DeletePerspective) self.Bind(wx.EVT_MENU, self.OnSavePerspective, id=ID_SavePerspective) self.Bind(wx.EVT_MENU_RANGE, self.OnRestorePerspective, id=ID_FirstPerspective, id2=ID_FirstPerspective+1000) #toolbar self.Bind(wx.EVT_TOOL, self.OnExportImage, id=ID_ExportImage) self.Bind(wx.EVT_TOOL, self.OnNext, id=ID_Next) self.Bind(wx.EVT_TOOL, self.OnPrevious, id=ID_Previous) #self.Bind(.EVT_AUITOOLBAR_TOOL_DROPDOWN, self.OnDropDownToolbarItem, id=ID_DropDownToolbarItem) #dir control treeCtrl = self.panelFolders.GetTreeCtrl() #tree.Bind(wx.EVT_LEFT_UP, self.OnDirCtrl1LeftUp) #tree.Bind(wx.EVT_LEFT_DOWN, self.OnGenericDirCtrl1LeftDown) treeCtrl.Bind(wx.EVT_LEFT_DCLICK, self.OnDirCtrlLeftDclick) #playlist tree self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DOWN, self.OnPlaylistsLeftDown) self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DCLICK, self.OnPlaylistsLeftDclick) #commands tree self.panelCommands.ExecuteButton.Bind(wx.EVT_BUTTON, self.OnExecute) self.panelCommands.CommandsTree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeCtrlCommandsLeftDown) #property editor self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged) self.panelProperties.pg.Bind(wxpg.EVT_PG_SELECTED, self.OnPropGridSelect) #results panel self.panelResults.results_list.OnCheckItem = self.OnResultsCheck def _GetActiveCurveIndex(self): playlist = self.GetActivePlaylist() #get the selected item from the tree selected_item = self.panelPlaylists.PlaylistsTree.GetSelection() #test if a playlist or a curve was double-clicked if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item): return -1 else: count = 0 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item) while selected_item.IsOk(): count += 1 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item) return count def _GetActivePlaylistName(self): #get the selected item from the tree selected_item = self.panelPlaylists.PlaylistsTree.GetSelection() #test if a playlist or a curve was double-clicked if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item): playlist_item = selected_item else: #get the name of the playlist playlist_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item) #now we have a playlist return self.panelPlaylists.PlaylistsTree.GetItemText(playlist_item) def _GetPlaylistTab(self, name): for index, page in enumerate(self.plotNotebook._tabs._pages): if page.caption == name: return index return -1 def _GetUniquePlaylistName(self, name): playlist_name = name count = 1 while playlist_name in self.playlists: playlist_name = ''.join([name, str(count)]) count += 1 return playlist_name def _SavePerspectiveToFile(self, name, perspective): filename = ''.join([name, '.txt']) filename = lh.get_file_path(filename, ['perspectives']) perspectivesFile = open(filename, 'w') perspectivesFile.write(perspective) perspectivesFile.close() def AddPlaylist(self, playlist=None, name='Untitled'): #TODO: change cursor or progressbar (maybe in statusbar) #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) if playlist and playlist.count > 0: playlist.name = self._GetUniquePlaylistName(name) playlist.reset() self.AddToPlaylists(playlist) def AddPlaylistFromFiles(self, files=[], name='Untitled'): #TODO: change cursor or progressbar (maybe in statusbar) #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) if files: playlist = Playlist.Playlist(self.drivers) for item in files: playlist.add_curve(item) if playlist.count > 0: playlist.name = self._GetUniquePlaylistName(name) playlist.reset() self.AddToPlaylists(playlist) def AddToPlaylists(self, playlist): if playlist.has_curves: #setup the playlist in the Playlist tree tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem() playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0) #add all curves to the Playlist tree curves = {} for index, curve in enumerate(playlist.curves): ##remove the extension from the name of the curve ##TODO: optional? #item_text, extension = os.path.splitext(curve.name) #curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, item_text, 1) curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, curve.name, 1) if index == playlist.index: self.panelPlaylists.PlaylistsTree.SelectItem(curve_ID) playlist.reset() #create the plot tab and add playlist to the dictionary plotPanel = wxmpl.PlotPanel(self, ID_FirstPlot + len(self.playlists)) notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True) tab_index = self.plotNotebook.GetSelection() figure = plotPanel.get_figure() self.playlists[playlist.name] = [playlist, figure] self.panelPlaylists.PlaylistsTree.Expand(playlist_root) self.statusbar.SetStatusText(playlist.get_status_string(), 0) self.UpdatePlot() def AppendToOutput(self, text): self.panelOutput.AppendText(''.join([text, '\n'])) def CreateApplicationIcon(self): iconFile = 'resources' + os.sep + 'microscope.ico' icon = wx.Icon(iconFile, wx.BITMAP_TYPE_ICO) self.SetIcon(icon) def CreateCommandLine(self): return wx.TextCtrl(self, -1, '', style=wx.NO_BORDER|wx.EXPAND) def CreatePanelAssistant(self): panel = wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE) panel.SetEditable(False) return panel def CreatePanelCommands(self): return hookecommands.Commands(self) def CreatePanelFolders(self): #set file filters filters = self.config['folders']['filters'] index = self.config['folders'].as_int('filterindex') #set initial directory folder = self.config['general']['workdir'] return wx.GenericDirCtrl(self, -1, dir=folder, size=(200, 250), style=wx.DIRCTRL_SHOW_FILTERS, filter=filters, defaultFilter=index) def CreatePanelOutput(self): return wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE) def CreatePanelPlaylists(self): return hookeplaylist.Playlists(self) def CreatePanelProperties(self): return hookepropertyeditor.PropertyEditor(self) def CreatePanelResults(self): return hookeresults.Results(self) def CreatePanelWelcome(self): ctrl = wx.html.HtmlWindow(self, -1, wx.DefaultPosition, wx.Size(400, 300)) introStr = '
See the DocumentationIndex for more information
' ctrl.SetPage(introStr) return ctrl def CreateMenuBar(self): menu_bar = wx.MenuBar() #file file_menu = wx.Menu() file_menu.Append(wx.ID_OPEN, '&Open playlist\tCtrl-O') file_menu.Append(wx.ID_SAVE, 'Save playlist\tCtrl-S') file_menu.AppendSeparator() file_menu.Append(wx.ID_EXIT, 'Exit\tCtrl-Q') #edit edit_menu = wx.Menu() edit_menu.Append(ID_ExportText, 'Export text...') edit_menu.Append(ID_ExportImage, 'Export image...') edit_menu.AppendSeparator(); edit_menu.Append(ID_Config, 'Preferences') #view view_menu = wx.Menu() view_menu.AppendCheckItem(ID_ViewFolders, 'Folders\tF5') view_menu.AppendCheckItem(ID_ViewPlaylists, 'Playlists\tF6') view_menu.AppendCheckItem(ID_ViewCommands, 'Commands\tF7') view_menu.AppendCheckItem(ID_ViewProperties, 'Properties\tF8') view_menu.AppendCheckItem(ID_ViewAssistant, 'Assistant\tF9') view_menu.AppendCheckItem(ID_ViewResults, 'Results\tF10') view_menu.AppendCheckItem(ID_ViewOutput, 'Output\tF11') #perspectives self._perspectives_menu = self.CreatePerspectivesMenu() #help help_menu = wx.Menu() help_menu.Append(wx.ID_ABOUT, 'About Hooke') #put it all together menu_bar.Append(file_menu, 'File') menu_bar.Append(edit_menu, 'Edit') menu_bar.Append(view_menu, 'View') menu_bar.Append(self._perspectives_menu, "Perspectives") menu_bar.Append(help_menu, 'Help') self.SetMenuBar(menu_bar) def CreateNotebook(self): # create the notebook off-window to avoid flicker client_size = self.GetClientSize() ctrl = aui.AuiNotebook(self, -1, wx.Point(client_size.x, client_size.y), wx.Size(430, 200), self._notebook_style) arts = [aui.AuiDefaultTabArt, aui.AuiSimpleTabArt, aui.VC71TabArt, aui.FF2TabArt, aui.VC8TabArt, aui.ChromeTabArt] art = arts[self._notebook_theme]() ctrl.SetArtProvider(art) #uncomment if we find a nice icon #page_bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16)) ctrl.AddPage(self.CreatePanelWelcome(), "Welcome", False) return ctrl def CreatePerspectivesMenu(self): menu = wx.Menu() menu.Append(ID_SavePerspective, "Save Perspective") menu.Append(ID_DeletePerspective, "Delete Perspective") menu.AppendSeparator() #add perspectives to menubar and _perspectives perspectivesDirectory = os.path.join(lh.hookeDir, 'perspectives') if os.path.isdir(perspectivesDirectory): perspectiveFileNames = os.listdir(perspectivesDirectory) for perspectiveFilename in perspectiveFileNames: filename = lh.get_file_path(perspectiveFilename, ['perspectives']) if os.path.isfile(filename): perspectiveFile = open(filename, 'rU') perspective = perspectiveFile.readline() perspectiveFile.close() if perspective != '': name, extension = os.path.splitext(perspectiveFilename) if extension == '.txt': menuItem = menu.AppendRadioItem(ID_FirstPerspective + len(self._perspectives), name) self._perspectives[name] = [len(self._perspectives), perspective] if self.config['perspectives']['active'] == name: menuItem.Check() #in case there are no perspectives if not self._perspectives: perspective = self._mgr.SavePerspective() self.config['perspectives']['default'] = 'Default' self._perspectives['Default'] = [0, perspective] menuItem = menu.AppendRadioItem(ID_FirstPerspective, 'Default') menuItem.Check() self._SavePerspectiveToFile('Default', perspective) return menu def CreateStatusbar(self): statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP) statusbar.SetStatusWidths([-2, -3]) statusbar.SetStatusText('Ready', 0) welcomeString=u'Welcome to Hooke (version '+__version__+', '+__release_name__+')!' statusbar.SetStatusText(welcomeString, 1) return statusbar def CreateToolBar(self): toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER) toolbar.SetToolBitmapSize(wx.Size(16,16)) toolbar_bmp1 = wx.ArtProvider_GetBitmap(wx.ART_QUESTION, wx.ART_OTHER, wx.Size(16, 16)) toolbar_bmpOpen = wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN, wx.ART_OTHER, wx.Size(16, 16)) toolbar_bmpSave = wx.ArtProvider_GetBitmap(wx.ART_FILE_SAVE, wx.ART_OTHER, wx.Size(16, 16)) toolbar_bmpExportText = wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16)) toolbar_bmpExportImage = wx.ArtProvider_GetBitmap(wx.ART_MISSING_IMAGE, wx.ART_OTHER, wx.Size(16, 16)) toolbar.AddLabelTool(101, 'Open', toolbar_bmpOpen) toolbar.AddLabelTool(102, 'Save', toolbar_bmpSave) toolbar.AddSeparator() toolbar.AddLabelTool(ID_ExportText, 'Export text...', toolbar_bmpExportText) toolbar.AddLabelTool(ID_ExportImage, 'Export image...', toolbar_bmpExportImage) toolbar.Realize() return toolbar def CreateToolBarNavigation(self): toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER) toolbar.SetToolBitmapSize(wx.Size(16,16)) toolbar_bmpBack = wx.ArtProvider_GetBitmap(wx.ART_GO_BACK, wx.ART_OTHER, wx.Size(16, 16)) toolbar_bmpForward = wx.ArtProvider_GetBitmap(wx.ART_GO_FORWARD, wx.ART_OTHER, wx.Size(16, 16)) toolbar.AddLabelTool(ID_Previous, 'Previous', toolbar_bmpBack, shortHelp='Previous curve') toolbar.AddLabelTool(ID_Next, 'Next', toolbar_bmpForward, shortHelp='Next curve') toolbar.Realize() return toolbar def DeleteFromPlaylists(self, name): if name in self.playlists: del self.playlists[name] tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem() item, cookie = self.panelPlaylists.PlaylistsTree.GetFirstChild(tree_root) while item.IsOk(): playlist_name = self.panelPlaylists.PlaylistsTree.GetItemText(item) if playlist_name == name: try: self.panelPlaylists.PlaylistsTree.Delete(item) except: pass item = self.panelPlaylists.PlaylistsTree.GetNextSibling(item) self.OnPlaylistsLeftDclick(None) def DeletePlotPage(self, name): index = self._GetPlaylistTab(name) plot = self.playlists[name][1] plot = None self.plotNotebook.RemovePage(index) self.DeleteFromPlaylists(name) def GetActiveCurve(self): playlist = self.GetActivePlaylist() if playlist is not None: return playlist.get_active_curve() return None def GetActivePlaylist(self): playlist_name = self._GetActivePlaylistName() if playlist_name in self.playlists: return self.playlists[playlist_name][0] return None def GetActivePlot(self): curve = self.GetActiveCurve() if curve is not None: return curve.plots[0] return None def GetDockArt(self): return self._mgr.GetArtProvider() def GetBoolFromConfig(self, *args): if len(args) == 2: plugin = args[0] section = args[0] key = args[1] elif len(args) == 3: plugin = args[0] section = args[1] key = args[2] if self.configs.has_key(plugin): config = self.configs[plugin] return config[section][key].as_bool('value') return None def GetFloatFromConfig(self, *args): if len(args) == 2: plugin = args[0] section = args[0] key = args[1] elif len(args) == 3: plugin = args[0] section = args[1] key = args[2] if self.configs.has_key(plugin): config = self.configs[plugin] return config[section][key].as_float('value') return None def GetIntFromConfig(self, *args): if len(args) == 2: plugin = args[0] section = args[0] key = args[1] elif len(args) == 3: plugin = args[0] section = args[1] key = args[2] if self.configs.has_key(plugin): config = self.configs[plugin] return config[section][key].as_int('value') return None def GetStringFromConfig(self, *args): if len(args) == 2: plugin = args[0] section = args[0] key = args[1] elif len(args) == 3: plugin = args[0] section = args[1] key = args[2] if self.configs.has_key(plugin): config = self.configs[plugin] return config[section][key]['value'] return None def GetPerspectiveMenuItem(self, name): index = self._perspectives[name][0] perspective_Id = ID_FirstPerspective + index menu_item = self.MenuBar.FindItemById(perspective_Id) return menu_item def HasPlotmanipulator(self, name): ''' returns True if the plotmanipulator 'name' is loaded, False otherwise ''' for plotmanipulator in self.plotmanipulators: if plotmanipulator[0] == name: return True return False def OnAbout(self, event): msg = 'Hooke\n\n'+\ 'A free, open source data analysis platform\n'+\ '(c) 2006-2008 Massimo Sandal\n\n'+\ '(c) 2009 Dr. Rolf Schmidt\n\n'+\ 'Released under the GNU GPL v2' dialog = wx.MessageDialog(self, msg, "About Hooke", wx.OK | wx.ICON_INFORMATION) dialog.ShowModal() dialog.Destroy() def OnClose(self, event): #apply changes self.config['main']['height'] = str(self.GetSize().GetHeight()) self.config['main']['left'] = str(self.GetPosition()[0]) self.config['main']['top'] = str(self.GetPosition()[1]) self.config['main']['width'] = str(self.GetSize().GetWidth()) # Writing the configuration file to 'hooke.ini' self.config.write() self._mgr.UnInit() del self._mgr self.Destroy() def OnDeletePerspective(self, event): pass def OnDirCtrlLeftDclick(self, event): file_path = self.panelFolders.GetPath() if os.path.isfile(file_path): if file_path.endswith('.hkp'): self.do_loadlist(file_path) else: pass event.Skip() def OnEraseBackground(self, event): event.Skip() def OnExecute(self, event): item = self.panelCommands.CommandsTree.GetSelection() if item.IsOk(): if self.panelCommands.CommandsTree.ItemHasChildren(item): pass else: #get the plugin parent = self.panelCommands.CommandsTree.GetItemParent(item) if not self.panelCommands.CommandsTree.ItemHasChildren(item): parent_text = self.panelCommands.CommandsTree.GetItemText(parent) item_text = self.panelCommands.CommandsTree.GetItemText(item) if item_text in ['genlist', 'loadlist', 'savelist']: property_values = self.panelProperties.GetPropertyValues() arg_str = '' for item in property_values: arg_str = ''.join([arg_str, item, '=r"', str(property_values[item]), '", ']) command = ''.join(['self.do_', item_text, '(', arg_str, ')']) else: command = ''.join(['self.do_', item_text, '()']) exec(command) pass def OnExit(self, event): self.Close() def OnExportImage(self, event): pass def OnNext(self, event): ''' NEXT Go to the next curve in the playlist. If we are at the last curve, we come back to the first. ----- Syntax: next, n ''' selected_item = self.panelPlaylists.PlaylistsTree.GetSelection() if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item): #GetFirstChild returns a tuple #we only need the first element next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(selected_item)[0] else: next_item = self.panelPlaylists.PlaylistsTree.GetNextSibling(selected_item) if not next_item.IsOk(): parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item) #GetFirstChild returns a tuple #we only need the first element next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(parent_item)[0] self.panelPlaylists.PlaylistsTree.SelectItem(next_item, True) playlist = self.playlists[self._GetActivePlaylistName()][0] if playlist.count > 1: playlist.next() self.statusbar.SetStatusText(playlist.get_status_string(), 0) self.UpdatePlot() def OnNotebookPageClose(self, event): ctrl = event.GetEventObject() playlist_name = ctrl.GetPageText(ctrl._curpage) self.DeleteFromPlaylists(playlist_name) def OnPaneClose(self, event): event.Skip() def OnPlaylistsLeftDclick(self, event): playlist_name = self._GetActivePlaylistName() #if that playlist already exists #we check if it is the active playlist (ie selected in panelPlaylists) #and switch to it if necessary if playlist_name in self.playlists: index = self.plotNotebook.GetSelection() current_playlist = self.plotNotebook.GetPageText(index) #new_playlist = self.playlists[playlist_name][0] #if current_playlist != new_playlist: if current_playlist != playlist_name: index = self._GetPlaylistTab(playlist_name) self.plotNotebook.SetSelection(index) #if a curve was double-clicked item = self.panelPlaylists.PlaylistsTree.GetSelection() #TODO: fix with get_active_curve if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item): index = self._GetActiveCurveIndex() else: index = 0 if index >= 0: playlist = self.playlists[playlist_name][0] playlist.index = index self.statusbar.SetStatusText(playlist.get_status_string(), 0) self.UpdatePlot() #if you uncomment the following line, the tree will collapse/expand as well #event.Skip() def OnPlaylistsLeftDown(self, event): hit_item, hit_flags = self.panelPlaylists.PlaylistsTree.HitTest(event.GetPosition()) if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0: #self.SetFocus() self.panelPlaylists.PlaylistsTree.SelectItem(hit_item) playlist_name = self._GetActivePlaylistName() playlist = self.playlists[playlist_name][0] #if a curve was clicked item = self.panelPlaylists.PlaylistsTree.GetSelection() if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item): #TODO: fix with get_active_curve index = self._GetActiveCurveIndex() if index >= 0: #playlist = self.playlists[playlist_name][0] playlist.index = index #self.playlists[playlist_name][0].index = index #else: ##self.playlists[playlist_name][0].index = 0 #playlist.index = index self.playlists[playlist_name][0] = playlist event.Skip() def OnPrevious(self, event): ''' PREVIOUS Go to the previous curve in the playlist. If we are at the first curve, we jump to the last. ------- Syntax: previous, p ''' #playlist = self.playlists[self._GetActivePlaylistName()][0] #select the previous curve and tell the user if we wrapped around #self.AppendToOutput(playlist.previous()) selected_item = self.panelPlaylists.PlaylistsTree.GetSelection() if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item): previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(selected_item) else: previous_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item) if not previous_item.IsOk(): parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item) previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(parent_item) self.panelPlaylists.PlaylistsTree.SelectItem(previous_item, True) playlist = self.playlists[self._GetActivePlaylistName()][0] if playlist.count > 1: playlist.previous() self.statusbar.SetStatusText(playlist.get_status_string(), 0) self.UpdatePlot() def OnPropGridChanged (self, event): prop = event.GetProperty() if prop: item_section = self.panelProperties.SelectedTreeItem item_plugin = self.panelCommands.CommandsTree.GetItemParent(item_section) plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin) config = self.configs[plugin] property_section = self.panelCommands.CommandsTree.GetItemText(item_section) property_key = prop.GetName() property_value = prop.GetValue() config[property_section][property_key]['value'] = property_value def OnPropGridSelect(self, event): pass def OnRestorePerspective(self, event): name = self.MenuBar.FindItemById(event.GetId()).GetLabel() self._mgr.LoadPerspective(self._perspectives[name][1]) self.config['perspectives']['active'] = name self._mgr.Update() all_panes = self._mgr.GetAllPanes() for pane in all_panes: if not pane.name.startswith('toolbar'): if pane.name == 'Assistant': self.MenuBar.FindItemById(ID_ViewAssistant).Check(pane.window.IsShown()) if pane.name == 'Folders': self.MenuBar.FindItemById(ID_ViewFolders).Check(pane.window.IsShown()) if pane.name == 'Playlists': self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown()) if pane.name == 'Commands': self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown()) if pane.name == 'Properties': self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown()) if pane.name == 'Output': self.MenuBar.FindItemById(ID_ViewOutput).Check(pane.window.IsShown()) if pane.name == 'Results': self.MenuBar.FindItemById(ID_ViewResults).Check(pane.window.IsShown()) def OnResultsCheck(self, index, flag): curve = self.GetActiveCurve() result = curve.data['results'][index]['visible'] = flag self.UpdatePlot() def OnSavePerspective(self, event): def nameExists(name): for item in self._perspectives_menu.GetMenuItems(): if item.GetText() == name: return True return False done = False while not done: dialog = wx.TextEntryDialog(self, 'Enter a name for the new perspective:', 'Save perspective') dialog.SetValue('New perspective') if dialog.ShowModal() != wx.ID_OK: return else: name = dialog.GetValue() if nameExists(name): 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) if dialogConfirm.ShowModal() == wx.ID_YES: done = True else: done = True perspective = self._mgr.SavePerspective() if nameExists(name): #check the corresponding menu item menuItem = self.GetPerspectiveMenuItem(name) #replace the perspectiveStr in _pespectives index = self._perspectives[name][0] self._perspectives[name] = [index, perspective] else: #simply add the new perspective to _perspectives index = len(self._perspectives) self._perspectives[name] = [len(self._perspectives), perspective] menuItem = self._perspectives_menu.AppendRadioItem(ID_FirstPerspective + len(self._perspectives), name) menuItem.Check() self._SavePerspectiveToFile(name, perspective) #uncheck all perspective menu items #as these are radio items, one item has to be checked at all times #the line 'menuItem.Check()' above actually checks a second item #but does not toggle #so we need to uncheck all other items afterwards #weirdly enough, menuitem.Toggle() doesn't do this properly either for item in self._perspectives_menu.GetMenuItems(): if item.IsCheckable(): if item.GetLabel() != name: item.Check(False) def OnView(self, event): menu_id = event.GetId() menu_item = self.MenuBar.FindItemById(menu_id) menu_label = menu_item.GetLabel() pane = self._mgr.GetPane(menu_label) pane.Show(not pane.IsShown()) #if we don't do the following, the Folders pane does not resize properly on hide/show if pane.caption == 'Folders' and pane.IsShown() and pane.IsDocked(): #folders_size = pane.GetSize() self.panelFolders.Fit() self._mgr.Update() def OnSize(self, event): event.Skip() def OnTreeCtrlCommandsLeftDown(self, event): hit_item, hit_flags = self.panelCommands.CommandsTree.HitTest(event.GetPosition()) if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0: self.panelCommands.CommandsTree.SelectItem(hit_item) self.panelProperties.SelectedTreeItem = hit_item #if a command was clicked properties = [] if not self.panelCommands.CommandsTree.ItemHasChildren(hit_item): item_plugin = self.panelCommands.CommandsTree.GetItemParent(hit_item) plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin) if self.configs.has_key(plugin): #config = self.panelCommands.CommandsTree.GetPyData(item_plugin) config = self.configs[plugin] section = self.panelCommands.CommandsTree.GetItemText(hit_item) #display docstring in help window doc_string = eval('self.do_' + section + '.__doc__') if section in config: for option in config[section]: properties.append([option, config[section][option]]) else: module = self.panelCommands.CommandsTree.GetItemText(hit_item) if module != 'general': doc_string = eval('plugins.' + module + '.' + module + 'Commands.__doc__') else: doc_string = 'The module "general" contains Hooke core functionality' if doc_string is not None: self.panelAssistant.ChangeValue(doc_string) hookepropertyeditor.PropertyEditor.Initialize(self.panelProperties, properties) event.Skip() def UpdatePlaylists(self): #setup the playlist in the Playlist tree tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem() playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0) #add all curves to the Playlist tree curves = {} for index, curve in enumerate(playlist.curves): ##remove the extension from the name of the curve ##TODO: optional? #item_text, extension = os.path.splitext(curve.name) #curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, item_text, 1) curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, curve.name, 1) if index == playlist.index: self.panelPlaylists.PlaylistsTree.SelectItem(curve_ID) #create the plot tab and add playlist to the dictionary plotPanel = wxmpl.PlotPanel(self, ID_FirstPlot + len(self.playlists)) notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True) #tab_index = self.plotNotebook.GetSelection() figure = plotPanel.get_figure() #self.playlists[playlist.name] = [playlist, tab_index, figure] self.playlists[playlist.name] = [playlist, figure] self.panelPlaylists.PlaylistsTree.Expand(playlist_root) self.statusbar.SetStatusText(playlist.get_status_string(), 0) self.UpdatePlot() #HELPER FUNCTIONS #Everything sending an event should be here def _measure_N_points(self, N, whatset=1): ''' general helper function for N-points measures ''' wx.PostEvent(self.frame,self.list_of_events['measure_points'](num_of_points=N, set=whatset)) while 1: try: points=self.frame.events_from_gui.get() break except Empty: pass return points def _clickize(self, xvector, yvector, index): ''' returns a ClickedPoint() object from an index and vectors of x, y coordinates ''' point = lh.ClickedPoint() point.index = index point.absolute_coords = xvector[index], yvector[index] point.find_graph_coords(xvector, yvector) return point #PLAYLIST INTERACTION COMMANDS #------------------------------- def do_genlist(self, folder=lh.hookeDir, filemask='*.*'): ''' GENLIST Generates a file playlist. Note it doesn't *save* it: see savelist for this. If [input files] is a directory, it will use all files in the directory for playlist. So: genlist dir genlist dir/ genlist dir/*.* are all equivalent syntax. ------------ Syntax: genlist [input files] ''' #args list is: input folder, file mask if os.path.isdir(folder): path = os.path.join(folder, filemask) #expanding correctly the input list with the glob module :) files = glob.glob(path) files.sort() #TODO: change cursor or progressbar (maybe in statusbar) #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) playlist = playlist.Playlist(self.drivers) for item in files: curve = playlist.add_curve(item) plot = copy.deepcopy(curve.plots[0]) #add the 'raw' data curve.add_data('raw', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot') curve.add_data('raw', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot') #apply all active plotmanipulators and add the 'manipulated' data for plotmanipulator in self.plotmanipulators: plot = plotmanipulator[1](plot, curve) curve.set_data('manipulated', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot') curve.add_data('manipulated', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot') if playlist.count > 0: playlist.name = self._GetUniquePlaylistName(os.path.basename(folder)) playlist.reset() self.AddToPlaylists(playlist) self.AppendToOutput(playlist.get_status_string()) else: self.AppendToOutput(''.join(['Cannot find folder ', folder])) def do_loadlist(self, filename): ''' LOADLIST Loads a file playlist ----------- Syntax: loadlist [playlist file] ''' #TODO: check for duplicate playlists, ask the user for a unique name #if self.playlist_name in self.playlists: #add hkp extension if necessary if not filename.endswith('.hkp'): filename = ''.join([filename, '.hkp']) #prefix with 'hookeDir' if just a filename or a relative path filename = lh.get_file_path(filename) if os.path.isfile(filename): #TODO: change cursor #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) playlist_new = playlist.Playlist(self.drivers) playlist_new.load(filename) if playlist_new.count > 0: for curve in playlist_new.curves: plot = copy.deepcopy(curve.plots[0]) for plotmanip in self.plotmanipulators: #to_plot = plotmanip[1](to_plot, curve) plot = plotmanip[1](plot, curve) curve.set_data('manipulated', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot') curve.add_data('manipulated', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot') self.AddToPlaylists(playlist_new) #else: ##TODO: display dialog self.AppendToOutput(playlist_new.get_status_string()) #TODO: change cursor #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) else: #TODO: display dialog self.AppendToOutput(''.join['File ', filename, ' not found.\n']) pass def do_savelist(self, filename): ''' SAVELIST Saves the current file playlist on disk. ------------ Syntax: savelist [filename] ''' #self.playlist_generics['pointer'] = self._GetActiveCurveIndex pointer = self._GetActiveCurveIndex() #autocomplete filename if not specified if not filename.endswith('.hkp'): filename = filename.join(['.hkp']) playlist = self.GetActivePlaylist() playlist.set_XML() playlist.save(filename) #PLOT INTERACTION COMMANDS #------------------------------- def UpdatePlot(self): def add_plot(plot): if plot['visible'] and plot['x'] and plot['y']: color = plot['color'] style = plot['style'] if style == 'plot': axes.plot(plot['x'], plot['y'], color=color, zorder=1) if style == 'scatter': axes.scatter(plot['x'], plot['y'], color=color, s=0.5, zorder=2) def add_plot2(plot): if plot.visible and plot.x and plot.y: if plot.style == 'plot': axes.plot(plot.x, plot.y, color=plot.color, zorder=1) if plot.style == 'scatter': axes.scatter(plot.x, plot.y, color=plot.color, s=0.5, zorder=2) playlist_name = self._GetActivePlaylistName() index = self._GetActiveCurveIndex() playlist = self.playlists[playlist_name][0] curve = playlist.get_active_curve() plot = playlist.get_active_plot() figure = self.playlists[playlist_name][1] figure.clf() exclude = None if curve.data.has_key('manipulated'): exclude = 'raw' elif curve.data.has_key('raw'): exclude = 'manipulated' if exclude is not None: #TODO: what is this good for? if not hasattr(self, 'subplot'): axes = figure.add_subplot(111) axes.set_title(plot.title) axes.set_xlabel(plot.units[0]) axes.set_ylabel(plot.units[1]) for set_of_plots in curve.data: if set_of_plots != exclude: plots = curve.data[set_of_plots] for each_plot in plots: add_plot(each_plot) #TODO: add multiple results support #for fit in curve.fits: if curve.fits.has_key('wlc'): for plot in curve.fits['wlc'].results: add_plot2(plot) self.panelResults.DisplayResults(curve.fits['wlc']) else: self.panelResults.ClearResults() axes.figure.canvas.draw() else: self.AppendToOutput('Not able to plot.') if __name__ == '__main__': app = Hooke(debug=__debug__) app.MainLoop()