Hello Ian,
Thank you for your reply and suggestions.
În data de Sîmbătă 30 Ianuarie 2010 04:49:27 am Ian Ward a scris:
> That might me partly my fault, the dialog.py example is quite old and
> creates classes that manage widgets instead of just creating new widgets
> that manage themselves (as you might do with a WidgetWrap subclass.) So
> it really isn't a great example anymore.
>
It was for me :)
I made the changes you suggested. I'm attaching the new file.
> The definitive "empty space" widget is SolidFill. You can replace this:
> body = urwid.Filler(urwid.Divider(),'top')
> with this:
> body = urwid.SolidFill(' ')
Done.
> A shadow effect can be better created with a second Overlay, but you
> need it shifted slightly off from the first. Here's how to do that:
>
> class OffsetOverlay(urwid.Overlay):
> def calculate_padding_filler(self, size, focus):
> l, r, t, b = self.__super.calculate_padding_filler(size, focus)
> return l+1, max(0, r-1), t+1, max(0, b-1)
>
> and then use Overlay(dialog_fg, OffsetOverlay(shadow, bg, ...), ...)
>
Well, that wasn't really clear for me, I'll take a look at it later...
>
> For MyCheckBox you're trying to stop enter from causing the state to
> change, so you could change your keypress method to:
> if key == 'enter': return key
> self.__super.keypress(size, key)
>
Done.
>
> You're using unhandled_input to add special handling of enter in one of
> your listboxes. A better approach would be to extend ListBox and put
> that special handling in a new keypress() method. That way it won't
> affect enter being pressed when other widgets are in focus. Also, if
> you grab 'enter' before passing it to the contents of the ListBox
> (calling self.__super.keypress) then you wouldn't need MyCheckBox at all.
>
Done
>
> Finally it would also be better to separate the loop handling from the
> dialog itself and make the dialog just another widget. I'll post again
> when I have time to update the example to do this.
>
Done (sort of...). Look at the implementation of DialogDisplay.show(). The
dialog now raises 2 kinds of exceptions: ChildDialogExit and MainDialogExit.
If a ChildDialogExit is encountered, then the loop is "re-run" (is it correct
to do this?).
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urwid
# Exceptions to handle DialogDisplay exit codes
class DialogExit(Exception):
def __init__(self, exitcode = 0):
self.exitcode = exitcode
class ChildDialogExit(DialogExit):
pass
class MainDialogExit(DialogExit):
pass
class OffsetOverlay(urwid.Overlay):
def calculate_padding_filler(self, size, focus):
l, r, t, b = self.__super.calculate_padding_filler(size, focus)
return l+1, max(0, r-1), t+1, max(0, b-1)
# MyFrame makes urwid.Frame switch
# focus between body and footer
# when pressing 'tab'
class MyFrame(urwid.Frame):
def keypress(self, size, key):
if key == 'tab':
if self.focus_part == 'body':
self.set_focus('footer')
return None
elif self.focus_part == 'footer':
self.set_focus('body')
return None
else:
# do default action if
# focus_part is 'header'
self.__super.keypress(size, key)
return self.__super.keypress(size, key)
class DialogDisplay(urwid.WidgetWrap):
palette = [
('body','black','white'),
('border','black','white'),
('shadow','white','black'),
('selectable','black', 'dark cyan'),
('focus','black','dark cyan','bold'),
('focustext','light gray','dark blue'),
('button normal','light gray', 'dark blue', 'standout'),
('button select','white', 'dark green'),
]
parent = None
def __init__(self, text, width, height, body=None, loop=None):
width = int(width)
if width <= 0:
width = ('relative', 80)
height = int(height)
if height <= 0:
height = ('relative', 80)
if body is None:
# fill space with nothing
self.body = urwid.SolidFill(' ')
fp = 'footer'
else:
self.body = body
fp = 'body'
self.frame = MyFrame(self.body, focus_part = fp)
if text is not None:
self.frame.header = urwid.Pile( [urwid.Text(text),
urwid.Divider(u'\u2550')] )
w = self.frame
# pad area around listbox
w = urwid.Padding(w, ('fixed left',2), ('fixed right',2))
w = urwid.Filler(w, ('fixed top',1), ('fixed bottom',1))
w = urwid.AttrWrap(w, 'body')
w = urwid.LineBox(w)
# "shadow" effect
w = urwid.Columns( [w,('fixed', 1, urwid.AttrWrap(
urwid.Filler(urwid.Text(('border',' ')), "top")
,'shadow'))])
w = urwid.Frame( w, footer =
urwid.AttrWrap(urwid.Text(('border',' ')),'shadow'))
if loop is None:
# this dialog is the main window
# create outermost border area
w = urwid.Padding(w, 'center', width )
w = urwid.Filler(w, 'middle', height )
w = urwid.AttrWrap( w, 'border' )
else:
# this dialog is a child window
# overlay it over the parent window
self.loop = loop
self.parent = self.loop.widget
w = urwid.Overlay(w, self.parent, 'center', width+2, 'middle', height+2)
self.view = w
# Call WidgetWrap.__init__ to correctly initialize ourselves
urwid.WidgetWrap.__init__(self, self.view)
def add_buttons(self, buttons):
l = []
for name, exitcode in buttons:
b = urwid.Button( name, self.button_press )
b.exitcode = exitcode
b = urwid.AttrWrap( b, 'button normal','button select' )
l.append( b )
self.buttons = urwid.GridFlow(l, 10, 3, 1, 'center')
self.frame.footer = urwid.Pile( [ urwid.Divider(u'\u2500'),
self.buttons ], focus_item = 1)
def button_press(self, button):
if self.parent is None:
# We are the main window,
# so raise an exception to
# quit the main loop
raise MainDialogExit(button.exitcode)
else:
# We are a child window,
# so restore the parent widget
# then raise a ChildDialogExit exception
self.loop.widget=self.parent
raise ChildDialogExit(button.exitcode)
def show(self):
if self.loop is None:
self.loop = urwid.MainLoop(self.view, self.palette)
exited = False
while not exited:
try:
self.loop.run()
except ChildDialogExit as e:
# Determine which dialog has exited
# and act accordingly
pass
except MainDialogExit:
exited = True
else:
self.loop.widget = self.view
# MyCheckBox makes urwid.CheckBox
# not to toggle when the 'enter'
# key is pressed
class MyCheckBox(urwid.CheckBox):
def keypress(self, size, key):
if key == 'enter': return key
return self.__super.keypress(size, key)
# MyListBox makes urwid.ListBox to
# advance to the next widget when
# the 'enter' key is pressed
class MyListBox(urwid.ListBox):
def keypress(self, size, key):
if key == 'enter':
# propagate keypress to the currently selected widget
# then advance to the next widget if the key was
# not processed
w, newpos = self.body.get_focus()
res = w.keypress((size, ), key)
if res is None:
return res
found = False
nextpos = newpos
while not found:
nextwidget, nextpos = self.body.get_next(nextpos)
if nextpos:
if nextwidget.selectable():
found = True
newpos = nextpos
else:
found = True
self.set_focus(newpos)
return key
return self.__super.keypress(size, key)
class AddToolDialog(DialogDisplay):
def __init__(self, text, width, height, loop = None):
self.loop = None
DialogDisplay.__init__(self, text, width, height, self.dialog_body(), loop)
def on_browse_button(self, button):
d = DialogDisplay( "Hello", 50, 10, None, self.loop)
d.add_buttons([ ("OK", 0), ("Cancel", 1) ])
d.show()
def button(self, caption, callback):
w = urwid.Button(caption, callback)
w = urwid.AttrWrap(w, 'button normal', 'button select')
return w
def dialog_body(self):
tools_list = ["First tool", "Second tool", "Third tool"]
self.widgets = []
# Tool id input
self.widgets += [urwid.Edit("Tool ID:")]
# Tool name input
self.widgets += [urwid.Edit("Name:")]
# Tool description input
self.widgets += [urwid.Edit("Description:")]
# Tool dependancies list
self.widgets += [urwid.Text("Dependancies:")]
self.widgets += [MyCheckBox(tool) for tool in tools_list]
# Configuration scripts
self.widgets += [urwid.Text("Configuration scripts:")]
self.widgets += [self.button("Browse", self.on_browse_button)]
self.listbox = MyListBox(urwid.SimpleListWalker([urwid.AttrWrap(w, None, 'reveal focus') for w in self.widgets]))
return self.listbox
def main():
view = AddToolDialog("Add a new tool", 100, 20)
view.add_buttons([ ("Quit", 0) ])
view.show()
if '__main__'==__name__:
main()
_______________________________________________
Urwid mailing list
[email protected]
http://lists.excess.org/mailman/listinfo/urwid