root/dialog.py

Revision 270:362d1c264767, 10.1 KB (checked in by Ian Ward, 4 months ago)

use MainLoop in dialog example

  • Property exe set to *
Line 
1#!/usr/bin/python
2#
3# Urwid example similar to dialog(1) program
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 example similar to dialog(1) program
24
25"""
26
27import sys
28
29import urwid
30
31
32class DialogExit(Exception):
33    pass
34
35
36class DialogDisplay:
37    palette = [
38        ('body','black','light gray', 'standout'),
39        ('border','black','dark blue'),
40        ('shadow','white','black'),
41        ('selectable','black', 'dark cyan'),
42        ('focus','white','dark blue','bold'),
43        ('focustext','light gray','dark blue'),
44        ]
45       
46    def __init__(self, text, height, width, body=None):
47        width = int(width)
48        if width <= 0:
49            width = ('relative', 80)
50        height = int(height)
51        if height <= 0:
52            height = ('relative', 80)
53   
54        self.body = body
55        if body is None:
56            # fill space with nothing
57            body = urwid.Filler(urwid.Divider(),'top')
58
59        self.frame = urwid.Frame( body, focus_part='footer')
60        if text is not None:
61            self.frame.header = urwid.Pile( [urwid.Text(text),
62                urwid.Divider()] )
63        w = self.frame
64       
65        # pad area around listbox
66        w = urwid.Padding(w, ('fixed left',2), ('fixed right',2))
67        w = urwid.Filler(w, ('fixed top',1), ('fixed bottom',1))
68        w = urwid.AttrWrap(w, 'body')
69       
70        # "shadow" effect
71        w = urwid.Columns( [w,('fixed', 2, urwid.AttrWrap(
72            urwid.Filler(urwid.Text(('border','  ')), "top")
73            ,'shadow'))])
74        w = urwid.Frame( w, footer = 
75            urwid.AttrWrap(urwid.Text(('border','  ')),'shadow'))
76
77        # outermost border area
78        w = urwid.Padding(w, 'center', width )
79        w = urwid.Filler(w, 'middle', height )
80        w = urwid.AttrWrap( w, 'border' )
81       
82        self.view = w
83
84
85    def add_buttons(self, buttons):
86        l = []
87        for name, exitcode in buttons:
88            b = urwid.Button( name, self.button_press )
89            b.exitcode = exitcode
90            b = urwid.AttrWrap( b, 'selectable','focus' )
91            l.append( b )
92        self.buttons = urwid.GridFlow(l, 10, 3, 1, 'center')
93        self.frame.footer = urwid.Pile( [ urwid.Divider(),
94            self.buttons ], focus_item = 1)
95
96    def button_press(self, button):
97        raise DialogExit(button.exitcode)
98
99    def main(self):
100        self.loop = urwid.MainLoop(self.view, self.palette)
101        try:
102            self.loop.run()
103        except DialogExit, e:
104            return self.on_exit( e.args[0] )
105       
106    def on_exit(self, exitcode):
107        return exitcode, ""
108       
109
110
111class InputDialogDisplay(DialogDisplay):
112    def __init__(self, text, height, width):
113        self.edit = urwid.Edit()
114        body = urwid.ListBox([self.edit])
115        body = urwid.AttrWrap(body, 'selectable','focustext')
116       
117        DialogDisplay.__init__(self, text, height, width, body)
118       
119        self.frame.set_focus('body')
120   
121    def unhandled_key(self, size, k):
122        if k in ('up','page up'):
123            self.frame.set_focus('body')
124        if k in ('down','page down'):
125            self.frame.set_focus('footer')
126        if k == 'enter':
127            # pass enter to the "ok" button
128            self.frame.set_focus('footer')
129            self.view.keypress( size, k )
130   
131    def on_exit(self, exitcode):
132        return exitcode, self.edit.get_edit_text()
133
134   
135class TextDialogDisplay(DialogDisplay):
136    def __init__(self, file, height, width):
137        l = []
138        # read the whole file (being slow, not lazy this time)
139        for line in open(file).readlines():
140            l.append( urwid.Text( line.rstrip() ))
141        body = urwid.ListBox(l)
142        body = urwid.AttrWrap(body, 'selectable','focustext')
143
144        DialogDisplay.__init__(self, None, height, width, body)
145
146
147    def unhandled_key(self, size, k):
148        if k in ('up','page up','down','page down'):
149            self.frame.set_focus('body')
150            self.view.keypress( size, k )
151            self.frame.set_focus('footer')
152
153
154class ListDialogDisplay(DialogDisplay):
155    def __init__(self, text, height, width, constr, items, has_default):
156        j = []
157        if has_default:   
158            k, tail = 3, ()
159        else:   
160            k, tail = 2, ("no",)
161        while items:
162            j.append( items[:k] + tail )
163            items = items[k:]
164                   
165        l = []
166        self.items = []
167        for tag, item, default in j:
168            w = constr( tag, default=="on" )
169            self.items.append(w)
170            w = urwid.Columns( [('fixed', 12, w), 
171                urwid.Text(item)], 2 )
172            w = urwid.AttrWrap(w, 'selectable','focus')
173            l.append(w)
174
175        lb = urwid.ListBox(l)
176        lb = urwid.AttrWrap( lb, "selectable" )
177        DialogDisplay.__init__(self, text, height, width, lb )
178       
179        self.frame.set_focus('body')
180   
181    def unhandled_key(self, size, k):
182        if k in ('up','page up'):
183            self.frame.set_focus('body')
184        if k in ('down','page down'):
185            self.frame.set_focus('footer')
186        if k == 'enter':
187            # pass enter to the "ok" button
188            self.frame.set_focus('footer')
189            self.buttons.set_focus(0)
190            self.view.keypress( size, k )
191
192    def on_exit(self, exitcode):
193        """Print the tag of the item selected."""
194        if exitcode != 0:
195            return exitcode, ""
196        s = ""
197        for i in self.items:
198            if i.get_state():
199                s = i.get_label()
200                break
201        return exitcode, s
202       
203   
204   
205
206class CheckListDialogDisplay(ListDialogDisplay):
207    def on_exit(self, exitcode):
208        """
209        Mimick dialog(1)'s --checklist exit.
210        Put each checked item in double quotes with a trailing space.
211        """
212        if exitcode != 0:
213            return exitcode, ""
214        l = []
215        for i in self.items:
216            if i.get_state():
217                l.append(i.get_label())
218        return exitcode, "".join(['"'+tag+'" ' for tag in l])
219
220
221
222
223class MenuItem(urwid.Text):
224    """A custom widget for the --menu option"""
225    def __init__(self, label):
226        urwid.Text.__init__(self, label)
227        self.state = False
228    def selectable(self):
229        return True
230    def keypress(self,size,key):
231        if key == "enter":
232            self.state = True
233            raise DialogExit, 0
234        return key
235    def mouse_event(self,size,event,button,col,row,focus):
236        if event=='mouse release':
237            self.state = True
238            raise DialogExit, 0
239        return False
240    def get_state(self):
241        return self.state
242    def get_label(self):
243        text, attr = self.get_text()
244        return text
245
246
247def do_checklist(text, height, width, list_height, *items):
248    def constr(tag, state):
249        return urwid.CheckBox(tag, state)
250    d = CheckListDialogDisplay( text, height, width, constr, items, True)
251    d.add_buttons([    ("OK", 0), ("Cancel", 1) ])
252    return d
253   
254def do_inputbox(text, height, width):
255    d = InputDialogDisplay( text, height, width )
256    d.add_buttons([    ("Exit", 0) ])
257    return d
258
259def do_menu(text, height, width, menu_height, *items):
260    def constr(tag, state ):
261        return MenuItem(tag)
262    d = ListDialogDisplay(text, height, width, constr, items, False)
263    d.add_buttons([    ("OK", 0), ("Cancel", 1) ])
264    return d
265
266def do_msgbox(text, height, width):
267    d = DialogDisplay( text, height, width )
268    d.add_buttons([    ("OK", 0) ])
269    return d
270
271def do_radiolist(text, height, width, list_height, *items):
272    radiolist = []
273    def constr(tag, state, radiolist=radiolist):
274        return urwid.RadioButton(radiolist, tag, state)
275    d = ListDialogDisplay( text, height, width, constr, items, True )
276    d.add_buttons([    ("OK", 0), ("Cancel", 1) ])
277    return d
278
279def do_textbox(file, height, width):
280    d = TextDialogDisplay( file, height, width )
281    d.add_buttons([    ("Exit", 0) ])
282    return d
283
284def do_yesno(text, height, width):
285    d = DialogDisplay( text, height, width )
286    d.add_buttons([    ("Yes", 0), ("No", 1) ])
287    return d
288
289MODES={    '--checklist':    (do_checklist, 
290        "text height width list-height [ tag item status ] ..."),
291    '--inputbox':    (do_inputbox, 
292        "text height width"),
293    '--menu':    (do_menu, 
294        "text height width menu-height [ tag item ] ..."),
295    '--msgbox':    (do_msgbox, 
296        "text height width"),
297    '--radiolist':    (do_radiolist, 
298        "text height width list-height [ tag item status ] ..."),
299    '--textbox':    (do_textbox,
300        "file height width"),
301    '--yesno':    (do_yesno, 
302        "text height width"),
303    }
304   
305
306def show_usage():
307    """
308    Display a helpful usage message.
309    """
310    modelist = [(mode, help) for (mode, (fn, help)) in MODES.items()]
311    modelist.sort()
312    sys.stdout.write(
313        __doc__ + 
314        "\n".join(["%-15s %s"%(mode,help) for (mode,help) in modelist])
315        + """
316
317height and width may be set to 0 to auto-size.
318list-height and menu-height are currently ignored.
319status may be either on or off.
320""" )
321
322
323def main():
324    if len(sys.argv) < 2 or not MODES.has_key(sys.argv[1]):
325        show_usage()
326        return
327   
328    # Create a DialogDisplay instance
329    fn, help = MODES[sys.argv[1]]
330    d = fn( * sys.argv[2:] )
331   
332    # Run it
333    exitcode, exitstring = d.main()
334   
335    # Exit
336    if exitstring:
337        sys.stderr.write(exitstring+"\n")
338   
339    sys.exit(exitcode)
340       
341
342if __name__=="__main__": 
343    main()
Note: See TracBrowser for help on using the browser.