Show More
@@ -8,10 +8,10 b' objects imported this way starts with ``i`` to minimize collisions.' | |||
|
8 | 8 | ``ipipe`` supports "pipeline expressions", which is something resembling Unix |
|
9 | 9 | pipes. An example is: |
|
10 | 10 | |
|
11 | iwalk | ifilter("name.endswith('.py')") | isort("size") | |
|
11 | >>> ienv | isort("_.key.lower()") | |
|
12 | ||
|
13 | This gives a listing of all environment variables sorted by name. | |
|
12 | 14 | |
|
13 | This gives a listing of all files in the current directory (and subdirectories) | |
|
14 | whose name ends with '.py' sorted by size. | |
|
15 | 15 | |
|
16 | 16 | There are three types of objects in a pipeline expression: |
|
17 | 17 | |
@@ -31,6 +31,78 b' There are three types of objects in a pipeline expression:' | |||
|
31 | 31 | expression. If a pipeline expression doesn't end in a display object a default |
|
32 | 32 | display objects will be used. One example is `οΏ½browse`` which is a ``curses`` |
|
33 | 33 | based browser. |
|
34 | ||
|
35 | ||
|
36 | Adding support for pipeline expressions to your own objects can be done through | |
|
37 | three extensions points (all of them optional): | |
|
38 | ||
|
39 | * An object that will be displayed as a row by a ``Display`` object should | |
|
40 | implement the method ``__xattrs__(self, mode)``. This method must return a | |
|
41 | sequence of attribute names. This sequence may also contain integers, which | |
|
42 | will be treated as sequence indizes. Also supported is ``None``, which uses | |
|
43 | the object itself and callables which will be called with the object as the | |
|
44 | an argument. If ``__xattrs__()`` isn't implemented ``(None,)`` will be used as | |
|
45 | the attribute sequence (i.e. the object itself (it's ``repr()`` format) will | |
|
46 | be being displayed. The global function ``xattrs()`` implements this | |
|
47 | functionality. | |
|
48 | ||
|
49 | * When an object ``foo`` is displayed in the header (or footer) of the browser | |
|
50 | ``foo.__xrepr__("header")`` (or ``foo.__xrepr__("footer")``) is called. | |
|
51 | Currently only ``"header"`` and ``"footer"`` are used as the mode argument. | |
|
52 | When the method doesn't recognize the mode argument, it should fall back to | |
|
53 | the ``repr()`` output. If the method ``__xrepr__()`` isn't defined the browser | |
|
54 | falls back to ``repr()``. The global function ``xrepr()`` implements this | |
|
55 | functionality. | |
|
56 | ||
|
57 | * Objects that can be iterated by ``Pipe``s must implement the method | |
|
58 | ``__xiter__(self, mode)``. ``mode`` can take the following values: | |
|
59 | ||
|
60 | - ``"default"``: This is the default value and ist always used by pipeline | |
|
61 | expressions. Other values are only used in the browser. | |
|
62 | - ``None``: This value is passed by the browser. The object must return an | |
|
63 | iterable of ``XMode`` objects describing all modes supported by the object. | |
|
64 | (This should never include ``"default"`` or ``None``). | |
|
65 | - Any other value that the object supports. | |
|
66 | ||
|
67 | The global function ``xiter()`` can be called to get such an iterator. If | |
|
68 | the method ``_xiter__`` isn't implemented, ``xiter()`` falls back to | |
|
69 | ``__iter__``. In addition to that, dictionaries and modules receive special | |
|
70 | treatment (returning an iterator over ``(key, value)`` pairs). This makes it | |
|
71 | possible to use dictionaries and modules in pipeline expressions, for example: | |
|
72 | ||
|
73 | >>> import sys | |
|
74 | >>> sys | ifilter("isinstance(_.value, int)") | idump | |
|
75 | key |value | |
|
76 | api_version| 1012 | |
|
77 | dllhandle | 503316480 | |
|
78 | hexversion | 33817328 | |
|
79 | maxint |2147483647 | |
|
80 | maxunicode | 65535 | |
|
81 | >>> sys.modules | ifilter("_.value is not None") | isort("_.key.lower()") | |
|
82 | ... | |
|
83 | ||
|
84 | Note: The expression strings passed to ``ifilter()`` and ``isort()`` can | |
|
85 | refer to the object to be filtered or sorted via the variable ``_``. When | |
|
86 | running under Python 2.4 it's also possible to refer to the attributes of | |
|
87 | the object directy, i.e.: | |
|
88 | ||
|
89 | >>> sys.modules | ifilter("_.value is not None") | isort("_.key.lower()") | |
|
90 | ||
|
91 | can be replaced with | |
|
92 | ||
|
93 | >>> sys.modules | ifilter("value is not None") | isort("key.lower()") | |
|
94 | ||
|
95 | In addition to expression strings, it's possible to pass callables (taking | |
|
96 | the object as an argument) to ``ifilter()``, ``isort()`` and ``ieval()``: | |
|
97 | ||
|
98 | >>> sys | ifilter(lambda _:isinstance(_.value, int)) \ | |
|
99 | ... | ieval(lambda _: (_.key, hex(_.value))) | idump | |
|
100 | 0 |1 | |
|
101 | api_version|0x3f4 | |
|
102 | dllhandle |0x1e000000 | |
|
103 | hexversion |0x20402f0 | |
|
104 | maxint |0x7fffffff | |
|
105 | maxunicode |0xffff | |
|
34 | 106 | """ |
|
35 | 107 | |
|
36 | 108 | import sys, os, os.path, stat, glob, new, csv, datetime |
@@ -88,6 +160,34 b' __all__ = [' | |||
|
88 | 160 | os.stat_float_times(True) # enable microseconds |
|
89 | 161 | |
|
90 | 162 | |
|
163 | class _AttrNamespace(object): | |
|
164 | """ | |
|
165 | Internal helper class that is used for providing a namespace for evaluating | |
|
166 | expressions containg attribute names of an object. | |
|
167 | ||
|
168 | This class can only be used with Python 2.4. | |
|
169 | """ | |
|
170 | def __init__(self, wrapped): | |
|
171 | self.wrapped = wrapped | |
|
172 | ||
|
173 | def __getitem__(self, name): | |
|
174 | if name == "_": | |
|
175 | return self.wrapped | |
|
176 | try: | |
|
177 | return getattr(self.wrapped, name) | |
|
178 | except AttributeError: | |
|
179 | raise KeyError(name) | |
|
180 | ||
|
181 | # Python 2.3 compatibility | |
|
182 | # fall back to a real dictionary (containing just the object itself) if we can't | |
|
183 | # use the _AttrNamespace class in eval() | |
|
184 | try: | |
|
185 | eval("_", None, _AttrNamespace(None)) | |
|
186 | except TypeError: | |
|
187 | def _AttrNamespace(wrapped): | |
|
188 | return {"_": wrapped} | |
|
189 | ||
|
190 | ||
|
91 | 191 | _default = object() |
|
92 | 192 | |
|
93 | 193 | def item(iterator, index, default=_default): |
@@ -124,23 +224,6 b' def item(iterator, index, default=_default):' | |||
|
124 | 224 | return default |
|
125 | 225 | |
|
126 | 226 | |
|
127 | class _AttrNamespace(object): | |
|
128 | """ | |
|
129 | Internal helper that is used for providing a namespace for evaluating | |
|
130 | expressions containg attribute names of an object. | |
|
131 | """ | |
|
132 | def __init__(self, wrapped): | |
|
133 | self.wrapped = wrapped | |
|
134 | ||
|
135 | def __getitem__(self, name): | |
|
136 | if name == "_": | |
|
137 | return self.wrapped | |
|
138 | try: | |
|
139 | return getattr(self.wrapped, name) | |
|
140 | except AttributeError: | |
|
141 | raise KeyError(name) | |
|
142 | ||
|
143 | ||
|
144 | 227 | class Table(object): |
|
145 | 228 | """ |
|
146 | 229 | A ``Table`` is an object that produces items (just like a normal Python |
@@ -148,6 +231,10 b' class Table(object):' | |||
|
148 | 231 | expression. The displayhook will open the default browser for such an object |
|
149 | 232 | (instead of simply printing the ``repr()`` result). |
|
150 | 233 | """ |
|
234 | ||
|
235 | # We want to support ``foo`` and ``foo()`` in pipeline expression: | |
|
236 | # So we implement the required operators (``|`` and ``+``) in the metaclass, | |
|
237 | # instantiate the class and forward the operator to the instance | |
|
151 | 238 | class __metaclass__(type): |
|
152 | 239 | def __iter__(self): |
|
153 | 240 | return iter(self()) |
@@ -174,18 +261,23 b' class Table(object):' | |||
|
174 | 261 | return False |
|
175 | 262 | |
|
176 | 263 | def __or__(self, other): |
|
264 | # autoinstantiate right hand side | |
|
177 | 265 | if isinstance(other, type) and issubclass(other, (Table, Display)): |
|
178 | 266 | other = other() |
|
267 | # treat simple strings and functions as ``ieval`` instances | |
|
179 | 268 | elif not isinstance(other, Display) and not isinstance(other, Table): |
|
180 | 269 | other = ieval(other) |
|
270 | # forward operations to the right hand side | |
|
181 | 271 | return other.__ror__(self) |
|
182 | 272 | |
|
183 | 273 | def __add__(self, other): |
|
274 | # autoinstantiate right hand side | |
|
184 | 275 | if isinstance(other, type) and issubclass(other, Table): |
|
185 | 276 | other = other() |
|
186 | 277 | return ichain(self, other) |
|
187 | 278 | |
|
188 | 279 | def __radd__(self, other): |
|
280 | # autoinstantiate left hand side | |
|
189 | 281 | if isinstance(other, type) and issubclass(other, Table): |
|
190 | 282 | other = other() |
|
191 | 283 | return ichain(other, self) |
@@ -206,6 +298,7 b' class Pipe(Table):' | |||
|
206 | 298 | return input | self() |
|
207 | 299 | |
|
208 | 300 | def __ror__(self, input): |
|
301 | # autoinstantiate left hand side | |
|
209 | 302 | if isinstance(input, type) and issubclass(input, Table): |
|
210 | 303 | input = input() |
|
211 | 304 | self.input = input |
@@ -249,9 +342,7 b' def _attrname(name):' | |||
|
249 | 342 | def xrepr(item, mode): |
|
250 | 343 | try: |
|
251 | 344 | func = item.__xrepr__ |
|
252 | except (KeyboardInterrupt, SystemExit): | |
|
253 | raise | |
|
254 | except Exception: | |
|
345 | except AttributeError: | |
|
255 | 346 | return repr(item) |
|
256 | 347 | else: |
|
257 | 348 | return func(mode) |
@@ -287,7 +378,7 b' def xiter(item, mode):' | |||
|
287 | 378 | def items(item): |
|
288 | 379 | fields = ("key", "value") |
|
289 | 380 | for key in sorted(item.__dict__): |
|
290 | yield Fields(fields, key, getattr(item, key)) | |
|
381 | yield Fields(fields, key=key, value=getattr(item, key)) | |
|
291 | 382 | return items(item) |
|
292 | 383 | elif isinstance(item, basestring): |
|
293 | 384 | if not len(item): |
@@ -526,7 +617,7 b' class ifile(object):' | |||
|
526 | 617 | "size", "blocks", "blksize", "isdir", "islink", |
|
527 | 618 | "mimetype", "encoding" |
|
528 | 619 | ) |
|
529 | return ("name","type", "size", "access", "owner", "group", "mdate") | |
|
620 | return ("name", "type", "size", "access", "owner", "group", "mdate") | |
|
530 | 621 | |
|
531 | 622 | def __xrepr__(self, mode): |
|
532 | 623 | if mode == "header" or mode == "footer": |
@@ -1085,6 +1176,7 b' class idump(Display):' | |||
|
1085 | 1176 | row[attrname] = (value, text) |
|
1086 | 1177 | rows.append(row) |
|
1087 | 1178 | |
|
1179 | stream.write("\n") | |
|
1088 | 1180 | for (i, attrname) in enumerate(self.attrs): |
|
1089 | 1181 | stream.write(_attrname(attrname)) |
|
1090 | 1182 | spc = colwidths[attrname] - len(_attrname(attrname)) |
@@ -1157,7 +1249,16 b' class idump(Display):' | |||
|
1157 | 1249 | |
|
1158 | 1250 | |
|
1159 | 1251 | class XMode(object): |
|
1252 | """ | |
|
1253 | An ``XMode`` object describes one enter mode available for an object | |
|
1254 | """ | |
|
1160 | 1255 | def __init__(self, object, mode, title=None, description=None): |
|
1256 | """ | |
|
1257 | Create a new ``XMode`` object for the object ``object``. This object | |
|
1258 | must support the enter mode ``mode`` (i.e. ``object.__xiter__(mode)`` | |
|
1259 | must return an iterable). ``title`` and ``description`` will be | |
|
1260 | displayed in the browser when selecting among the available modes. | |
|
1261 | """ | |
|
1161 | 1262 | self.object = object |
|
1162 | 1263 | self.mode = mode |
|
1163 | 1264 | self.title = title |
@@ -1234,7 +1335,7 b' pagedown' | |||
|
1234 | 1335 | Move the cursor down one page (minus overlap). |
|
1235 | 1336 | |
|
1236 | 1337 | pageup |
|
1237 | Move the cursor up one page. | |
|
1338 | Move the cursor up one page (minus overlap). | |
|
1238 | 1339 | |
|
1239 | 1340 | left |
|
1240 | 1341 | Move the cursor left. |
@@ -1257,8 +1358,8 b' pickattr' | |||
|
1257 | 1358 | 'Pick' the attribute under the cursor (i.e. the row/column the cursor is on). |
|
1258 | 1359 | |
|
1259 | 1360 | pickallattrs |
|
1260 | Pick' the complete column under the cursor (i.e. the attribute under the cursor | |
|
1261 |
from all currently fetched objects |
|
|
1361 | Pick' the complete column under the cursor (i.e. the attribute under the cursor) | |
|
1362 | from all currently fetched objects. These attributes will be returned as a list. | |
|
1262 | 1363 | |
|
1263 | 1364 | tooglemark |
|
1264 | 1365 | Mark/unmark the object under the cursor. Marked objects have a '!' after the |
@@ -1269,16 +1370,17 b' pickmarked' | |||
|
1269 | 1370 | |
|
1270 | 1371 | pickmarkedattr |
|
1271 | 1372 | 'Pick' the attribute under the cursor from all marked objects (This returns a |
|
1272 | list) | |
|
1373 | list). | |
|
1273 | 1374 | |
|
1274 | 1375 | enterdefault |
|
1275 | 1376 | Enter the object under the cursor. (what this mean depends on the object |
|
1276 | itself). This opens a new browser 'level'. | |
|
1377 | itself (i.e. how it implements the '__xiter__' method). This opens a new browser | |
|
1378 | 'level'. | |
|
1277 | 1379 | |
|
1278 | 1380 | enter |
|
1279 | 1381 | Enter the object under the cursor. If the object provides different enter modes |
|
1280 |
a menu of all modes will be presented |
|
|
1281 |
or 'enterdefault' command) |
|
|
1382 | a menu of all modes will be presented; choice one and enter it (via the 'enter' | |
|
1383 | or 'enterdefault' command). | |
|
1282 | 1384 | |
|
1283 | 1385 | enterattr |
|
1284 | 1386 | Enter the attribute under the cursor. |
@@ -1289,7 +1391,7 b' Leave the current browser level and go back to the previous one.' | |||
|
1289 | 1391 | detail |
|
1290 | 1392 | Show a detail view of the object under the cursor. This shows the name, type, |
|
1291 | 1393 | doc string and value of the object attributes (and it might show more attributes |
|
1292 |
than in the list view |
|
|
1394 | than in the list view, depending on the object). | |
|
1293 | 1395 | |
|
1294 | 1396 | markrange |
|
1295 | 1397 | Mark all objects from the last marked object before the current cursor position |
@@ -1310,18 +1412,29 b' This screen.' | |||
|
1310 | 1412 | |
|
1311 | 1413 | if curses is not None: |
|
1312 | 1414 | class UnassignedKeyError(Exception): |
|
1313 |
|
|
|
1415 | """ | |
|
1416 | Exception that is used for reporting unassigned keys. | |
|
1417 | """ | |
|
1314 | 1418 | |
|
1315 | 1419 | |
|
1316 | 1420 | class UnknownCommandError(Exception): |
|
1317 |
|
|
|
1421 | """ | |
|
1422 | Exception that is used for reporting unknown command (this should never | |
|
1423 | happen). | |
|
1424 | """ | |
|
1318 | 1425 | |
|
1319 | 1426 | |
|
1320 | 1427 | class CommandError(Exception): |
|
1321 |
|
|
|
1428 | """ | |
|
1429 | Exception that is used for reporting that a command can't be executed. | |
|
1430 | """ | |
|
1322 | 1431 | |
|
1323 | 1432 | |
|
1324 | 1433 | class Style(object): |
|
1434 | """ | |
|
1435 | Store foreground color, background color and attribute (bold, underlined | |
|
1436 | etc.) for ``curses``. | |
|
1437 | """ | |
|
1325 | 1438 | __slots__ = ("fg", "bg", "attrs") |
|
1326 | 1439 | |
|
1327 | 1440 | def __init__(self, fg, bg, attrs=0): |
@@ -1331,6 +1444,8 b' if curses is not None:' | |||
|
1331 | 1444 | |
|
1332 | 1445 | |
|
1333 | 1446 | class _BrowserCachedItem(object): |
|
1447 | # This is used internally by ``ibrowse`` to store a item together with its | |
|
1448 | # marked status. | |
|
1334 | 1449 | __slots__ = ("item", "marked") |
|
1335 | 1450 | |
|
1336 | 1451 | def __init__(self, item): |
@@ -1339,6 +1454,7 b' if curses is not None:' | |||
|
1339 | 1454 | |
|
1340 | 1455 | |
|
1341 | 1456 | class _BrowserHelp(object): |
|
1457 | # This is used internally by ``ibrowse`` for displaying the help screen. | |
|
1342 | 1458 | def __init__(self, browser): |
|
1343 | 1459 | self.browser = browser |
|
1344 | 1460 | |
@@ -1378,6 +1494,10 b' if curses is not None:' | |||
|
1378 | 1494 | |
|
1379 | 1495 | |
|
1380 | 1496 | class _BrowserLevel(object): |
|
1497 | # This is used internally to store the state (iterator, fetch items, | |
|
1498 | # position of cursor and screen, etc.) of one browser level | |
|
1499 | # An ``ibrowse`` object keeps multiple ``_BrowserLevel`` objects in | |
|
1500 | # a stack. | |
|
1381 | 1501 | def __init__(self, browser, input, iterator, mainsizey, *attrs): |
|
1382 | 1502 | self.browser = browser |
|
1383 | 1503 | self.input = input |
@@ -1407,6 +1527,7 b' if curses is not None:' | |||
|
1407 | 1527 | self.calcdisplayattr() |
|
1408 | 1528 | |
|
1409 | 1529 | def fetch(self, count): |
|
1530 | # Try to fill ``self.items`` with at least ``count`` objects. | |
|
1410 | 1531 | have = len(self.items) |
|
1411 | 1532 | while not self.exhausted and have < count: |
|
1412 | 1533 | try: |
@@ -1419,7 +1540,11 b' if curses is not None:' | |||
|
1419 | 1540 | self.items.append(_BrowserCachedItem(item)) |
|
1420 | 1541 | |
|
1421 | 1542 | def calcdisplayattrs(self): |
|
1543 | # Calculate which attributes are available from the objects that are | |
|
1544 | # currently visible on screen (and store it in ``self.displayattrs``) | |
|
1422 | 1545 | attrnames = set() |
|
1546 | # If the browser object specifies a fixed list of attributes, | |
|
1547 | # simply use it. | |
|
1423 | 1548 | if self.attrs: |
|
1424 | 1549 | self.displayattrs = self.attrs |
|
1425 | 1550 | else: |
@@ -1432,6 +1557,10 b' if curses is not None:' | |||
|
1432 | 1557 | attrnames.add(attrname) |
|
1433 | 1558 | |
|
1434 | 1559 | def getrow(self, i): |
|
1560 | # Return a dictinary with the attributes for the object | |
|
1561 | # ``self.items[i]``. Attribute names are taken from | |
|
1562 | # ``self.displayattrs`` so ``calcdisplayattrs()`` must have been | |
|
1563 | # called before. | |
|
1435 | 1564 | row = {} |
|
1436 | 1565 | item = self.items[i].item |
|
1437 | 1566 | for attrname in self.displayattrs: |
@@ -1448,7 +1577,11 b' if curses is not None:' | |||
|
1448 | 1577 | return row |
|
1449 | 1578 | |
|
1450 | 1579 | def calcwidths(self): |
|
1451 | # Recalculate the displayed fields and their width | |
|
1580 | # Recalculate the displayed fields and their width. | |
|
1581 | # ``calcdisplayattrs()'' must have been called and the cache | |
|
1582 | # for attributes of the objects on screen (``self.displayrows``) | |
|
1583 | # must have been filled. This returns a dictionary mapping | |
|
1584 | # colmn names to width. | |
|
1452 | 1585 | self.colwidths = {} |
|
1453 | 1586 | for row in self.displayrows: |
|
1454 | 1587 | for attrname in self.displayattrs: |
@@ -1467,7 +1600,8 b' if curses is not None:' | |||
|
1467 | 1600 | self.datasizex = sum(self.colwidths.itervalues()) + len(self.colwidths) |
|
1468 | 1601 | |
|
1469 | 1602 | def calcdisplayattr(self): |
|
1470 | # Find out on which attribute the cursor is on | |
|
1603 | # Find out on which attribute the cursor is on and store this | |
|
1604 | # information in ``self.displayattr``. | |
|
1471 | 1605 | pos = 0 |
|
1472 | 1606 | for attrname in self.displayattrs: |
|
1473 | 1607 | if pos+self.colwidths[attrname] >= self.curx: |
@@ -1478,6 +1612,10 b' if curses is not None:' | |||
|
1478 | 1612 | self.displayattr = None |
|
1479 | 1613 | |
|
1480 | 1614 | def moveto(self, x, y, refresh=False): |
|
1615 | # Move the cursor to the position ``(x,y)`` (in data coordinates, | |
|
1616 | # not in screen coordinates). If ``refresh`` is true, all cached | |
|
1617 | # values will be recalculated (e.g. because the list has been | |
|
1618 | # resorted to screen position etc. are no longer valid). | |
|
1481 | 1619 | olddatastarty = self.datastarty |
|
1482 | 1620 | oldx = self.curx |
|
1483 | 1621 | oldy = self.cury |
@@ -1506,7 +1644,6 b' if curses is not None:' | |||
|
1506 | 1644 | endy = min(self.datastarty+self.mainsizey, len(self.items)) |
|
1507 | 1645 | self.displayrows = map(self.getrow, xrange(self.datastarty, endy)) |
|
1508 | 1646 | self.calcwidths() |
|
1509 | ||
|
1510 | 1647 | # Did we scroll vertically => update displayrows |
|
1511 | 1648 | # and various other attributes |
|
1512 | 1649 | elif self.datastarty != olddatastarty: |
@@ -1690,27 +1827,61 b' if curses is not None:' | |||
|
1690 | 1827 | } |
|
1691 | 1828 | |
|
1692 | 1829 | def __init__(self, *attrs): |
|
1830 | """ | |
|
1831 | Create a new browser. If ``attrs`` is not empty, it is the list | |
|
1832 | of attributes that will be displayed in the browser, otherwise | |
|
1833 | these will be determined by the objects on screen. | |
|
1834 | """ | |
|
1693 | 1835 | self.attrs = attrs |
|
1836 | ||
|
1837 | # Stack of browser levels | |
|
1694 | 1838 | self.levels = [] |
|
1695 |
|
|
|
1696 |
self.step |
|
|
1697 | self._dobeep = True # Beep on the edges of the data area? | |
|
1839 | # how many colums to scroll (Changes when accelerating) | |
|
1840 | self.stepx = 1. | |
|
1841 | ||
|
1842 | # how many rows to scroll (Changes when accelerating) | |
|
1843 | self.stepy = 1. | |
|
1844 | ||
|
1845 | # Beep on the edges of the data area? (Will be set to ``False`` | |
|
1846 | # once the cursor hits the edge of the screen, so we don't get | |
|
1847 | # multiple beeps). | |
|
1848 | self._dobeep = True | |
|
1849 | ||
|
1850 | # Cache for registered ``curses`` colors. | |
|
1698 | 1851 | self._colors = {} |
|
1699 | 1852 | self._maxcolor = 1 |
|
1700 | self._headerlines = 1 # How many header lines do we want to paint (the numbers of levels we have, but with an upper bound) | |
|
1701 | self._firstheaderline = 0 # Index of first header line | |
|
1702 | self.scr = None # curses window | |
|
1703 | self._report = None # report in the footer line | |
|
1853 | ||
|
1854 | # How many header lines do we want to paint (the numbers of levels | |
|
1855 | # we have, but with an upper bound) | |
|
1856 | self._headerlines = 1 | |
|
1857 | ||
|
1858 | # Index of first header line | |
|
1859 | self._firstheaderline = 0 | |
|
1860 | ||
|
1861 | # curses window | |
|
1862 | self.scr = None | |
|
1863 | # report in the footer line (error, executed command etc.) | |
|
1864 | self._report = None | |
|
1704 | 1865 | |
|
1705 | 1866 | def nextstepx(self, step): |
|
1867 | """ | |
|
1868 | Accelerate horizontally. | |
|
1869 | """ | |
|
1706 | 1870 | return max(1., min(step*self.acceleratex, |
|
1707 | 1871 | self.maxspeedx*self.levels[-1].mainsizex)) |
|
1708 | 1872 | |
|
1709 | 1873 | def nextstepy(self, step): |
|
1874 | """ | |
|
1875 | Accelerate vertically. | |
|
1876 | """ | |
|
1710 | 1877 | return max(1., min(step*self.acceleratey, |
|
1711 | 1878 | self.maxspeedy*self.levels[-1].mainsizey)) |
|
1712 | 1879 | |
|
1713 | 1880 | def getstyle(self, style): |
|
1881 | """ | |
|
1882 | Register the ``style`` with ``curses`` or get it from the cache, | |
|
1883 | if it has been registered before. | |
|
1884 | """ | |
|
1714 | 1885 | try: |
|
1715 | 1886 | return self._colors[style.fg, style.bg] | style.attrs |
|
1716 | 1887 | except KeyError: |
@@ -1722,12 +1893,20 b' if curses is not None:' | |||
|
1722 | 1893 | return c |
|
1723 | 1894 | |
|
1724 | 1895 | def addstr(self, y, x, begx, endx, s, style): |
|
1896 | """ | |
|
1897 | A version of ``curses.addstr()`` that can handle ``x`` coordinates | |
|
1898 | that are outside the screen. | |
|
1899 | """ | |
|
1725 | 1900 | s2 = s[max(0, begx-x):max(0, endx-x)] |
|
1726 | 1901 | if s2: |
|
1727 | 1902 | self.scr.addstr(y, max(x, begx), s2, self.getstyle(style)) |
|
1728 | 1903 | return len(s) |
|
1729 | 1904 | |
|
1730 | 1905 | def format(self, value): |
|
1906 | """ | |
|
1907 | Formats one attribute and returns an ``(alignment, string, style)`` | |
|
1908 | tuple. | |
|
1909 | """ | |
|
1731 | 1910 | if value is None: |
|
1732 | 1911 | return (-1, repr(value), self.style_type_none) |
|
1733 | 1912 | elif isinstance(value, str): |
@@ -1769,6 +1948,8 b' if curses is not None:' | |||
|
1769 | 1948 | return (-1, repr(value), self.style_default) |
|
1770 | 1949 | |
|
1771 | 1950 | def _calcheaderlines(self, levels): |
|
1951 | # Calculate how many headerlines do we have to display, if we have | |
|
1952 | # ``levels`` browser levels | |
|
1772 | 1953 | if levels is None: |
|
1773 | 1954 | levels = len(self.levels) |
|
1774 | 1955 | self._headerlines = min(self.maxheaders, levels) |
@@ -1782,9 +1963,17 b' if curses is not None:' | |||
|
1782 | 1963 | return Style(style.fg, style.bg, style.attrs | curses.A_BOLD) |
|
1783 | 1964 | |
|
1784 | 1965 | def report(self, msg): |
|
1966 | """ | |
|
1967 | Store the message ``msg`` for display below the footer line. This | |
|
1968 | will be displayed as soon as the screen is redrawn. | |
|
1969 | """ | |
|
1785 | 1970 | self._report = msg |
|
1786 | 1971 | |
|
1787 | 1972 | def enter(self, item, mode, *attrs): |
|
1973 | """ | |
|
1974 | Enter the object ``item`` in the mode ``mode``. If ``attrs`` is | |
|
1975 | specified, it will be used as a fixed list of attributes to display. | |
|
1976 | """ | |
|
1788 | 1977 | try: |
|
1789 | 1978 | iterator = xiter(item, mode) |
|
1790 | 1979 | except (KeyboardInterrupt, SystemExit): |
@@ -1804,6 +1993,10 b' if curses is not None:' | |||
|
1804 | 1993 | self.levels.append(level) |
|
1805 | 1994 | |
|
1806 | 1995 | def keylabel(self, keycode): |
|
1996 | """ | |
|
1997 | Return a pretty name for the ``curses`` key ``keycode`` (used in the | |
|
1998 | help screen and in reports about unassigned keys). | |
|
1999 | """ | |
|
1807 | 2000 | if keycode <= 0xff: |
|
1808 | 2001 | specialsnames = { |
|
1809 | 2002 | ord("\n"): "RETURN", |
@@ -1821,6 +2014,9 b' if curses is not None:' | |||
|
1821 | 2014 | return str(keycode) |
|
1822 | 2015 | |
|
1823 | 2016 | def cmd_help(self): |
|
2017 | """ | |
|
2018 | The help command | |
|
2019 | """ | |
|
1824 | 2020 | for level in self.levels: |
|
1825 | 2021 | if isinstance(level.input, _BrowserHelp): |
|
1826 | 2022 | curses.beep() |
@@ -1830,6 +2026,10 b' if curses is not None:' | |||
|
1830 | 2026 | self.enter(_BrowserHelp(self), "default") |
|
1831 | 2027 | |
|
1832 | 2028 | def _dodisplay(self, scr): |
|
2029 | """ | |
|
2030 | This method is the workhorse of the browser. It handles screen | |
|
2031 | drawing and the keyboard. | |
|
2032 | """ | |
|
1833 | 2033 | self.scr = scr |
|
1834 | 2034 | curses.halfdelay(1) |
|
1835 | 2035 | footery = 2 |
@@ -2144,6 +2344,7 b' if curses is not None:' | |||
|
2144 | 2344 | defaultdisplay = ibrowse |
|
2145 | 2345 | __all__.append("ibrowse") |
|
2146 | 2346 | else: |
|
2347 | # No curses (probably Windows) => use ``idump`` as the default display. | |
|
2147 | 2348 | defaultdisplay = idump |
|
2148 | 2349 | |
|
2149 | 2350 |
General Comments 0
You need to be logged in to leave comments.
Login now