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']
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'
def inputter(): term = Terminal() with term.cbreak(): key = term.inkey(1) if key and not key.is_sequence: return key return None
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))
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
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
def draw_exit(terminal: blessed.Terminal) -> None: terminal.exit_fullscreen()
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()
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)))
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)
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)))
#!/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
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
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()
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']]
def related_tags(self, tags): term = Terminal() print(term.bold + f"Related tags: {tags}" + term.normal)
def menu_choice(self, show): term = Terminal() print(term.clear()) print(show) sleep(1) print(term.clear())
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()))
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 '''
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
def __init__(self): self.term = Terminal() self.roku = None
def info_out(msgs): term = Terminal() _out(msgs, None, term.blue)
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
def warn_out(msgs): """ Print the given error message (or list of messages) and exit. """ term = Terminal() _out(msgs, "WARNING", term.yellow)
def draw_clear(terminal: blessed.Terminal) -> None: terminal.clear()
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
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
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 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()
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))
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))
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')))
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())
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)',