X-Git-Url: http://git.tremily.us/?p=hooke.git;a=blobdiff_plain;f=hooke%2Fhooke.py;h=b9a659e77397d191ed913939b0bcb816d86378c3;hp=3662b51ba2219151a76f4c47751b5178c3fc1996;hb=b90995fb4b6d8151df862d40edc8c369d7052cfa;hpb=ee0cc702f9257cf5fdfb89fea2ebf70fbde3e54a diff --git a/hooke/hooke.py b/hooke/hooke.py index 3662b51..b9a659e 100644 --- a/hooke/hooke.py +++ b/hooke/hooke.py @@ -1,1262 +1,265 @@ -#!/usr/bin/env python - -''' -HOOKE - A force spectroscopy review & analysis tool - -Copyright (C) 2008-2010 Massimo Sandal (University of Bologna, Italy). - Rolf Schmidt (Concordia University, Canada). - -This program is released under the GNU General Public License version 2. -''' - -from libhooke import WX_GOOD - -import wxversion -wxversion.select(WX_GOOD) -import copy -import cStringIO -import os -import os.path -import sys -import glob -import time - -import imp -import wx -import wx.html -import wx.aui -import wxmpl -import wx.lib.agw.aui as aui -import wx.propgrid as wxpg - -import libhooke as lh -from config import config -import drivers -import plugins -import hookecommands -import hookeplaylist -import hookepropertyeditor -import hookeresults -import playlist - -global __version__ - -__version__ = lh.HOOKE_VERSION[0] -__release_name__ = lh.HOOKE_VERSION[1] - -#TODO: order menu items, get rid of all unused IDs -ID_ExportText = wx.NewId() -ID_ExportImage = wx.NewId() -ID_Config = wx.NewId() -ID_About = wx.NewId() -ID_Next = wx.NewId() -ID_Previous = wx.NewId() - -ID_ViewAssistant = wx.NewId() -ID_ViewCommands = wx.NewId() -ID_ViewFolders = wx.NewId() -ID_ViewOutput = wx.NewId() -ID_ViewPlaylists = wx.NewId() -ID_ViewProperties = wx.NewId() -ID_ViewResults = wx.NewId() - -ID_CommandsList = wx.NewId() -ID_CommandsListBox = wx.NewId() - -ID_TextContent = wx.NewId() -ID_TreeContent = wx.NewId() -ID_HTMLContent = wx.NewId() -ID_SizeReportContent = wx.NewId() -ID_DeletePerspective = wx.NewId() -ID_SavePerspective = wx.NewId() - -ID_FirstPerspective = ID_SavePerspective + 1000 -#I hope we'll never have more than 1000 perspectives -ID_FirstPlot = ID_SavePerspective + 2000 - -class Hooke(wx.App): - - def OnInit(self): - self.SetAppName('Hooke') - self.SetVendorName('') - - #set the Hooke directory - lh.hookeDir = os.path.abspath(os.path.dirname(__file__)) - - windowPosition = (config['main']['left'], config['main']['top']) - windowSize = (config['main']['width'], config['main']['height']) - - #setup the splashscreen - if config['splashscreen']['show']: - filename = lh.get_file_path('hooke.jpg', ['resources']) - if os.path.isfile(filename): - bitmap = wx.Image(filename).ConvertToBitmap() - splashStyle = wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT - splashDuration = config['splashscreen']['duration'] - wx.SplashScreen(bitmap, splashStyle, splashDuration, None, -1) - wx.Yield() - ''' - we need for the splash screen to disappear - for whatever reason splashDuration and sleep do not correspond to each other - at least not on Windows - maybe it's because duration is in milliseconds and sleep in seconds - thus we need to increase the sleep time a bit - a factor of 1.2 seems to work quite well - ''' - sleepFactor = 1.2 - time.sleep(sleepFactor * splashDuration / 1000) - - plugin_objects = [] - for plugin in config['plugins']: - if 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]) - module = __import__(plugin_name) - #get the file that contains the plugin - class_file = getattr(plugins, plugin) - #get the class that contains the commands - class_object = getattr(class_file, plugin + 'Commands') - plugin_objects.append(class_object) - - def make_command_class(*bases): - #create metaclass with plugins and plotmanipulators - return type(HookeFrame)("HookeFramePlugged", bases + (HookeFrame,), {}) - frame = make_command_class(*plugin_objects)(parent=None, id=wx.ID_ANY, title='Hooke', pos=windowPosition, size=windowSize) - frame.Show(True) - self.SetTopWindow(frame) - - return True - - def OnExit(self): - #TODO: write values to ini file if necessary - return True - - -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 = '

