root/calc.py

Revision 266:a8368450a335, 26.4 KB (checked in by Ian Ward, 4 months ago)

use MainLoop in calc example

  • Property exe set to *
Line 
1#!/usr/bin/python
2#
3# Urwid advanced example column calculator application
4#    Copyright (C) 2004-2009  Ian Ward
5#
6#    This library is free software; you can redistribute it and/or
7#    modify it under the terms of the GNU Lesser General Public
8#    License as published by the Free Software Foundation; either
9#    version 2.1 of the License, or (at your option) any later version.
10#
11#    This library is distributed in the hope that it will be useful,
12#    but WITHOUT ANY WARRANTY; without even the implied warranty of
13#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14#    Lesser General Public License for more details.
15#
16#    You should have received a copy of the GNU Lesser General Public
17#    License along with this library; if not, write to the Free Software
18#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19#
20# Urwid web site: http://excess.org/urwid/
21
22"""
23Urwid advanced example column calculator application
24
25Features:
26- multiple separate list boxes within columns
27- custom edit widget for editing calculator cells
28- custom parent widget for links to other columns
29- custom list walker to show and hide cell results as required
30- custom wrap and align modes for editing right-1 aligned numbers
31- outputs commands that may be used to recreate expression on exit
32"""
33
34import urwid
35import urwid.raw_display
36import urwid.web_display
37
38# use appropriate Screen class
39if urwid.web_display.is_web_request():
40    Screen = urwid.web_display.Screen
41else:
42    Screen = urwid.raw_display.Screen
43
44
45def div_or_none(a,b):
46    """Divide a by b.  Return result or None on divide by zero."""
47    if b == 0: 
48        return None
49    return a/b
50
51# operators supported and the functions used to calculate a result
52OPERATORS = {
53    '+': (lambda a, b: a+b),
54    '-': (lambda a, b: a-b),
55    '*': (lambda a, b: a*b),
56    '/': div_or_none,
57    }
58
59# the uppercase versions of keys used to switch columns
60COLUMN_KEYS = list( "?ABCDEF" )
61
62# these lists are used to determine when to display errors
63EDIT_KEYS = OPERATORS.keys() + COLUMN_KEYS + ['backspace','delete']
64MOVEMENT_KEYS = ['up','down','left','right','page up','page down']
65
66# Event text
67E_no_such_column = "Column %s does not exist."
68E_no_more_columns = "Maxumum number of columns reached."
69E_new_col_cell_not_empty = "Column must be started from an empty cell."
70E_invalid_key = "Invalid key '%s'."
71E_no_parent_column = "There is no parent column to return to."
72E_cant_combine = "Cannot combine cells with sub-expressions."
73E_invalid_in_parent_cell = "Cannot enter numbers into parent cell."
74E_invalid_in_help_col = [
75    "Help Column is in focus.  Press ", 
76    ('key',COLUMN_KEYS[1]),"-",('key',COLUMN_KEYS[-1]), 
77    " to select another column."]
78
79# Shared layout object
80CALC_LAYOUT = None
81
82
83class CalcEvent:
84    """Events triggered by user input."""
85   
86    attr = 'event'
87   
88    def __init__(self, message):
89        self.message = message
90   
91    def widget(self):
92        """Return a widget containing event information"""
93        text = urwid.Text( self.message, 'center' )
94        return urwid.AttrWrap( text, self.attr )
95
96class ColumnDeleteEvent(CalcEvent):
97    """Sent when user wants to delete a column"""
98
99    attr = 'confirm'
100
101    def __init__(self, letter, from_parent=0):
102        self.message = ["Press ", ('key',"BACKSPACE"),
103            " again to confirm column removal."]
104        self.letter = letter
105
106class UpdateParentEvent:
107    """Sent when parent columns may need to be updated."""
108    pass
109
110
111class Cell:
112    def __init__(self, op ):
113        self.op = op
114        self.is_top = op is None
115        self.child = None
116        self.setup_edit()
117        self.result = urwid.Text("", layout=CALC_LAYOUT)
118   
119    def show_result(self, next_cell):
120        """Return whether this widget should display its result.
121
122        next_cell -- the cell following self or None"""
123       
124        if self.is_top: 
125            return False
126        if next_cell is None:
127            return True
128        if self.op == "+" and next_cell.op == "+":
129            return False
130        return True
131       
132   
133    def setup_edit(self):
134        """Create the standard edit widget for this cell."""
135       
136        self.edit = urwid.IntEdit()
137        if not self.is_top:
138            self.edit.set_caption( self.op + " " )
139        self.edit.set_layout( None, None, CALC_LAYOUT )
140
141    def get_value(self):
142        """Return the numeric value of the cell."""
143       
144        if self.child is not None:
145            return self.child.get_result()
146        else:
147            return long("0"+self.edit.edit_text)
148   
149    def get_result(self):
150        """Return the numeric result of this cell's operation."""
151       
152        if self.is_top:
153            return self.get_value()
154        if self.result.text == "": 
155            return None
156        return long(self.result.text)
157
158    def set_result(self, result):
159        """Set the numeric result for this cell."""
160       
161        if result == None:
162            self.result.set_text("")
163        else:
164            self.result.set_text( "%d" %result )
165   
166    def become_parent(self, column, letter):
167        """Change the edit widget to a parent cell widget."""
168       
169        self.child = column
170        self.edit = ParentEdit( self.op, letter )
171   
172    def remove_child(self):
173        """Change the edit widget back to a standard edit widget."""
174       
175        self.child = None
176        self.setup_edit()
177
178    def is_empty( self ):
179        """Return True if the cell is "empty"."""
180
181        return self.child is None and self.edit.edit_text == ""
182
183
184class ParentEdit(urwid.Edit):
185    """Edit widget modified to link to a child column"""
186   
187    def __init__(self, op, letter):
188        """Use the operator and letter of the child column as caption
189       
190        op -- operator or None
191        letter -- letter of child column
192        remove_fn -- function to call when user wants to remove child
193                     function takes no parameters
194        """
195       
196        urwid.Edit.__init__(self, layout=CALC_LAYOUT)
197        self.op = op
198        self.set_letter( letter )
199   
200    def set_letter(self, letter):
201        """Set the letter of the child column for display."""
202       
203        self.letter = letter
204        caption = "("+letter+")"
205        if self.op is not None:
206            caption = self.op+" "+caption
207        self.set_caption(caption)
208       
209    def keypress(self, size, key):
210        """Disable usual editing, allow only removing of child""" 
211       
212        if key == "backspace":
213            raise ColumnDeleteEvent(self.letter, from_parent=True)
214        elif key in list("0123456789"):
215            raise CalcEvent, E_invalid_in_parent_cell
216        else:
217            return key
218       
219
220class CellWalker(urwid.ListWalker):
221    def __init__(self, content):
222        self.content = urwid.MonitoredList(content)
223        self.content.modified = self._modified
224        self.focus = (0,0)
225        # everyone can share the same divider widget
226        self.div = urwid.Divider("-")
227
228    def get_cell(self, i):
229        if i < 0 or i >= len(self.content):
230            return None
231        else:
232            return self.content[i]
233
234    def _get_at_pos(self, pos):
235        i, sub = pos
236        assert sub in (0,1,2)
237        if i < 0 or i >= len(self.content):
238            return None, None
239        if sub == 0:
240            edit = self.content[i].edit
241            return urwid.AttrWrap(edit, 'edit', 'editfocus'), pos
242        elif sub == 1:
243            return self.div, pos
244        else:
245            return self.content[i].result, pos
246   
247    def get_focus(self):
248        return self._get_at_pos(self.focus)
249   
250    def set_focus(self, focus):
251        self.focus = focus
252   
253    def get_next(self, start_from):
254        i, sub = start_from
255        assert sub in (0,1,2)
256        if sub == 0:
257            show_result = self.content[i].show_result(
258                self.get_cell(i+1))
259            if show_result:
260                return self._get_at_pos( (i, 1) )
261            else:
262                return self._get_at_pos( (i+1, 0) )
263        elif sub == 1:
264            return self._get_at_pos( (i, 2) )
265        else:
266            return self._get_at_pos( (i+1, 0) )
267   
268    def get_prev(self, start_from):
269        i, sub = start_from
270        assert sub in (0,1,2)
271        if sub == 0:
272            if i == 0: return None, None
273            show_result = self.content[i-1].show_result(
274                self.content[i])
275            if show_result:
276                return self._get_at_pos( (i-1, 2) )
277            else:
278                return self._get_at_pos( (i-1, 0) )
279        elif sub == 1:
280            return self._get_at_pos( (i, 0) )
281        else:
282            return self._get_at_pos( (i, 1) )
283   
284           
285class CellColumn( urwid.WidgetWrap ):
286    def __init__(self, letter):
287        self.walker = CellWalker([Cell(None)])
288        self.content = self.walker.content
289        self.listbox = urwid.ListBox( self.walker )
290        self.set_letter( letter )
291        urwid.WidgetWrap.__init__(self, self.frame)
292   
293    def set_letter(self, letter):
294        """Set the column header with letter."""
295       
296        self.letter = letter
297        header = urwid.AttrWrap(
298            urwid.Text( ["Column ",('key',letter)],
299            layout = CALC_LAYOUT), 'colhead' )
300        self.frame = urwid.Frame( self.listbox, header )
301           
302    def keypress(self, size, key):
303        key = self.frame.keypress( size, key)
304        if key is None: 
305            changed = self.update_results()
306            if changed:
307                raise UpdateParentEvent()
308            return
309       
310        f, (i, sub) = self.walker.get_focus()
311        if sub != 0: 
312            # f is not an edit widget
313            return key
314        if OPERATORS.has_key(key):
315            # move trailing text to new cell below
316            edit = self.walker.get_cell(i).edit
317            cursor_pos = edit.edit_pos
318            tail = edit.edit_text[cursor_pos:]
319            edit.set_edit_text( edit.edit_text[:cursor_pos] )
320           
321            new_cell = Cell( key )
322            new_cell.edit.set_edit_text( tail )
323            self.content[i+1:i+1] = [new_cell]
324           
325            changed = self.update_results()
326            self.move_focus_next( size )
327            self.content[i+1].edit.set_edit_pos(0)
328            if changed:
329                raise UpdateParentEvent()
330            return
331           
332        elif key == 'backspace':
333            # unhandled backspace, we're at beginning of number
334            # append current number to cell above, removing operator
335            above = self.walker.get_cell(i-1)
336            if above is None:
337                # we're the first cell
338                raise ColumnDeleteEvent( self.letter, 
339                    from_parent=False )
340           
341            edit = self.walker.get_cell(i).edit
342            # check that we can combine
343            if above.child is not None:
344                # cell above is parent
345                if edit.edit_text:
346                    # ..and current not empty, no good
347                    raise CalcEvent, E_cant_combine
348                above_pos = 0
349            else:   
350                # above is normal number cell
351                above_pos = len(above.edit.edit_text)
352                above.edit.set_edit_text( above.edit.edit_text +
353                    edit.edit_text )
354
355            self.move_focus_prev( size )
356            self.content[i-1].edit.set_edit_pos(above_pos)
357            del self.content[i]
358            changed = self.update_results()
359            if changed:
360                raise UpdateParentEvent()
361            return
362       
363        elif key == 'delete':
364            # pull text from next cell into current
365            cell = self.walker.get_cell(i)
366            below = self.walker.get_cell(i+1)
367            if cell.child is not None:
368                # this cell is a parent
369                raise CalcEvent, E_cant_combine
370            if below is None:
371                # nothing below
372                return key
373            if below.child is not None:
374                # cell below is a parent
375                raise CalcEvent, E_cant_combine
376           
377            edit = self.walker.get_cell(i).edit
378            edit.set_edit_text( edit.edit_text +
379                below.edit.edit_text )
380
381            del self.content[i+1]
382            changed = self.update_results()
383            if changed:
384                raise UpdateParentEvent()
385            return
386        return key
387
388   
389    def move_focus_next(self, size):
390        f, (i, sub) = self.walker.get_focus()
391        assert i<len(self.content)-1
392       
393        ni = i
394        while ni == i:
395            self.frame.keypress(size, 'down')
396            nf, (ni, nsub) = self.walker.get_focus()
397   
398    def move_focus_prev(self, size):
399        f, (i, sub) = self.walker.get_focus()
400        assert i>0
401       
402        ni = i
403        while ni == i:
404            self.frame.keypress(size, 'up')
405            nf, (ni, nsub) = self.walker.get_focus()
406           
407           
408    def update_results( self, start_from=None ):
409        """Update column.  Return True if final result changed.
410       
411        start_from -- Cell to start updating from or None to start from
412                      the current focus (default None)
413        """
414       
415        if start_from is None:
416            f, (i, sub) = self.walker.get_focus()
417        else:
418            i = self.content.index(start_from)
419            if i == None: return False
420       
421        focus_cell = self.walker.get_cell(i)
422       
423        if focus_cell.is_top:
424            x = focus_cell.get_value()
425            last_op = None
426        else:   
427            last_cell = self.walker.get_cell(i-1)
428            x = last_cell.get_result()
429           
430            if x is not None and focus_cell.op is not None:
431                x = OPERATORS[focus_cell.op]( x, 
432                    focus_cell.get_value() )
433            focus_cell.set_result(x)
434       
435        for cell in self.content[i+1:]:
436            if cell.op is None:
437                x = None
438            if x is not None:
439                x = OPERATORS[cell.op]( x, cell.get_value() )
440            if cell.get_result() == x:
441                return False
442            cell.set_result(x)
443
444        return True
445
446   
447    def create_child( self, letter ):
448        """Return (parent cell,child column) or None,None on failure."""
449        f, (i, sub) = self.walker.get_focus()
450        if sub != 0: 
451            # f is not an edit widget
452            return None, None
453
454        cell = self.walker.get_cell(i)
455        if cell.child is not None:
456            raise CalcEvent, E_new_col_cell_not_empty
457        if cell.edit.edit_text:
458            raise CalcEvent, E_new_col_cell_not_empty
459
460        child = CellColumn( letter )
461        cell.become_parent( child, letter )
462
463        return cell, child
464
465    def is_empty( self ):
466        """Return True if this column is empty."""
467
468        return len(self.content)==1 and self.content[0].is_empty()
469
470   
471    def get_expression(self):
472        """Return the expression as a printable string."""
473       
474        l = []
475        for c in self.content:
476            if c.op is not None: # only applies to first cell
477                l.append(c.op)
478            if c.child is not None:
479                l.append("("+c.child.get_expression()+")")
480            else:
481                l.append("%d"%c.get_value())
482               
483        return "".join(l)
484
485    def get_result(self):
486        """Return the result of the last cell in the column."""
487       
488        return self.content[-1].get_result()
489       
490
491       
492
493class HelpColumn(urwid.BoxWidget):
494    help_text = [
495        ('title', "Column Calculator"),
496        "",
497        [ "Numbers: ", ('key', "0"), "-", ('key', "9") ],
498        "" ,
499        [ "Operators: ",('key', "+"), ", ", ('key', "-"), ", ",
500            ('key', "*"), " and ", ('key', "/")],
501        "",
502        [ "Editing: ", ('key', "BACKSPACE"), " and ",('key', "DELETE")],
503        "",
504        [ "Movement: ", ('key', "UP"), ", ", ('key', "DOWN"), ", ",
505            ('key', "LEFT"), ", ", ('key', "RIGHT"), ", ",
506            ('key', "PAGE UP"), " and ", ('key', "PAGE DOWN") ],
507        "",
508        [ "Sub-expressions: ", ('key', "("), " and ", ('key', ")") ],
509        "",
510        [ "Columns: ", ('key', COLUMN_KEYS[0]), " and ", 
511            ('key',COLUMN_KEYS[1]), "-", 
512            ('key',COLUMN_KEYS[-1]) ],
513        "",
514        [ "Exit: ", ('key', "Q") ],
515        "",
516        "",
517        ["Column Calculator does operations in the order they are ",
518            "typed, not by following usual precedence rules.  ",
519            "If you want to calculate ", ('key', "12 - 2 * 3"), 
520            " with the multiplication happening before the ",
521            "subtraction you must type ", 
522            ('key', "12 - (2 * 3)"), " instead."],
523        ]
524       
525    def __init__(self):
526        self.head = urwid.AttrWrap(
527            urwid.Text(["Help Column ", ('key',"?")],
528                layout = CALC_LAYOUT),
529            'help')
530        self.foot = urwid.AttrWrap( 
531            urwid.Text(["[text continues.. press ",
532            ('key',"?"), " then scroll]"]), 'helpnote' )
533        self.items = [urwid.Text(x) for x in self.help_text]
534        self.listbox = urwid.ListBox(urwid.SimpleListWalker(self.items))
535        self.body = urwid.AttrWrap( self.listbox, 'help' )
536        self.frame = urwid.Frame( self.body, header=self.head)
537   
538    def render(self, size, focus=False):
539        maxcol, maxrow = size
540        head_rows = self.head.rows((maxcol,))
541        if "bottom" in self.listbox.ends_visible( 
542            (maxcol, maxrow-head_rows) ):
543            self.frame.footer = None
544        else:
545            self.frame.footer = self.foot
546
547        return self.frame.render( (maxcol, maxrow), focus)
548   
549    def keypress( self, size, key ):
550        return self.frame.keypress( size, key )
551       
552
553class CalcDisplay:
554    palette = [
555        ('body','white', 'dark blue'),
556        ('edit','yellow', 'dark blue'),
557        ('editfocus','yellow','dark cyan', 'bold'),
558        ('key','dark cyan', 'light gray', ('standout','underline')),
559        ('title', 'white', 'light gray', ('bold','standout')),
560        ('help', 'black', 'light gray', 'standout'),
561        ('helpnote', 'dark green', 'light gray'),
562        ('colhead', 'black', 'light gray', 'standout'),
563        ('event', 'light red', 'black', 'standout'),
564        ('confirm', 'yellow', 'black', 'bold'),
565        ]
566   
567    def __init__(self):
568        self.columns = urwid.Columns([HelpColumn(), CellColumn("A")], 1)
569        self.col_list = self.columns.widget_list
570        self.columns.set_focus_column( 1 )
571        view = urwid.AttrWrap(self.columns, 'body')
572        self.view = urwid.Frame(view) # for showing messages
573        self.col_link = {}
574
575    def main(self):
576        self.loop = urwid.MainLoop(self.view, self.palette, screen=Screen(),
577            input_filter=self.input_filter)
578        self.loop.run()
579
580        # on exit write the formula and the result to the console
581        expression, result = self.get_expression_result()
582        print "Paste this expression into a new Column Calculator session to continue editing:"
583        print expression
584        print "Result:", result
585
586    def input_filter(self, input, raw_input):
587        if 'q' in input or 'Q' in input:
588            raise urwid.ExitMainLoop()
589       
590        # handle other keystrokes
591        for k in input:
592            try:
593                self.wrap_keypress(k)
594                self.event = None
595                self.view.footer = None
596            except CalcEvent, e:
597                # display any message
598                self.event = e
599                self.view.footer = e.widget()
600       
601        # remove all input from further processing by MainLoop
602        return []
603           
604    def wrap_keypress(self, key):
605        """Handle confirmation and throw event on bad input."""
606       
607        try:
608            key = self.keypress(key)
609           
610        except ColumnDeleteEvent, e:
611            if e.letter == COLUMN_KEYS[1]:
612                # cannot delete the first column, ignore key
613                return
614           
615            if not self.column_empty( e.letter ):
616                # need to get two in a row, so check last event
617                if not isinstance(self.event,ColumnDeleteEvent):
618                    # ask for confirmation
619                    raise e
620            self.delete_column(e.letter)
621           
622        except UpdateParentEvent, e:
623            self.update_parent_columns()
624            return
625       
626        if key is None:
627            return
628
629        if self.columns.get_focus_column() == 0:
630            if key not in ('up','down','page up','page down'):
631                raise CalcEvent, E_invalid_in_help_col
632       
633        if key not in EDIT_KEYS and key not in MOVEMENT_KEYS:
634            raise CalcEvent, E_invalid_key % key.upper()
635       
636    def keypress(self, key):
637        """Handle a keystroke."""
638
639        self.loop.process_input([key])
640       
641        if key.upper() in COLUMN_KEYS:
642            # column switch
643            i = COLUMN_KEYS.index(key.upper())
644            if i >= len( self.col_list ): 
645                raise CalcEvent, E_no_such_column % key.upper()
646            self.columns.set_focus_column( i )
647            return
648        elif key == "(":
649            # open a new column
650            if len( self.col_list ) >= len(COLUMN_KEYS):
651                raise CalcEvent, E_no_more_columns
652            i = self.columns.get_focus_column()
653            if i == 0: 
654                # makes no sense in help column
655                return key
656            col = self.col_list[i]
657            new_letter = COLUMN_KEYS[len(self.col_list)]
658            parent, child = col.create_child( new_letter )
659            if child is None:
660                # something invalid in focus
661                return key
662            self.col_list.append(child)
663            self.set_link( parent, col, child )
664            self.columns.set_focus_column(len(self.col_list)-1)
665       
666        elif key == ")":
667            i = self.columns.get_focus_column()
668            if i == 0:
669                # makes no sense in help column
670                return key
671            col = self.col_list[i]
672            parent, pcol = self.get_parent( col )
673            if parent is None: 
674                # column has no parent
675                raise CalcEvent, E_no_parent_column
676           
677            new_i = self.col_list.index( pcol )
678            self.columns.set_focus_column( new_i )
679        else:
680            return key
681   
682    def set_link( self, parent, pcol, child ):
683        """Store the link between a parent cell and child column.
684       
685        parent -- parent Cell object
686        pcol -- CellColumn where parent resides
687        child -- child CellColumn object"""
688
689        self.col_link[ child ] = parent, pcol
690   
691    def get_parent( self, child ):
692        """Return the parent and parent column for a given column."""
693
694        return self.col_link.get( child, (None,None) )
695   
696    def column_empty(self, letter):
697        """Return True if the column passed is empty."""
698       
699        i = COLUMN_KEYS.index(letter)
700        col = self.col_list[i]
701        return col.is_empty()
702       
703   
704    def delete_column(self, letter):
705        """Delete the column with the given letter."""
706       
707        i = COLUMN_KEYS.index(letter)
708        col = self.col_list[i]
709       
710        parent, pcol = self.get_parent( col )
711
712        f = self.columns.get_focus_column()
713        if f == i:
714            # move focus to the parent column
715            f = self.col_list.index(pcol)
716            self.columns.set_focus_column(f)
717       
718        parent.remove_child()
719        pcol.update_results(parent)
720        del self.col_list[i]
721       
722        # delete children of this column
723        keep_right_cols = []
724        remove_cols = [col]
725        for rcol in self.col_list[i:]:
726            parent, pcol = self.get_parent( rcol )
727            if pcol in remove_cols:
728                remove_cols.append( rcol )
729            else:
730                keep_right_cols.append( rcol )
731        for rc in remove_cols:
732            # remove the links
733            del self.col_link[rc]
734        # keep only the non-children
735        self.col_list[i:] = keep_right_cols       
736
737        # fix the letter assigmnents
738        for j in range(i, len(self.col_list)):
739            col = self.col_list[j]
740            # fix the column heading
741            col.set_letter( COLUMN_KEYS[j] )
742            parent, pcol = self.get_parent( col )
743            # fix the parent cell
744            parent.edit.set_letter( COLUMN_KEYS[j] )
745
746    def update_parent_columns(self):
747        "Update the parent columns of the current focus column."
748
749        f = self.columns.get_focus_column()
750        col = self.col_list[f]
751        while 1:
752            parent, pcol = self.get_parent(col)
753            if pcol is None: 
754                return
755
756            changed = pcol.update_results( start_from = parent )
757            if not changed: 
758                return
759            col = pcol
760           
761           
762    def get_expression_result(self):
763        """Return (expression, result) as strings."""
764       
765        col = self.col_list[1]
766        return col.get_expression(), "%d"%col.get_result()
767           
768
769
770class CalcNumLayout(urwid.TextLayout):
771    """
772    TextLayout class for bottom-right aligned numbers with a space on
773    the last line for the cursor.
774    """
775    def layout( self, text, width, align, wrap ):
776        """
777        Return layout structure for calculator number display.
778        """
779        lt = len(text) + 1 # extra space for cursor
780        r = (lt) % width # remaining segment not full width wide
781        linestarts = range( r, lt, width )
782        l = []
783        if linestarts:
784            if r:
785                # right-align the remaining segment on 1st line
786                l.append( [(width-r,None),(r, 0, r)] )
787            # fill all but the last line
788            for x in linestarts[:-1]:
789                l.append( [(width, x, x+width)] )
790            s = linestarts[-1]
791            # add the last line with a cursor hint
792            l.append( [(width-1, s, lt-1), (0, lt-1)] )
793        elif lt-1:
794            # all fits on one line, so right align the text
795            # with a cursor hint at the end
796            l.append( [(width-lt,None),(lt-1,0,lt-1), (0,lt-1)] )
797        else:
798            # nothing on the line, right align a cursor hint
799            l.append( [(width-1,None),(0,0)] )
800
801        return l
802
803   
804       
805       
806def main():
807    """Launch Column Calculator."""
808    global CALC_LAYOUT
809    CALC_LAYOUT = CalcNumLayout()
810   
811    urwid.web_display.set_preferences("Column Calculator")
812    # try to handle short web requests quickly
813    if urwid.web_display.handle_short_request():
814        return
815   
816    CalcDisplay().main()
817
818if '__main__'==__name__ or urwid.web_display.is_web_request():
819    main()
Note: See TracBrowser for help on using the browser.