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

Reply via email to