Welcome to Hooke

' + \ - '

Features

' + \ - '' + \ - '

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.') - - -ID_PaneBorderSize = wx.ID_HIGHEST + 1 -ID_SashSize = ID_PaneBorderSize + 1 -ID_CaptionSize = ID_PaneBorderSize + 2 -ID_BackgroundColor = ID_PaneBorderSize + 3 -ID_SashColor = ID_PaneBorderSize + 4 -ID_InactiveCaptionColor = ID_PaneBorderSize + 5 -ID_InactiveCaptionGradientColor = ID_PaneBorderSize + 6 -ID_InactiveCaptionTextColor = ID_PaneBorderSize + 7 -ID_ActiveCaptionColor = ID_PaneBorderSize + 8 -ID_ActiveCaptionGradientColor = ID_PaneBorderSize + 9 -ID_ActiveCaptionTextColor = ID_PaneBorderSize + 10 -ID_BorderColor = ID_PaneBorderSize + 11 -ID_GripperColor = ID_PaneBorderSize + 12 - - -#---------------------------------------------------------------------- - -if __name__ == '__main__': - - ## now, silence a deprecation warning for py2.3 - #import warnings - #warnings.filterwarnings("ignore", "integer", DeprecationWarning, "wxPython.gdi") - - redirect=True - if __debug__: - redirect=False - app = Hooke(redirect=redirect) - - app.MainLoop() - - +# Copyright (C) 2008-2010 Fabrizio Benedetti +# Massimo Sandal +# Rolf Schmidt +# W. Trevor King +# +# This file is part of Hooke. +# +# Hooke is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Hooke is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +# Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with Hooke. If not, see +# . + +"""Hooke - A force spectroscopy review & analysis tool. +""" + +if False: # Queue pickle error debugging code + """The Hooke class is passed back from the CommandEngine process + to the main process via a :class:`multiprocessing.queues.Queue`, + which uses :mod:`pickle` for serialization. There are a number of + objects that are unpicklable, and the error messages are not + always helpful. This block of code hooks you into the Queue's + _feed method so you can print out useful tidbits to help find the + particular object that is gumming up the pickle works. + """ + import multiprocessing.queues + import sys + feed = multiprocessing.queues.Queue._feed + def new_feed (buffer, notempty, send, writelock, close): + def s(obj): + print 'SEND:', obj, dir(obj) + for a in dir(obj): + attr = getattr(obj, a) + #print ' ', a, attr, type(attr) + if obj.__class__.__name__ == 'Hooke': + # Set suspect attributes to None until you resolve the + # PicklingError. Then fix whatever is breaking the + # pickling. + #obj.commands = None + #obj.drivers = None + #obj.plugins = None + #obj.ui = None + pass + sys.stdout.flush() + send(obj) + feed(buffer, notempty, s, writelock, close) + multiprocessing.queues.Queue._feed = staticmethod(new_feed) + +from ConfigParser import NoSectionError +import logging +import logging.config +import multiprocessing +import optparse +import os.path +import Queue +import unittest +import StringIO +import sys + +from . import version +from . import engine +from . import config as config_mod +from . import playlist +from . import plugin as plugin_mod +from . import driver as driver_mod +from . import ui + + +class Hooke (object): + def __init__(self, config=None, debug=0): + self.debug = debug + default_settings = (config_mod.DEFAULT_SETTINGS + + plugin_mod.default_settings() + + driver_mod.default_settings() + + ui.default_settings()) + if config == None: + config = config_mod.HookeConfigParser( + paths=config_mod.DEFAULT_PATHS, + default_settings=default_settings) + config.read() + self.config = config + self.load_log() + self.load_plugins() + self.load_drivers() + self.load_ui() + self.engine = engine.CommandEngine() + self.playlists = playlist.NoteIndexList() + + def load_log(self): + config_file = StringIO.StringIO() + self.config.write(config_file) + logging.config.fileConfig(StringIO.StringIO(config_file.getvalue())) + # Don't attach the logger because it contains an unpicklable + # thread.lock. Instead, grab it directly every time you need it. + #self.log = logging.getLogger('hooke') + + def load_plugins(self): + self.plugins = plugin_mod.load_graph( + plugin_mod.PLUGIN_GRAPH, self.config, include_section='plugins') + self.configure_plugins() + self.commands = [] + for plugin in self.plugins: + self.commands.extend(plugin.commands()) + self.command_by_name = dict( + [(c.name, c) for c in self.commands]) + + def load_drivers(self): + self.drivers = plugin_mod.load_graph( + driver_mod.DRIVER_GRAPH, self.config, include_section='drivers') + self.configure_drivers() + + def load_ui(self): + self.ui = ui.load_ui(self.config) + self.configure_ui() + + def configure_plugins(self): + for plugin in self.plugins: + self._configure_item(plugin) + + def configure_drivers(self): + for driver in self.drivers: + self._configure_item(driver) + + def configure_ui(self): + self._configure_item(self.ui) + + def _configure_item(self, item): + conditions = self.config.items('conditions') + try: + item.config = dict(self.config.items(item.setting_section)) + except NoSectionError: + item.config = {} + for key,value in conditions: + if key not in item.config: + item.config[key] = value + + def close(self, save_config=False): + if save_config == True: + self.config.write() # Does not preserve original comments + + def run_command(self, command, arguments): + """Run the command named `command` with `arguments` using + :meth:`~hooke.engine.CommandEngine.run_command`. + + Allows for running commands without spawning another process + as in :class:`HookeRunner`. + """ + self.engine.run_command(self, command, arguments) + + +class HookeRunner (object): + def run(self, hooke): + """Run Hooke's main execution loop. + + Spawns a :class:`hooke.engine.CommandEngine` subprocess and + then runs the UI, rejoining the `CommandEngine` process after + the UI exits. + """ + ui_to_command,command_to_ui,command = self._setup_run(hooke) + try: + hooke.ui.run(hooke.commands, ui_to_command, command_to_ui) + finally: + hooke = self._cleanup_run(ui_to_command, command_to_ui, command) + return hooke + + def run_lines(self, hooke, lines): + """Run the pre-set commands `lines` with the "command line" UI. + + Allows for non-interactive sessions that are otherwise + equivalent to :meth:'.run'. + """ + cmdline = ui.load_ui(hooke.config, 'command line') + ui_to_command,command_to_ui,command = self._setup_run(hooke) + try: + cmdline.run_lines( + hooke.commands, ui_to_command, command_to_ui, lines) + finally: + hooke = self._cleanup_run(ui_to_command, command_to_ui, command) + return hooke + + def _setup_run(self, hooke): + ui_to_command = multiprocessing.Queue() + command_to_ui = multiprocessing.Queue() + manager = multiprocessing.Manager() + command = multiprocessing.Process(name='command engine', + target=hooke.engine.run, args=(hooke, ui_to_command, command_to_ui)) + command.start() + hooke.engine = None # no more need for the UI-side version. + return (ui_to_command, command_to_ui, command) + + def _cleanup_run(self, ui_to_command, command_to_ui, command): + log = logging.getLogger('hooke') + log.debug('cleanup sending CloseEngine') + ui_to_command.put(engine.CloseEngine()) + hooke = None + while not isinstance(hooke, Hooke): + log.debug('cleanup waiting for Hooke instance from the engine.') + hooke = command_to_ui.get(block=True) + log.debug('cleanup got %s instance' % type(hooke)) + command.join() + return hooke + + +def main(): + p = optparse.OptionParser() + p.add_option( + '--version', dest='version', default=False, action='store_true', + help="Print Hooke's version information and exit.") + p.add_option( + '-s', '--script', dest='script', metavar='FILE', + help='Script of command line Hooke commands to run.') + p.add_option( + '-c', '--command', dest='commands', metavar='COMMAND', + action='append', default=[], + help='Add a command line Hooke command to run.') + p.add_option( + '-p', '--persist', dest='persist', action='store_true', default=False, + help="Don't exit after running a script or commands.") + p.add_option( + '--save-config', dest='save_config', + action='store_true', default=False, + help="Automatically save a changed configuration on exit.") + p.add_option( + '--debug', dest='debug', action='store_true', default=False, + help="Enable debug logging.") + options,arguments = p.parse_args() + if len(arguments) > 0: + print >> sys.stderr, 'More than 0 arguments to %s: %s' \ + % (sys.argv[0], arguments) + p.print_help(sys.stderr) + sys.exit(1) + + hooke = Hooke(debug=__debug__) + runner = HookeRunner() + + if options.version == True: + print version() + sys.exit(0) + if options.debug == True: + hooke.config.set( + section='handler_hand1', option='level', value='NOTSET') + hooke.load_log() + if options.script != None: + with open(os.path.expanduser(options.script), 'r') as f: + options.commands.extend(f.readlines()) + if len(options.commands) > 0: + try: + hooke = runner.run_lines(hooke, options.commands) + finally: + if options.persist == False: + hooke.close(save_config=options.save_config) + sys.exit(0) + + try: + hooke = runner.run(hooke) + finally: + hooke.close(save_config=options.save_config)