X-Git-Url: http://git.tremily.us/?p=hooke.git;a=blobdiff_plain;f=hooke%2Fui%2Fgui%2F__init__.py;h=f83936d0a1c09959f042a6ccecbca58e5af4c95d;hp=69f5a61588a7c698ba9bdbbb45f3a43942b722ae;hb=46e8c517dc689996eee20655831f878b8a25e4d2;hpb=a69e9a38afd86cd12a26c244b546fefe3f5d00b6 diff --git a/hooke/ui/gui/__init__.py b/hooke/ui/gui/__init__.py index 69f5a61..f83936d 100644 --- a/hooke/ui/gui/__init__.py +++ b/hooke/ui/gui/__init__.py @@ -1,6 +1,6 @@ # Copyright -"""Defines :class:`GUI` providing a wxWindows interface to Hooke. +"""Defines :class:`GUI` providing a wxWidgets interface to Hooke. """ WX_GOOD=['2.8'] @@ -26,8 +26,7 @@ import wx.lib.evtmgr as evtmgr from matplotlib.ticker import FuncFormatter -from ... import version -from ...command import CommandExit, Exit, Command, Argument, StoreValue +from ...command import CommandExit, Exit, Success, Failure, Command, Argument from ...config import Setting from ...interaction import Request, BooleanRequest, ReloadUserInterfaceConfig from ...ui import UserInterface, CommandMessage @@ -75,14 +74,18 @@ class HookeFrame (wx.Frame): # Create the menubar after the panes so that the default # perspective is created with all panes open - self._c['menu bar'] = menu.MenuBar( - callbacks={}, - ) + self._c['menu bar'] = menu.HookeMenuBar( + parent=self, + callbacks={ + 'close': self._on_close, + 'about': self._on_about, + }) self.SetMenuBar(self._c['menu bar']) - self._c['status bar'] = statubar.StatusBar( + self._c['status bar'] = statusbar.StatusBar( parent=self, style=wx.ST_SIZEGRIP) + self.SetStatusBar(self._c['status bar']) self._update_perspectives() self._bind_events() @@ -101,28 +104,31 @@ class HookeFrame (wx.Frame): def _setup_panels(self): client_size = self.GetClientSize() for label,p,style in [ - ('folders', wx.GenericDirCtrl( - parent=self, - dir=self.gui.config['folders-workdir'], - size=(200, 250), - style=wx.DIRCTRL_SHOW_FILTERS, - filter=self.gui.config['folders-filters'], - defaultFilter=int(self.gui.config['folders-filter-index'])), 'left'), #HACK: config should convert - ('playlists', panel.playlist.Playlist( - config=self.gui.config, - callbacks={}, - parent=self, - style=wx.WANTS_CHARS|wx.NO_BORDER, - # WANTS_CHARS so the panel doesn't eat the Return key. - size=(160, 200)), 'left'), - ('note', panel.note.Note(self), 'left'), - ('notebook', Notebook( - parent=self, - pos=wx.Point(client_size.x, client_size.y), - size=wx.Size(430, 200), - style=aui.AUI_NB_DEFAULT_STYLE - | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER), 'center'), - ('commands', panel.commands.Commands( +# ('folders', wx.GenericDirCtrl( +# parent=self, +# dir=self.gui.config['folders-workdir'], +# size=(200, 250), +# style=wx.DIRCTRL_SHOW_FILTERS, +# filter=self.gui.config['folders-filters'], +# defaultFilter=int(self.gui.config['folders-filter-index'])), 'left'), #HACK: config should convert +# ('playlists', panel.PANELS['playlist']( +# callbacks={}, +# config=self.gui.config, +# parent=self, +# style=wx.WANTS_CHARS|wx.NO_BORDER, +# # WANTS_CHARS so the panel doesn't eat the Return key. +# size=(160, 200)), 'left'), +# ('note', panel.note.Note( +# parent=self +# style=wx.WANTS_CHARS|wx.NO_BORDER, +# size=(160, 200)), 'left'), +# ('notebook', Notebook( +# parent=self, +# pos=wx.Point(client_size.x, client_size.y), +# size=wx.Size(430, 200), +# style=aui.AUI_NB_DEFAULT_STYLE +# | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER), 'center'), + ('commands', panel.PANELS['commands']( commands=self.commands, selected=self.gui.config['selected command'], callbacks={ @@ -134,49 +140,52 @@ class HookeFrame (wx.Frame): parent=self, style=wx.WANTS_CHARS|wx.NO_BORDER, # WANTS_CHARS so the panel doesn't eat the Return key. - size=(160, 200)), 'right'), +# size=(160, 200) + ), 'center'), #('properties', panel.propertyeditor.PropertyEditor(self),'right'), - ('assistant', wx.TextCtrl( - parent=self, - pos=wx.Point(0, 0), - size=wx.Size(150, 90), - style=wx.NO_BORDER|wx.TE_MULTILINE), 'right'), - ('output', wx.TextCtrl( - parent=self, - pos=wx.Point(0, 0), - size=wx.Size(150, 90), - style=wx.NO_BORDER|wx.TE_MULTILINE), 'bottom'), - ('results', panel.results.Results(self), 'bottom'), +# ('assistant', wx.TextCtrl( +# parent=self, +# pos=wx.Point(0, 0), +# size=wx.Size(150, 90), +# style=wx.NO_BORDER|wx.TE_MULTILINE), 'right'), +# ('output', wx.TextCtrl( +# parent=self, +# pos=wx.Point(0, 0), +# size=wx.Size(150, 90), +# style=wx.NO_BORDER|wx.TE_MULTILINE), 'bottom'), +# ('results', panel.results.Results(self), 'bottom'), ]: self._add_panel(label, p, style) - self._c['assistant'].SetEditable(False) + #self._c['assistant'].SetEditable(False) def _add_panel(self, label, panel, style): self._c[label] = panel cap_label = label.capitalize() info = aui.AuiPaneInfo().Name(cap_label).Caption(cap_label) - if style == 'left': - info.Left().CloseButton(True).MaximizeButton(False) + info.PaneBorder(False).CloseButton(True).MaximizeButton(False) + if style == 'top': + info.Top() elif style == 'center': - info.CenterPane().PaneBorder(False) + info.CenterPane() + elif style == 'left': + info.Left() elif style == 'right': - info.Right().CloseButton(True).MaximizeButton(False) + info.Right() else: assert style == 'bottom', style - info.Bottom().CloseButton(True).MaximizeButton(False) + info.Bottom() self._c['manager'].AddPane(panel, info) def _setup_toolbars(self): - self._c['navbar'] = navbar.NavBar( + self._c['navigation bar'] = navbar.NavBar( callbacks={ 'next': self._next_curve, 'previous': self._previous_curve, }, parent=self, style=wx.TB_FLAT | wx.TB_NODIVIDER) - self._c['manager'].AddPane( - self._c['navbar'], + self._c['navigation bar'], aui.AuiPaneInfo().Name('Navigation').Caption('Navigation' ).ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False ).RightDockable(False)) @@ -188,11 +197,10 @@ 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(wx.EVT_MENU, self._on_close, id=wx.ID_EXIT) - self.Bind(wx.EVT_MENU, self._on_about, id=wx.ID_ABOUT) self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPaneClose) self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self._on_notebook_page_close) + return # TODO: cleanup for value in self._c['menu bar']._c['view']._c.values(): self.Bind(wx.EVT_MENU_RANGE, self._on_view, value) @@ -212,6 +220,65 @@ class HookeFrame (wx.Frame): #results panel self.panelResults.results_list.OnCheckItem = self.OnResultsCheck + def _command_by_name(self, name): + cs = [c for c in self.commands if c.name == name] + if len(cs) == 0: + raise KeyError(name) + elif len(cs) > 1: + raise Exception('Multiple commands named "%s"' % name) + return cs[0] + + def execute_command(self, _class=None, method=None, + command=None, args=None): + self.inqueue.put(CommandMessage(command, args)) + results = [] + while True: + msg = self.outqueue.get() + results.append(msg) + print type(msg), msg + if isinstance(msg, Exit): + self._on_close() + break + elif isinstance(msg, CommandExit): + # TODO: display command complete + break + elif isinstance(msg, ReloadUserInterfaceConfig): + self.gui.reload_config(msg.config) + continue + elif isinstance(msg, Request): + h = handler.HANDLERS[msg.type] + h.run(self, msg) # TODO: pause for response? + continue + pp = getattr( + self, '_postprocess_%s' % command.name.replace(' ', '_'), None) + if pp != None: + pp(command=command, results=results) + return results + + def _handle_request(self, msg): + """Repeatedly try to get a response to `msg`. + """ + if prompt == None: + raise NotImplementedError('_%s_request_prompt' % msg.type) + prompt_string = prompt(msg) + parser = getattr(self, '_%s_request_parser' % msg.type, None) + if parser == None: + raise NotImplementedError('_%s_request_parser' % msg.type) + error = None + while True: + if error != None: + self.cmd.stdout.write(''.join([ + error.__class__.__name__, ': ', str(error), '\n'])) + self.cmd.stdout.write(prompt_string) + value = parser(msg, self.cmd.stdin.readline()) + try: + response = msg.response(value) + break + except ValueError, error: + continue + self.inqueue.put(response) + + def _GetActiveFileIndex(self): lib.playlist.Playlist = self.GetActivePlaylist() #get the selected item from the tree @@ -249,34 +316,12 @@ class HookeFrame (wx.Frame): perspectivesFile.write(perspective) perspectivesFile.close() - def execute_command(self, _class, method, command, args): - self.cmd.inqueue.put(CommandMessage(command, args)) - while True: - msg = self.cmd.outqueue.get() - if isinstance(msg, Exit): - return True - elif isinstance(msg, CommandExit): - self.cmd.stdout.write(msg.__class__.__name__+'\n') - self.cmd.stdout.write(str(msg).rstrip()+'\n') - break - elif isinstance(msg, ReloadUserInterfaceConfig): - self.cmd.ui.reload_config(msg.config) - continue - elif isinstance(msg, Request): - self._handle_request(msg) - continue - self.cmd.stdout.write(str(msg).rstrip()+'\n') - #TODO: run the command - #command = ''.join(['self.do_', item_text, '()']) - #self.AppendToOutput(command + '\n') - #exec(command) - - def select_plugin(self, _class, method, plugin): + def select_plugin(self, _class=None, method=None, plugin=None): for option in config[section]: properties.append([option, config[section][option]]) def select_command(self, _class, method, command): - self.select_plugin(command.plugin) + self.select_plugin(plugin=command.plugin) plugin = self.GetItemText(selected_item) if plugin != 'core': doc_string = eval('plugins.' + plugin + '.' + plugin + 'Commands.__doc__') @@ -375,7 +420,7 @@ class HookeFrame (wx.Frame): perspectives_list.sort() index = perspectives_list.index(name) perspective_Id = ID_FirstPerspective + index - menu_item = self.MenuBar.FindItemById(perspective_Id) + menu_item = self._c['menu bar'].FindItemById(perspective_Id) return menu_item else: return None @@ -389,17 +434,16 @@ class HookeFrame (wx.Frame): return True return False - def _on_about(self, event): - message = 'Hooke\n\n'+\ - 'A free, open source data analysis platform\n\n'+\ - 'Copyright 2006-2008 by Massimo Sandal\n'+\ - 'Copyright 2010 by Dr. Rolf Schmidt\n\n'+\ - 'Hooke is released under the GNU General Public License version 2.' - dialog = wx.MessageDialog(self, message, 'About Hooke', wx.OK | wx.ICON_INFORMATION) + def _on_about(self, *args): + dialog = wx.MessageDialog( + parent=self, + message=self.gui._splash_text(), + caption='About Hooke', + style=wx.OK|wx.ICON_INFORMATION) dialog.ShowModal() dialog.Destroy() - def _on_close(self, event): + def _on_close(self, *args): # apply changes self.gui.config['main height'] = str(self.GetSize().GetHeight()) self.gui.config['main left'] = str(self.GetPosition()[0]) @@ -446,13 +490,13 @@ class HookeFrame (wx.Frame): self._on_restore_perspective) def _on_restore_perspective(self, event): - name = self.MenuBar.FindItemById(event.GetId()).GetLabel() + name = self._c['menu bar'].FindItemById(event.GetId()).GetLabel() self._restore_perspective(name) def _on_save_perspective(self, event): def nameExists(name): - menu_position = self.MenuBar.FindMenu('Perspective') - menu = self.MenuBar.GetMenu(menu_position) + menu_position = self._c['menu bar'].FindMenu('Perspective') + menu = self._c['menu bar'].GetMenu(menu_position) for item in menu.GetMenuItems(): if item.GetText() == name: return True @@ -512,7 +556,8 @@ class HookeFrame (wx.Frame): options=sorted(os.listdir(self.gui.config['perspective path'])), message="\nPlease check the perspectives\n\nyou want to delete and click 'Delete'.\n", button_id=wx.ID_DELETE, - button_callback=self._on_delete_perspective, + callbacks={'button': self._on_delete_perspective}, + selection_style='multiple', parent=self, label='Delete perspective(s)', style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) @@ -525,13 +570,13 @@ class HookeFrame (wx.Frame): # ) that makes the radio item indicator in the menu disappear. # The code should be fine once this issue is fixed. - def _on_delete_perspective(self, event, items, selected_items): - for item in selected_items: - self._perspectives.remove(item) - if item == self.gui.config['active perspective']: + def _on_delete_perspective(self, _class, method, options, selected): + for p in selected: + self._perspectives.remove(p) + if p == self.gui.config['active perspective']: self.gui.config['active perspective'] = 'Default' path = os.path.join(self.gui.config['perspective path'], - item+'.txt') + p+'.txt') remove(path) self._update_perspective_menu() @@ -546,13 +591,32 @@ class HookeFrame (wx.Frame): event.Skip() def _next_curve(self, *args): - ''' - 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 - ''' + """Call the `next curve` command. + """ + results = self.execute_command( + command=self._command_by_name('next curve')) + if isinstance(results[-1], Success): + self.execute_command( + command=self._command_by_name('get curve')) + + def _previous_curve(self, *args): + """Call the `previous curve` command. + """ + self.execute_command( + command=self._command_by_name('previous curve')) + if isinstance(results[-1], Success): + self.execute_command( + command=self._command_by_name('get curve')) + + def _postprocess_get_curve(self, command, results): + """Update `self` to show the curve. + """ + if not isinstance(results[-1], Success): + return # error executing 'get curve' + assert len(results) == 2, results + curve = results[0] + print curve + selected_item = self._c['playlists']._c['tree'].GetSelection() if self._c['playlists']._c['tree'].ItemHasChildren(selected_item): #GetFirstChild returns a tuple @@ -574,33 +638,6 @@ class HookeFrame (wx.Frame): self.UpdateNote() self.UpdatePlot() - def _previous_curve(self, *args): - ''' - 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._c['playlists']._c['tree'].GetSelection() - if self._c['playlists']._c['tree'].ItemHasChildren(selected_item): - previous_item = self._c['playlists']._c['tree'].GetLastChild(selected_item) - else: - previous_item = self._c['playlists']._c['tree'].GetPrevSibling(selected_item) - if not previous_item.IsOk(): - parent_item = self._c['playlists']._c['tree'].GetItemParent(selected_item) - previous_item = self._c['playlists']._c['tree'].GetLastChild(parent_item) - self._c['playlists']._c['tree'].SelectItem(previous_item, True) - playlist = self.GetActivePlaylist() - if playlist.count > 1: - playlist.previous() - self._c['status bar'].set_playlist(playlist) - self.UpdateNote() - self.UpdatePlot() - def _on_notebook_page_close(self, event): ctrl = event.GetEventObject() playlist_name = ctrl.GetPageText(ctrl._curpage) @@ -642,7 +679,7 @@ class HookeFrame (wx.Frame): def _on_view(self, event): menu_id = event.GetId() - menu_item = self.MenuBar.FindItemById(menu_id) + menu_item = self._c['menu bar'].FindItemById(menu_id) menu_label = menu_item.GetLabel() pane = self._c['manager'].GetPane(menu_label)