diff options
| author | Ian Ward <ian@excess.org> | 2012-09-18 14:57:03 -0400 |
|---|---|---|
| committer | Ian Ward <ian@excess.org> | 2012-09-18 14:57:03 -0400 |
| commit | c93f671c8d0b3dc1764ead8746da96fa905ab859 (patch) | |
| tree | 39ef641505a6e942426a3691fe4ef6f5e2a28b9b /docs/tutorial | |
| parent | 44c5a021d49cdbb98297fb207a057784a207e4c5 (diff) | |
| download | urwid-c93f671c8d0b3dc1764ead8746da96fa905ab859.tar.gz | |
tutorial: update edit+button, listbox+listwalker examples
--HG--
branch : feature-sphinx
Diffstat (limited to 'docs/tutorial')
| -rw-r--r-- | docs/tutorial/edit.py | 19 | ||||
| -rw-r--r-- | docs/tutorial/frlb.py | 25 | ||||
| -rw-r--r-- | docs/tutorial/frlb.py.xdotool | 4 | ||||
| -rw-r--r-- | docs/tutorial/frlb1.png | bin | 416 -> 499 bytes | |||
| -rw-r--r-- | docs/tutorial/frlb2.png | bin | 651 -> 728 bytes | |||
| -rw-r--r-- | docs/tutorial/frlb3.png | bin | 765 -> 927 bytes | |||
| -rw-r--r-- | docs/tutorial/frlb4.png | bin | 850 -> 932 bytes | |||
| -rw-r--r-- | docs/tutorial/index.rst | 107 | ||||
| -rw-r--r-- | docs/tutorial/lbcont.py | 34 | ||||
| -rw-r--r-- | docs/tutorial/lbcont4.png | bin | 0 -> 1059 bytes |
10 files changed, 90 insertions, 99 deletions
diff --git a/docs/tutorial/edit.py b/docs/tutorial/edit.py index 3f55507..b7d9817 100644 --- a/docs/tutorial/edit.py +++ b/docs/tutorial/edit.py @@ -4,18 +4,15 @@ def exit_on_q(key): if key in ('q', 'Q'): raise urwid.ExitMainLoop() -class Question(urwid.Edit): - def __init__(self): - super(Question, self).__init__(u"What is your name?\n") - +class QuestionBox(urwid.Filler): def keypress(self, size, key): - key = super(Question, self).keypress(size, key) - if key == 'enter': - fill.body = urwid.Text(u"Nice to meet you,\n" - + self.edit_text + u".\n\nPress Q to exit.") - return None - return key + if key != 'enter': + return super(QuestionBox, self).keypress(size, key) + self.original_widget = urwid.Text( + u"Nice to meet you,\n%s.\n\nPress Q to exit." % + edit.edit_text) -fill = urwid.Filler(Question()) +edit = urwid.Edit(u"What is your name?\n") +fill = QuestionBox(edit) loop = urwid.MainLoop(fill, unhandled_input=exit_on_q) loop.run() diff --git a/docs/tutorial/frlb.py b/docs/tutorial/frlb.py index 6b3a332..c01c1b2 100644 --- a/docs/tutorial/frlb.py +++ b/docs/tutorial/frlb.py @@ -1,19 +1,20 @@ import urwid -def exit_on_cr(key): - if key == 'enter': - raise urwid.ExitMainLoop() - -def on_ask_change(edit, new_edit_text): - assert edit is ask # we are passed our edit widget - reply.set_text(('I say', - u"Nice to meet you, " + new_edit_text)) - palette = [('I say', 'default,bold', 'default', 'bold'),] ask = urwid.Edit(('I say', u"What is your name?\n")) reply = urwid.Text(u"") -listbox = urwid.ListBox(urwid.SimpleFocusListWalker([ask, reply])) +button = urwid.Button(u'Exit') +div = urwid.Divider() +pile = urwid.Pile([ask, div, reply, div, button]) +top = urwid.Filler(pile, valign='top') + +def on_ask_change(edit, new_edit_text): + reply.set_text(('I say', u"Nice to meet you, %s" % new_edit_text)) + +def on_exit_clicked(button): + raise urwid.ExitMainLoop() + urwid.connect_signal(ask, 'change', on_ask_change) +urwid.connect_signal(button, 'click', on_exit_clicked) -loop = urwid.MainLoop(listbox, palette, unhandled_input=exit_on_cr) -loop.run() +urwid.MainLoop(top, palette).run() diff --git a/docs/tutorial/frlb.py.xdotool b/docs/tutorial/frlb.py.xdotool index cfde3c4..02a1690 100644 --- a/docs/tutorial/frlb.py.xdotool +++ b/docs/tutorial/frlb.py.xdotool @@ -1,4 +1,4 @@ windowsize --usehints $RXVTWINDOWID 21 7 type --window $RXVTWINDOWID 'Tim t' -type --window $RXVTWINDOWID 'he Ench' -type --window $RXVTWINDOWID 'anter' +type --window $RXVTWINDOWID 'he Enchanter' +key --window $RXVTWINDOWID Down diff --git a/docs/tutorial/frlb1.png b/docs/tutorial/frlb1.png Binary files differindex 249d8ec..b5c45be 100644 --- a/docs/tutorial/frlb1.png +++ b/docs/tutorial/frlb1.png diff --git a/docs/tutorial/frlb2.png b/docs/tutorial/frlb2.png Binary files differindex b0ca366..dfe9526 100644 --- a/docs/tutorial/frlb2.png +++ b/docs/tutorial/frlb2.png diff --git a/docs/tutorial/frlb3.png b/docs/tutorial/frlb3.png Binary files differindex ea74972..ba3ae36 100644 --- a/docs/tutorial/frlb3.png +++ b/docs/tutorial/frlb3.png diff --git a/docs/tutorial/frlb4.png b/docs/tutorial/frlb4.png Binary files differindex f120af0..c31405b 100644 --- a/docs/tutorial/frlb4.png +++ b/docs/tutorial/frlb4.png diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst index bc2ace8..1a4c911 100644 --- a/docs/tutorial/index.rst +++ b/docs/tutorial/index.rst @@ -183,15 +183,18 @@ method. Customizing decoration or container widgets to handle input this way is a common pattern in Urwid applications. This pattern is easier to maintain and extend than handling all special input in an ``unhandled_input`` function. -* ``Question`` is an :class:`Edit` widget that starts with the caption - ``"What is your name?\n"``. The newline at the end of the caption - makes the user input start on the next row. -* ``Question.keypress()`` allows most keystrokes to be handled by - :meth:`Edit.keypress` so the user to enter their name. -* When *ENTER* is pressed the Question widget is replaced with - a :class:`Text` response. -* When the user presses *Q* after the response is displayed the - ``exit_on_q`` function will cause the program to exit. +* In ``QuestionBox.keypress()`` all keypresses except *ENTER* are passed along to + the default :meth:`Filler.keypress` which sends them to the + child :meth:`Edit.keypress` method. +* Note that names containing *Q* can be entered into the :class:`Edit` + widget without causing the program to exit because :meth:`Edit.keypress` + indicates that it has handled the key by returning ``None``. + See :meth:`Widget.keypress` for more information. +* When *ENTER* is pressed the child widget (``original_widget``) is changed + to :class:`Text` widget. +* :class:`Text` widgets don't handle any keyboard input so all input + ends up in the ``unhandled_input`` function ``exit_on_q()``, allowing the + user to exit the program. .. image:: edit1.png @@ -199,34 +202,26 @@ and extend than handling all special input in an ``unhandled_input`` function. .. image:: edit3.png -Events and ListBox Widget -------------------------- +Edit and Button Signals +----------------------- This program asks for your name and responds "Nice to meet you, (your name)" -*while* you type your name. *ENTER* exits. +*while* you type your name. Press *DOWN* then *SPACE* or *ENTER* to exit. .. literalinclude:: frlb.py :linenos: * An :class:`Edit` widget and :class:`Text` reply widget are created, like in the previous example. -* A :class:`SimpleListWalker` is then created to manage the - contents and focus of our :class:`ListBox`. The - :class:`SimpleListWalker` behaves just like a list of widgets, - and can be modified like a regular list. -* A :class:`ListBox` is created and passed the - :class:`SimpleListWalker`. The :class:`ListBox` - is a box widget and allows scrolling through its contents. This example is - simple enough that we could have used a :class:`Pile` widget - and :class:`Filler` widget instead, but we will see how the - :class:`ListBox` can be useful in later examples. * The :func:`connect_signal` function is used to attach our - :func:`on_ask_change` function to our :class:`Edit` widget's - *change* event. Now any time the content of the :class:`Edit` + ``on_ask_change()`` function to our :class:`Edit` widget's + *change* signal. Now any time the content of the :class:`Edit` widget changes :func:`on_ask_change` will be called and passed the new content. -* Now :func:`on_ask_change` updates the reply text as the user enters their - name. +* Finally we attach our ``on_exit_clicked()`` function to our + exit :class:`Button`'s *click* signal. +* ``on_ask_change()`` updates the reply text as the user enters their + name and ``on_exit_click()`` exits. .. image:: frlb1.png .. image:: frlb2.png @@ -234,8 +229,8 @@ This program asks for your name and responds "Nice to meet you, (your name)" .. image:: frlb4.png -Modifying ListBox Content -------------------------- +ListBox and SimpleFocusListWalker +--------------------------------- This program asks for your name and responds ``Nice to meet you, (your name).`` It then asks again, and again. Old values may be changed and the responses will @@ -243,37 +238,39 @@ be updated when you press *ENTER*. *ENTER* on a blank line exits. .. literalinclude:: lbcont.py -* When the user presses *ENTER*: - - * The widget in focus and its current position is retrieved by calling the - :meth:`ListBox.get_focus` function. - * If the widget in focus does not have an :attr:`edit_text` attribute, then - it is not one of the :class:`Edit` widgets we are interested - in. One of the :class:`Text` widgets might receive focus if - it covers the entire visible area of the :class:`ListBox` - widget and there is no :class:`Edit` widget to take focus. - While this is unlikely, it should be handled or the program will fail when - trying to access it. - * If there is no edit text we exit the program. - * The widget after the widget in focus (if any exists) is replaced with a - response. - * If there is no widget after that widget, a new :class:`Edit` - widget is created. - * The focus is then moved to the next :class:`Edit` widget by - calling :meth:`ListBox.set_focus`. - -* All other keys are passed to the top widget to handle. The - :class:`ListBox` widget does most of the hard work: - - * *UP* and *DOWN* will change the focus and/or scroll the widgets in the list - box. - * *PAGE UP* and *PAGE DOWN* will try to move the focus one screen up or down. - * The cursor's column is maintained as best as possible when moving from one - :class:`Edit` widget to another. +:class:`ListBox` widgets let you scroll through a number of flow widgets +vertically. It handles *UP*, *DOWN*, *PAGE UP* and *PAGE DOWN* keystrokes +and changing the focus for you. :ref:`listbox-contents` is managed by +a "list walker", one of the list walkers that is easiest to use is +:class:`SimpleFocusListWalker`. + +:class:`SimpleFocusListWalker` is like a normal python list of widgets, but +any time you insert or remove widgets the focus position is updated +automatically. + +Here we are customizing our :class:`ListBox`'s keypress handling by overriding +it in a subclass. + +* ``question()`` is used to build widgets to communicate with the user. + Here we return a :class:`Pile` widget with a single :class:`Edit` widget + to start. +* We retrieve the name entered with :attr:`ListBox.focus` to get the + :class:`Pile` in focus, the standard + :ref:`container widget <container-widgets>` method ``[0]`` to get the + first child of the pile and :attr:`Edit.edit_text`. +* For the response we use the fact that we can treat + :attr:`Pile.contents` like a list of widgets and options to create or + replace any existing response by assigning to ``[1:]``. We assign + the new response with the default :meth:`Pile.options`. +* To add another question after the current one we treat our + :class:`SimpleFocusListWalker` like a normal list of widgets and call + ``insert()``, then update the focus position to the widget we just + created. .. image:: lbcont1.png .. image:: lbcont2.png .. image:: lbcont3.png +.. image:: lbcont4.png Menu Example diff --git a/docs/tutorial/lbcont.py b/docs/tutorial/lbcont.py index f71d5bb..b2bc8a4 100644 --- a/docs/tutorial/lbcont.py +++ b/docs/tutorial/lbcont.py @@ -1,33 +1,29 @@ import urwid -class Question(urwid.Edit): - def __init__(self): - super(Question, self).__init__(('I say', u"What is your name?\n")) - - def keypress(self, size, key): - key = super(Question, self).keypress(size, key) - if key == 'enter' and not self.edit_text: - raise urwid.ExitMainLoop() - return key +def question(): + return urwid.Pile([urwid.Edit(('I say', u"What is your name?\n"))]) def answer(name): return urwid.Text(('I say', u"Nice to meet you, " + name + "\n")) class ConversationListBox(urwid.ListBox): def __init__(self): - super(ConversationListBox, self).__init__( - urwid.SimpleFocusListWalker([Question()])) + body = urwid.SimpleFocusListWalker([question()]) + super(ConversationListBox, self).__init__(body) def keypress(self, size, key): key = super(ConversationListBox, self).keypress(size, key) - if key != 'enter' or not hasattr(self.focus, 'edit_text'): + if key != 'enter': return key - # replace or add response below + name = self.focus[0].edit_text + if not name: + raise urwid.ExitMainLoop() + # replace or add response + self.focus.contents[1:] = [(answer(name), self.focus.options())] pos = self.focus_position - self.body[pos + 1:pos + 2] = [answer(self.focus.edit_text)] - if not self.body[pos + 2:pos + 3]: self.body.append(Question()) - self.set_focus(pos + 2) + # add a new question + self.body.insert(pos + 1, question()) + self.focus_position = pos + 1 -palette = [('I say', 'default,bold', 'default', 'bold'),] -loop = urwid.MainLoop(ConversationListBox(), palette) -loop.run() +palette = [('I say', 'default,bold', 'default'),] +urwid.MainLoop(ConversationListBox(), palette).run() diff --git a/docs/tutorial/lbcont4.png b/docs/tutorial/lbcont4.png Binary files differnew file mode 100644 index 0000000..de655ab --- /dev/null +++ b/docs/tutorial/lbcont4.png |
