summaryrefslogtreecommitdiff
path: root/docs/tutorial
diff options
context:
space:
mode:
authorIan Ward <ian@excess.org>2012-10-10 16:33:37 -0400
committerIan Ward <ian@excess.org>2012-10-10 16:33:37 -0400
commit68ee140b2d0e2730aef2d230f02c401cf46e76ae (patch)
treeb64e12ee91d2444d6474a96fdce51118cb15b59b /docs/tutorial
parent05182dfb128a24d0f5ec32b706fdfee96998d6c2 (diff)
downloadurwid-68ee140b2d0e2730aef2d230f02c401cf46e76ae.tar.gz
tutorial: menu3/4 examples updated and described
--HG-- branch : feature-sphinx
Diffstat (limited to 'docs/tutorial')
-rw-r--r--docs/tutorial/index.rst39
-rw-r--r--docs/tutorial/menu3.py20
-rw-r--r--docs/tutorial/menu4.py43
-rw-r--r--docs/tutorial/menu44.pngbin1480 -> 1312 bytes
4 files changed, 61 insertions, 41 deletions
diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst
index b0987bd..b9b4dc2 100644
--- a/docs/tutorial/index.rst
+++ b/docs/tutorial/index.rst
@@ -357,18 +357,25 @@ and new widget classes are used instead of factory functions.
* *MenuButton* is a customized :class:`Button` widget. :class:`Button` uses
:class:`WidgetWrap` to create its appearance and this class replaces the
- display widget created by :class:`Button` by assigning to ``self._w`` in its
- constructor.
+ display widget created by :class:`Button` by the wrapped widget in
+ *self._w*.
* *SubMenu* is implemented with a *MenuButton* but uses :class:`WidgetWrap`
to hide the implementation instead of inheriting from *MenuButton*.
- Storing the menu that will be opened by this button as an attribute is a
- good alternative to using factory functions and closures.
-* *Menu* is implemented with a :class:`ListBox` hidden with :class:`WidgetWrap`.
- This will make the implementation opaque to outside users so things like
- setting the focus or iterating over children will not work as it would
- if a simple factory function was used. This might make sense if you want
- to add a whole new interface to your class or control how its children are
- accessed.
+ The constructor builds a widget for the menu that this button will open
+ and stores it in *self.menu*.
+* *Choice* is like *SubMenu* but displays the item chosen instead of
+ another menu.
+
+The *palette* used in this example includes an entry with the special name
+``None``. The foreground and background specified in this entry are used
+as a default when no other display attribute is specified.
+
+* *HorizontalBoxes* arranges the menus displayed similar to the previous
+ example. There is no special handling required for going to previous
+ menus here because :class:`Columns` already handles switching focus
+ when *LEFT* or *RIGHT* is pressed. :class:`AttrMap` with the *focus_map*
+ dict is used to change the appearance of a number of the display attributes
+ when a menu is in focus.
.. image:: menu31.png
.. image:: menu32.png
@@ -378,9 +385,21 @@ and new widget classes are used instead of factory functions.
Adventure Game
--------------
+We can use the same sort of code to build a simple adventure game. Instead
+of menus we have "places" and instead of submenus and parent menus we just
+have "exits". This example scrolls previous places off the top of the
+screen, allowing you to scroll back to view but not interact with previous
+places.
+
.. literalinclude:: menu4.py
:linenos:
+This example starts to show some separation between the application logic
+and the widgets that have been created. The *AdventureGame* class is
+responsible for all the changes that happen through the game and manages
+the topmost widget, but isn't a widget itself. This is a good pattern to
+follow as your application grows larger.
+
.. image:: menu41.png
.. image:: menu42.png
.. image:: menu43.png
diff --git a/docs/tutorial/menu3.py b/docs/tutorial/menu3.py
index eefedc9..c48f42b 100644
--- a/docs/tutorial/menu3.py
+++ b/docs/tutorial/menu3.py
@@ -11,20 +11,16 @@ class SubMenu(urwid.WidgetWrap):
def __init__(self, caption, choices):
super(SubMenu, self).__init__(MenuButton(
[caption, u"\N{HORIZONTAL ELLIPSIS}"], self.open_menu))
- self.menu = Menu(caption, choices)
+ line = urwid.Divider(u'\N{LOWER ONE QUARTER BLOCK}')
+ listbox = urwid.ListBox(urwid.SimpleFocusListWalker([
+ urwid.AttrMap(urwid.Text([u"\n ", caption]), 'heading'),
+ urwid.AttrMap(line, 'line'),
+ urwid.Divider()] + choices + [urwid.Divider()]))
+ self.menu = urwid.AttrMap(listbox, 'options')
def open_menu(self, button):
top.open_box(self.menu)
-class Menu(urwid.WidgetWrap):
- def __init__(self, title, choices):
- line = urwid.AttrMap(urwid.Divider(u'\N{LOWER ONE QUARTER BLOCK}'),
- 'line')
- listbox = urwid.ListBox(urwid.SimpleFocusListWalker([
- urwid.AttrMap(urwid.Text([u"\n ", title]), 'heading'),
- line, urwid.Divider()] + choices + [urwid.Divider()]))
- super(Menu, self).__init__(urwid.AttrMap(listbox, 'options'))
-
class Choice(urwid.WidgetWrap):
def __init__(self, caption):
super(Choice, self).__init__(
@@ -40,7 +36,7 @@ class Choice(urwid.WidgetWrap):
def exit_program(key):
raise urwid.ExitMainLoop()
-menu_top = Menu(u'Main Menu', [
+menu_top = SubMenu(u'Main Menu', [
SubMenu(u'Applications', [
SubMenu(u'Accessories', [
Choice(u'Text Editor'),
@@ -81,5 +77,5 @@ class HorizontalBoxes(urwid.Columns):
self.focus_position = len(self.contents) - 1
top = HorizontalBoxes()
-top.open_box(menu_top)
+top.open_box(menu_top.menu)
urwid.MainLoop(urwid.Filler(top, 'middle', 10), palette).run()
diff --git a/docs/tutorial/menu4.py b/docs/tutorial/menu4.py
index 7d07688..129ff50 100644
--- a/docs/tutorial/menu4.py
+++ b/docs/tutorial/menu4.py
@@ -1,7 +1,5 @@
import urwid
-inventory = set()
-
class ActionButton(urwid.Button):
def __init__(self, caption, callback):
super(ActionButton, self).__init__("")
@@ -20,7 +18,7 @@ class Place(urwid.WidgetWrap):
getattr(child, 'choices', []).insert(0, self)
def enter_place(self, button):
- top.move_to(self)
+ game.update_place(self)
class Thing(urwid.WidgetWrap):
def __init__(self, name):
@@ -29,13 +27,8 @@ class Thing(urwid.WidgetWrap):
self.name = name
def take_thing(self, button):
- loop.process_input(["up"]) # move focus off this widget
self._w = urwid.Text(u" - %s (taken)" % self.name)
- inventory.add(self.name)
- if inventory >= set([u'sugar', u'lemon', u'jug']):
- response = urwid.Text(u'You can make lemonade!\n')
- done = ActionButton(u' - Joy', exit_program)
- loop.widget = urwid.Filler(urwid.Pile([response, done]))
+ game.take_thing(self)
def exit_program(button):
raise urwid.ExitMainLoop()
@@ -63,16 +56,28 @@ map_top = Place(u'porch', [
]),
])
-class AdventureLog(urwid.ListBox):
+class AdventureGame(object):
def __init__(self):
- log = urwid.SimpleFocusListWalker([])
- super(AdventureLog, self).__init__(log)
+ self.log = urwid.SimpleFocusListWalker([])
+ self.top = urwid.ListBox(self.log)
+ self.inventory = set()
+ self.update_place(map_top)
- def move_to(self, place):
- self.body.append(urwid.Pile([place.heading] + place.choices))
- self.focus_position = len(self.body) - 1
+ def update_place(self, place):
+ if self.log: # disable interaction with previous place
+ self.log[-1] = urwid.WidgetDisable(self.log[-1])
+ self.log.append(urwid.Pile([place.heading] + place.choices))
+ self.top.focus_position = len(self.log) - 1
+ self.place = place
+
+ def take_thing(self, thing):
+ self.inventory.add(thing.name)
+ if self.inventory >= set([u'sugar', u'lemon', u'jug']):
+ response = urwid.Text(u'You can make lemonade!\n')
+ done = ActionButton(u' - Joy', exit_program)
+ self.log[:] = [response, done]
+ else:
+ self.update_place(self.place)
-top = AdventureLog()
-top.move_to(map_top)
-loop = urwid.MainLoop(top, palette=[('reversed', 'standout', '')])
-loop.run()
+game = AdventureGame()
+urwid.MainLoop(game.top, palette=[('reversed', 'standout', '')]).run()
diff --git a/docs/tutorial/menu44.png b/docs/tutorial/menu44.png
index 2efacc7..7988daa 100644
--- a/docs/tutorial/menu44.png
+++ b/docs/tutorial/menu44.png
Binary files differ