|
1 ========= |
|
2 Blessings |
|
3 ========= |
|
4 |
|
5 Coding with Blessings looks like this... :: |
|
6 |
|
7 from blessings import Terminal |
|
8 |
|
9 t = Terminal() |
|
10 |
|
11 print t.bold('Hi there!') |
|
12 print t.bold_red_on_bright_green('It hurts my eyes!') |
|
13 |
|
14 with t.location(0, t.height - 1): |
|
15 print 'This is at the bottom.' |
|
16 |
|
17 Or, for byte-level control, you can drop down and play with raw terminal |
|
18 capabilities:: |
|
19 |
|
20 print '{t.bold}All your {t.red}bold and red base{t.normal}'.format(t=t) |
|
21 print t.wingo(2) |
|
22 |
|
23 The Pitch |
|
24 ========= |
|
25 |
|
26 Blessings lifts several of curses_' limiting assumptions, and it makes your |
|
27 code pretty, too: |
|
28 |
|
29 * Use styles, color, and maybe a little positioning without clearing the whole |
|
30 screen first. |
|
31 * Leave more than one screenful of scrollback in the buffer after your program |
|
32 exits, like a well-behaved command-line app should. |
|
33 * Get rid of all those noisy, C-like calls to ``tigetstr`` and ``tparm``, so |
|
34 your code doesn't get crowded out by terminal bookkeeping. |
|
35 * Act intelligently when somebody redirects your output to a file, omitting the |
|
36 terminal control codes the user doesn't want to see (optional). |
|
37 |
|
38 .. _curses: http://docs.python.org/library/curses.html |
|
39 |
|
40 Before And After |
|
41 ---------------- |
|
42 |
|
43 Without Blessings, this is how you'd print some underlined text at the bottom |
|
44 of the screen:: |
|
45 |
|
46 from curses import tigetstr, setupterm, tparm |
|
47 from fcntl import ioctl |
|
48 from os import isatty |
|
49 import struct |
|
50 import sys |
|
51 from termios import TIOCGWINSZ |
|
52 |
|
53 # If we want to tolerate having our output piped to other commands or |
|
54 # files without crashing, we need to do all this branching: |
|
55 if hasattr(sys.stdout, 'fileno') and isatty(sys.stdout.fileno()): |
|
56 setupterm() |
|
57 sc = tigetstr('sc') |
|
58 cup = tigetstr('cup') |
|
59 rc = tigetstr('rc') |
|
60 underline = tigetstr('smul') |
|
61 normal = tigetstr('sgr0') |
|
62 else: |
|
63 sc = cup = rc = underline = normal = '' |
|
64 print sc # Save cursor position. |
|
65 if cup: |
|
66 # tigetnum('lines') doesn't always update promptly, hence this: |
|
67 height = struct.unpack('hhhh', ioctl(0, TIOCGWINSZ, '\000' * 8))[0] |
|
68 print tparm(cup, height - 1, 0) # Move cursor to bottom. |
|
69 print 'This is {under}underlined{normal}!'.format(under=underline, |
|
70 normal=normal) |
|
71 print rc # Restore cursor position. |
|
72 |
|
73 Phew! That was long and full of incomprehensible trash! Let's try it again, |
|
74 this time with Blessings:: |
|
75 |
|
76 from blessings import Terminal |
|
77 |
|
78 term = Terminal() |
|
79 with term.location(0, term.height - 1): |
|
80 print 'This is', term.underline('pretty!') |
|
81 |
|
82 Much better. |
|
83 |
|
84 What It Provides |
|
85 ================ |
|
86 |
|
87 Blessings provides just one top-level object: ``Terminal``. Instantiating a |
|
88 ``Terminal`` figures out whether you're on a terminal at all and, if so, does |
|
89 any necessary terminal setup. After that, you can proceed to ask it all sorts |
|
90 of things about the terminal. Terminal terminal terminal. |
|
91 |
|
92 Simple Formatting |
|
93 ----------------- |
|
94 |
|
95 Lots of handy formatting codes ("capabilities" in low-level parlance) are |
|
96 available as attributes on a ``Terminal``. For example:: |
|
97 |
|
98 from blessings import Terminal |
|
99 |
|
100 term = Terminal() |
|
101 print 'I am ' + term.bold + 'bold' + term.normal + '!' |
|
102 |
|
103 You can also use them as wrappers so you don't have to say ``normal`` |
|
104 afterward:: |
|
105 |
|
106 print 'I am', term.bold('bold') + '!' |
|
107 |
|
108 Or, if you want fine-grained control while maintaining some semblance of |
|
109 brevity, you can combine it with Python's string formatting, which makes |
|
110 attributes easy to access:: |
|
111 |
|
112 print 'All your {t.red}base {t.underline}are belong to us{t.normal}'.format(t=term) |
|
113 |
|
114 Simple capabilities of interest include... |
|
115 |
|
116 * ``bold`` |
|
117 * ``reverse`` |
|
118 * ``underline`` |
|
119 * ``no_underline`` (which turns off underlining) |
|
120 * ``blink`` |
|
121 * ``normal`` (which turns off everything, even colors) |
|
122 * ``clear_eol`` (clear to the end of the line) |
|
123 * ``clear_bol`` (clear to beginning of line) |
|
124 * ``clear_eos`` (clear to end of screen) |
|
125 |
|
126 Here are a few more which are less likely to work on all terminals: |
|
127 |
|
128 * ``dim`` |
|
129 * ``italic`` and ``no_italic`` |
|
130 * ``shadow`` and ``no_shadow`` |
|
131 * ``standout`` and ``no_standout`` |
|
132 * ``subscript`` and ``no_subscript`` |
|
133 * ``superscript`` and ``no_superscript`` |
|
134 * ``flash`` (which flashes the screen once) |
|
135 |
|
136 Note that, while the inverse of ``underline`` is ``no_underline``, the only way |
|
137 to turn off ``bold`` or ``reverse`` is ``normal``, which also cancels any |
|
138 custom colors. This is because there's no way to tell the terminal to undo |
|
139 certain pieces of formatting, even at the lowest level. |
|
140 |
|
141 You might notice that the above aren't the typical incomprehensible terminfo |
|
142 capability names; we alias a few of the harder-to-remember ones for |
|
143 readability. However, you aren't limited to these: you can reference any |
|
144 string-returning capability listed on the `terminfo man page`_ by the name |
|
145 under the "Cap-name" column: for example, ``term.rum``. |
|
146 |
|
147 .. _`terminfo man page`: http://www.manpagez.com/man/5/terminfo/ |
|
148 |
|
149 Color |
|
150 ----- |
|
151 |
|
152 16 colors, both foreground and background, are available as easy-to-remember |
|
153 attributes:: |
|
154 |
|
155 from blessings import Terminal |
|
156 |
|
157 term = Terminal() |
|
158 print term.red + term.on_green + 'Red on green? Ick!' + term.normal |
|
159 print term.bright_red + term.on_bright_blue + 'This is even worse!' + term.normal |
|
160 |
|
161 You can also call them as wrappers, which sets everything back to normal at the |
|
162 end:: |
|
163 |
|
164 print term.red_on_green('Red on green? Ick!') |
|
165 print term.yellow('I can barely see it.') |
|
166 |
|
167 The available colors are... |
|
168 |
|
169 * ``black`` |
|
170 * ``red`` |
|
171 * ``green`` |
|
172 * ``yellow`` |
|
173 * ``blue`` |
|
174 * ``magenta`` |
|
175 * ``cyan`` |
|
176 * ``white`` |
|
177 |
|
178 You can set the background color instead of the foreground by prepending |
|
179 ``on_``, as in ``on_blue``. There is also a ``bright`` version of each color: |
|
180 for example, ``on_bright_blue``. |
|
181 |
|
182 There is also a numerical interface to colors, which takes an integer from |
|
183 0-15:: |
|
184 |
|
185 term.color(5) + 'Hello' + term.normal |
|
186 term.on_color(3) + 'Hello' + term.normal |
|
187 |
|
188 term.color(5)('Hello') |
|
189 term.on_color(3)('Hello') |
|
190 |
|
191 If some color is unsupported (for instance, if only the normal colors are |
|
192 available, not the bright ones), trying to use it will, on most terminals, have |
|
193 no effect: the foreground and background colors will stay as they were. You can |
|
194 get fancy and do different things depending on the supported colors by checking |
|
195 `number_of_colors`_. |
|
196 |
|
197 .. _`number_of_colors`: http://packages.python.org/blessings/#blessings.Terminal.number_of_colors |
|
198 |
|
199 Compound Formatting |
|
200 ------------------- |
|
201 |
|
202 If you want to do lots of crazy formatting all at once, you can just mash it |
|
203 all together:: |
|
204 |
|
205 from blessings import Terminal |
|
206 |
|
207 term = Terminal() |
|
208 print term.bold_underline_green_on_yellow + 'Woo' + term.normal |
|
209 |
|
210 Or you can use your newly coined attribute as a wrapper, which implicitly sets |
|
211 everything back to normal afterward:: |
|
212 |
|
213 print term.bold_underline_green_on_yellow('Woo') |
|
214 |
|
215 This compound notation comes in handy if you want to allow users to customize |
|
216 the formatting of your app: just have them pass in a format specifier like |
|
217 "bold_green" on the command line, and do a quick ``getattr(term, |
|
218 that_option)('Your text')`` when you do your formatting. |
|
219 |
|
220 I'd be remiss if I didn't credit couleur_, where I probably got the idea for |
|
221 all this mashing. |
|
222 |
|
223 .. _couleur: http://pypi.python.org/pypi/couleur |
|
224 |
|
225 Parametrized Capabilities |
|
226 ------------------------- |
|
227 |
|
228 Some capabilities take parameters. Rather than making you dig up ``tparm()`` |
|
229 all the time, we simply make such capabilities into callable strings. You can |
|
230 pass the parameters right in:: |
|
231 |
|
232 from blessings import Terminal |
|
233 |
|
234 term = Terminal() |
|
235 print term.move(10, 1) |
|
236 |
|
237 Here are some of interest: |
|
238 |
|
239 ``move`` |
|
240 Position the cursor elsewhere. Parameters are y coordinate, then x |
|
241 coordinate. |
|
242 ``move_x`` |
|
243 Move the cursor to the given column. |
|
244 ``move_y`` |
|
245 Move the cursor to the given row. |
|
246 |
|
247 You can also reference any other string-returning capability listed on the |
|
248 `terminfo man page`_ by its name under the "Cap-name" column. |
|
249 |
|
250 .. _`terminfo man page`: http://www.manpagez.com/man/5/terminfo/ |
|
251 |
|
252 Height and Width |
|
253 ---------------- |
|
254 |
|
255 It's simple to get the height and width of the terminal, in characters:: |
|
256 |
|
257 from blessings import Terminal |
|
258 |
|
259 term = Terminal() |
|
260 height = term.height |
|
261 width = term.width |
|
262 |
|
263 These are newly updated each time you ask for them, so they're safe to use from |
|
264 SIGWINCH handlers. |
|
265 |
|
266 Temporary Repositioning |
|
267 ----------------------- |
|
268 |
|
269 Sometimes you need to flit to a certain location, print something, and then |
|
270 return: for example, when updating a progress bar at the bottom of the screen. |
|
271 ``Terminal`` provides a context manager for doing this concisely:: |
|
272 |
|
273 from blessings import Terminal |
|
274 |
|
275 term = Terminal() |
|
276 with term.location(0, term.height - 1): |
|
277 print 'Here is the bottom.' |
|
278 print 'This is back where I came from.' |
|
279 |
|
280 Parameters to ``location()`` are ``x`` and then ``y``, but you can also pass |
|
281 just one of them, leaving the other alone. For example... :: |
|
282 |
|
283 with term.location(y=10): |
|
284 print 'We changed just the row.' |
|
285 |
|
286 If you want to reposition permanently, see ``move``, in an example above. |
|
287 |
|
288 Pipe Savvy |
|
289 ---------- |
|
290 |
|
291 If your program isn't attached to a terminal, like if it's being piped to |
|
292 another command or redirected to a file, all the capability attributes on |
|
293 ``Terminal`` will return empty strings. You'll get a nice-looking file without |
|
294 any formatting codes gumming up the works. |
|
295 |
|
296 If you want to override this--like if you anticipate your program being piped |
|
297 through ``less -r``, which handles terminal escapes just fine--pass |
|
298 ``force_styling=True`` to the ``Terminal`` constructor. |
|
299 |
|
300 In any case, there is an ``is_a_tty`` attribute on ``Terminal`` that lets you |
|
301 see whether the attached stream seems to be a terminal. If it's false, you |
|
302 might refrain from drawing progress bars and other frippery, since you're |
|
303 apparently headed into a pipe:: |
|
304 |
|
305 from blessings import Terminal |
|
306 |
|
307 term = Terminal() |
|
308 if term.is_a_tty: |
|
309 with term.location(0, term.height - 1): |
|
310 print 'Progress: [=======> ]' |
|
311 print term.bold('Important stuff') |
|
312 |
|
313 Shopping List |
|
314 ============= |
|
315 |
|
316 There are decades of legacy tied up in terminal interaction, so attention to |
|
317 detail and behavior in edge cases make a difference. Here are some ways |
|
318 Blessings has your back: |
|
319 |
|
320 * Uses the terminfo database so it works with any terminal type |
|
321 * Provides up-to-the-moment terminal height and width, so you can respond to |
|
322 terminal size changes (SIGWINCH signals). (Most other libraries query the |
|
323 ``COLUMNS`` and ``LINES`` environment variables or the ``cols`` or ``lines`` |
|
324 terminal capabilities, which don't update promptly, if at all.) |
|
325 * Avoids making a mess if the output gets piped to a non-terminal |
|
326 * Works great with standard Python string templating |
|
327 * Provides convenient access to all terminal capabilities, not just a sugared |
|
328 few |
|
329 * Outputs to any file-like object, not just stdout |
|
330 * Keeps a minimum of internal state, so you can feel free to mix and match with |
|
331 calls to curses or whatever other terminal libraries you like |
|
332 |
|
333 Blessings does not provide... |
|
334 |
|
335 * Native color support on the Windows command prompt. However, it should work |
|
336 when used in concert with colorama_. |
|
337 |
|
338 .. _colorama: http://pypi.python.org/pypi/colorama/0.2.4 |
|
339 |
|
340 Bugs |
|
341 ==== |
|
342 |
|
343 Bugs or suggestions? Visit the `issue tracker`_. |
|
344 |
|
345 .. _`issue tracker`: https://github.com/erikrose/blessings/issues/new |
|
346 |
|
347 License |
|
348 ======= |
|
349 |
|
350 Blessings is under the MIT License. See the LICENSE file. |
|
351 |
|
352 Version History |
|
353 =============== |
|
354 |
|
355 1.3 |
|
356 * Add ``number_of_colors``, which tells you how many colors the terminal |
|
357 supports. |
|
358 * Made ``color(n)`` and ``on_color(n)`` callable to wrap a string, like the |
|
359 named colors can. Also, make them both fall back to the ``setf`` and |
|
360 ``setb`` capabilities (like the named colors do) if the ANSI ``setaf`` and |
|
361 ``setab`` aren't available. |
|
362 * Allow ``color`` attr to act as an unparametrized string, not just a |
|
363 callable. |
|
364 * Make ``height`` and ``width`` examine any passed-in stream before falling |
|
365 back to stdout. (This rarely if ever affects actual behavior; it's mostly |
|
366 philosophical.) |
|
367 * Make caching simpler and slightly more efficient. |
|
368 * Get rid of a reference cycle between Terminals and FormattingStrings. |
|
369 * Update docs to reflect that terminal addressing (as in ``location()``) is |
|
370 0-based. |
|
371 |
|
372 1.2 |
|
373 * Added support for Python 3! We need 3.2.3 or greater, because the curses |
|
374 library couldn't decide whether to accept strs or bytes before that |
|
375 (http://bugs.python.org/issue10570). |
|
376 * Everything that comes out of the library is now unicode. This lets us |
|
377 support Python 3 without making a mess of the code, and Python 2 should |
|
378 continue to work unless you were testing types (and badly). Please file a |
|
379 bug if this causes trouble for you. |
|
380 * Changed to the MIT License for better world domination. |
|
381 * Added Sphinx docs. |
|
382 |
|
383 1.1 |
|
384 * Added nicely named attributes for colors. |
|
385 * Introduced compound formatting. |
|
386 * Added wrapper behavior for styling and colors. |
|
387 * Let you force capabilities to be non-empty, even if the output stream is |
|
388 not a terminal. |
|
389 * Added the ``is_a_tty`` attribute for telling whether the output stream is a |
|
390 terminal. |
|
391 * Sugared the remaining interesting string capabilities. |
|
392 * Let ``location()`` operate on just an x *or* y coordinate. |
|
393 |
|
394 1.0 |
|
395 * Extracted Blessings from nose-progressive, my `progress-bar-having, |
|
396 traceback-shortcutting, rootin', tootin' testrunner`_. It provided the |
|
397 tootin' functionality. |
|
398 |
|
399 .. _`progress-bar-having, traceback-shortcutting, rootin', tootin' testrunner`: http://pypi.python.org/pypi/nose-progressive/ |