X-Git-Url: http://git.tremily.us/?p=hooke.git;a=blobdiff_plain;f=hooke%2Fui%2Fgui%2F__init__.py;h=a4d91f1b045946bdeb8933942a384a635ff61e0a;hp=ed03c2fe9ada1568296711aae5870db515a01ef2;hb=56725ab3fbcd43c517ea7ebbed17f951ae736db8;hpb=f6ece66e5850e6b2d8743faa532552097e60bfd7 diff --git a/hooke/ui/gui/__init__.py b/hooke/ui/gui/__init__.py index ed03c2f..a4d91f1 100644 --- a/hooke/ui/gui/__init__.py +++ b/hooke/ui/gui/__init__.py @@ -46,8 +46,9 @@ import wx.lib.evtmgr as evtmgr from ...command import CommandExit, Exit, Success, Failure, Command, Argument from ...config import Setting +from ...engine import CommandMessage from ...interaction import Request, BooleanRequest, ReloadUserInterfaceConfig -from ...ui import UserInterface, CommandMessage +from ...ui import UserInterface from .dialog.selection import Selection as SelectionDialog from .dialog.save_file import select_save_file from . import menu as menu @@ -70,7 +71,9 @@ class HookeFrame (wx.Frame): self._perspectives = {} # {name: perspective_str} self._c = {} - self.SetIcon(wx.Icon(self.gui.config['icon image'], wx.BITMAP_TYPE_ICO)) + self.SetIcon(wx.Icon( + os.path.expanduser(self.gui.config['icon image']), + wx.BITMAP_TYPE_ICO)) # setup frame manager self._c['manager'] = aui.AuiManager() @@ -114,17 +117,7 @@ class HookeFrame (wx.Frame): self._setup_perspectives() self._bind_events() - - self.execute_command( - command=self._command_by_name('load playlist'), - args={'input':'test/data/test'},#vclamp_picoforce/playlist'}, - ) - self.execute_command( - command=self._command_by_name('load playlist'), - args={'input':'test/data/vclamp_picoforce/playlist'}, - ) return # TODO: cleanup - self.playlists = self._c['playlist'].Playlists self._displayed_plot = None #load default list, if possible self.do_loadlist(self.GetStringFromConfig('core', 'preferences', 'playlists')) @@ -174,7 +167,7 @@ class HookeFrame (wx.Frame): commands=self.commands, selected=self.gui.config['selected command'], callbacks={ - 'execute': self.execute_command, + 'execute': self.explicit_execute_command, 'select_plugin': self.select_plugin, 'select_command': self.select_command, # 'selection_changed': self.panelProperties.select(self, method, command), #SelectedTreeItem = selected_item, @@ -190,13 +183,9 @@ class HookeFrame (wx.Frame): style=wx.WANTS_CHARS, # WANTS_CHARS so the panel doesn't eat the Return key. ), 'center'), -# ('assistant', wx.TextCtrl( -# parent=self, -# pos=wx.Point(0, 0), -# size=wx.Size(150, 90), -# style=wx.NO_BORDER|wx.TE_MULTILINE), 'right'), (panel.PANELS['plot']( callbacks={ + '_set_status_text': self._on_plot_status_text, }, parent=self, style=wx.WANTS_CHARS|wx.NO_BORDER, @@ -209,10 +198,12 @@ class HookeFrame (wx.Frame): size=wx.Size(150, 90), style=wx.TE_READONLY|wx.NO_BORDER|wx.TE_MULTILINE), 'bottom'), -# ('results', panel.results.Results(self), 'bottom'), ]: self._add_panel(p, style) - #self._c['assistant'].SetEditable(False) + self.execute_command( # setup already loaded playlists + command=self._command_by_name('playlists')) + self.execute_command( # setup already loaded curve + command=self._command_by_name('get curve')) def _add_panel(self, panel, style): self._c[panel.name] = panel @@ -253,17 +244,11 @@ class HookeFrame (wx.Frame): self.Bind(wx.EVT_ERASE_BACKGROUND, self._on_erase_background) self.Bind(wx.EVT_SIZE, self._on_size) self.Bind(wx.EVT_CLOSE, self._on_close) - self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPaneClose) - self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self._on_notebook_page_close) + self.Bind(aui.EVT_AUI_PANE_CLOSE, self._on_pane_close) return # TODO: cleanup treeCtrl = self._c['folders'].GetTreeCtrl() treeCtrl.Bind(wx.EVT_LEFT_DCLICK, self._on_dir_ctrl_left_double_click) - - #property editor - self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged) - #results panel - self.panelResults.results_list.OnCheckItem = self.OnResultsCheck def _on_about(self, *args): dialog = wx.MessageDialog( @@ -276,18 +261,23 @@ class HookeFrame (wx.Frame): dialog.ShowModal() dialog.Destroy() + def _on_size(self, event): + event.Skip() + def _on_close(self, *args): self.log.info('closing GUI framework') # apply changes - self.gui.config['main height'] = str(self.GetSize().GetHeight()) - self.gui.config['main left'] = str(self.GetPosition()[0]) - self.gui.config['main top'] = str(self.GetPosition()[1]) - self.gui.config['main width'] = str(self.GetSize().GetWidth()) - # push changes back to Hooke.config? + self._set_config('main height', self.GetSize().GetHeight()) + self._set_config('main left', self.GetPosition()[0]) + self._set_config('main top', self.GetPosition()[1]) + self._set_config('main width', self.GetSize().GetWidth()) self._c['manager'].UnInit() del self._c['manager'] self.Destroy() + def _on_erase_background(self, event): + event.Skip() + # Panel utility functions @@ -311,12 +301,18 @@ class HookeFrame (wx.Frame): raise Exception('Multiple commands named "%s"' % name) return cs[0] + def explicit_execute_command(self, _class=None, method=None, + command=None, args=None): + return self.execute_command( + _class=_class, method=method, command=command, args=args, + explicit_user_call=True) + def execute_command(self, _class=None, method=None, - command=None, args=None): + command=None, args=None, explicit_user_call=False): if args == None: args = {} if ('property editor' in self._c - and self.gui.config['selected command'] == command): + and self.gui.config['selected command'] == command.name): for name,value in self._c['property editor'].get_values().items(): arg = self._c['property editor']._argument_from_label.get( name, None) @@ -331,13 +327,31 @@ class HookeFrame (wx.Frame): index = int(name[len(arg.name):]) args[arg.name][index] = value for arg in command.arguments: - if arg.count != 1 and arg.name in args: + if arg.name not in args: + continue # undisplayed argument, e.g. 'driver' types. + count = arg.count + if hasattr(arg, '_display_count'): # support HACK in props_from_argument() + count = arg._display_count + if count != 1 and arg.name in args: keys = sorted(args[arg.name].keys()) - assert keys == range(arg.count), keys + assert keys == range(count), keys args[arg.name] = [args[arg.name][i] - for i in range(arg.count)] - self.log.debug('executing %s with %s' % (command.name, args)) - self.inqueue.put(CommandMessage(command, args)) + for i in range(count)] + if arg.count == -1: + while (len(args[arg.name]) > 0 + and args[arg.name][-1] == None): + args[arg.name].pop() + if len(args[arg.name]) == 0: + args[arg.name] = arg.default + cm = CommandMessage(command.name, args) + self.gui._submit_command( + cm, self.inqueue, explicit_user_call=explicit_user_call) + # TODO: skip responses for commands that were captured by the + # command stack. We'd need to poll on each request, remember + # capture state, or add a flag to the response... + return self._handle_response(command_message=cm) + + def _handle_response(self, command_message): results = [] while True: msg = self.outqueue.get() @@ -356,9 +370,11 @@ class HookeFrame (wx.Frame): h.run(self, msg) # TODO: pause for response? continue pp = getattr( - self, '_postprocess_%s' % command.name.replace(' ', '_'), - self._postprocess_text) - pp(command=command, args=args, results=results) + self, '_postprocess_%s' % command_message.command.replace(' ', '_'), + self._postprocess_text) + pp(command=command_message.command, + args=command_message.arguments, + results=results) return results def _handle_request(self, msg): @@ -384,6 +400,10 @@ class HookeFrame (wx.Frame): continue self.inqueue.put(response) + def _set_config(self, option, value, section=None): + self.gui._set_config(section=section, option=option, value=value, + ui_to_command_queue=self.inqueue, + response_handler=self._handle_response) # Command-specific postprocessing @@ -401,6 +421,34 @@ class HookeFrame (wx.Frame): self._c['output'].write(result.__class__.__name__+'\n') self._c['output'].write(str(result).rstrip()+'\n') + def _postprocess_playlists(self, command, args={}, results=None): + """Update `self` to show the playlists. + """ + if not isinstance(results[-1], Success): + self._postprocess_text(command, results=results) + return + assert len(results) == 2, results + playlists = results[0] + if 'playlist' in self._c: + for playlist in playlists: + if self._c['playlist'].is_playlist_loaded(playlist): + self._c['playlist'].update_playlist(playlist) + else: + self._c['playlist'].add_playlist(playlist) + + def _postprocess_new_playlist(self, command, args={}, results=None): + """Update `self` to show the new playlist. + """ + if not isinstance(results[-1], Success): + self._postprocess_text(command, results=results) + return + assert len(results) == 2, results + playlist = results[0] + if 'playlist' in self._c: + loaded = self._c['playlist'].is_playlist_loaded(playlist) + assert loaded == False, loaded + self._c['playlist'].add_playlist(playlist) + def _postprocess_load_playlist(self, command, args={}, results=None): """Update `self` to show the playlist. """ @@ -409,7 +457,7 @@ class HookeFrame (wx.Frame): return assert len(results) == 2, results playlist = results[0] - self._c['playlist']._c['tree'].add_playlist(playlist) + self._c['playlist'].add_playlist(playlist) def _postprocess_get_playlist(self, command, args={}, results=[]): if not isinstance(results[-1], Success): @@ -417,7 +465,10 @@ class HookeFrame (wx.Frame): return assert len(results) == 2, results playlist = results[0] - self._c['playlist']._c['tree'].update_playlist(playlist) + if 'playlist' in self._c: + loaded = self._c['playlist'].is_playlist_loaded(playlist) + assert loaded == True, loaded + self._c['playlist'].update_playlist(playlist) def _postprocess_get_curve(self, command, args={}, results=[]): """Update `self` to show the curve. @@ -435,9 +486,9 @@ class HookeFrame (wx.Frame): else: raise NotImplementedError() if 'note' in self._c: - self._c['note'].set_text(curve.info['note']) + self._c['note'].set_text(curve.info.get('note', '')) if 'playlist' in self._c: - self._c['playlist']._c['tree'].set_selected_curve( + self._c['playlist'].set_selected_curve( playlist, curve) if 'plot' in self._c: self._c['plot'].set_curve(curve, config=self.gui.config) @@ -452,6 +503,25 @@ class HookeFrame (wx.Frame): """ pass + def _postprocess_glob_curves_to_playlist( + self, command, args={}, results=[]): + """Update `self` to show new curves. + """ + if not isinstance(results[-1], Success): + self._postprocess_text(command, results=results) + return + if 'playlist' in self._c: + if args.get('playlist', None) != None: + playlist = args['playlist'] + pname = playlist.name + loaded = self._c['playlist'].is_playlist_name_loaded(pname) + assert loaded == True, loaded + for curve in results[:-1]: + self._c['playlist']._add_curve(pname, curve) + else: + self.execute_command( + command=self._command_by_name('get playlist')) + def _postprocess_zero_block_surface_contact_point( self, command, args={}, results=[]): """Update the curve, since the available columns may have changed. @@ -470,207 +540,10 @@ class HookeFrame (wx.Frame): - # TODO: cruft - - def _GetActiveFileIndex(self): - lib.playlist.Playlist = self.GetActivePlaylist() - #get the selected item from the tree - selected_item = self._c['playlist']._c['tree'].GetSelection() - #test if a playlist or a curve was double-clicked - if self._c['playlist']._c['tree'].ItemHasChildren(selected_item): - return -1 - else: - count = 0 - selected_item = self._c['playlist']._c['tree'].GetPrevSibling(selected_item) - while selected_item.IsOk(): - count += 1 - selected_item = self._c['playlist']._c['tree'].GetPrevSibling(selected_item) - return count - - def _GetPlaylistTab(self, name): - for index, page in enumerate(self._c['notebook']._tabs._pages): - if page.caption == name: - return index - return -1 - - def select_plugin(self, _class=None, method=None, plugin=None): - pass - - def AddPlaylistFromFiles(self, files=[], name='Untitled'): - if files: - playlist = lib.playlist.Playlist(self, self.drivers) - for item in files: - playlist.add_curve(item) - if playlist.count > 0: - playlist.name = self._GetUniquePlaylistName(name) - playlist.reset() - self.AddTayliss(playlist) - - def AppliesPlotmanipulator(self, name): - ''' - Returns True if the plotmanipulator 'name' is applied, False otherwise - name does not contain 'plotmanip_', just the name of the plotmanipulator (e.g. 'flatten') - ''' - return self.GetBoolFromConfig('core', 'plotmanipulators', name) - - def ApplyPlotmanipulators(self, plot, plot_file): - ''' - Apply all active plotmanipulators. - ''' - if plot is not None and plot_file is not None: - manipulated_plot = copy.deepcopy(plot) - for plotmanipulator in self.plotmanipulators: - if self.GetBoolFromConfig('core', 'plotmanipulators', plotmanipulator.name): - manipulated_plot = plotmanipulator.method(manipulated_plot, plot_file) - return manipulated_plot - - def GetActiveFigure(self): - playlist_name = self.GetActivePlaylistName() - figure = self.playlists[playlist_name].figure - if figure is not None: - return figure - return None - - def GetActiveFile(self): - playlist = self.GetActivePlaylist() - if playlist is not None: - return playlist.get_active_file() - return None - - def GetActivePlot(self): - playlist = self.GetActivePlaylist() - if playlist is not None: - return playlist.get_active_file().plot - return None - - def GetDisplayedPlot(self): - plot = copy.deepcopy(self.displayed_plot) - #plot.curves = [] - #plot.curves = copy.deepcopy(plot.curves) - return plot - - def GetDisplayedPlotCorrected(self): - plot = copy.deepcopy(self.displayed_plot) - plot.curves = [] - plot.curves = copy.deepcopy(plot.corrected_curves) - return plot - - def GetDisplayedPlotRaw(self): - plot = copy.deepcopy(self.displayed_plot) - plot.curves = [] - plot.curves = copy.deepcopy(plot.raw_curves) - return plot - - def GetDockArt(self): - return self._c['manager'].GetArtProvider() - - def GetPlotmanipulator(self, name): - ''' - Returns a plot manipulator function from its name - ''' - for plotmanipulator in self.plotmanipulators: - if plotmanipulator.name == name: - return plotmanipulator - return None - - def HasPlotmanipulator(self, name): - ''' - returns True if the plotmanipulator 'name' is loaded, False otherwise - ''' - for plotmanipulator in self.plotmanipulators: - if plotmanipulator.command == name: - return True - return False - - - def _on_dir_ctrl_left_double_click(self, event): - file_path = self.panelFolders.GetPath() - if os.path.isfile(file_path): - if file_path.endswith('.hkp'): - self.do_loadlist(file_path) - event.Skip() - - def _on_erase_background(self, event): - event.Skip() - - def _on_notebook_page_close(self, event): - ctrl = event.GetEventObject() - playlist_name = ctrl.GetPageText(ctrl._curpage) - self.DeleteFromPlaylists(playlist_name) - - def OnPaneClose(self, event): - event.Skip() - - def OnPropGridChanged (self, event): - prop = event.GetProperty() - if prop: - item_section = self.panelProperties.SelectedTreeItem - item_plugin = self._c['commands']._c['tree'].GetItemParent(item_section) - plugin = self._c['commands']._c['tree'].GetItemText(item_plugin) - config = self.gui.config[plugin] - property_section = self._c['commands']._c['tree'].GetItemText(item_section) - property_key = prop.GetName() - property_value = prop.GetDisplayedString() - - config[property_section][property_key]['value'] = property_value - - def OnResultsCheck(self, index, flag): - results = self.GetActivePlot().results - if results.has_key(self.results_str): - results[self.results_str].results[index].visible = flag - results[self.results_str].update() - self.UpdatePlot() - - - def _on_size(self, event): - event.Skip() - - def UpdatePlaylistsTreeSelection(self): - playlist = self.GetActivePlaylist() - if playlist is not None: - if playlist.index >= 0: - self._c['status bar'].set_playlist(playlist) - self.UpdateNote() - self.UpdatePlot() - - def _on_curve_select(self, playlist, curve): - #create the plot tab and add playlist to the dictionary - plotPanel = panel.plot.PlotPanel(self, ID_FirstPlot + len(self.playlists)) - notebook_tab = self._c['notebook'].AddPage(plotPanel, playlist.name, True) - #tab_index = self._c['notebook'].GetSelection() - playlist.figure = plotPanel.get_figure() - self.playlists[playlist.name] = playlist - #self.playlists[playlist.name] = [playlist, figure] - self._c['status bar'].set_playlist(playlist) - self.UpdateNote() - self.UpdatePlot() - - - def _on_playlist_left_doubleclick(self): - index = self._c['notebook'].GetSelection() - current_playlist = self._c['notebook'].GetPageText(index) - if current_playlist != playlist_name: - index = self._GetPlaylistTab(playlist_name) - self._c['notebook'].SetSelection(index) - self._c['status bar'].set_playlist(playlist) - self.UpdateNote() - self.UpdatePlot() - - def _on_playlist_delete(self, playlist): - notebook = self.Parent.plotNotebook - index = self.Parent._GetPlaylistTab(playlist.name) - notebook.SetSelection(index) - notebook.DeletePage(notebook.GetSelection()) - self.Parent.DeleteFromPlaylists(playlist_name) - - - # Command panel interface def select_command(self, _class, method, command): #self.select_plugin(plugin=command.plugin) - if 'assistant' in self._c: - self._c['assitant'].ChangeValue(command.help) self._c['property editor'].clear() self._c['property editor']._argument_from_label = {} for argument in command.arguments: @@ -702,7 +575,21 @@ class HookeFrame (wx.Frame): self._c['property editor']._argument_from_label[label] = ( argument) - self.gui.config['selected command'] = command # TODO: push to engine + self._set_config('selected command', command.name) + + def select_plugin(self, _class=None, method=None, plugin=None): + pass + + + + # Folders panel interface + + def _on_dir_ctrl_left_double_click(self, event): + file_path = self.panelFolders.GetPath() + if os.path.isfile(file_path): + if file_path.endswith('.hkp'): + self.do_loadlist(file_path) + event.Skip() @@ -730,6 +617,7 @@ class HookeFrame (wx.Frame): pass def _on_delete_curve(self, _class, method, playlist, curve): + # TODO: execute_command 'remove curve from playlist' os.remove(curve.path) def _on_set_selected_playlist(self, _class, method, playlist): @@ -767,6 +655,14 @@ class HookeFrame (wx.Frame): + # Plot panel interface + + def _on_plot_status_text(self, _class, method, text): + if 'status bar' in self._c: + self._c['status bar'].set_plot_text(text) + + + # Navbar interface def _next_curve(self, *args): @@ -791,6 +687,13 @@ class HookeFrame (wx.Frame): # Panel display handling + def _on_pane_close(self, event): + pane = event.pane + view = self._c['menu bar']._c['view'] + if pane.name in view._c.keys(): + view._c[pane.name].Check(False) + event.Skip() + def _on_panel_visibility(self, _class, method, panel_name, visible): pane = self._c['manager'].GetPane(panel_name) pane.Show(visible) @@ -806,7 +709,7 @@ class HookeFrame (wx.Frame): self._perspectives = { 'Default': self._c['manager'].SavePerspective(), } - path = self.gui.config['perspective path'] + path = os.path.expanduser(self.gui.config['perspective path']) if os.path.isdir(path): files = sorted(os.listdir(path)) for fname in files: @@ -824,7 +727,7 @@ class HookeFrame (wx.Frame): selected_perspective = self.gui.config['active perspective'] if not self._perspectives.has_key(selected_perspective): - self.gui.config['active perspective'] = 'Default' # TODO: push to engine's Hooke + self._set_config('active perspective', 'Default') self._restore_perspective(selected_perspective, force=True) self._update_perspective_menu() @@ -869,7 +772,7 @@ class HookeFrame (wx.Frame): def _restore_perspective(self, name, force=False): if name != self.gui.config['active perspective'] or force == True: self.log.debug('restore perspective %s' % name) - self.gui.config['active perspective'] = name # TODO: push to engine's Hooke + self._set_config('active perspective', name) self._c['manager'].LoadPerspective(self._perspectives[name]) self._c['manager'].Update() for pane in self._c['manager'].GetAllPanes(): @@ -883,7 +786,7 @@ class HookeFrame (wx.Frame): if name == 'Default': name = 'New perspective' name = select_save_file( - directory=self.gui.config['perspective path'], + directory=os.path.expanduser(self.gui.config['perspective path']), name=name, extension=self.gui.config['perspective extension'], parent=self, @@ -892,7 +795,8 @@ class HookeFrame (wx.Frame): if name == None: return self._save_perspective( - perspective, self.gui.config['perspective path'], name=name, + perspective, + os.path.expanduser(self.gui.config['perspective path']), name=name, extension=self.gui.config['perspective extension']) def _on_delete_perspective(self, *args, **kwargs): @@ -908,11 +812,13 @@ class HookeFrame (wx.Frame): style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) dialog.CenterOnScreen() dialog.ShowModal() + if dialog.canceled == True: + return names = [options[i] for i in dialog.selected] dialog.Destroy() self._delete_perspectives( - self.gui.config['perspective path'], names=names, - extension=self.gui.config['perspective extension']) + os.path.expanduser(self.gui.config['perspective path']), + names=names, extension=self.gui.config['perspective extension']) def _on_select_perspective(self, _class, method, name): self._restore_perspective(name) @@ -964,7 +870,7 @@ class HookeApp (wx.App): def _setup_splash_screen(self): if self.gui.config['show splash screen'] == True: - path = self.gui.config['splash screen image'] + path = os.path.expanduser(self.gui.config['splash screen image']) if os.path.isfile(path): duration = self.gui.config['splash screen duration'] wx.SplashScreen(