def participant_context(exp: Experiment, participant: ExperimentSection):
    term = Terminal()
    exp.session_data['term'] = term
    exp.session_data['stimuli'] = [
        jpg_to_ascii(os.path.join('stimuli', '{:02}.jpg'.format(i)), background='light', height=term.height - 2)
        for i in range(1, 16)
    ]

    with term.fullscreen():
        print(term.move(int(term.width//2), int(term.height//2)))
        response_type = participant.data['response_type']
        instructions = exp.experiment_data['instructions']
        for i, instruction in enumerate(instructions, 1):
            message = get_message(instruction, response_type)

            if message.startswith('_example'):
                show_example(term, response_type, message.split('_')[2], exp.session_data['stimuli'],
                             exp.experiment_data['example_response_message'],
                             ascii_stimuli=exp.experiment_data.get('ascii_stimuli', True),
                             stimulus_time=8)

            else:
                reset(term)
                wrapped_multiline_print_at_location(term, message, X_MARGIN, Y_MARGIN, int(term.width//2))
                with term.location(term.width//2, term.height - 3):
                    print('{}/{}'.format(i, len(instructions)))
                time.sleep(1)  # Ensure we don't accidentally skip a message.
                input()
        yield
        reset(term)
        print(exp.experiment_data['exit_message'])
        input()

    del exp.session_data['term']
Ejemplo n.º 2
0
 def __init__(self, kind, stream, rows, columns):
     """ Class initializer. """
     self._rows = rows
     self._columns = columns
     BlessedTerminal.__init__(self, kind, stream)
     if sys.platform.lower().startswith('win32'):
         self._normal = '\x1b[m'
Ejemplo n.º 3
0
def inputter():
    term = Terminal()
    with term.cbreak():
        key = term.inkey(1)
        if key and not key.is_sequence:
            return key
        return None
Ejemplo n.º 4
0
def main():
    """
    Displays all known key capabilities that may match the terminal.
    As each key is pressed on input, it is lit up and points are scored.
    """
    try:
        from x84.bbs import getterminal, echo

        term = getterminal()
    except (ImportError, AttributeError):
        from blessed import Terminal
        import sys

        term = Terminal()

        def echo(text):
            sys.stdout.write(u"{}".format(text))
            sys.stdout.flush()

    echo(u"".join((term.normal, term.height * u"\r\n", term.home, term.clear_eos)))

    with term.raw():
        inp = u""
        echo(u"Press Q to exit.\r\n")
        while inp.upper() != "Q":
            inp = term.inkey(timeout=10.0)
            disp_inp = inp.__str__() if inp.is_sequence else inp
            echo(u"{0!r}: code={1!r} name={2!r}\r\n".format(disp_inp, inp.code, inp.name))
Ejemplo n.º 5
0
class Manager(object):
    """

    Args:
        stream(:py:term:`file object`): Output stream. If :py:data:`None`,
            defaults to :py:data:`sys.stdout`
        counter_class(:py:term:`class`): Progress bar class (Default: :py:class:`Counter`)
        set_scroll(bool): Enable scroll area redefinition (Default: :py:data:`True`)
        companion_stream(:py:term:`file object`): See :ref:`companion_stream <companion_stream>`
            below. (Default: :py:data:`None`)
        enabled(bool): Status (Default: True)
        no_resize(bool): Disable resizing support
        threaded(bool): When True resize handling is deferred until next write (Default: False
            unless multiple threads or multiple processes are detected)
        kwargs(Dict[str, Any]): Any additional :py:term:`keyword arguments<keyword argument>`
            will be used as default values when :py:meth:`counter` is called.

    Manager class for outputting progress bars to streams attached to TTYs

    Progress bars are displayed at the bottom of the screen
    with standard output displayed above.

    .. _companion_stream:

    **companion_stream**

        A companion stream is a :py:term:`file object` that shares a TTY with
        the primary output stream. The cursor position in the companion stream will be
        moved in coordination with the primary stream.

        If the value is :py:data:`None`, :py:data:`sys.stdout` and :py:data:`sys.stderr` will
        be used as companion streams. Unless explicitly
        specified, a stream which is not attached to a TTY (the case when
        redirected to a file), will not be used as a companion stream.

    """

    # pylint: disable=too-many-instance-attributes
    def __init__(self, stream=None, counter_class=Counter, **kwargs):

        self.stream = sys.stdout if stream is None else stream
        self.counter_class = counter_class
        self.status_bar_class = StatusBar

        self.counters = OrderedDict()
        self.enabled = kwargs.get('enabled', True)  # Double duty for counters
        self.no_resize = kwargs.pop('no_resize', False)
        self.set_scroll = kwargs.pop('set_scroll', True)
        self.threaded = kwargs.pop('threaded', None)  # Defer evaluation
        self.term = Terminal(stream=self.stream)

        # Set up companion stream
        self.companion_stream = kwargs.pop('companion_stream', None)
        if self.companion_stream is None:

            # Account for calls with original output
            if self.stream is sys.__stdout__ and sys.__stderr__.isatty():
                self.companion_stream = sys.__stderr__
            elif self.stream is sys.__stderr__ and sys.__stdout__.isatty():
                self.companion_stream = sys.__stdout__

            # Account for output redirection
            elif self.stream is sys.stdout and sys.stderr.isatty():
                self.companion_stream = sys.stderr
            elif self.stream is sys.stderr and sys.stdout.isatty():
                self.companion_stream = sys.stdout

        # Set up companion terminal
        if self.companion_stream:
            self.companion_term = Terminal(stream=self.companion_stream)
        else:
            self.companion_term = None

        self.autorefresh = []
        self._buffer = []
        self._companion_buffer = []
        self.height = self.term.height
        self.process_exit = False
        self.refresh_lock = False
        self._resize = False
        self.resize_lock = False
        self.scroll_offset = 1
        self.width = self.term.width

        if not self.no_resize and RESIZE_SUPPORTED:
            self.sigwinch_orig = signal.getsignal(signal.SIGWINCH)

        self.defaults = kwargs  # Counter defaults

    def __repr__(self):
        return '%s(stream=%r)' % (self.__class__.__name__, self.stream)

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.stop()

    def counter(self, position=None, **kwargs):
        """
        Args:
            position(int): Line number counting from the bottom of the screen
            autorefresh(bool): Refresh this counter when other bars are drawn
            replace(:py:class:`PrintableCounter`): Replace given counter with new. Position ignored.
            kwargs(Dict[str, Any]): Any additional :py:term:`keyword arguments<keyword argument>`
                are passed to :py:class:`Counter`

        Returns:
            :py:class:`Counter`: Instance of counter class

        Get a new progress bar instance

        If ``position`` is specified, the counter's position will be pinned.
        A :py:exc:`ValueError` will be raised if ``position`` exceeds the screen height or
        has already been pinned by another counter.

        If ``autorefresh`` is :py:data:`True`, this bar will be redrawn whenever another bar is
        drawn assuming it had been ``min_delta`` seconds since the last update. This is usually
        unnecessary.

        .. note:: Counters are not automatically drawn when created because fields may be missing
                  if subcounters are used. To force the counter to draw before updating,
                  call :py:meth:`~Counter.refresh`.

        """

        return self._add_counter(self.counter_class,
                                 position=position,
                                 **kwargs)

    def status_bar(self, *args, **kwargs):
        """
        Args:
            position(int): Line number counting from the bottom of the screen
            autorefresh(bool): Refresh this counter when other bars are drawn
            replace(:py:class:`PrintableCounter`): Replace given counter with new. Position ignored.
            kwargs(Dict[str, Any]): Any additional :py:term:`keyword arguments<keyword argument>`
                are passed to :py:class:`StatusBar`

        Returns:
            :py:class:`StatusBar`: Instance of status bar class

        Get a new status bar instance

        If ``position`` is specified, the counter's position can change dynamically if
        additional counters are called without a ``position`` argument.

        If ``autorefresh`` is :py:data:`True`, this bar will be redrawn whenever another bar is
        drawn assuming it had been ``min_delta`` seconds since the last update. Generally,
        only need when ``elapsed`` is used in :ref:`status_format <status_format>`.

        """

        position = kwargs.pop('position', None)

        return self._add_counter(self.status_bar_class,
                                 *args,
                                 position=position,
                                 **kwargs)

    def _add_counter(self, counter_class, *args, **kwargs):  # pylint: disable=too-many-branches
        """
        Args:
            counter_class(:py:class:`PrintableCounter`): Class to instantiate
            position(int): Line number counting from the bottom of the screen
            autorefresh(bool): Refresh this counter when other bars are drawn
            replace(:py:class:`PrintableCounter`): Replace given counter with new. Position ignored.
            kwargs(Dict[str, Any]): Any additional :py:term:`keyword arguments<keyword argument>`
                are passed to :py:class:`Counter`

        Returns:
            :py:class:`Counter`: Instance of counter class

        Get a new instance of the given class and add it to the manager

        If ``position`` is specified, the counter's position can change dynamically if
        additional counters are called without a ``position`` argument.

        """

        position = kwargs.pop('position', None)
        autorefresh = kwargs.pop('autorefresh', False)
        replace = kwargs.pop('replace', None)

        # List of counters to refresh due to new position
        toRefresh = []

        # Add default values to kwargs
        for key, val in self.defaults.items():
            if key not in kwargs:
                kwargs[key] = val
        kwargs['manager'] = self

        # Create counter
        new = counter_class(*args, **kwargs)
        if autorefresh:
            self.autorefresh.append(new)

        # Get pinned counters
        # pylint: disable=protected-access
        pinned = {
            pos: ctr
            for ctr, pos in self.counters.items() if ctr._pinned
        }

        # Manage replacement
        if replace is not None:
            if replace not in self.counters:
                raise ValueError(
                    'Counter to replace is not currently managed: %r' %
                    replace)

            # Remove old counter
            position = self.counters[replace]
            replace.leave = False
            replace.close()

            # Replace old counter with new counter
            self.counters[new] = position
            if replace._pinned:
                new._pinned = True
                pinned[position] = new

        # Position specified
        elif position is not None:
            if position < 1:
                raise ValueError('Counter position %d is less than 1.' %
                                 position)
            if position in pinned:
                raise ValueError('Counter position %d is already occupied.' %
                                 position)
            if position > self.height:
                raise ValueError(
                    'Counter position %d is greater than terminal height.' %
                    position)
            new._pinned = True  # pylint: disable=protected-access
            self.counters[new] = position
            pinned[position] = new

        # Dynamic placement
        else:
            # Set for now, but will change
            self.counters[new] = 0

        # Refresh status bars only, counters may have subcounters
        if counter_class is self.status_bar_class:
            toRefresh.append(new)

        # Iterate through all counters in reverse order
        pos = 1
        for ctr in reversed(self.counters):

            if ctr in pinned.values():
                continue

            old_pos = self.counters[ctr]

            while pos in pinned:
                pos += 1

            if pos != old_pos:

                # Don't refresh new counter, already accounted for
                if ctr is not new:
                    ctr.clear(flush=False)
                    toRefresh.append(ctr)

                self.counters[ctr] = pos

            pos += 1

        self._set_scroll_area()
        for ctr in reversed(toRefresh):
            ctr.refresh(flush=False)
        self._flush_streams()

        return new

    def _stage_resize(self, *args, **kwarg):  # pylint: disable=unused-argument
        """
        Called when a window resize signal is detected
        """

        # Set semaphore to trigger resize on next write
        self._resize = True

        if self.threaded:
            # Reset update time to avoid any delay in resize
            for counter in self.counters:
                counter.last_update = 0

        else:
            # If not threaded, handle resize now
            self._resize_handler()

    def _resize_handler(self):
        """
        Called when a window resize has been detected

        Resets the scroll window
        """

        # Make sure only one resize handler is running
        if self.resize_lock:
            return

        self.resize_lock = True
        buffer = self._buffer
        term = self.term

        oldHeight = self.height
        newHeight = self.height = term.height
        newWidth = term.width

        if newHeight < oldHeight:
            buffer.append(term.move(max(0, newHeight - self.scroll_offset), 0))
            buffer.append(u'\n' * (2 * max(self.counters.values())))
        elif newHeight > oldHeight and self.threaded:
            buffer.append(term.move(newHeight, 0))
            buffer.append(u'\n' * (self.scroll_offset - 1))

        buffer.append(term.move(max(0, newHeight - self.scroll_offset), 0))
        buffer.append(term.clear_eos)

        self.width = newWidth
        self._set_scroll_area(force=True)

        for counter in self.counters:
            counter.refresh(flush=False)
        self._flush_streams()

        self.resize_lock = False

    def _set_scroll_area(self, force=False):
        """
        Args:
            force(bool): Set the scroll area even if no change in height and position is detected

        Sets the scroll window based on the counter positions
        """

        # Save scroll offset for resizing
        oldOffset = self.scroll_offset
        newOffset = max(self.counters.values()) + 1
        if newOffset > oldOffset:
            self.scroll_offset = newOffset
            use_new = True
        else:
            use_new = False

        if not self.enabled:
            return

        # Set exit handling only once
        if not self.process_exit:
            atexit.register(self._at_exit)
            if not self.no_resize and RESIZE_SUPPORTED:
                if self.threaded is None:
                    self.threaded = (
                        threading.active_count() > 1  # Multiple threads
                        or multiprocessing.active_children(
                        )  # Main process with children
                        or multiprocessing.current_process().name !=
                        'MainProcess'  # Child process
                    )
                signal.signal(signal.SIGWINCH, self._stage_resize)
            self.process_exit = True

        if self.set_scroll:

            buffer = self._buffer
            term = self.term
            scrollPosition = max(0, self.height - self.scroll_offset)

            if force or use_new:

                # Add line feeds so we don't overwrite existing output
                if use_new:
                    buffer.append(term.move(max(0, self.height - oldOffset),
                                            0))
                    buffer.append(u'\n' * (newOffset - oldOffset))

                # Reset scroll area
                buffer.append(term.hide_cursor)
                buffer.append(term.csr(0, scrollPosition))

            # Always reset position
            buffer.append(term.move(scrollPosition, 0))
            if self.companion_term is not None:
                self._companion_buffer.append(term.move(scrollPosition, 0))

    def _flush_streams(self):
        """
        Convenience method for flushing streams
        """

        buffer = self._buffer
        companion_buffer = self._companion_buffer

        if buffer:
            self.stream.write(u''.join(buffer))

        self.stream.flush()

        if self.companion_stream is not None:
            if companion_buffer:
                self.companion_stream.write(u''.join(companion_buffer))
            self.companion_stream.flush()

        del buffer[:]  # Python 2.7 does not support list.clear()
        del companion_buffer[:]

    def _at_exit(self):
        """
        Resets terminal to normal configuration
        """

        if not self.process_exit:
            return

        try:
            term = self.term
            buffer = self._buffer

            if self.set_scroll:
                buffer.append(self.term.normal_cursor)
                buffer.append(self.term.csr(0, self.height - 1))

            buffer.append(term.move(term.height, 0))
            buffer.append(term.cud1 or u'\n')

            self._flush_streams()

        except ValueError:  # Possibly closed file handles
            pass

    def remove(self, counter):
        """
        Args:
            counter(:py:class:`Counter`): Progress bar or status bar instance

        Remove bar instance from manager

        Does not error if instance is not managed by this manager

        Generally this method should not be called directly,
        instead used :py:meth:`Counter.close`.
        """

        if not counter.leave:
            try:
                del self.counters[counter]
                self.autorefresh.remove(counter)
            except (KeyError, ValueError):
                pass

    def stop(self):
        """
        Clean up and reset terminal

        This method should be called when the manager and counters will no longer be needed.

        Any progress bars that have ``leave`` set to :py:data:`True` or have not been closed
        will remain on the console. All others will be cleared.

        Manager and all counters will be disabled.

        """

        if not self.enabled:
            return

        buffer = self._buffer
        term = self.term
        height = term.height
        positions = self.counters.values()

        if not self.no_resize and RESIZE_SUPPORTED:
            signal.signal(signal.SIGWINCH, self.sigwinch_orig)

        try:
            for num in range(self.scroll_offset - 1, 0, -1):
                if num not in positions:
                    buffer.append(term.move(height - num, 0))
                    buffer.append(term.clear_eol)

        finally:

            # Reset terminal
            if self.set_scroll:
                buffer.append(term.normal_cursor)
                buffer.append(term.csr(0, self.height - 1))
                if self.companion_term:
                    self._companion_buffer.extend(
                        (term.normal_cursor, term.csr(0, self.height - 1),
                         term.move(height, 0)))

            # Re-home cursor
            buffer.append(term.move(height, 0))

            self.process_exit = False
            self.enabled = False
            for counter in self.counters:
                counter.enabled = False

        # Feed terminal if lowest position isn't cleared
        if 1 in positions:
            buffer.append(term.cud1 or '\n')

        self._flush_streams()

    def write(self, output='', flush=True, counter=None, **kwargs):
        """
        Args:
            output(str): Output string or callable
            flush(bool): Flush the output stream after writing
            counter(:py:class:`Counter`): Bar being written (for position and auto-refresh)
            kwargs(dict): Additional arguments passed when output is callable

        Write to the stream.

        The position is determined by the counter or defaults to the bottom of the terminal

        If ``output`` is callable, it will be called with any additional keyword arguments
        to produce the output string
        """

        if not self.enabled:
            return

        # If resize signal was caught, handle resize
        if self._resize and not self.resize_lock:
            try:
                self._resize_handler()
            finally:
                self._resize = False

            return

        position = self.counters[counter] if counter else 0
        term = self.term

        # If output is callable, call it with supplied arguments
        if callable(output):
            output = output(**kwargs)

        try:
            self._buffer.extend((term.move(self.height - position,
                                           0), u'\r', term.clear_eol, output))

        finally:
            # Reset position and scrolling
            if not self.refresh_lock:
                if self.autorefresh:
                    self._autorefresh(exclude=(counter, ))
                self._set_scroll_area()
                if flush:
                    self._flush_streams()

    def _autorefresh(self, exclude):
        """
        Args:
            exclude(list): Iterable of bars to ignore when auto-refreshing

        Refresh any bars specified for auto-refresh
        """

        self.refresh_lock = True
        current_time = time.time()

        for counter in self.autorefresh:

            if counter in exclude or counter.min_delta > current_time - counter.last_update:
                continue

            counter.refresh()

        self.refresh_lock = False
Ejemplo n.º 6
0
    def print_to(
        self,
        fp,
        with_colors=True,  # deprecated arg
        show_cmd=False,
        show_full_cmd=False,
        show_user=False,
        show_pid=False,
        show_fan_speed=None,
        show_codec="",
        show_power=None,
        gpuname_width=16,
        term=None,
        show_pmemory=False,
    ):
        if term is None:
            term = Terminal(stream=sys.stdout)

        # color settings
        colors = {}

        def _conditional(cond_fn,
                         true_value,
                         false_value,
                         error_value=term.bold_black):
            try:
                return cond_fn() and true_value or false_value
            except Exception:
                return error_value

        _ENC_THRESHOLD = 50

        colors['C0'] = term.normal
        colors['C1'] = term.cyan
        colors['CBold'] = term.bold
        colors['CName'] = term.blue
        colors['CTemp'] = _conditional(lambda: self.temperature < 50, term.red,
                                       term.bold_red)
        colors['FSpeed'] = _conditional(lambda: self.fan_speed < 30, term.cyan,
                                        term.bold_cyan)
        colors['CMemU'] = term.bold_yellow
        colors['CMemT'] = term.yellow
        colors['CMemP'] = term.yellow
        colors['CCPUMemU'] = term.yellow
        colors['CUser'] = term.bold_black  # gray
        colors['CUtil'] = _conditional(lambda: self.utilization < 30,
                                       term.green, term.bold_green)
        colors['CUtilEnc'] = _conditional(
            lambda: self.utilization_enc < _ENC_THRESHOLD, term.green,
            term.bold_green)
        colors['CUtilDec'] = _conditional(
            lambda: self.utilization_dec < _ENC_THRESHOLD, term.green,
            term.bold_green)
        colors['CCPUUtil'] = term.green
        colors['CPowU'] = _conditional(
            lambda: (self.power_limit is not None and float(self.power_draw) /
                     self.power_limit < 0.4), term.magenta, term.bold_magenta)
        colors['CPowL'] = term.magenta
        colors['CCmd'] = term.color(24)  # a bit dark
        colors['PUser'] = term.turquoise
        colors['PTime'] = term.aquamarine4

        if not with_colors:
            for k in list(colors.keys()):
                colors[k] = ''

        def _repr(v, none_value='??'):
            return none_value if v is None else v

        # build one-line display information
        # we want power use optional, but if deserves being grouped with
        # temperature and utilization
        reps = u"%(C1)s[{entry[index]}]%(C0)s " \
            "%(CName)s{entry[name]:{gpuname_width}}%(C0)s |" \
            "%(CTemp)s{entry[temperature.gpu]:>3}°C%(C0)s, "

        if show_fan_speed:
            reps += "%(FSpeed)s{entry[fan.speed]:>3} %%%(C0)s, "

        reps += "%(CUtil)s{entry[utilization.gpu]:>3} %%%(C0)s"
        if show_codec:
            codec_info = []
            if "enc" in show_codec:
                codec_info.append(
                    "%(CBold)sE: %(C0)s"
                    "%(CUtilEnc)s{entry[utilization.enc]:>3} %%%(C0)s")
            if "dec" in show_codec:
                codec_info.append(
                    "%(CBold)sD: %(C0)s"
                    "%(CUtilDec)s{entry[utilization.dec]:>3} %%%(C0)s")
            reps += " ({})".format("  ".join(codec_info))

        if show_power:
            reps += ",  %(CPowU)s{entry[power.draw]:>3}%(C0)s "
            if show_power is True or 'limit' in show_power:
                reps += "/ %(CPowL)s{entry[enforced.power.limit]:>3}%(C0)s "
                reps += "%(CPowL)sW%(C0)s"
            else:
                reps += "%(CPowU)sW%(C0)s"

        reps += " | %(C1)s%(CMemU)s{entry[memory.used]:>5}%(C0)s " \
            "/ %(CMemT)s{entry[memory.total]:>5}%(C0)s MB"
        reps = (reps) % colors
        reps = reps.format(entry={k: _repr(v)
                                  for k, v in self.entry.items()},
                           gpuname_width=gpuname_width)
        reps += " |"

        def process_repr(p):
            r = ''
            if not show_cmd or show_user:
                r += "{CUser}{}{C0}".format(_repr(p['username'], '--'),
                                            **colors)
            if show_cmd:
                if r:
                    r += ':'
                com = _repr(p.get('command', p['pid']), '--')
                if "@" in com:
                    letters = com[1:3]
                    rest, tleft = com[3:].split("#")
                    r += "{PUser}{}{C1}{}{PTime}({}){C0}".format(
                        letters, rest, tleft, **colors)
                else:
                    r += "{C1}{}{C0}".format(com, **colors)

            if show_pid:
                r += ("/%s" % _repr(p['pid'], '--'))
            if show_pmemory:
                r += '({CMemP}{}M{C0})'.format(
                    _repr(p['gpu_memory_usage'], '?'), **colors)
            else:
                r += ' :'
            return r

        def full_process_info(p):
            r = "{C0} ├─ {:>6} ".format(_repr(p['pid'], '--'), **colors)
            r += "{C0}({CCPUUtil}{:4.0f}%{C0}, {CCPUMemU}{:>6}{C0})".format(
                _repr(p['cpu_percent'], '--'),
                util.bytes2human(_repr(p['cpu_memory_usage'], 0)), **colors)
            full_command_pretty = util.prettify_commandline(
                p['full_command'], colors['C1'], colors['CCmd'])
            r += "{C0}: {CCmd}{}{C0}".format(_repr(full_command_pretty, '?'),
                                             **colors)
            return r

        processes = self.entry['processes']
        full_processes = []
        if processes is None:
            # None (not available)
            reps += ' ({})'.format(NOT_SUPPORTED)
        else:
            for p in processes:
                reps += ' ' + process_repr(p)
                if show_full_cmd:
                    full_processes.append(os.linesep + full_process_info(p))
        if show_full_cmd and full_processes:
            full_processes[-1] = full_processes[-1].replace('├', '└', 1)
            reps += ''.join(full_processes)
        fp.write(reps)
        return fp
Ejemplo n.º 7
0
def draw_exit(terminal: blessed.Terminal) -> None:
    terminal.exit_fullscreen()
Ejemplo n.º 8
0
    def move_layer_down(self):
        if self.dim == 3:
            if self.activeLayer > 0:
                self.activeLayer -= 1
                self.updateDrawData()
                self.damaged = True

    # There's no real limit on how big a box can be, internally.
    # We just have a window into it, and that's the 'size'.
    # We should be able to shift the viewport...
    # ... so we should be able to store and sort through lines of data.


msgbus = MsgBus()
terminal = Terminal()
inputsys = Input(msgbus, terminal)
import sys
appstate = AppState(msgbus, terminal, sys.argv[1])
termprint = TerminalPrinter(msgbus, terminal, appstate)
dataloader = H5DataLoader(msgbus, appstate, termprint, terminal)
msgbus.RegisterSystem(appstate)
msgbus.RegisterSystem(termprint)
msgbus.RegisterSystem(inputsys)
msgbus.RegisterSystem(dataloader)
msgbus.LaunchSystems()
# Let's create a message and load up the file!
msg = Msg('new_box', mtype='H5_LOAD', code=None)
appstate.SendMessage(msg)
msgbus.MainLoop()
Ejemplo n.º 9
0
    def mainRender(needsRender, menu, selection):
        term = Terminal()

        if needsRender == 1:
            print(term.clear())
            print(term.move_y(term.height // 16))
            print(
                term.black_on_cornsilk4(term.center('IOTstack Gitea Options')))
            print("")
            print(term.center(commonTopBorder(renderMode)))
            print(term.center(commonEmptyLine(renderMode)))
            print(
                term.center(
                    "{bv}      Select Option to configure                                                {bv}"
                    .format(bv=specialChars[renderMode]["borderVertical"])))
            print(term.center(commonEmptyLine(renderMode)))

        if needsRender >= 1:
            renderHotZone(term, menu, selection, hotzoneLocation)

        if needsRender == 1:
            print(term.center(commonEmptyLine(renderMode)))
            print(term.center(commonEmptyLine(renderMode)))
            if not hideHelpText:
                print(term.center(commonEmptyLine(renderMode)))
                print(
                    term.center(
                        "{bv}      Controls:                                                                 {bv}"
                        .format(
                            bv=specialChars[renderMode]["borderVertical"])))
                print(
                    term.center(
                        "{bv}      [Up] and [Down] to move selection cursor                                  {bv}"
                        .format(
                            bv=specialChars[renderMode]["borderVertical"])))
                print(
                    term.center(
                        "{bv}      [H] Show/hide this text                                                   {bv}"
                        .format(
                            bv=specialChars[renderMode]["borderVertical"])))
                print(
                    term.center(
                        "{bv}      [Enter] to run command or save input                                      {bv}"
                        .format(
                            bv=specialChars[renderMode]["borderVertical"])))
                print(
                    term.center(
                        "{bv}      [Escape] to go back to build stack menu                                   {bv}"
                        .format(
                            bv=specialChars[renderMode]["borderVertical"])))
                print(term.center(commonEmptyLine(renderMode)))
            print(term.center(commonEmptyLine(renderMode)))
            print(term.center(commonBottomBorder(renderMode)))
Ejemplo n.º 10
0
class RokuCLI():
    """ Command-line interpreter for processing user input and relaying
    commands to Roku """
    def __init__(self):
        self.term = Terminal()
        self.roku = None

    def parseargs(self):
        parser = argparse.ArgumentParser(
            description='Interactive command-line control of Roku devices')
        parser.add_argument(
            'ipaddr',
            nargs='?',
            help=('IP address of Roku to connect to. By default, will ' +
                  'automatically detect Roku within LAN.'))
        return parser.parse_args()

    def text_entry(self):
        """ Relay literal text entry from user to Roku until
        <Enter> or <Esc> pressed. """

        allowed_sequences = set(['KEY_ENTER', 'KEY_ESCAPE', 'KEY_DELETE'])

        sys.stdout.write('Enter text (<Esc> to abort) : ')
        sys.stdout.flush()

        # Track start column to ensure user doesn't backspace too far
        start_column = self.term.get_location()[1]
        cur_column = start_column

        with self.term.cbreak():
            val = ''
            while val != 'KEY_ENTER' and val != 'KEY_ESCAPE':
                val = self.term.inkey()
                if not val:
                    continue
                elif val.is_sequence:
                    val = val.name
                    if val not in allowed_sequences:
                        continue

                if val == 'KEY_ENTER':
                    self.roku.enter()
                elif val == 'KEY_ESCAPE':
                    pass
                elif val == 'KEY_DELETE':
                    self.roku.backspace()
                    if cur_column > start_column:
                        sys.stdout.write(u'\b \b')
                        cur_column -= 1
                else:
                    self.roku.literal(val)
                    sys.stdout.write(val)
                    cur_column += 1
                sys.stdout.flush()

            # Clear to beginning of line
            sys.stdout.write(self.term.clear_bol)
            sys.stdout.write(self.term.move(self.term.height, 0))
            sys.stdout.flush()

    def run(self):
        ipaddr = self.parseargs().ipaddr

        # If IP not specified, use Roku discovery and let user choose
        if ipaddr:
            self.roku = Roku(ipaddr)
        else:
            self.roku = discover_roku()

        if not self.roku:
            return

        print(usage_menu)

        cmd_func_map = {
            'B': self.roku.back,
            'KEY_DELETE': self.roku.back,
            'H': self.roku.home,
            'h': self.roku.left,
            'KEY_LEFT': self.roku.left,
            'j': self.roku.down,
            'KEY_DOWN': self.roku.down,
            'k': self.roku.up,
            'KEY_UP': self.roku.up,
            'l': self.roku.right,
            'KEY_RIGHT': self.roku.right,
            'KEY_ENTER': self.roku.select,
            'R': self.roku.replay,
            'i': self.roku.info,
            'r': self.roku.reverse,
            'f': self.roku.forward,
            ' ': self.roku.play,
            '/': self.text_entry
        }

        # Main interactive loop
        with self.term.cbreak():
            val = ''
            while val.lower() != 'q':
                val = self.term.inkey()
                if not val:
                    continue
                if val.is_sequence:
                    val = val.name
                if val in cmd_func_map:
                    try:
                        cmd_func_map[val]()
                    except:
                        print('Unable to communicate with roku at ' +
                              str(self.roku.host) + ':' + str(self.roku.port))
                        sys.exit(1)
Ejemplo n.º 11
0
    def mainRender(needsRender, menu, selection):
        term = Terminal()

        if needsRender == 1:
            print(term.clear())
            print(term.move_y(6 - hotzoneLocation[0]))
            print(term.black_on_cornsilk4(term.center('Native Installs')))
            print("")
            print(term.center(commonTopBorder(renderMode)))
            print(term.center(commonEmptyLine(renderMode)))
            print(
                term.center(
                    "{bv}      Select service to install                                                 {bv}"
                    .format(bv=specialChars[renderMode]["borderVertical"])))
            print(term.center(commonEmptyLine(renderMode)))
            print(term.center(commonEmptyLine(renderMode)))
            print(term.center(commonEmptyLine(renderMode)))

        if needsRender >= 1:
            renderHotZone(term, menu, selection, hotzoneLocation)

        if needsRender == 1:
            print(term.center(commonEmptyLine(renderMode)))
            if not hideHelpText:
                if term.height < 30:
                    print(term.center(commonEmptyLine(renderMode)))
                    print(
                        term.center(
                            "{bv}      Not enough vertical room to render controls help text                     {bv}"
                            .format(bv=specialChars[renderMode]
                                    ["borderVertical"])))
                    print(term.center(commonEmptyLine(renderMode)))
                else:
                    print(term.center(commonEmptyLine(renderMode)))
                    print(
                        term.center(
                            "{bv}      Controls:                                                                 {bv}"
                            .format(bv=specialChars[renderMode]
                                    ["borderVertical"])))
                    print(
                        term.center(
                            "{bv}      [Up] and [Down] to move selection cursor                                  {bv}"
                            .format(bv=specialChars[renderMode]
                                    ["borderVertical"])))
                    print(
                        term.center(
                            "{bv}      [H] Show/hide this text                                                   {bv}"
                            .format(bv=specialChars[renderMode]
                                    ["borderVertical"])))
                    print(
                        term.center(
                            "{bv}      [Enter] to run command                                                    {bv}"
                            .format(bv=specialChars[renderMode]
                                    ["borderVertical"])))
                    print(
                        term.center(
                            "{bv}      [Escape] to go back to main menu                                          {bv}"
                            .format(bv=specialChars[renderMode]
                                    ["borderVertical"])))
                    print(term.center(commonEmptyLine(renderMode)))
            print(term.center(commonEmptyLine(renderMode)))
            print(term.center(commonBottomBorder(renderMode)))
Ejemplo n.º 12
0
#!/usr/bin/python3
from blessed import Terminal
import sys
import subprocess
import os
import time
import types
import signal
from deps.chars import specialChars
from deps.version_check import checkVersion

term = Terminal()

# Settings/Consts
requiredDockerVersion = "18.2.0"

# Vars
selectionInProgress = True
currentMenuItemIndex = 0
menuNavigateDirection = 0
projectStatusPollRateRefresh = 1
promptFiles = False
buildComplete = None
hotzoneLocation = [((term.height // 16) + 6), 0]
screenActive = True

# Render Modes:
#  0 = No render needed
#  1 = Full render
#  2 = Hotzone only
needsRender = 1
Ejemplo n.º 13
0
def main():
    """Program entry point."""
    above = lambda csr, n: (Cursor(y=max(0, csr.y - n), x=csr.x, term=csr.term)
                            )

    below = lambda csr, n: (Cursor(
        y=min(csr.term.height - 1, csr.y + n), x=csr.x, term=csr.term))

    right_of = lambda csr, n: (Cursor(
        y=csr.y, x=min(csr.term.width - 1, csr.x + n), term=csr.term))

    left_of = lambda csr, n: (Cursor(
        y=csr.y, x=max(0, csr.x - n), term=csr.term))

    home = lambda csr: (Cursor(y=csr.y, x=0, term=csr.term))

    end = lambda csr: (Cursor(y=csr.y, x=csr.term.width - 1, term=csr.term))

    bottom = lambda csr: (Cursor(y=csr.term.height - 1, x=csr.x, term=csr.term)
                          )

    center = lambda csr: Cursor(csr.term.height // 2, csr.term.width // 2, csr.
                                term)

    lookup_move = lambda inp_code, csr, term: {
        # arrows, including angled directionals
        csr.term.KEY_END:
        below(left_of(csr, 1), 1),
        csr.term.KEY_KP_1:
        below(left_of(csr, 1), 1),
        csr.term.KEY_DOWN:
        below(csr, 1),
        csr.term.KEY_KP_2:
        below(csr, 1),
        csr.term.KEY_PGDOWN:
        below(right_of(csr, 1), 1),
        csr.term.KEY_LR:
        below(right_of(csr, 1), 1),
        csr.term.KEY_KP_3:
        below(right_of(csr, 1), 1),
        csr.term.KEY_LEFT:
        left_of(csr, 1),
        csr.term.KEY_KP_4:
        left_of(csr, 1),
        csr.term.KEY_CENTER:
        center(csr),
        csr.term.KEY_KP_5:
        center(csr),
        csr.term.KEY_RIGHT:
        right_of(csr, 1),
        csr.term.KEY_KP_6:
        right_of(csr, 1),
        csr.term.KEY_HOME:
        above(left_of(csr, 1), 1),
        csr.term.KEY_KP_7:
        above(left_of(csr, 1), 1),
        csr.term.KEY_UP:
        above(csr, 1),
        csr.term.KEY_KP_8:
        above(csr, 1),
        csr.term.KEY_PGUP:
        above(right_of(csr, 1), 1),
        csr.term.KEY_KP_9:
        above(right_of(csr, 1), 1),

        # shift + arrows
        csr.term.KEY_SLEFT:
        left_of(csr, 10),
        csr.term.KEY_SRIGHT:
        right_of(csr, 10),
        csr.term.KEY_SDOWN:
        below(csr, 10),
        csr.term.KEY_SUP:
        above(csr, 10),

        # carriage return
        csr.term.KEY_ENTER:
        home(below(csr, 1)),
    }.get(inp_code, csr)

    term = Terminal()
    csr = Cursor(0, 0, term)
    screen = {}
    with term.hidden_cursor(), \
            term.raw(), \
            term.location(), \
            term.fullscreen(), \
            term.keypad():
        inp = None
        while True:
            echo_yx(csr, term.reverse(screen.get((csr.y, csr.x), u' ')))
            inp = term.inkey()

            if inp == chr(3):
                # ^c exits
                break

            elif inp == chr(19):
                # ^s saves
                echo_yx(home(bottom(csr)),
                        term.ljust(term.bold_white(u'Filename: ')))
                echo_yx(right_of(home(bottom(csr)), len(u'Filename: ')), u'')
                save(screen, readline(term))
                echo_yx(home(bottom(csr)), term.clear_eol)
                redraw(term=term,
                       screen=screen,
                       start=home(bottom(csr)),
                       end=end(bottom(csr)))
                continue

            elif inp == chr(12):
                # ^l refreshes
                redraw(term=term, screen=screen)

            else:
                n_csr = lookup_move(inp.code, csr, term)

            if n_csr != csr:
                # erase old cursor,
                echo_yx(csr, screen.get((csr.y, csr.x), u' '))
                csr = n_csr

            elif input_filter(inp):
                echo_yx(csr, inp)
                screen[(csr.y, csr.x)] = inp.__str__()
                n_csr = right_of(csr, 1)
                if n_csr == csr:
                    # wrap around margin
                    n_csr = home(below(csr, 1))
                csr = n_csr
Ejemplo n.º 14
0
    def __init__(self, args):
        self.term = Terminal()
        self.prev = {}
        self.poll_delay = args.poll_delay
        self.blink_delay = args.blink_delay
        self.commands = [{
            'command': 'getNeighbors'
        }, {
            'command': 'getNodeInfo'
        }]
        self.txkeys = [{
            'keyshort': 'ad',
            'sortkey': '1',
            'header': 'Neighbor Address',
            'key': 'neighborAddress',
            'col': 0,
            'sortcolumn': 'address'
        }, {
            'keyshort': 'at',
            'sortkey': '2',
            'header': 'All tx',
            'key': 'numberOfAllTransactions',
            'col': 3,
            'sortcolumn': 'numberOfAllTransactions'
        }, {
            'keyshort': 'nt',
            'sortkey': '3',
            'header': 'New tx',
            'key': 'numberOfNewTransactions',
            'col': 4,
            'sortcolumn': 'numberOfNewTransactions'
        }, {
            'keyshort': 'st',
            'sortkey': '4',
            'header': 'Sent tx',
            'key': 'numberOfSentTransactions',
            'col': 5,
            'sortcolumn': 'numberOfSentTransactions'
        }, {
            'keyshort': 'rt',
            'sortkey': '5',
            'header': 'Random tx',
            'key': 'numberOfRandomTransactionRequests',
            'col': 6,
            'sortcolumn': 'numberOfRandomTransactionRequests'
        }, {
            'keyshort': 'it',
            'sortkey': '6',
            'header': 'Invalid tx',
            'key': 'numberOfInvalidTransactions',
            'col': 7,
            'sortcolumn': 'numberOfInvalidTransactions'
        }, {
            'keyshort': 'xt',
            'sortkey': '7',
            'header': 'Stale tx',
            'key': 'numberOfStaleTransactions',
            'col': 8,
            'sortcolumn': 'numberOfStaleTransactions'
        }]
        self.randSeed = random.randint(0, 100000)
        self.baseline = dict()
        self.baselineStr = ['Off', 'On']
        self.baselineToggle = 0
        self.obscureAddrToggle = args.obscure_address
        self.width = 0
        self.height = 0
        self.oldheight = 0
        self.oldwidth = 0
        self.incommunicados = 0
        self.localhost = self.set_local_node()
        self.duration_hist = list()
        self.duration = 0
        self.duration_avg = 0
        self.sortmode = False
        self.sortcolumn = None
        self.sortorderlist = ["", " " + u"\u25BC", " " + u"\u25B2"]
        self.sortorder = None
        self.mss_0 = ""
        self.prev_ms_start = 0

        # Initiate column sort
        if args.sort:
            try:
                if args.sort < 0:
                    self.sortorder = self.sortorderlist[1]
                else:
                    self.sortorder = self.sortorderlist[2]
                args.sort = abs(args.sort)
                self.sortcolumn = self.txkeys[args.sort - 1]['sortcolumn']
            except IndexError:
                self.sortcolumn = self.txkeys[0]['sortcolumn']

        # Set authentication header if required
        if args.username is not None:
            auth_str = '%s:%s' % (args.username, args.password)
            auth_token = base64.b64encode(auth_str.encode("utf-8"))
            HEADERS['Authorization'] = 'Basic %s' % auth_token.decode()
Ejemplo n.º 15
0
class IriTop:

    global HEADERES

    def __init__(self, args):
        self.term = Terminal()
        self.prev = {}
        self.poll_delay = args.poll_delay
        self.blink_delay = args.blink_delay
        self.commands = [{
            'command': 'getNeighbors'
        }, {
            'command': 'getNodeInfo'
        }]
        self.txkeys = [{
            'keyshort': 'ad',
            'sortkey': '1',
            'header': 'Neighbor Address',
            'key': 'neighborAddress',
            'col': 0,
            'sortcolumn': 'address'
        }, {
            'keyshort': 'at',
            'sortkey': '2',
            'header': 'All tx',
            'key': 'numberOfAllTransactions',
            'col': 3,
            'sortcolumn': 'numberOfAllTransactions'
        }, {
            'keyshort': 'nt',
            'sortkey': '3',
            'header': 'New tx',
            'key': 'numberOfNewTransactions',
            'col': 4,
            'sortcolumn': 'numberOfNewTransactions'
        }, {
            'keyshort': 'st',
            'sortkey': '4',
            'header': 'Sent tx',
            'key': 'numberOfSentTransactions',
            'col': 5,
            'sortcolumn': 'numberOfSentTransactions'
        }, {
            'keyshort': 'rt',
            'sortkey': '5',
            'header': 'Random tx',
            'key': 'numberOfRandomTransactionRequests',
            'col': 6,
            'sortcolumn': 'numberOfRandomTransactionRequests'
        }, {
            'keyshort': 'it',
            'sortkey': '6',
            'header': 'Invalid tx',
            'key': 'numberOfInvalidTransactions',
            'col': 7,
            'sortcolumn': 'numberOfInvalidTransactions'
        }, {
            'keyshort': 'xt',
            'sortkey': '7',
            'header': 'Stale tx',
            'key': 'numberOfStaleTransactions',
            'col': 8,
            'sortcolumn': 'numberOfStaleTransactions'
        }]
        self.randSeed = random.randint(0, 100000)
        self.baseline = dict()
        self.baselineStr = ['Off', 'On']
        self.baselineToggle = 0
        self.obscureAddrToggle = args.obscure_address
        self.width = 0
        self.height = 0
        self.oldheight = 0
        self.oldwidth = 0
        self.incommunicados = 0
        self.localhost = self.set_local_node()
        self.duration_hist = list()
        self.duration = 0
        self.duration_avg = 0
        self.sortmode = False
        self.sortcolumn = None
        self.sortorderlist = ["", " " + u"\u25BC", " " + u"\u25B2"]
        self.sortorder = None
        self.mss_0 = ""
        self.prev_ms_start = 0

        # Initiate column sort
        if args.sort:
            try:
                if args.sort < 0:
                    self.sortorder = self.sortorderlist[1]
                else:
                    self.sortorder = self.sortorderlist[2]
                args.sort = abs(args.sort)
                self.sortcolumn = self.txkeys[args.sort - 1]['sortcolumn']
            except IndexError:
                self.sortcolumn = self.txkeys[0]['sortcolumn']

        # Set authentication header if required
        if args.username is not None:
            auth_str = '%s:%s' % (args.username, args.password)
            auth_token = base64.b64encode(auth_str.encode("utf-8"))
            HEADERS['Authorization'] = 'Basic %s' % auth_token.decode()

    @property
    def get_local_ips(self):
        return check_output(['/bin/hostname',
                             '--all-ip-addresses']).rstrip().split()

    def set_local_node(self):
        local_ips = ['localhost', '127.0.0.1', '::1']
        local_ips.extend(self.get_local_ips)
        if urlparse(NODE.lower()).hostname in local_ips:
            return True
        return False

    def run(self, stdscr):

        stdscr.clear()
        node = None

        print("IRITop connecting to node %s..." % self.showAddress(NODE))

        with self.term.hidden_cursor():
            val = ""
            tlast = 0
            self.hist = {}

            while val.lower() != 'q':

                random.seed(self.randSeed)

                val = self.term.inkey(timeout=self.blink_delay)

                # Sort mode detection
                if val.lower() == 's':
                    if self.sortmode is False:
                        self.sortmode = True
                    else:
                        self.sortmode = False
                if self.sortmode:
                    if self.sortorder is None:
                        self.sortorder = self.sortorderlist[2]
                    keylist = []
                    for k in self.txkeys:
                        keylist.append(k['sortkey'])
                    key = val.lower()
                    if key in keylist:
                        for k in self.txkeys:
                            if key == k['sortkey']:
                                # Toggle sort direction
                                if self.sortcolumn == k['sortcolumn']:
                                    if self.sortorder == self.sortorderlist[2]:
                                        self.sortorder = self.sortorderlist[1]
                                    else:
                                        self.sortorder = self.sortorderlist[2]
                                else:
                                    self.sortorder = self.sortorderlist[2]
                                # Set sort column
                                self.sortcolumn = k['sortcolumn']
                                self.sortmode = False

                self.oldheight, self.oldwidth = self.height, self.width
                self.height, self.width = self.term.height, self.term.width

                if int(time.time()) - tlast > self.poll_delay:

                    if node:
                        self.prev_ms_start = node["milestoneStartIndex"]

                    startTime = int(round(time.time() * 1000))
                    results = [
                        fetch_data(self.commands[i])
                        for i in range(len(self.commands))
                    ]
                    endTime = int(round(time.time() * 1000))
                    self.logDuration(endTime - startTime)

                    neighbors = None
                    node = None
                    for data, e in results:
                        if e is not None:
                            raise Exception("Error fetching data from node:"
                                            " %s\n" % e)
                        if 'appName' in data.keys():
                            node = data
                        elif 'neighbors' in data.keys():
                            neighbors = data['neighbors']

                    tlast = int(time.time())

                    for neighbor in neighbors:
                        for txkey in self.txkeys[1:]:
                            if txkey['key'] not in neighbor:
                                neighbor[txkey['key']] = 0
                                neighbor[txkey['keyshort']] = 0
                                neighbor['%sDelta' % txkey['key']] = 0

                    # Keep history of tx
                    tx_history = {}
                    for neighbor in neighbors:
                        for txkey in self.txkeys[1:]:
                            self.historizer(txkey['keyshort'], txkey['key'],
                                            tx_history, neighbor)
                    self.hist = tx_history

                if val.lower() == 'o':
                    self.obscureAddrToggle = self.obscureAddrToggle ^ 1

                if val.lower() == 'b':
                    for neighbor in neighbors:
                        for txkey in self.txkeys[1:]:
                            self.baseline[self.getBaselineKey(neighbor,
                                          txkey['keyshort'])] = \
                                          neighbor[txkey['key']]
                    self.baselineToggle = self.baselineToggle ^ 1

                if ((self.oldheight != self.height)
                        or (self.oldwidth != self.width)):
                    print(self.term.clear)

                print(
                    self.term.move(0, 0) + self.term.black_on_cyan(
                        "IRITop - Simple IOTA IRI Node Monitor (%s)".ljust(
                            self.width) % __VERSION__))

                for neighbor in neighbors:
                    for txkey in self.txkeys[1:]:
                        key = self.getBaselineKey(neighbor, txkey['keyshort'])
                        if key not in self.baseline:
                            self.baseline[key] = 0

                self.show(1, 0, "App Name", node, "appName")
                self.show(2, 0, "App Version", node, "appVersion")

                s = self.term.cyan("Free: ") + \
                    str(node["jreFreeMemory"]//MB) + \
                    " Mb  " + \
                    self.term.cyan("Max: ") + \
                    str(node["jreMaxMemory"]//MB) + \
                    " Mb " + \
                    self.term.cyan("Total: ") + \
                    str(node["jreTotalMemory"]//MB) + " Mb   "
                self.show_string(1, 1, "JRE Memory", s)

                self.show_histogram(2,
                                    1,
                                    "JRE Memory",
                                    node["jreTotalMemory"] -
                                    node["jreFreeMemory"],
                                    node["jreMaxMemory"],
                                    0.8,
                                    span=2)

                ms_start = node["milestoneStartIndex"]
                delta_ms_start = self.prev_ms_start - ms_start
                self.mss_1 = self.mss_0
                self.mss_0 = ("%s" % ms_start) + (
                    "" if delta_ms_start == 0 else " (%d)" % delta_ms_start)
                self.show_string(3, 2, "", " " * 16)
                self.show_string(3,
                                 2,
                                 "Milestone Start",
                                 self.mss_0,
                                 prev=self.mss_1)

                self.show(4, 2, "Milestone Index", node,
                          "latestMilestoneIndex")
                self.show(5, 2, "Milestone Solid", node,
                          "latestSolidSubtangleMilestoneIndex")

                self.show(3, 0, "JRE Version", node, "jreVersion")
                self.show(4, 1, "Tips", node, "tips")
                self.show(3, 1, "Tx To Request", node, "transactionsToRequest")

                self.show_string(6, 0, "Node Address", self.showAddress(NODE))

                self.show_string(4, 0, "Baseline",
                                 self.baselineStr[self.baselineToggle])
                self.show_string(
                    5, 0, "Response Time",
                    str(self.duration) + " ms " + self.term.cyan("Avg: ") +
                    str(self.duration_avg) + " ms   ")
                neighborCount = "%s" % node['neighbors']
                if self.incommunicados > 0:
                    neighborCount += self.term.red(" / %d " %
                                                   self.incommunicados)
                else:
                    neighborCount += "    "
                self.show_string(6, 2, "Neighbors", neighborCount)

                if self.localhost:
                    self.show_string(5, 1, "Load Average", getloadavg())
                else:
                    self.show_string(5, 1, "Load Average", 'N/A')

                self.show_neighbors(7, neighbors)

    def logDuration(self, duration):
        self.duration = duration
        self.duration_hist.append(duration)
        self.duration_avg = int(
            sum(self.duration_hist) / len(self.duration_hist))
        # Limit history to the last 5 minutes of calls
        if len(self.duration_hist) > (60 * 5 / self.poll_delay):
            del self.duration_hist[0]

    def showAddress(self, address):
        if self.obscureAddrToggle == 1:
            return scrambleAddress(address)
        return address

    def getBaselineKey(self, neighbor, subkey):
        return "%s:%s" % (neighbor['address'], subkey)

    def historizer(self, txtype, wsid, hd, n):
        nid = "%s-%s" % (n['address'], txtype)
        nidd = "%s-%sd" % (n['address'], txtype)
        c = n[wsid]
        try:
            p = self.hist[nid]
            hd[nid] = c
            if p > 0:
                hd[nidd] = c - p
            else:
                hd[nidd] = 0
        except KeyError:
            hd[nid] = 0
            hd[nidd] = 0

        n["%sDelta" % wsid] = hd[nidd]

    def show(self, row, col, label, dictionary, value):

        x1 = (self.width // 3) * col
        x2 = x1 + 18

        vs = self.term.bright_cyan(str(dictionary[value]))

        # Highlight if no neighbors
        if value == "neighbors" and dictionary[value] == 0:
            vs = self.term.red(str(dictionary[value]))

        # Highlight if latest milestone is out of sync with
        # the solid milestone
        if value == "latestSolidSubtangleMilestoneIndex":
            diff = dictionary["latestSolidSubtangleMilestoneIndex"] - \
              dictionary["latestMilestoneIndex"]

            if diff < 0 and diff >= -2:
                vs = self.term.yellow(str(dictionary[value]) + "*")
            elif diff < -2:
                vs = self.term.red(str(dictionary[value]) + " (!)")

        if value in self.prev and dictionary[value] != self.prev[value]:
            vs = self.term.on_blue(vs)

        print(self.term.move(row, x1) + self.term.cyan(label + ":"))
        print(self.term.move(row, x2) + vs + "  ")

        self.prev[value] = dictionary[value]

    def show_string(self, row, col, label, value, prev=""):

        x1 = (self.width // 3) * col
        x2 = x1 + 18

        value = str(value)
        if prev != "" and value != prev:
            value = self.term.on_blue(value)

        print(self.term.move(row, x1) + self.term.cyan(label + ":"))
        print(
            self.term.move(row, x2) + self.term.bright_cyan(str(value) + "  "))

    def show_histogram(self,
                       row,
                       col,
                       label,
                       value,
                       value_max,
                       warning_limit=0.8,
                       span=1):

        label_width = 18
        col_width = ((self.width // 3) - label_width) + \
                    ((span - 1) * (self.width // 3))
        x1 = (self.width // 3) * col
        x2 = x1 + label_width
        bw = col_width - 2

        vm = bw
        v = int(value / value_max * bw)
        vl = int(warning_limit * vm)

        mG = v
        mY = 0
        mR = 0
        if v > vl:
            mR = v - vl
            mG = mG - mR
        mB = bw - (mR + mG)

        if value > (value_max * warning_limit):
            mY = mG
            mG = 0

        print(self.term.move(row, x1) + self.term.cyan(label + ":"))
        print(
            self.term.move(row, x2) + self.term.white("[") +
            self.term.green("|" * mG) + self.term.yellow("|" * mY) +
            self.term.red("#" * mR) + self.term.bright_black("-" * mB) +
            self.term.white("]"))

    def show_neighbors(self, row, neighbors):
        global ITER
        cols = 9
        height, width = self.term.height, self.term.width
        cw = width // cols
        cw1 = width - ((cols - 1) * cw)
        cwl = [
            0,
        ]
        for c in range(cols - 1):
            cwl.append(cw1 + (c * cw))

        self.incommunicados = 0
        revso = True if self.sortorder == self.sortorderlist[2] else False

        for k in self.txkeys:
            ch = k['header'] + (' [%s]' % k['sortkey'] if self.sortmode else (
                self.sortorderlist[1] if revso else self.sortorderlist[2])
                                if self.sortcolumn == k['sortcolumn'] else '')
            ch += "" if k['keyshort'] != 'ad' else " " * (cw * 4 - len(ch))
            print(
                self.term.move(row, cwl[k['col']]) +
                self.term.black_on_green(ch.rjust(cw)))

        row += 1

        # Sort neighbors
        ordered_neighbors = []
        if self.sortcolumn is None:
            self.sortorder = None
            ordered_neighbors = neighbors
        else:
            if self.sortorder is None:
                self.sortorder = self.sortorderlist[0]
            ordered_neighbors = sorted(neighbors,
                                       key=lambda k: k[self.sortcolumn],
                                       reverse=revso)

        # Show Neighbors
        for neighbor in ordered_neighbors:
            self.show_neighbor(row, neighbor, cwl, cw, height)
            row += 1

        # Blank spare neighbor rows
        for blankrow in range(row, height - 2):
            print(self.term.move(blankrow, 0) + " " * width)

        print(
            self.term.move(height - 2, 0 * cw) +
            self.term.black_on_cyan("Q to exit - "
                                    "B to reset tx to a zero baseline - "
                                    "O to obscure addresses - "
                                    "S# to sort column".ljust(width)))

        ITER += 1

    def txString(self, neighbor, key, keydelta, keyshort, column_width):
        txcnt = neighbor[key] - (self.baseline[self.getBaselineKey(
            neighbor, keyshort)] * self.baselineToggle)
        return ("%d (%d)" % (txcnt, neighbor[keydelta])).rjust(column_width)

    def show_neighbor(self, row, neighbor, column_start_list, column_width,
                      height):
        global ITER

        neighbor['addr'] = self.showAddress(neighbor['connectionType'] +
                                            "://" + neighbor['address'])

        # Create display string
        for txkey in self.txkeys[1:]:
            neighbor[txkey['keyshort']] = \
                    self.txString(neighbor,
                                  txkey['key'],
                                  '%sDelta' % txkey['key'],
                                  txkey['keyshort'],
                                  column_width)

        # Highlight neighbors that are incommunicado
        incommunicado = False
        if (neighbor['numberOfAllTransactionsDelta'] == 0 and ITER >
            (6 * self.poll_delay)):
            neighbor['addr'] = "(!) " + neighbor['addr']
            incommunicado = True
            self.incommunicados += 1

        # Pad/Trim neighbor address
        ncolw = 3 * (column_width + 1)
        if len(neighbor['addr']) < ncolw:
            # pad
            neighbor['addr'] = neighbor['addr'].ljust(ncolw, ' ')
        elif len(neighbor['addr']) > ncolw:
            # trim
            neighbor['addr'] = neighbor['addr'][0:ncolw]

        value_at = "neighbor-%s-at" % neighbor['address']
        if (value_at in self.prev and
                neighbor['numberOfAllTransactions'] != self.prev[value_at]):
            neighbor['at'] = self.term.cyan(neighbor['at'])

        if neighbor['numberOfInvalidTransactions'] > 0:
            neighbor['it'] = \
                self.term.red(str(neighbor['numberOfInvalidTransactions'])
                              .rjust(column_width))

        # Blink changed value
        for txkey in self.txkeys[1:]:
            neighborkey = "neighbor-%s-%s" % (neighbor['address'],
                                              txkey['keyshort'])
            if (neighborkey in self.prev
                    and neighbor[txkey['key']] != self.prev[neighborkey]):
                neighbor[txkey['keyshort']] = \
                    self.term.cyan(neighbor[txkey['keyshort']])

        # do not display any neighbors crossing the height of the terminal
        if row < height - 2:
            print(
                self.term.move(row, column_start_list[0]) +
                (self.term.white(neighbor['addr'])
                 if not incommunicado else self.term.red(neighbor['addr'])))
            for txkey in self.txkeys[1:]:
                print(
                    self.term.move(row, column_start_list[txkey['col']]) +
                    self.term.green(neighbor[txkey['keyshort']]))

        # Store previous value
        for txkey in self.txkeys[1:]:
            neighborkey = "neighbor-%s-%s" % (neighbor['address'],
                                              txkey['keyshort'])
            self.prev[neighborkey] = neighbor[txkey['key']]
Ejemplo n.º 16
0
 def related_tags(self, tags):
     term = Terminal()
     print(term.bold + f"Related tags: {tags}" + term.normal)
Ejemplo n.º 17
0
 def menu_choice(self, show):
     term = Terminal()
     print(term.clear())
     print(show)
     sleep(1)
     print(term.clear())
Ejemplo n.º 18
0
 def test_colors(self, mock_user_conf):
     mock_user_conf.return_value = {}
     stream = StringIO()
     _out('Hello world', None, Terminal().red, stream)
     # RHEL 6 doesn't have self.assertRegexpMatches unfortunately
     self.assertTrue(re.match('.+Hello world.+\n', stream.getvalue()))
Ejemplo n.º 19
0
import os
import sys
import time
import signal
from blessed import Terminal

from multiprocessing import Process, Queue

import listener
import pusher


term = Terminal()
queue = Queue()


def _clear():
    ''' clear screen wrapper '''
    if os.name == 'nt':
        os.system('cls')
    else:
        os.system('clear')


def input_section():
    ''' return section of user input '''
    return 0


def sent_section():
    ''' return section of sent messages '''
Ejemplo n.º 20
0
def main():
    from blessed import Terminal
    from deps.chars import specialChars, commonTopBorder, commonBottomBorder, commonEmptyLine
    global renderMode
    import time
    import subprocess

    global signal
    global dockerCommandsSelectionInProgress
    global mainMenuList
    global currentMenuItemIndex
    global screenActive
    global hideHelpText
    global needsRender

    try:  # If not already set, then set it.
        hideHelpText = hideHelpText
    except:
        hideHelpText = False

    term = Terminal()
    hotzoneLocation = [7, 0]  # Top text

    def onResize(sig, action):
        global mainMenuList
        global currentMenuItemIndex
        if (screenActive):
            mainRender(1, mainMenuList, currentMenuItemIndex)

    def installHassIo():
        print(term.clear())
        print("Install Home Assistant Supervisor")
        print("./.native/hassio_supervisor.sh")
        res = subprocess.call("./.native/hassio_supervisor.sh", shell=True)
        print("")
        if res == 0:
            print(
                "Preinstallation complete. Your system may run slow for a few hours as Hass.io installs its services."
            )
            print(
                "Press [Up] or [Down] arrow key to show the menu if it has scrolled too far."
            )
        else:
            print("Preinstallation not completed.")
        input("Process terminated. Press [Enter] to show menu and continue.")
        time.sleep(0.5)
        return True

    def installRtl433():
        print(term.clear())
        print("Install RTL_433")
        print("bash ./.native/rtl_433.sh")
        subprocess.call("bash ./.native/rtl_433.sh", shell=True)
        print("")
        input("Process terminated. Press [Enter] to show menu and continue.")
        return True

    def installRpiEasy():
        print(term.clear())
        print("Install RPIEasy")
        print("bash ./.native/rpieasy.sh")
        subprocess.call("bash ./.native/rpieasy.sh", shell=True)
        print("")
        input("Process terminated. Press [Enter] to show menu and continue.")
        return True

    def installDockerAndCompose():
        print(term.clear())
        print("Install docker")
        print("Install docker-compose")
        print("bash ./scripts/install_docker.sh install")
        subprocess.call("bash ./scripts/install_docker.sh install", shell=True)
        print("")
        input("Process terminated. Press [Enter] to show menu and continue.")
        return True

    def upgradeDockerAndCompose():
        print(term.clear())
        print("Install docker")
        print("Install docker-compose")
        print("bash ./scripts/install_docker.sh upgrade")
        subprocess.call("bash ./scripts/install_docker.sh upgrade", shell=True)
        print("")
        input("Process terminated. Press [Enter] to show menu and continue.")
        return True

    def goBack():
        global dockerCommandsSelectionInProgress
        global needsRender
        global screenActive
        screenActive = False
        dockerCommandsSelectionInProgress = False
        needsRender = 1
        return True

    mainMenuList = [
        ["Hass.io (Supervisor)", installHassIo], ["RTL_433", installRtl433],
        ["RPIEasy", installRpiEasy],
        ["Upgrade Docker and Docker-Compose", upgradeDockerAndCompose],
        ["Install Docker and Docker-Compose", installDockerAndCompose],
        ["Back", goBack]
    ]

    dockerCommandsSelectionInProgress = True
    currentMenuItemIndex = 0
    menuNavigateDirection = 0

    # Render Modes:
    #  0 = No render needed
    #  1 = Full render
    #  2 = Hotzone only
    needsRender = 1

    def renderHotZone(term, menu, selection, hotzoneLocation):
        print(term.move(hotzoneLocation[0], hotzoneLocation[1]))
        lineLengthAtTextStart = 71

        for (index, menuItem) in enumerate(menu):
            toPrint = ""
            if index == selection:
                toPrint += (
                    '{bv} -> {t.blue_on_green} {title} {t.normal} <-'.format(
                        t=term,
                        title=menuItem[0],
                        bv=specialChars[renderMode]["borderVertical"]))
            else:
                toPrint += ('{bv}    {t.normal} {title}    '.format(
                    t=term,
                    title=menuItem[0],
                    bv=specialChars[renderMode]["borderVertical"]))

            for i in range(lineLengthAtTextStart - len(menuItem[0])):
                toPrint += " "

            toPrint += "{bv}".format(
                bv=specialChars[renderMode]["borderVertical"])

            toPrint = term.center(toPrint)

            print(toPrint)

    def mainRender(needsRender, menu, selection):
        term = Terminal()

        if needsRender == 1:
            print(term.clear())
            print(term.move_y(6 - hotzoneLocation[0]))
            print(term.black_on_cornsilk4(term.center('Native Installs')))
            print("")
            print(term.center(commonTopBorder(renderMode)))
            print(term.center(commonEmptyLine(renderMode)))
            print(
                term.center(
                    "{bv}      Select service to install                                                 {bv}"
                    .format(bv=specialChars[renderMode]["borderVertical"])))
            print(term.center(commonEmptyLine(renderMode)))
            print(term.center(commonEmptyLine(renderMode)))
            print(term.center(commonEmptyLine(renderMode)))

        if needsRender >= 1:
            renderHotZone(term, menu, selection, hotzoneLocation)

        if needsRender == 1:
            print(term.center(commonEmptyLine(renderMode)))
            if not hideHelpText:
                if term.height < 30:
                    print(term.center(commonEmptyLine(renderMode)))
                    print(
                        term.center(
                            "{bv}      Not enough vertical room to render controls help text                     {bv}"
                            .format(bv=specialChars[renderMode]
                                    ["borderVertical"])))
                    print(term.center(commonEmptyLine(renderMode)))
                else:
                    print(term.center(commonEmptyLine(renderMode)))
                    print(
                        term.center(
                            "{bv}      Controls:                                                                 {bv}"
                            .format(bv=specialChars[renderMode]
                                    ["borderVertical"])))
                    print(
                        term.center(
                            "{bv}      [Up] and [Down] to move selection cursor                                  {bv}"
                            .format(bv=specialChars[renderMode]
                                    ["borderVertical"])))
                    print(
                        term.center(
                            "{bv}      [H] Show/hide this text                                                   {bv}"
                            .format(bv=specialChars[renderMode]
                                    ["borderVertical"])))
                    print(
                        term.center(
                            "{bv}      [Enter] to run command                                                    {bv}"
                            .format(bv=specialChars[renderMode]
                                    ["borderVertical"])))
                    print(
                        term.center(
                            "{bv}      [Escape] to go back to main menu                                          {bv}"
                            .format(bv=specialChars[renderMode]
                                    ["borderVertical"])))
                    print(term.center(commonEmptyLine(renderMode)))
            print(term.center(commonEmptyLine(renderMode)))
            print(term.center(commonBottomBorder(renderMode)))

    def runSelection(selection):
        global needsRender
        import types
        if len(mainMenuList[selection]) > 1 and isinstance(
                mainMenuList[selection][1], types.FunctionType):
            mainMenuList[selection][1]()
            needsRender = 1
        else:
            print(
                term.green_reverse(
                    'IOTstack Error: No function assigned to menu item: "{}"'.
                    format(mainMenuList[selection][0])))

    def isMenuItemSelectable(menu, index):
        if len(menu) > index:
            if len(menu[index]) > 2:
                if menu[index][2]["skip"] == True:
                    return False
        return True

    if __name__ == 'builtins':
        term = Terminal()
        with term.fullscreen():
            global screenActive
            screenActive = True
            signal.signal(signal.SIGWINCH, onResize)
            menuNavigateDirection = 0
            mainRender(needsRender, mainMenuList, currentMenuItemIndex)
            dockerCommandsSelectionInProgress = True
            with term.cbreak():
                while dockerCommandsSelectionInProgress:
                    menuNavigateDirection = 0

                    if not needsRender == 0:  # Only rerender when changed to prevent flickering
                        mainRender(needsRender, mainMenuList,
                                   currentMenuItemIndex)
                        needsRender = 0

                    key = term.inkey(esc_delay=0.05)
                    if key.is_sequence:
                        if key.name == 'KEY_TAB':
                            menuNavigateDirection += 1
                        if key.name == 'KEY_DOWN':
                            menuNavigateDirection += 1
                        if key.name == 'KEY_UP':
                            menuNavigateDirection -= 1
                        if key.name == 'KEY_ENTER':
                            runSelection(currentMenuItemIndex)
                            if dockerCommandsSelectionInProgress == False:
                                screenActive = False
                                return True
                            mainRender(1, mainMenuList, currentMenuItemIndex)
                        if key.name == 'KEY_ESCAPE':
                            screenActive = False
                            dockerCommandsSelectionInProgress = False
                            return True
                    elif key:
                        if key == 'h':  # H pressed
                            if hideHelpText:
                                hideHelpText = False
                            else:
                                hideHelpText = True
                            mainRender(1, mainMenuList, currentMenuItemIndex)

                    if menuNavigateDirection != 0:  # If a direction was pressed, find next selectable item
                        currentMenuItemIndex += menuNavigateDirection
                        currentMenuItemIndex = currentMenuItemIndex % len(
                            mainMenuList)
                        needsRender = 2

                        while not isMenuItemSelectable(mainMenuList,
                                                       currentMenuItemIndex):
                            currentMenuItemIndex += menuNavigateDirection
                            currentMenuItemIndex = currentMenuItemIndex % len(
                                mainMenuList)
        screenActive = False
        return True

    screenActive = False
    return True
Ejemplo n.º 21
0
 def __init__(self):
     self.term = Terminal()
     self.roku = None
Ejemplo n.º 22
0
def info_out(msgs):
    term = Terminal()
    _out(msgs, None, term.blue)
Ejemplo n.º 23
0
def main():
    import os
    import time
    import ruamel.yaml
    import signal
    import sys
    from blessed import Terminal

    from deps.chars import specialChars, commonTopBorder, commonBottomBorder, commonEmptyLine
    from deps.consts import servicesDirectory, templatesDirectory
    from deps.common_functions import getExternalPorts, getInternalPorts, checkPortConflicts, enterPortNumberWithWhiptail

    yaml = ruamel.yaml.YAML()
    yaml.preserve_quotes = True

    global dockerComposeServicesYaml  # The loaded memory YAML of all checked services
    global toRun  # Switch for which function to run when executed
    global buildHooks  # Where to place the options menu result
    global currentServiceName  # Name of the current service
    global issues  # Returned issues dict
    global haltOnErrors  # Turn on to allow erroring
    global hideHelpText  # Showing and hiding the help controls text
    global serviceService

    serviceService = servicesDirectory + currentServiceName
    serviceTemplate = templatesDirectory + currentServiceName

    try:  # If not already set, then set it.
        hideHelpText = hideHelpText
    except:
        hideHelpText = False

    # runtime vars
    portConflicts = []

    # This lets the menu know whether to put " >> Options " or not
    # This function is REQUIRED.
    def checkForOptionsHook():
        try:
            buildHooks["options"] = callable(runOptionsMenu)
        except:
            buildHooks["options"] = False
            return buildHooks
        return buildHooks

    # This function is REQUIRED.
    def checkForPreBuildHook():
        try:
            buildHooks["preBuildHook"] = callable(preBuild)
        except:
            buildHooks["preBuildHook"] = False
            return buildHooks
        return buildHooks

    # This function is REQUIRED.
    def checkForPostBuildHook():
        try:
            buildHooks["postBuildHook"] = callable(postBuild)
        except:
            buildHooks["postBuildHook"] = False
            return buildHooks
        return buildHooks

    # This function is REQUIRED.
    def checkForRunChecksHook():
        try:
            buildHooks["runChecksHook"] = callable(runChecks)
        except:
            buildHooks["runChecksHook"] = False
            return buildHooks
        return buildHooks

    # This service will not check anything unless this is set
    # This function is optional, and will run each time the menu is rendered
    def runChecks():
        checkForIssues()
        return []

    # This function is optional, and will run after the docker-compose.yml file is written to disk.
    def postBuild():
        return True

    # This function is optional, and will run just before the build docker-compose.yml code.
    def preBuild():
        return True

    # #####################################
    # Supporting functions below
    # #####################################

    def checkForIssues():
        for (index, serviceName) in enumerate(dockerComposeServicesYaml):
            if not currentServiceName == serviceName:  # Skip self
                currentServicePorts = getExternalPorts(
                    currentServiceName, dockerComposeServicesYaml)
                portConflicts = checkPortConflicts(serviceName,
                                                   currentServicePorts,
                                                   dockerComposeServicesYaml)
                if (len(portConflicts) > 0):
                    issues["portConflicts"] = portConflicts

    # #####################################
    # End Supporting functions
    # #####################################

    ############################
    # Menu Logic
    ############################

    global currentMenuItemIndex
    global selectionInProgress
    global menuNavigateDirection
    global needsRender

    selectionInProgress = True
    currentMenuItemIndex = 0
    menuNavigateDirection = 0
    needsRender = 1
    term = Terminal()
    hotzoneLocation = [((term.height // 16) + 6), 0]

    def goBack():
        global selectionInProgress
        global needsRender
        selectionInProgress = False
        needsRender = 1
        return True

    def enterPortNumberExec():
        # global term
        global needsRender
        global dockerComposeServicesYaml
        externalPort = getExternalPorts(currentServiceName,
                                        dockerComposeServicesYaml)[0]
        internalPort = getInternalPorts(currentServiceName,
                                        dockerComposeServicesYaml)[0]
        newPortNumber = enterPortNumberWithWhiptail(term,
                                                    dockerComposeServicesYaml,
                                                    currentServiceName,
                                                    hotzoneLocation,
                                                    externalPort)

        if newPortNumber > 0:
            dockerComposeServicesYaml[currentServiceName]["ports"][
                0] = "{newExtPort}:{oldIntPort}".format(
                    newExtPort=newPortNumber, oldIntPort=internalPort)
            createMenu()
        needsRender = 1

    def onResize(sig, action):
        global giteaBuildOptions
        global currentMenuItemIndex
        mainRender(1, giteaBuildOptions, currentMenuItemIndex)

    giteaBuildOptions = []

    def createMenu():
        global giteaBuildOptions
        try:
            giteaBuildOptions = []
            portNumber = getExternalPorts(currentServiceName,
                                          dockerComposeServicesYaml)[0]
            giteaBuildOptions.append([
                "Change external WUI Port Number from: {port}".format(
                    port=portNumber), enterPortNumberExec
            ])
        except:  # Error getting port
            pass
        giteaBuildOptions.append(["Go back", goBack])

    def runOptionsMenu():
        createMenu()
        menuEntryPoint()
        return True

    def renderHotZone(term, menu, selection, hotzoneLocation):
        lineLengthAtTextStart = 71
        print(term.move(hotzoneLocation[0], hotzoneLocation[1]))
        for (index, menuItem) in enumerate(menu):
            toPrint = ""
            if index == selection:
                toPrint += (
                    '{bv} -> {t.blue_on_green} {title} {t.normal} <-'.format(
                        t=term,
                        title=menuItem[0],
                        bv=specialChars[renderMode]["borderVertical"]))
            else:
                toPrint += ('{bv}    {t.normal} {title}    '.format(
                    t=term,
                    title=menuItem[0],
                    bv=specialChars[renderMode]["borderVertical"]))

            for i in range(lineLengthAtTextStart - len(menuItem[0])):
                toPrint += " "

            toPrint += "{bv}".format(
                bv=specialChars[renderMode]["borderVertical"])

            toPrint = term.center(toPrint)

            print(toPrint)

    def mainRender(needsRender, menu, selection):
        term = Terminal()

        if needsRender == 1:
            print(term.clear())
            print(term.move_y(term.height // 16))
            print(
                term.black_on_cornsilk4(term.center('IOTstack Gitea Options')))
            print("")
            print(term.center(commonTopBorder(renderMode)))
            print(term.center(commonEmptyLine(renderMode)))
            print(
                term.center(
                    "{bv}      Select Option to configure                                                {bv}"
                    .format(bv=specialChars[renderMode]["borderVertical"])))
            print(term.center(commonEmptyLine(renderMode)))

        if needsRender >= 1:
            renderHotZone(term, menu, selection, hotzoneLocation)

        if needsRender == 1:
            print(term.center(commonEmptyLine(renderMode)))
            print(term.center(commonEmptyLine(renderMode)))
            if not hideHelpText:
                print(term.center(commonEmptyLine(renderMode)))
                print(
                    term.center(
                        "{bv}      Controls:                                                                 {bv}"
                        .format(
                            bv=specialChars[renderMode]["borderVertical"])))
                print(
                    term.center(
                        "{bv}      [Up] and [Down] to move selection cursor                                  {bv}"
                        .format(
                            bv=specialChars[renderMode]["borderVertical"])))
                print(
                    term.center(
                        "{bv}      [H] Show/hide this text                                                   {bv}"
                        .format(
                            bv=specialChars[renderMode]["borderVertical"])))
                print(
                    term.center(
                        "{bv}      [Enter] to run command or save input                                      {bv}"
                        .format(
                            bv=specialChars[renderMode]["borderVertical"])))
                print(
                    term.center(
                        "{bv}      [Escape] to go back to build stack menu                                   {bv}"
                        .format(
                            bv=specialChars[renderMode]["borderVertical"])))
                print(term.center(commonEmptyLine(renderMode)))
            print(term.center(commonEmptyLine(renderMode)))
            print(term.center(commonBottomBorder(renderMode)))

    def runSelection(selection):
        import types
        global giteaBuildOptions
        if len(giteaBuildOptions[selection]) > 1 and isinstance(
                giteaBuildOptions[selection][1], types.FunctionType):
            giteaBuildOptions[selection][1]()
        else:
            print(
                term.green_reverse(
                    'IOTstack Error: No function assigned to menu item: "{}"'.
                    format(nodeRedBuildOptions[selection][0])))

    def isMenuItemSelectable(menu, index):
        if len(menu) > index:
            if len(menu[index]) > 2:
                if menu[index][2]["skip"] == True:
                    return False
        return True

    def menuEntryPoint():
        # These need to be reglobalised due to eval()
        global currentMenuItemIndex
        global selectionInProgress
        global menuNavigateDirection
        global needsRender
        global hideHelpText
        global giteaBuildOptions
        term = Terminal()
        with term.fullscreen():
            menuNavigateDirection = 0
            mainRender(needsRender, giteaBuildOptions, currentMenuItemIndex)
            selectionInProgress = True
            with term.cbreak():
                while selectionInProgress:
                    menuNavigateDirection = 0

                    if needsRender:  # Only rerender when changed to prevent flickering
                        mainRender(needsRender, giteaBuildOptions,
                                   currentMenuItemIndex)
                        needsRender = 0

                    key = term.inkey(esc_delay=0.05)
                    if key.is_sequence:
                        if key.name == 'KEY_TAB':
                            menuNavigateDirection += 1
                        if key.name == 'KEY_DOWN':
                            menuNavigateDirection += 1
                        if key.name == 'KEY_UP':
                            menuNavigateDirection -= 1
                        if key.name == 'KEY_LEFT':
                            goBack()
                        if key.name == 'KEY_ENTER':
                            runSelection(currentMenuItemIndex)
                        if key.name == 'KEY_ESCAPE':
                            return True
                    elif key:
                        if key == 'h':  # H pressed
                            if hideHelpText:
                                hideHelpText = False
                            else:
                                hideHelpText = True
                            mainRender(1, giteaBuildOptions,
                                       currentMenuItemIndex)

                    if menuNavigateDirection != 0:  # If a direction was pressed, find next selectable item
                        currentMenuItemIndex += menuNavigateDirection
                        currentMenuItemIndex = currentMenuItemIndex % len(
                            giteaBuildOptions)
                        needsRender = 2

                        while not isMenuItemSelectable(giteaBuildOptions,
                                                       currentMenuItemIndex):
                            currentMenuItemIndex += menuNavigateDirection
                            currentMenuItemIndex = currentMenuItemIndex % len(
                                giteaBuildOptions)
        return True

    ####################
    # End menu section
    ####################

    if haltOnErrors:
        eval(toRun)()
    else:
        try:
            eval(toRun)()
        except:
            pass
Ejemplo n.º 24
0
def warn_out(msgs):
    """
    Print the given error message (or list of messages) and exit.
    """
    term = Terminal()
    _out(msgs, "WARNING", term.yellow)
Ejemplo n.º 25
0
def draw_clear(terminal: blessed.Terminal) -> None:
    terminal.clear()
Ejemplo n.º 26
0
class UIBlackTerminal:
    def __init__(self, log_name, **kwargs):
        """

        :param log_name: (Required) Name of log file to be written in local directory (Only Alphanumeric chars permitted)
        :type log_name: str
        :keyword restart_log: (bool) Whether the log should be started anew upon each execution
        :keyword log_level: (int) 0 - 7 - Conforms to https://en.wikipedia.org/wiki/Syslog#Severity_level
        :keyword sysloghost: (str) Hostname or IP of Syslog server (Can also be localhost)
        :keyword syslogport: (int) Port of Syslog server, (514 is standard on many systems)
        :keyword simple: (bool) Use the simplest textual output while preserving logging and other features
        """
        self._lock = threading.Lock()
        sysloghost = kwargs.get("sysloghost", None)
        syslogport = kwargs.get("syslogport", None)
        restart_log = kwargs.get("restart_log", True)
        log_level = kwargs.get("log_level", 6)
        simple = kwargs.get("simple", False)

        if not simple:
            try:
                override_mode = str(os.environ["UIOVERRIDE"]).strip().lower()
            except KeyError:
                override_mode = ""
            if override_mode == "simple":
                self.rich_ui = False
            else:
                self.rich_ui = True
        else:
            self.rich_ui = False
        if restart_log:
            filemode = "w"
        else:
            filemode = "a"
        if isinstance(log_name, str):
            # Keep only alphanumerics
            self._program_name = re.sub("\W", "", log_name)
            # Truncate name to 50 chars
            self._program_name = self._program_name[0:50]
            self._program_name = self._program_name.lower()
            if len(self._program_name) < 3:
                # Keep only alphanumerics in case __name__ has wierdness
                self._program_name = re.sub("\W", "", __name__).lower()
        else:
            self._program_name = re.sub("\W", "", __name__).lower()
        full_path = pathlib.Path.cwd() / "logs"
        full_path.mkdir(parents=True, exist_ok=True)
        full_path = full_path / f"{self._program_name}.log"
        self._logger = logging.getLogger(self._program_name)
        format_string_local = f"{self._program_name}: %(levelname)s - %(asctime)s - %(message)s"
        format_string_syslog = f"{self._program_name}: %(levelname)s - %(message)s"
        logging.basicConfig(
            filename=full_path,
            filemode=filemode,
            format=format_string_local,
            datefmt="%y-%b-%d %H:%M:%S (%z)",
        )
        if isinstance(sysloghost, str) and isinstance(syslogport, int):
            self.handler = logging.handlers.SysLogHandler(address=(sysloghost,
                                                                   syslogport))
            self.handler.formatter = logging.Formatter(format_string_syslog)
            self._logger.addHandler(self.handler)
        self.console_a_percentage = 0.75
        self.console_b_percentage = 1 - self.console_a_percentage
        self.set_log_level(log_level)

        self._title = None
        self._pattern_text = re.compile("([ -~])")

        self._low_latency_index = 0
        self.low_latency_max = 1000

        self._term = Terminal()
        self._term.enter_fullscreen()
        self._term.hidden_cursor()

        self._default_bg = f"{self._term.on_black}"
        self._window_bg = f"{self._term.reverse}"
        self._error_bg = f"{self._term.on_white}"
        self._warn_bg = f"{self._term.on_black}"

        self._default_style = f"{self._term.normal}{self._term.white}{self._default_bg}"
        self._window_style = f"{self._term.normal}{self._term.white}{self._window_bg}"
        self._error_style = f"{self._term.normal}{self._term.red}{self._error_bg}"
        self._warn_style = f"{self._term.normal}{self._term.yellow}{self._warn_bg}"
        self._default_style = f"{self._term.normal}{self._term.snow3}{self._default_bg}"

        self.update_counter_interval = 10
        self._update_counter = 0
        self.console_scrollback = 500
        self._contents_console_a = [""]
        self._contents_console_b = [""]
        self._previous_height = self._term.height
        self._previous_width = self._term.width
        self.last_updates_heuristic_enabled = True
        self._max_last_updates = 5
        self._last_updates = [datetime.now()]
        self.heuristic_target_seconds = 10

    def set_log_level(self, log_level):
        """
        Sets logging level
        :param log_level: 0 - 7 Conforms to https://en.wikipedia.org/wiki/Syslog#Severity_level
        :type log_level: int
        """
        if log_level == 7:
            self._logger.setLevel(logging.DEBUG)
        elif log_level in [6, 5]:
            self._logger.setLevel(logging.INFO)
        elif log_level == 4:
            self._logger.setLevel(logging.WARNING)
        elif log_level == 3:
            self._logger.setLevel(logging.ERROR)
        elif log_level in [2, 1, 0]:
            self._logger.setLevel(logging.CRITICAL)
        else:
            self._logger.setLevel(logging.NOTSET)

    def set_low_latency_refresh_interval(self, interval: int):
        """
        When using the low_latency argument for various functions,
        specify the number of display intervals before refreshing
        :param interval: Number of intervals before refreshing the display
        :type interval: int
        """
        if round(interval) < 0:
            interval = 100
        elif round(interval) > 9223372036854775807:
            # Old Python max int size, just trying to keep this somewhat sane
            interval = 9223372036854775807
        self.update_counter_interval = round(interval)

    def _center_pad_text(self, text, **kwargs):
        """
        Centers a string inside a designated length of pad characters (usually whitespace)
        :param text: (str)(Required) Text to be centered
        :keyword pad: (str) A single character used to pad out the whitespace
        :keyword total_len: (int)(Required) Total size that text should be centered and padded into
        :return: (str) The padded string
        """
        pad = kwargs.get("pad", " ")
        total_len = kwargs.get("total_len", None)
        if len(text) >= total_len:
            # Nothing to pad out, the text meets or exceeds the size allotted
            return text
        center = round(total_len / 2)
        text_center = round(len(text) / 2)
        differential = center - text_center
        remainder = total_len - (differential + len(text))
        final = f"{pad * differential}{text}{pad * remainder}"
        return final

    def _get_dimensions(self, console_letter):
        if console_letter.lower() == "a":
            percentage = self.console_a_percentage
        elif console_letter.lower() == "b":
            percentage = self.console_b_percentage

        width = self._term.width
        height = round(self._term.height * percentage)

        if console_letter.lower() == "a":
            if self._title is None:
                ceiling = 0
            else:
                ceiling = 1
        else:
            ceiling = self._term.height - height

        return width, height, ceiling

    def _horiz_divider_dimensions(self):
        width, height, ceiling = self._get_dimensions("b")
        divider_height = ceiling - 1
        return width, divider_height

    def _draw_divider(self):
        if self._term.does_styling and self.rich_ui:
            width, divider_height = self._horiz_divider_dimensions()
            text = "═" * width
            self.print(text, 0, divider_height, True)
        else:
            return
        return

    def _skip_iteration(self, is_low_latency_enabled):
        # Low latency was set, have we hit max?
        if is_low_latency_enabled:
            if self._low_latency_index >= self.low_latency_max:
                # We hit the max, so run this iteration and reset index
                self._low_latency_index = 0
                return False
            else:
                self._low_latency_index += 1  # Have not hit max, so increment
                return True  # and skip this execution
        return False

    def _len_printable(self, text):
        return len(self._term.strip(text))

    def _refresh_console(self, console_letter):
        fixed_width, fixed_height, ceiling = self._get_dimensions(
            console_letter)
        self._draw_divider()
        while len(self._contents_console_a) >= self.console_scrollback:
            self._contents_console_a.pop(0)
        while len(self._contents_console_b) >= self.console_scrollback:
            self._contents_console_b.pop(0)

        if console_letter.lower() == "a":
            print_height = fixed_height - 2
            contents = self._contents_console_a
        else:
            print_height = (ceiling + fixed_height) - 1
            contents = self._contents_console_b

        for index in range(len(contents) - 1, 0, -1):
            if print_height < ceiling:
                break
            actual_len = self._len_printable(contents[index])
            pad = " " * (fixed_width - (actual_len + 1))
            if actual_len > fixed_width:
                offset = actual_len - (fixed_width - 5)
                result = f"{contents[index][:-offset]}..."
            else:
                result = contents[index]
            self.print(
                f"{result}{pad}",
                0,
                print_height,
                True,
            )
            print_height -= 1

    def _refresh_consoles(self):
        self._refresh_console("a")
        self._refresh_console("b")

    def _calculate_update_heuristic(self):
        """
        Calculates whether the console is being refreshed at the correct interval to prevent screen flickering
        The default is one refresh every 10 seconds.
        This entire process is governed by the variable self.heuristic_target_seconds
        :return: None
        """
        if self.last_updates_heuristic_enabled:
            self._last_updates.append(datetime.now())
        if len(self._last_updates) < 5:
            return

        while True:
            if len(self._last_updates) > self._max_last_updates:
                self._last_updates.pop(0)
            else:
                break

        target = self.heuristic_target_seconds
        deltas = []
        previous_datetime = 0
        for current_datetime in self._last_updates:
            if previous_datetime == 0:
                # This is the first entry, so store and move on
                previous_datetime = current_datetime
                continue
            delta = (current_datetime - previous_datetime).seconds
            deltas.append(delta)
            previous_datetime = current_datetime
        del previous_datetime
        # Super performant way to average a list, shaves tiny amounts of time from this function
        average = reduce(lambda x, y: x + y, deltas) / len(deltas)
        if average <= target:
            # The refresh times are trending too low, so bump up the interval incrementally
            ms_to_change = math.sqrt(target - average)
            self.update_counter_interval += ms_to_change * 20
        elif average > target:
            # The refresh times are trending too high, so walk down the interval incrementally
            ms_to_change = math.sqrt(average - target)
            self.update_counter_interval += ms_to_change * 10
        if self.update_counter_interval <= 0:
            self.update_counter_interval = 1

    def _check_update(self):
        self._lock.acquire()
        self._update_counter += 1
        previous_total = self._previous_height + self._previous_width
        current_total = self._term.height + self._term.width
        if self._update_counter >= self.update_counter_interval:
            # This is only reset when triggered by the counter
            self._update_counter = 0
            self.clear()
            self._display_main_title()
            self._refresh_consoles()
            # This is only run when the counter is triggered
            self._calculate_update_heuristic()
        elif current_total != previous_total:
            self._previous_height = self._term.height
            self._previous_width = self._term.width
            self.clear()
            self._display_main_title()
            self._refresh_consoles()
        self._lock.release()

    def _get_time_string(self):
        now = datetime.now()
        if self._term.does_styling and self.rich_ui:
            return f"{self._term.olivedrab}[{self._term.turquoise}{now.strftime('%H:%M')}{self._term.olivedrab}]{self._default_style} "
        else:
            return f"[{now.strftime('%H:%M')}] "

    def print(self, text, right=None, down=None, ignore_log=False):
        """
        Prints normally, or prints to a specified X,Y coordinate on screen.
        Optionally, can ignore logging of text.
        :param text: Text to be written on screen
        :type text: str
        :param right: X coordinate on screen
        :type right: int
        :param down: Y coordinate on screen
        :type down: int
        :param ignore_log: Prevent logging of the text
        :type ignore_log: bool
        """
        if not ignore_log:
            self._logger.info(text)
        # Check if output is going into a pipe or other unformatted output
        if self._term.does_styling and self.rich_ui:
            actual_len = self._len_printable(text)
            if (down is not None) and (right is not None):
                if down > self._term.height:
                    # Since all the text will not be displayable, skip
                    return
                if right > self._term.width:
                    return
                if right + actual_len > self._term.width:
                    # Truncate the string to prevent wraparound
                    # We take off the right side of the string to deal with formatting non-printables being on the left
                    offset = self._term.width - right
                    trim = actual_len - offset
                    if trim < 1:
                        return
                    text = f"{text[:-trim]}"
                if right < 0:
                    return
                if down < 0:
                    return
                with self._term.location():
                    print(
                        self._term.move(down, right) +
                        f"{self._default_style}{text}{self._default_style}",
                        end="",
                    )
            else:
                print(f"{self._default_style}{text}")
        else:
            print(text)

    def _clear_console(self):
        bar = " " * self._term.width
        for row in range(1, self._term.height):
            self.print(bar, 0, row, True)

    def console(self, text, low_latency=False, ignore_log=False, **kwargs):
        """
        Write text to the virtual console similar to standard lib print()
        :param text: Text to be printed
        :type text: str
        :param low_latency: Save text, but only print every 100 iterations to reduce latency
        :type low_latency: bool
        :param ignore_log: Do not log text
        :type ignore_log: bool
        :return:
        """
        if not ignore_log and not low_latency:
            self._logger.info(text)
        elif not ignore_log and low_latency:
            self._logger.debug(text)

        console = kwargs.get("console", "a")
        if console == "a":
            self._contents_console_a.append(text)
            while len(self._contents_console_a) >= self.console_scrollback:
                self._contents_console_a.pop(0)
        elif console == "b":
            self._contents_console_b.append(text)
            while len(self._contents_console_b) >= self.console_scrollback:
                self._contents_console_b.pop(0)

        if self._skip_iteration(low_latency):
            return

        self._check_update()

    def notice(self, text, **kwargs):
        self._logger.info(text)
        if not self._logger.level <= logging.INFO:
            return
        # Check if output is going into a pipe or other unformatted output
        if self._term.does_styling and self.rich_ui:
            self.console(
                f"{self._get_time_string()}{self._default_style}{text}",
                False,
                True,
                **kwargs,
            )
        else:
            print(f"{self._get_time_string()}{text}")

    def info(self, *args, **kwargs):
        self.notice(*args, **kwargs)

    def debug(self, text, **kwargs):
        self._logger.debug(text)
        if not self._logger.level <= logging.DEBUG:
            return
        # Check if output is going into a pipe or other unformatted output
        if self._term.does_styling and self.rich_ui:
            self.console(
                f"{self._get_time_string()}{self._default_style}{text}",
                False,
                True,
                **kwargs,
            )
        else:
            print(f"{self._get_time_string()}{text}")

    def error(self, text, **kwargs):
        self._logger.error(text)
        if not self._logger.level <= logging.ERROR:
            return
        # Check if output is going into a pipe or other unformatted output
        if self._term.does_styling and self.rich_ui:
            self.console(
                f"{self._get_time_string()}{self._error_style}{text}",
                False,
                True,
                **kwargs,
            )
        else:
            print(f"{self._get_time_string()}{text}")

    def warn(self, text, **kwargs):
        self._logger.warning(text)
        if not self._logger.level <= logging.WARNING:
            return
        # Check if output is going into a pipe or other unformatted output
        if self._term.does_styling and self.rich_ui:
            self.console(
                f"{self._get_time_string()}{self._warn_style}{text}",
                False,
                True,
                **kwargs,
            )
        else:
            print(f"{self._get_time_string()}{text}")

    def print_center(self,
                     text,
                     style=None,
                     corner=None,
                     ignore_logging=False):
        self._check_update()
        if not ignore_logging:
            self._logger.info(text)
        if not self._term.does_styling or not self.rich_ui:
            print(text)
            return
        if style is None:
            style = self._window_style
        if corner is None:
            corner = " "

        center_text = len(text) // 2

        left_side = (self._term.width // 2) - (center_text + 4)
        right_side = (self._term.width // 2) + (center_text + 2)
        top_side = (self._term.height // 2) - 1
        bottom_side = (self._term.height // 2) + 1
        total_len = right_side - left_side

        if corner != " ":
            bar = " " * (total_len - 2)
            bar = f"{style}{corner}{bar}{corner}"
        else:
            bar = " " * total_len
            bar = f"{style}{bar}"

        padded_text = self._center_pad_text(text, total_len=total_len, pad=" ")

        self.print(bar, left_side, top_side, True)
        self.print(f"{style}{padded_text}", left_side, top_side + 1, True)
        self.print(bar, left_side, bottom_side, True)

    def error_center(self, text):
        self._logger.error(text)
        if self._logger.level >= logging.ERROR:
            return
        self.print_center(text, self._error_style, "!", True)

    def warn_center(self, text):
        self._logger.warning(text)
        if self._logger.level >= logging.WARNING:
            return
        self.print_center(text, self._warn_style, "*", True)

    def bold(self, text):
        # Check if output is going into a pipe or other unformatted output
        if self._term.does_styling and self.rich_ui:
            return f"{self._term.bold}{text}"
        else:
            return f"{text}"

    def window_text(self, text):
        # Check if output is going into a pipe or other unformatted output
        if self._term.does_styling and self.rich_ui:
            return f"{self._window_style}{text}"
        else:
            return f"{text}"

    def underline(self, text):
        # Check if output is going into a pipe or other unformatted output
        if self._term.does_styling and self.rich_ui:
            return f"{self._term.underline}{text}{self._term.no_underline}"
        else:
            return f"{text}"

    def clear(self):
        if self._term.does_styling and self.rich_ui:
            print(self._term.clear())
            self._display_main_title()

    def quit(self):
        self._term.exit_fullscreen

    def input(self, question=None, obfuscate=False, max_len=None):
        self._check_update()
        self._lock.acquire()
        input_height = self._term.height - 1
        input_offset = 2

        if question is None:
            question = "Press [Enter] to continue:"
        else:
            # Truncate questions to the length of the terminal window
            question = question[0:self._term.width - input_offset]
        self._logger.debug(question)
        self.print(f"{question}", input_offset, input_height - 1, True)

        if max_len is None:
            max_len = self._term.width - 3
        result = ""
        with self._term.cbreak():
            while True:
                val = ""
                val = self._term.inkey()
                found = self._pattern_text.match(val)
                if val.name == "KEY_ENTER":
                    break
                elif val.is_sequence:
                    print("got sequence: {0}.".format(
                        (str(val), val.name, val.code)))
                elif val.name == "KEY_BACKSPACE" or val.name == "KEY_DELETE":
                    self.print(" " * len(result), input_offset, input_height,
                               True)
                    result = result[:-1]
                    if obfuscate:
                        self.print("*" * len(result), input_offset,
                                   input_height, True)
                    else:
                        self.print(result, input_offset, input_height, True)
                elif found is not None:
                    if (len(result) + 1) <= max_len:
                        result = f"{result}{val}"
                    else:
                        continue
                    if obfuscate:
                        self.print("*" * len(result), input_offset,
                                   input_height, True)
                    else:
                        self.print(result, input_offset, input_height, True)
        self.print(f"{' ' * self._term.width}", 0, input_height - 1, True)
        self.print(f"{' ' * self._term.width}", 0, input_height, True)
        self._lock.release()
        return result

    def _display_main_title(self):
        if not self._term.does_styling or not self.rich_ui:
            return
        if self._title is None:
            return
        center_text = len(self._title) // 2
        center_screen = self._term.width // 2
        final_location = center_screen - center_text
        location_tuple = self._term.get_location()
        if location_tuple[0] < 1:
            print("")
            # Moving the console cursor down by one to prevent overwriting title
        self.print(self.window_text(f"{' ' * self._term.width}"), 0, 0, True)
        self.print(self.window_text(self._title), final_location, 0, True)

    def set_main_title(self, new_title):
        if new_title is not None:
            # Truncate titles to the length of the terminal window
            new_title = new_title[0:self._term.width]
        self._title = new_title
        self._logger.info(self._title)
        self._display_main_title()

    def ask_list(self, question, menu_list):
        self._check_update()
        self._lock.acquire()
        self._logger.debug(question)
        menu_height = self._term.height // 2
        menu_offset = self._term.width // 2
        menu_top = menu_height - (len(menu_list) + 1)
        # Truncate questions to the length of the terminal window
        question = question[0:self._term.width - 2]
        self.print(f"{question}", (menu_offset - len(question)), menu_top - 2)
        index = 0
        for menu_item in menu_list:
            item_offset = menu_offset
            self.print(f"{menu_item}", item_offset, (menu_top + index), True)
            index += 2

        index = 0
        index_max = len(menu_list) - 1
        with self._term.cbreak():
            while True:
                self.print(
                    f"{self._term.reverse}{menu_list[index]}",
                    menu_offset,
                    (menu_top + (index * 2)),
                    True,
                )
                val = self._term.inkey()
                if val.name == "KEY_ENTER":
                    break
                elif val.name == "KEY_UP":
                    self.print(
                        f"{menu_list[index]}",
                        menu_offset,
                        (menu_top + (index * 2)),
                        True,
                    )
                    index -= 1
                    if index < 0:
                        index = index_max
                elif val.name == "KEY_DOWN":
                    self.print(
                        f"{menu_list[index]}",
                        menu_offset,
                        (menu_top + (index * 2)),
                        True,
                    )
                    index += 1
                    if index > index_max:
                        index = 0

        self._lock.release()
        return menu_list[index]

        return result

    def _gradient_red_green(self, percent):
        if percent > 100 or percent < 0:
            red = 0
            green = 0
            blue = 100
        else:
            red = 2 * (100 - percent)
            green = 2 * percent
            blue = 0
        if self._term.does_styling and self.rich_ui:
            return self._term.color_rgb(red, green, blue)
        else:
            return self._default_style

    def load_bar(self,
                 title,
                 iteration,
                 total,
                 low_latency=False,
                 bar_length=50):
        self._logger.debug(title)
        if self._skip_iteration(low_latency):
            return
        self._check_update()
        if (bar_length + 6) > self._term.width:
            bar_length = self._term.width - 6
        bar_left_extent = (self._term.width // 2) - ((bar_length + 2) // 2)
        bar_upward_extent = self._term.height // 2
        title_left_extent = (self._term.width // 2) - ((len(title) + 2) // 2)
        try:
            percent = int(round((iteration / total) * 100))
            fill_len = int(round((bar_length * percent) / 100))
            bar_fill = "█" * fill_len
            bar_empty = " " * (bar_length - fill_len)
            progress_bar = f"{self._warn_style}[{self._gradient_red_green(percent)}{bar_fill + bar_empty}{self._warn_style}]{self._default_style}"
            if self._term.does_styling and self.rich_ui:
                padded_title = self._center_pad_text(title,
                                                     total_len=bar_length)
                self.print(f"{padded_title}", bar_left_extent,
                           bar_upward_extent - 1, True)
                for offset in range(0, 3):
                    if offset == 1:
                        suffix = f" {percent}%"
                    else:
                        suffix = ""
                    self.print(
                        f"{progress_bar}{suffix}",
                        bar_left_extent,
                        bar_upward_extent + offset,
                        True,
                    )
            else:
                suffix = f" {percent}%"
                self.print(
                    f"{progress_bar}{suffix}",
                    0,
                    0,
                    True,
                )

        except ZeroDivisionError:
            pass

    def ask_yn(self, question, default_response=False):
        self._logger.debug(question)
        self._check_update()
        self._lock.acquire()
        menu_height = self._term.height // 2
        menu_offset = self._term.width // 2
        no_offset = menu_offset + 8
        yes_offset = menu_offset - 8
        menu_top = menu_height - 1
        # Truncate questions to the length of the terminal window
        question = question[0:self._term.width - 2]
        self.print(f"{question}", (menu_offset - (len(question) // 2)),
                   menu_top - 2, True)

        self.print("YES", yes_offset, menu_height, True)
        self.print("NO", no_offset, menu_height, True)

        index = default_response
        with self._term.cbreak():
            while True:
                if index:
                    self.print(f"{self._term.reverse}YES", yes_offset,
                               menu_height, True)
                    self.print(f"{self._term.default}NO", no_offset,
                               menu_height, True)
                else:
                    self.print(f"{self._term.default}YES", yes_offset,
                               menu_height, True)
                    self.print(f"{self._term.reverse}NO", no_offset,
                               menu_height, True)
                val = self._term.inkey()
                if val.name == "KEY_ENTER":
                    break
                elif val.name == "KEY_RIGHT" or val == "n":
                    index = False
                elif val.name == "KEY_LEFT" or val == "y":
                    index = True
        self._lock.release()
        return index

    def wrapper(self, func: object) -> object:
        """
        Use the following example in your own code:
        from uiblack.terminal import UIBlackTerminal
        ui = UIBlackTerminal()

        @ui.wrapper  # Notice the function is wrapped using "pie" syntax before each function
        def badfunc():
            raise KeyError

        :param func: function object
        :type func: object
        :return: wrapped function call, safe from exceptions, but all of them logged
        """
        def function_wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                trace = traceback.format_exc(limit=-1).replace("\n", " >> ")
                # Yes, I could have used self_logger.exception(), but this way ensures a single line output on the log
                console = kwargs.get("console", "a")
                self.error(f"Exception: {trace}", console=console)
                self._check_update()

        return function_wrapper
Ejemplo n.º 27
0
import time
from blessed import Terminal
t = Terminal()
import os
import re


def sleep(times):
    with t.cbreak():
        t.inkey(timeout=times)


def escape_ansi(line):
    ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]')
    return ansi_escape.sub('', line)


def clear():
    print(t.clear, end='', flush=True)
    pass  #no


def ms(milliseconds):
    """convert milliseconds to seconds"""
    return milliseconds / 1000


class painted:
    def __init__(self, text, color_object):
        self.namedcolor = color_object.namedcolor
        self.color = color_object.color
Ejemplo n.º 28
0
    def __init__(self, log_name, **kwargs):
        """

        :param log_name: (Required) Name of log file to be written in local directory (Only Alphanumeric chars permitted)
        :type log_name: str
        :keyword restart_log: (bool) Whether the log should be started anew upon each execution
        :keyword log_level: (int) 0 - 7 - Conforms to https://en.wikipedia.org/wiki/Syslog#Severity_level
        :keyword sysloghost: (str) Hostname or IP of Syslog server (Can also be localhost)
        :keyword syslogport: (int) Port of Syslog server, (514 is standard on many systems)
        :keyword simple: (bool) Use the simplest textual output while preserving logging and other features
        """
        self._lock = threading.Lock()
        sysloghost = kwargs.get("sysloghost", None)
        syslogport = kwargs.get("syslogport", None)
        restart_log = kwargs.get("restart_log", True)
        log_level = kwargs.get("log_level", 6)
        simple = kwargs.get("simple", False)

        if not simple:
            try:
                override_mode = str(os.environ["UIOVERRIDE"]).strip().lower()
            except KeyError:
                override_mode = ""
            if override_mode == "simple":
                self.rich_ui = False
            else:
                self.rich_ui = True
        else:
            self.rich_ui = False
        if restart_log:
            filemode = "w"
        else:
            filemode = "a"
        if isinstance(log_name, str):
            # Keep only alphanumerics
            self._program_name = re.sub("\W", "", log_name)
            # Truncate name to 50 chars
            self._program_name = self._program_name[0:50]
            self._program_name = self._program_name.lower()
            if len(self._program_name) < 3:
                # Keep only alphanumerics in case __name__ has wierdness
                self._program_name = re.sub("\W", "", __name__).lower()
        else:
            self._program_name = re.sub("\W", "", __name__).lower()
        full_path = pathlib.Path.cwd() / "logs"
        full_path.mkdir(parents=True, exist_ok=True)
        full_path = full_path / f"{self._program_name}.log"
        self._logger = logging.getLogger(self._program_name)
        format_string_local = f"{self._program_name}: %(levelname)s - %(asctime)s - %(message)s"
        format_string_syslog = f"{self._program_name}: %(levelname)s - %(message)s"
        logging.basicConfig(
            filename=full_path,
            filemode=filemode,
            format=format_string_local,
            datefmt="%y-%b-%d %H:%M:%S (%z)",
        )
        if isinstance(sysloghost, str) and isinstance(syslogport, int):
            self.handler = logging.handlers.SysLogHandler(address=(sysloghost,
                                                                   syslogport))
            self.handler.formatter = logging.Formatter(format_string_syslog)
            self._logger.addHandler(self.handler)
        self.console_a_percentage = 0.75
        self.console_b_percentage = 1 - self.console_a_percentage
        self.set_log_level(log_level)

        self._title = None
        self._pattern_text = re.compile("([ -~])")

        self._low_latency_index = 0
        self.low_latency_max = 1000

        self._term = Terminal()
        self._term.enter_fullscreen()
        self._term.hidden_cursor()

        self._default_bg = f"{self._term.on_black}"
        self._window_bg = f"{self._term.reverse}"
        self._error_bg = f"{self._term.on_white}"
        self._warn_bg = f"{self._term.on_black}"

        self._default_style = f"{self._term.normal}{self._term.white}{self._default_bg}"
        self._window_style = f"{self._term.normal}{self._term.white}{self._window_bg}"
        self._error_style = f"{self._term.normal}{self._term.red}{self._error_bg}"
        self._warn_style = f"{self._term.normal}{self._term.yellow}{self._warn_bg}"
        self._default_style = f"{self._term.normal}{self._term.snow3}{self._default_bg}"

        self.update_counter_interval = 10
        self._update_counter = 0
        self.console_scrollback = 500
        self._contents_console_a = [""]
        self._contents_console_b = [""]
        self._previous_height = self._term.height
        self._previous_width = self._term.width
        self.last_updates_heuristic_enabled = True
        self._max_last_updates = 5
        self._last_updates = [datetime.now()]
        self.heuristic_target_seconds = 10
Ejemplo n.º 29
0
    def print_formatted(self,
                        fp=sys.stdout,
                        force_color=False,
                        no_color=False,
                        show_cmd=False,
                        show_full_cmd=False,
                        show_user=False,
                        show_pid=False,
                        show_fan_speed=None,
                        show_codec="",
                        show_power=None,
                        gpuname_width=16,
                        show_header=True,
                        eol_char=os.linesep,
                        show_pmemory=False):
        # ANSI color configuration
        if force_color and no_color:
            raise ValueError("--color and --no_color can't"
                             " be used at the same time")

        if force_color:
            TERM = os.getenv('TERM') or 'xterm-256color'
            t_color = Terminal(kind=TERM, force_styling=True)

            # workaround of issue #32 (watch doesn't recognize sgr0 characters)
            t_color._normal = u'\x1b[0;10m'
        elif no_color:
            t_color = Terminal(force_styling=None)
        else:
            t_color = Terminal()  # auto, depending on isatty

        # appearance settings
        entry_name_width = [len(g.entry['name']) for g in self]
        gpuname_width = max([gpuname_width or 0] + entry_name_width)

        # header
        if show_header:
            if IS_WINDOWS:
                # no localization is available; just use a reasonable default
                # same as str(timestr) but without ms
                timestr = self.query_time.strftime('%Y-%m-%d %H:%M:%S')
            else:
                time_format = locale.nl_langinfo(locale.D_T_FMT)
                timestr = self.query_time.strftime(time_format)
            header_template = '{t.bold_white}{hostname:{width}}{t.normal}  '
            header_template += '{timestr}  '
            header_template += '{t.bold_black}{driver_version}{t.normal}'

            header_msg = header_template.format(
                hostname=self.hostname,
                width=gpuname_width + 3,  # len("[?]")
                timestr=timestr,
                driver_version=self.driver_version,
                t=t_color,
            )

            fp.write(header_msg.strip())
            fp.write(eol_char)

        # body
        for g in self:
            g.print_to(fp,
                       show_cmd=show_cmd,
                       show_full_cmd=show_full_cmd,
                       show_user=show_user,
                       show_pid=show_pid,
                       show_fan_speed=show_fan_speed,
                       show_codec=show_codec,
                       show_power=show_power,
                       gpuname_width=gpuname_width,
                       term=t_color,
                       show_pmemory=show_pmemory)
            fp.write(eol_char)

        fp.flush()
Ejemplo n.º 30
0
from blessed import Terminal

t = Terminal()

print(t.bold('Hi there!'))
print(t.bold_red_on_bright_green('It hurts my eyes!'))

with t.location(0, t.height - 1):
    print(t.center(t.blink('press any key to continue.')))

with t.cbreak():
    inp = t.inkey()
print('You pressed ' + repr(inp))
Ejemplo n.º 31
0
    def do_l(self, arg):
        'Lists current portfolio'
        t = Terminal()
        portfolio = self.trader.portfolios()
        if portfolio['extended_hours_equity']:
            equity = float(portfolio['extended_hours_equity'])
        else:
            equity = float(portfolio['equity'])

        eq = '%.2f' % equity
        previous_close = float(portfolio['adjusted_equity_previous_close'])
        change = equity - previous_close
        change_pct = '%.2f' % (change / previous_close * 100.0)
        change_pct = color_data(change_pct)
        change = color_data(change)

        account_details = self.trader.get_account()
        if 'margin_balances' in account_details:
            buying_power = account_details['margin_balances'][
                'unallocated_margin_cash']

        account_table = SingleTable(
            [['Portfolio Value', 'Change', 'Buying Power'],
             [eq, change + ' (' + change_pct + '%)', buying_power]], 'Account')
        print((account_table.table))

        # Load Stocks
        positions = self.trader.securities_owned()
        instruments = [
            position['instrument'] for position in positions['results']
        ]
        symbols = [
            self.get_symbol(position['instrument'])
            for position in positions['results']
        ]

        market_data = self.trader.get_stock_marketdata(instruments)

        table_data = []
        table_data.append([
            "Symbol", "Last", "Shares", "Equity", "Avg Cost", "Return", "Day",
            "EquityChange", "Day %"
        ])

        i = 0
        for position in positions['results']:
            quantity = int(float(position['quantity']))
            symbol = self.get_symbol(position['instrument'])
            price = market_data[i]['last_trade_price']
            total_equity = float(price) * quantity
            buy_price = float(position['average_buy_price'])
            p_l = total_equity - buy_price * quantity
            day_change = float(market_data[i]['last_trade_price']) - float(
                market_data[i]['previous_close'])
            day_change_q_val = '{:04.2f}'.format(quantity * day_change)
            day_change_pct = '{:04.2f}'.format(
                float((day_change / float(market_data[i]['previous_close'])) *
                      100))
            table_data.append([
                symbol, price, quantity, total_equity, buy_price,
                color_data(p_l),
                color_data(day_change),
                color_data(day_change_q_val),
                color_data(day_change_pct)
            ])
            i += 1

        table = SingleTable(table_data, 'Portfolio')
        table.inner_row_border = True
        table.justify_columns = {0: 'center'}

        print((table.table))
Ejemplo n.º 32
0
def main():
    """Program entry point."""
    # pylint: disable=too-many-locals
    #         Too many local variables (20/15)
    term = Terminal()
    worm = [Location(x=term.width // 2, y=term.height // 2)]
    worm_length = 2
    bearing = Direction(*LEFT)
    direction = left_of
    nibble = Nibble(location=worm[0], value=0)
    color_nibble = term.black_on_green
    color_worm = term.yellow_reverse
    color_head = term.red_reverse
    color_bg = term.on_blue
    echo(term.move(1, 1))
    echo(color_bg(term.clear))

    # speed is actually a measure of time; the shorter, the faster.
    speed = 0.1
    modifier = 0.93
    inp = None

    echo(term.move(term.height, 0))
    with term.hidden_cursor(), term.cbreak(), term.location():
        while inp not in (u'q', u'Q'):

            # delete the tail of the worm at worm_length
            if len(worm) > worm_length:
                echo(term.move(*worm.pop(0)))
                echo(color_bg(u' '))

            # compute head location
            head = worm.pop()

            # check for hit against self; hitting a wall results in the (y, x)
            # location being clipped, -- and death by hitting self (not wall).
            if hit_any(head, worm):
                break

            # get the next nibble, which may be equal to ours unless this
            # nibble has been struck by any portion of our worm body.
            n_nibble = next_nibble(term, nibble, head, worm)

            # get the next worm_length and speed, unless unchanged.
            worm_length = next_wormlength(nibble, head, worm_length)
            speed = next_speed(nibble, head, speed, modifier)

            if n_nibble != nibble:
                # erase the old one, careful to redraw the nibble contents
                # with a worm color for those portions that overlay.
                for (yloc, xloc) in nibble_locations(*nibble):
                    echo(u''.join((term.move(yloc, xloc),
                                   (color_worm if
                                    (yloc, xloc) == head else color_bg)(u' '),
                                   term.normal)))
                # and draw the new,
                echo(
                    term.move(*n_nibble.location) +
                    (color_nibble('{}'.format(n_nibble.value))))

            # display new worm head
            echo(term.move(*head) + color_head(head_glyph(direction)))

            # and its old head (now, a body piece)
            if worm:
                echo(term.move(*(worm[-1])))
                echo(color_worm(u' '))
            echo(term.move(*head))

            # wait for keyboard input, which may indicate
            # a new direction (up/down/left/right)
            inp = term.inkey(timeout=speed)

            # discover new direction, given keyboard input and/or bearing.
            nxt_direction = next_bearing(term, inp.code, bearing)

            # discover new bearing, given new direction compared to prev
            nxt_bearing = change_bearing(nxt_direction, head, term)

            # disallow new bearing/direction when flipped: running into
            # oneself, for example traveling left while traveling right.
            if not bearing_flipped(bearing, nxt_bearing):
                direction = nxt_direction
                bearing = nxt_bearing

            # append the prior `head' onto the worm, then
            # a new `head' for the given direction.
            worm.extend([head, direction(head, term)])

            # re-assign new nibble,
            nibble = n_nibble

    echo(term.normal)
    score = (worm_length - 1) * 100
    echo(u''.join((term.move(term.height - 1, 1), term.normal)))
    echo(u''.join((u'\r\n', u'score: {}'.format(score), u'\r\n')))
Ejemplo n.º 33
0
 def test_turn_off_colors(self, mock_user_conf):
     mock_user_conf.return_value = {'COLOR': '0'}
     stream = StringIO()
     _out('Hello world', None, Terminal().red, stream)
     self.assertEquals('Hello world\n', stream.getvalue())
Ejemplo n.º 34
0
        net.update_type)
    t1 = '------     -----                                   |  Learning Rate: {0}\n'.format(
        net.learning_rate)
    t2 = '|_0_|------|_0_|- -                                |  Momentum: {0}\n'.format(
        net.momentum)
    t4 = '----- - -  -----    -  -----     Expected Output   ------------------------------------------------\n'
    t5 = '|_1_|------|_1_|-------|_0_|-------> 0 or 1        \n'
    t7 = '-----  - - -----  -  - -----                       \n'
    t8 = '|_B_|------|_2_|-   -  |_B_|                       \n'
    t10 = '       - - ----- - -                              \n'
    t11 = '       -  -|_3_|- -                               \n'
    t12 = '        -  ----- -                                \n'
    t13 = '          -|_B_|-                                 \n'
    display = t0 + t1 + t2 + t4 + t5 + t7 + t8 + t10 + t11 + t12 + t13

    term = Terminal()
    ui = HSplit(
        VSplit(
            Text(text=f"{display}",
                 title='Network Display',
                 border_color=2,
                 color=3),
            Log(title='Training Data', border_color=2, color=3),
            Log(title=
                '|---EPOCH--|----------------MSE------------|--------------OUTPUT--------------|------EXPECTED-------|',
                border_color=5,
                color=3),
            HGauge(val=50, title="Epochs", border_color=5),
        ),
        VSplit(
            Log(title='Layer 0 to Layer 1 (Delta, Batch Delta Weight)',