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