term.py
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:9k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. '''
  2. @file term.py
  3. @brief a better shutil.copytree replacement
  4. $LicenseInfo:firstyear=2007&license=mit$
  5. Copyright (c) 2007-2010, Linden Research, Inc.
  6. Permission is hereby granted, free of charge, to any person obtaining a copy
  7. of this software and associated documentation files (the "Software"), to deal
  8. in the Software without restriction, including without limitation the rights
  9. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. copies of the Software, and to permit persons to whom the Software is
  11. furnished to do so, subject to the following conditions:
  12. The above copyright notice and this permission notice shall be included in
  13. all copies or substantial portions of the Software.
  14. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. THE SOFTWARE.
  21. $/LicenseInfo$
  22. '''
  23. #http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475116
  24. import sys, re
  25. class TerminalController:
  26.     """
  27.     A class that can be used to portably generate formatted output to
  28.     a terminal.  
  29.     
  30.     `TerminalController` defines a set of instance variables whose
  31.     values are initialized to the control sequence necessary to
  32.     perform a given action.  These can be simply included in normal
  33.     output to the terminal:
  34.         >>> term = TerminalController()
  35.         >>> print 'This is '+term.GREEN+'green'+term.NORMAL
  36.     Alternatively, the `render()` method can used, which replaces
  37.     '${action}' with the string required to perform 'action':
  38.         >>> term = TerminalController()
  39.         >>> print term.render('This is ${GREEN}green${NORMAL}')
  40.     If the terminal doesn't support a given action, then the value of
  41.     the corresponding instance variable will be set to ''.  As a
  42.     result, the above code will still work on terminals that do not
  43.     support color, except that their output will not be colored.
  44.     Also, this means that you can test whether the terminal supports a
  45.     given action by simply testing the truth value of the
  46.     corresponding instance variable:
  47.         >>> term = TerminalController()
  48.         >>> if term.CLEAR_SCREEN:
  49.         ...     print 'This terminal supports clearning the screen.'
  50.     Finally, if the width and height of the terminal are known, then
  51.     they will be stored in the `COLS` and `LINES` attributes.
  52.     """
  53.     # Cursor movement:
  54.     BOL = ''             #: Move the cursor to the beginning of the line
  55.     UP = ''              #: Move the cursor up one line
  56.     DOWN = ''            #: Move the cursor down one line
  57.     LEFT = ''            #: Move the cursor left one char
  58.     RIGHT = ''           #: Move the cursor right one char
  59.     # Deletion:
  60.     CLEAR_SCREEN = ''    #: Clear the screen and move to home position
  61.     CLEAR_EOL = ''       #: Clear to the end of the line.
  62.     CLEAR_BOL = ''       #: Clear to the beginning of the line.
  63.     CLEAR_EOS = ''       #: Clear to the end of the screen
  64.     # Output modes:
  65.     BOLD = ''            #: Turn on bold mode
  66.     BLINK = ''           #: Turn on blink mode
  67.     DIM = ''             #: Turn on half-bright mode
  68.     REVERSE = ''         #: Turn on reverse-video mode
  69.     NORMAL = ''          #: Turn off all modes
  70.     # Cursor display:
  71.     HIDE_CURSOR = ''     #: Make the cursor invisible
  72.     SHOW_CURSOR = ''     #: Make the cursor visible
  73.     # Terminal size:
  74.     COLS = None          #: Width of the terminal (None for unknown)
  75.     LINES = None         #: Height of the terminal (None for unknown)
  76.     # Foreground colors:
  77.     BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = ''
  78.     
  79.     # Background colors:
  80.     BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = ''
  81.     BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = ''
  82.     
  83.     _STRING_CAPABILITIES = """
  84.     BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1
  85.     CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold
  86.     BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0
  87.     HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split()
  88.     _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split()
  89.     _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split()
  90.     def __init__(self, term_stream=sys.stdout):
  91.         """
  92.         Create a `TerminalController` and initialize its attributes
  93.         with appropriate values for the current terminal.
  94.         `term_stream` is the stream that will be used for terminal
  95.         output; if this stream is not a tty, then the terminal is
  96.         assumed to be a dumb terminal (i.e., have no capabilities).
  97.         """
  98.         # Curses isn't available on all platforms
  99.         try: import curses
  100.         except: return
  101.         # If the stream isn't a tty, then assume it has no capabilities.
  102.         if not term_stream.isatty(): return
  103.         # Check the terminal type.  If we fail, then assume that the
  104.         # terminal has no capabilities.
  105.         try: curses.setupterm()
  106.         except: return
  107.         # Look up numeric capabilities.
  108.         self.COLS = curses.tigetnum('cols')
  109.         self.LINES = curses.tigetnum('lines')
  110.         
  111.         # Look up string capabilities.
  112.         for capability in self._STRING_CAPABILITIES:
  113.             (attrib, cap_name) = capability.split('=')
  114.             setattr(self, attrib, self._tigetstr(cap_name) or '')
  115.         # Colors
  116.         set_fg = self._tigetstr('setf')
  117.         if set_fg:
  118.             for i,color in zip(range(len(self._COLORS)), self._COLORS):
  119.                 setattr(self, color, curses.tparm(set_fg, i) or '')
  120.         set_fg_ansi = self._tigetstr('setaf')
  121.         if set_fg_ansi:
  122.             for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
  123.                 setattr(self, color, curses.tparm(set_fg_ansi, i) or '')
  124.         set_bg = self._tigetstr('setb')
  125.         if set_bg:
  126.             for i,color in zip(range(len(self._COLORS)), self._COLORS):
  127.                 setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '')
  128.         set_bg_ansi = self._tigetstr('setab')
  129.         if set_bg_ansi:
  130.             for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
  131.                 setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '')
  132.     def _tigetstr(self, cap_name):
  133.         # String capabilities can include "delays" of the form "$<2>".
  134.         # For any modern terminal, we should be able to just ignore
  135.         # these, so strip them out.
  136.         import curses
  137.         cap = curses.tigetstr(cap_name) or ''
  138.         return re.sub(r'$<d+>[/*]?', '', cap)
  139.     def render(self, template):
  140.         """
  141.         Replace each $-substitutions in the given template string with
  142.         the corresponding terminal control string (if it's defined) or
  143.         '' (if it's not).
  144.         """
  145.         return re.sub(r'$$|${w+}', self._render_sub, template)
  146.     def _render_sub(self, match):
  147.         s = match.group()
  148.         if s == '$$': return s
  149.         else: return getattr(self, s[2:-1])
  150. #######################################################################
  151. # Example use case: progress bar
  152. #######################################################################
  153. class ProgressBar:
  154.     """
  155.     A 3-line progress bar, which looks like::
  156.     
  157.                                 Header
  158.         20% [===========----------------------------------]
  159.                            progress message
  160.     The progress bar is colored, if the terminal supports color
  161.     output; and adjusts to the width of the terminal.
  162.     """
  163.     BAR = '%3d%% ${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}n'
  164.     HEADER = '${BOLD}${CYAN}%s${NORMAL}nn'
  165.         
  166.     def __init__(self, term, header):
  167.         self.term = term
  168.         if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL):
  169.             raise ValueError("Terminal isn't capable enough -- you "
  170.                              "should use a simpler progress dispaly.")
  171.         self.width = self.term.COLS or 75
  172.         self.bar = term.render(self.BAR)
  173.         self.header = self.term.render(self.HEADER % header.center(self.width))
  174.         self.cleared = 1 #: true if we haven't drawn the bar yet.
  175.         self.update(0, '')
  176.     def update(self, percent, message):
  177.         if self.cleared:
  178.             sys.stdout.write(self.header)
  179.             self.cleared = 0
  180.         n = int((self.width-10)*percent)
  181.         sys.stdout.write(
  182.             self.term.BOL + self.term.UP + self.term.CLEAR_EOL +
  183.             (self.bar % (100*percent, '='*n, '-'*(self.width-10-n))) +
  184.             self.term.CLEAR_EOL + message.center(self.width))
  185.     def clear(self):
  186.         if not self.cleared:
  187.             sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL +
  188.                              self.term.UP + self.term.CLEAR_EOL +
  189.                              self.term.UP + self.term.CLEAR_EOL)
  190.             self.cleared = 1