def __init__(self, frame): Console.__init__(self) # Register commands commands = [ # File commands ('open', self.open, 'open EEGFILE [FMT]'), ('pwd', self.pwd, 'pwd'), ('cd', self.cd, 'cd DIR'), ('ls', self.ls, 'ls [DIR]'), ('onexit', self.onexit, 'onexit'), # View commands ('focus', self.focus, 'focus [PANEL]'), ('show', self.show, 'show [PANEL [ACTION]]'), ('mode', self.mode, 'mode MODE'), # Navigation commands ('time', self.time, 'time [TIMESTR]'), ('go', self.go, 'go [left [X] | right [X] | to TIME]'), ('scale', self.scale, 'scale [down|up|SCALE]'), ('zoom', self.zoom, 'zoom [in|out|SECONDS]'), ('reset', self.reset, 'reset'), # For development ('profile', self.profile, 'profile'), ] for cmdname, func, usage in commands: self.add_command(cmdname, func, usage) # Initialise data used below self.display = Display(frame) self.eegfile = None
class ViewerConsole(Console): """Extends Console to provide directory exploration""" # Page widths in seconds PAGE_WIDTHS = [ 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 100000 ] # Scales in EEG file dimension (hopefully uV) SCALES = [ 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 100000 ] def __init__(self, frame): Console.__init__(self) # Register commands commands = [ # File commands ('open', self.open, 'open EEGFILE [FMT]'), ('pwd', self.pwd, 'pwd'), ('cd', self.cd, 'cd DIR'), ('ls', self.ls, 'ls [DIR]'), ('onexit', self.onexit, 'onexit'), # View commands ('focus', self.focus, 'focus [PANEL]'), ('show', self.show, 'show [PANEL [ACTION]]'), ('mode', self.mode, 'mode MODE'), # Navigation commands ('time', self.time, 'time [TIMESTR]'), ('go', self.go, 'go [left [X] | right [X] | to TIME]'), ('scale', self.scale, 'scale [down|up|SCALE]'), ('zoom', self.zoom, 'zoom [in|out|SECONDS]'), ('reset', self.reset, 'reset'), # For development ('profile', self.profile, 'profile'), ] for cmdname, func, usage in commands: self.add_command(cmdname, func, usage) # Initialise data used below self.display = Display(frame) self.eegfile = None # Commands provided by ViewerConsole def open(self, eegname=None, fmt=None): """Open EEG file for viewing open EEGFILE: open file EEGFILE base on extension open EEGFILE FMT: open EEGFILE as FMT typ EEG file Type 'formats' to list supported formats. """ # Open EEG file if eegname is None: if fmt is None: fmt = 'edf' newfile = openeeg(sys.stdin, mode='r', fmt=fmt, cached=True) else: if not os.path.exists(eegname): raise ConsoleError("file '{0}' does not exist".format(eegname)) newfile = openeeg(eegname, mode='r', fmt=fmt) # Close previous file and keep new if self.eegfile: self.eegfile.close() self.eegfile = newfile # Update display self.display.set_eegfile(newfile) self.display.redraw() yield "file '{0}' opened.\n".format(newfile.name) def pwd(self): """Display the current working directory""" yield os.getcwd() + '\n' def cd(self, newdir): """Change the working directory""" if newdir == '-': if not hasattr(self, '_olddir'): raise ConsoleError('No previous directory') newdir = self._olddir yield 'cd {0}\n'.format(newdir) if not os.path.exists(newdir): raise ConsoleError("The path '{0}' does not exist".format(newdir)) if not os.path.isdir(newdir): raise ConsoleError("The path '{0}' is not a directory".format(newdir)) self._olddir = os.getcwd() os.chdir(newdir) yield os.getcwd() + '\n' def ls(self, path=None): if path is None: path = os.getcwd() yield ', '.join(path for path in os.listdir(path)) + '\n' def onexit(self): """Quit the program""" frame = self.display.frame if hasattr(frame,'_cursor_file') and frame._cursor_file: frame.SaveCursors(frame._cursor_file) yield # ::::::::::::::::::::: view ::::::::::::::::::::::::::::::::::::::::: def _check_panel(self, panelname): if panelname not in self.display.panel_names(): msg = "Unrecognised panel '{0}'. type 'panels' to list" raise ConsoleError(msg.format(panelname)) def focus(self, panelname=None): """Set keyboard focus to PANEL e.g.: focus: list panels that can be focussed focus PANEL: set focus to PANEL """ if panelname is None: yield ', '.join(sorted(self.display.panel_names())) + '\n' return self._check_panel(panelname) self.display.focus_panel(panelname) yield def show(self, panelname=None, action='toggle'): """Toggle showing PANEL e.g.: show: list showable panels show PANEL: toggle showing PANEL show PANEL [toggle|on|off|query]: toggle, set on/off, or print \\the show status of PANEL """ if panelname is None: yield ', '.join(sorted(self.display.panel_names())) + '\n' return action = action.lower() self._check_panel(panelname) showing = self.display.panel_showing(panelname) if action == 'toggle': showing = not showing elif action == 'on': showing = True elif action == 'off': showing = False elif action == 'query': words = {True:'on', False:'off'} msg = "panel '{0}' showing status is '{1}'" yield msg.format(panelname, words[showing]) return else: raise ConsoleError("Unrecognised action") # Now communicate action to panel self.display.show_panel(panelname, showing) def mode(self, mode): """Set the graphics mode to MODE mode [gl|mpl]: set the graphics mode to gl/mpl """ try: self.display.set_graphics_mode(mode) except ImportError: raise ConsoleError("Mode unavailable") except ValueError: raise ConsoleError("Unrecognised mode") self.display.redraw() yield # ::::::::::::::::::::: navigation ::::::::::::::::::::::::::::::::::::::: def reset(self): """Restore default view reset: restore default view""" self.display.reset() self.display.redraw() yield def time(self, timestr=None): """Print the time of the current EEG view or interpretation of the \\time string: timestr e.g.: time: print time of left edge of current view. time TIMESTR: print full date and time corresponding to TIMESTR in the \\context of the current EEG file. TIMESTR should be a string of the form [DATE-]TIME where \\DATE is of the form 'dd/MM[/[YY]YY]' and \\TIME is of the form 'HH:mm[:ss[.ssss]]' (seconds can be non-whole \\number in decimal notation). """ if not self.eegfile: raise ConsoleError("No EEG file open") if timestr is None: t1, t2 = self.display.get_xlim() t = self.eegfile.as_datetime(t1) yield 'Display time is:\n' else: t = parse_time_string(timestr, self.eegfile.tstart) yield 'Time string interpreted as:\n' yield '{0}\n'.format(t) def go(self, flag=None, arg=None): """Query or set the viewed time within the EEG file. go: display the time of the left edge of the screen go left: go left by 1 screen width go right: go right by 1 screen width go left X: go left by X screen widths go right X: go right by X screen widths go to TIMESTR: go to time specified by TIMESTR Type 'help time' for information on specifying times.""" # Get start time and screen width t1, t2 = self.display.get_xlim() t, w = t1, t2 - t1 if flag is not None: # Apply default value for arg and check command if arg is None: if flag in ('left', 'right'): arg = 1 elif flag == 'to': raise ConsoleError("need to specify time with 'go to'") else: raise ConsoleError("unknown command: '{0}'".format(flag)) # Now run appropriate command if flag == 'left': t -= w * make_int(parse_float(arg)) elif flag == 'right': t += w * make_int(parse_float(arg)) elif flag == 'to': dt = parse_time_string(arg, self.eegfile.tstart) t = self.eegfile.as_seconds(dt) # Apply the new time t1, t2 = self._force_bounds((t, t + w), _errors=True) self.display.set_xlim((t1, t2)) self.display.redraw() dt = self.eegfile.as_datetime(t1) yield "Current time is {0}\n".format(dt) def zoom(self, seconds=None): """Query or set the width of the EEG view in seconds zoom: display the current page width value zoom SECONDS: set the page width to SECONDS seconds zoom in: set the page width one level smaller zoom out: set the page width one level larger""" t1, t2 = self.display.get_xlim() t, w = t1, t2 - t1 if seconds is not None: if seconds == 'in': w = find_next_smaller(w, self.PAGE_WIDTHS) elif seconds =='out': w = find_next_bigger(w, self.PAGE_WIDTHS) else: w = make_int(parse_float(seconds)) t1, t2 = self._force_bounds((t, t + w), _errors=True) self.display.set_xlim((t1, t2)) self.display.redraw() yield "Screen width is {0} seconds\n".format(make_int(t2 - t1)) def scale(self, scale=None): """Query or set the vertical EEG scale. scale: display current scale value scale SCALE: set the scale to SCALE""" s = self.display.get_scale() if scale is not None: if scale == 'down': s = find_next_smaller(s, self.SCALES) elif scale == 'up': s = find_next_bigger(s, self.SCALES) else: s = make_int(parse_float(level)) self.display.set_scale(s) self.display.redraw() yield "Display scale is {0} signal units\n".format(s) def profile(self, panby): """Test the graphics performance by continuously panning the \\display profile: pan continuously""" while True: tlims = self.display.get_xlim() for n in self.go('right', panby): yield '' self.display.frame.eegpanel.canvas.Update() if self.display.get_xlim() == tlims: break yield 'done\n' def _force_bounds(self, newxlim, _errors): """Adjust inner limits to fit into the outer limits""" oldxlim = self.display.get_xlim() x1, x2 = newxlim xmin, xmax = self.display.outer_limits if x2 - x1 > xmax - xmin: newxlim = xmin, xmax self._check(newxlim, _errors, "Already showing whole file") elif x1 < xmin: delta = xmin - x1 newxlim = x1 + delta, x2 + delta self._check(newxlim, _errors, "Already at beginning of file") elif x2 > xmax: delta = x2 - xmax newxlim = x1 - delta, x2 - delta self._check(newxlim, _errors, "Already at end of file") else: newxlim = x1, x2 return newxlim def _check(self, newxlim, _errors, msg): if _errors and newxlim == self.display.get_xlim(): raise ConsoleError(msg) def _interactive_pan(self, seconds): """Pan the view by ammount seconds. This function is the handler for interactive panning using the mouse""" t1, t2 = self.display.get_xlim() xlim = self._force_bounds((t1 + seconds, t2 + seconds), _errors=False) self.display.set_xlim(xlim) self.display.redraw() def _interactive_zoom(self, xlim, yfactor): """Pan the view by ammount seconds. This function is the handler for interactive panning using the mouse""" # Compute new scale xlim = self._force_bounds(xlim, _errors=False) self.display.set_xlim(xlim) self.display.set_scale(self.display.get_scale() / float(yfactor)) self.display.redraw()