예제 #1
0
    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
예제 #2
0
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()