diff options
| author | Ian Ward <ian@excess.org> | 2012-09-22 20:31:01 -0400 |
|---|---|---|
| committer | Ian Ward <ian@excess.org> | 2012-09-22 20:31:01 -0400 |
| commit | daefdead5fad770775aa584fc61ea55ef3e8ac89 (patch) | |
| tree | 9af532ce1c5c32542f2fa20a311055c40984975f /docs/tutorial | |
| parent | 9e73e7756f277a5edd6954043729d03fb47c8ca2 (diff) | |
| download | urwid-daefdead5fad770775aa584fc61ea55ef3e8ac89.tar.gz | |
docs: move some sections from tutorial to widget manual page
--HG--
branch : feature-sphinx
rename : docs/tutorial/wanat.py => docs/manual/wanat.py
rename : docs/tutorial/wanat_multi.py => docs/manual/wanat_multi.py
rename : docs/tutorial/wanat_new.py => docs/manual/wanat_new.py
rename : docs/tutorial/wcur1.py => docs/manual/wcur1.py
rename : docs/tutorial/wcur2.py => docs/manual/wcur2.py
rename : docs/tutorial/wmod.py => docs/manual/wmod.py
rename : docs/tutorial/wsel.py => docs/manual/wsel.py
Diffstat (limited to 'docs/tutorial')
| -rw-r--r-- | docs/tutorial/index.rst | 385 | ||||
| -rw-r--r-- | docs/tutorial/wanat.py | 22 | ||||
| -rw-r--r-- | docs/tutorial/wanat_multi.py | 17 | ||||
| -rw-r--r-- | docs/tutorial/wanat_new.py | 17 | ||||
| -rw-r--r-- | docs/tutorial/wcur1.py | 35 | ||||
| -rw-r--r-- | docs/tutorial/wcur2.py | 7 | ||||
| -rw-r--r-- | docs/tutorial/wmod.py | 15 | ||||
| -rw-r--r-- | docs/tutorial/wsel.py | 34 |
8 files changed, 0 insertions, 532 deletions
diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst index 24de181..1e2ef2b 100644 --- a/docs/tutorial/index.rst +++ b/docs/tutorial/index.rst @@ -331,390 +331,5 @@ default decorations .. image:: menu43.png .. image:: menu44.png -.. _zen-listbox: - -Zen of ListBox -============== - -ListBox Focus and Scrolling ---------------------------- - -The :class:`ListBox` is a box widget that contains flow widgets. -Its contents are displayed stacked vertically, and the -:class:`ListBox` allows the user to scroll through its content. -One of the flow widgets displayed in the :class:`ListBox` is the -focus widget. The :class:`ListBox` passes key presses to the -focus widget to allow the user to interact with it. If the focus widget does -not handle a keypress then the :class:`ListBox` may handle the -keypress by scrolling and/or selecting another widget to become the focus -widget. - -The :class:`ListBox` tries to do the most sensible thing when -scrolling and changing focus. When the widgets displayed are all -:class:`Text` widgets or other unselectable widgets then the -:class:`ListBox` will behave like a web browser does when the -user presses *UP*, *DOWN*, *PAGE UP* and *PAGE DOWN*: new text is immediately -scrolled in from the top or bottom. The :class:`ListBox` chooses -one of the visible widgets as its focus widget when scrolling. When scrolling -up the :class:`ListBox` chooses the topmost widget as the focus, -and when scrolling down the :class:`ListBox` chooses the -bottommost widget as the focus. - -When all the widgets displayed are not selectable the user would typically have -no way to tell which widget is in focus, but if we wrap the widgets with -:class:`AttrWrap` we can see what is happening while the -focus changes: - -.. literalinclude:: lbscr.py - :linenos: - -.. image:: lbscr1.png -.. image:: lbscr2.png -.. image:: lbscr3.png -.. image:: lbscr4.png -.. image:: lbscr5.png -.. image:: lbscr6.png - -The :class:`ListBox` remembers the location of the widget in -focus as either an "offset" or an "inset". An offset is the number of rows -between the top of the :class:`ListBox` and the beginning of the -focus widget. An offset of zero corresponds to a widget with its top aligned -with the top of the :class:`ListBox`. An inset is the fraction -of rows of the focus widget that are "above" the top of the -:class:`ListBox` and not visible. The -:class:`ListBox` uses this method of remembering the focus -widget location so that when the :class:`ListBox` is resized the -text displayed will stay roughly aligned with the top of the -:class:`ListBox`. - -.. image:: lbscr7.png -.. image:: lbscr8.png -.. image:: lbscr9.png - -When there are selectable widgets in the :class:`ListBox` the -focus will move between the selectable widgets, skipping the unselectable -widgets. The :class:`ListBox` will try to scroll all the rows of -a selectable widget into view so that the user can see the new focus widget in -its entirety. This behavior can be used to bring more than a single widget into -view by using composite widgets to combine a selectable widget with other -widgets that should be displayed at the same time. - - -Dynamic ListBox with ListWalker -------------------------------- - -While the :class:`ListBox` stores the location of its focus -widget, it does not directly store the actual focus widget or other contents of -the :class:`ListBox`. The storage of a -:class:`ListBox`'s content is delegated to a "List Walker" -object. If a list of widgets is passed to the :class:`ListBox` -constructor then it creates a :class:`SimpleListWalker` object -to manage the list. - -When the :class:`ListBox` is `rendering a canvas`_ or `handling -input`_ it will: - -.. _rendering a canvas: :meth:`ListBox.render` -.. _handling input: :meth:`ListBox.keypress` - -1. Call the :meth:`get_focus` method of its list walker object. This method - will return the focus widget and a position object. -2. Optionally call the :meth:`get_prev` method of its List Walker object one or - more times, initially passing the focus position and then passing the new - position returned on each successive call. This method will return the - widget and position object "above" the position passed. -3. Optionally call the :meth:`get_next` method of its List Walker object one or - more times, similarly, to collect widgets and position objects "below" the - focus position. -4. Optionally call the :meth:`set_focus` method passing one of the position - objects returned in the previous steps. - -This is the only way the :class:`ListBox` accesses its contents, -and it will not store copies of any of the widgets or position objects beyond -the current rendering or input handling operation. - -The :class:`SimpleListWalker` stores a list of widgets, and uses -integer indexes into this list as its position objects. It stores the focus -position as an integer, so if you insert a widget into the list above the focus -position then you need to remember to increment the focus position in the -:class:`SimpleListWalker` object or the contents of the -:class:`ListBox` will shift. - -A custom List Walker object may be passed to the -:class:`ListBox` constructor instead of a plain list of widgets. -List Walker objects must implement the :ref:`list-walker-interface`. - -The fib.py_ example program demonstrates a custom list walker that doesn't -store any widgets. It uses a tuple of two successive Fibonacci numbers as its -position objects and it generates Text widgets to display the numbers on the -fly. The result is a :class:`ListBox` that can scroll through an -unending list of widgets. - -The edit.py_ example program demonstrates a custom list walker that loads lines -from a text file only as the user scrolls them into view. This allows even -huge files to be opened almost instantly. - -The browse.py_ example program demonstrates a custom list walker that uses a -tuple of strings as position objects, one for the parent directory and one for -the file selected. The widgets are cached in a separate class that is accessed -using a dictionary indexed by parent directory names. This allows the -directories to be read only as required. The custom list walker also allows -directories to be hidden from view when they are "collapsed". - -.. _fib.py: http://excess.org/urwid/browser/examples/fib.py -.. _edit.py: http://excess.org/urwid/browser/examples/edit.py -.. _browse.py: http://excess.org/urwid/browser/examples/browse.py - - -Setting the Focus ------------------ - -The easiest way to change the current :class:`ListBox` focus is -to call the :meth:`ListBox.set_focus` method. This method doesn't -require that you know the :class:`ListBox`'s current dimensions -``(maxcol, maxrow)``. It will wait until the next call to either keypress or -render to complete setting the offset and inset values using the dimensions -passed to that method. - -The position object passed to :meth:`set_focus` must be compatible with the -List Walker object that the :class:`ListBox` is using. For -:class:`SimpleListWalker` the position is the integer index of -the widget within the list. - -The *coming_from* parameter should be set if you know that the old position is -"above" or "below" the previous position. When the -:class:`ListBox` completes setting the offset and inset values -it tries to find the old widget among the visible widgets. If the old widget is -still visible, if will try to avoid causing the :class:`ListBox` -contents to scroll up or down from its previous position. If the widget is not -visible, then the :class:`ListBox` will: - -* Display the new focus at the bottom of the :class:`ListBox` if - *coming_from* is "above". -* Display the new focus at the top of the :class:`ListBox` if - *coming_from* is "below". -* Display the new focus in the middle of the :class:`ListBox` if - coming_from is ``None``. - -If you know exactly where you want to display the new focus widget within the -:class:`ListBox` you may call -:meth:`ListBox.set_focus_valign`. This method lets you specify -the *top*, *bottom*, *middle*, a relative position or the exact number of rows -from the top or bottom of the :class:`ListBox`. - - -Combining Widgets -================= - - -Piling Widgets --------------- - -:class:`Pile` widgets are used to combine multiple widgets by -stacking them vertically. A Pile can manage selectable widgets by keeping track -of which widget is in focus and it can handle moving the focus between widgets -when the user presses the *UP* and *DOWN* keys. A Pile will also work well when -used within a :class:`ListBox`. - -A Pile is selectable only if its focus widget is selectable. If you create a -Pile containing one Text widget and one Edit widget the Pile will choose the -Edit widget as its default focus widget. To change the pile's focus widget you -can call :meth:`Pile.set_focus`. - - -Dividing into Columns ---------------------- - -:class:`Columns` widgets may be used to arrange either flow -widgets or box widgets horizontally into columns. Columns widgets will manage -selectable widgets by keeping track of which column is in focus and it can -handle moving the focus between columns when the user presses the *LEFT* and -*RIGHT* keys. Columns widgets also work well when used within a -:class:`ListBox`. - -Columns widgets are selectable only if the column in focus is selectable. If a -focus column is not specified the first selectable widget will be chosen as the -focus column. The :meth:`Columns.set_focus` method may be used -to select the focus column. - - -``GridFlow`` Arrangment ------------------------ - -The :class:`GridFlow` widget is a flow widget designed for use -with :class:`Button`, :class:`CheckBox` and -:class:`RadioButton` widgets. It renders all the widgets it -contains the same width and it arranges them from left to right and top to -bottom. - -The GridFlow widget uses Pile, Columns, Padding and Divider widgets to build a -display widget that will handle the keyboard input and rendering. When the -GridFlow widget is resized it regenerates the display widget to accommodate the -new space. - - -``Overlay`` widgets -------------------- - -The :class:`Overlay` widget is a box widget that contains two -other box widgets. The bottom widget is rendered the full size of the Overlay -widget and the top widget is placed on top, obscuring an area of the bottom -widget. This widget can be used to create effects such as overlapping "windows" -or pop-up menus. - -The Overlay widget always treats the top widget as the one in focus. All -keyboard input will be passed to the top widget. - -If you want to use a flow flow widget for the top widget, first wrap the flow -widget with a :class:`Filler` widget. - - - -.. _creating-custom-widgets: - -Creating Custom Widgets -======================= - - -Modifying Existing Widgets --------------------------- - -The easiest way to create a custom widget is to modify an existing widget. -This can be done by either subclassing the original widget or by wrapping it. -Subclassing is appropriate when you need to interact at a very low level with -the original widget, such as if you are creating a custom edit widget with -different behavior than the usual Edit widgets. If you are creating a custom -widget that doesn't need tight coupling with the original widget, such as a -widget that displays customer address information, then wrapping is more -appropriate. - -The :class:`WidgetWrap` class simplifies wrapping existing -widgets. You can create a custom widget simply by creating a subclass of -WidgetWrap and passing a widget into WidgetWrap's constructor. - -This is an example of a custom widget that uses WidgetWrap: - -.. literalinclude:: wmod.py - :linenos: - -The above code creates a group of RadioButtons and provides a method to -query the state of the buttons. - -Wrapped widgets may also override the standard widget methods. These methods -are described in following sections. - -Anatomy of a Widget -------------------- - -Any object that follows the `Widget interface definition`_ may be used as a -widget. Box widgets must implement selectable_ and render_ methods, and flow -widgets must implement selectable, render and rows_ methods. - -.. _Widget interface definition: :class:`Widget` -.. _selectable: :meth:`Widget.selectable` -.. _render: :meth:`Widget.render` -.. _rows: :meth:`Widget.rows` - -.. literalinclude:: wanat.py - :linenos: -The above code implements two widget classes. Pudding is a flow widget and -BoxPudding is a box widget. Pudding will render as much "Pudding" as will fit -in a single row, and BoxPudding will render as much "Pudding" as will fit into -the entire area given. -Note that the rows and render methods' focus parameter must have a default -value of False. Also note that for flow widgets the number of rows returned by -the rows method must match the number of rows rendered by the render method. - -In most cases it is easier to let other widgets handle the rendering and row -calculations for you: - -.. literalinclude:: wanat_new.py - :linenos: - -The NewPudding class behaves the same way as the Pudding class above, but in -NewPudding you can change the way the widget appears by modifying only the -display_widget method, whereas in the Pudding class you may have to modify both -the render and rows methods. - -To improve the efficiency of your Urwid application you should be careful of -how long your rows methods take to execute. The rows methods may be called many -times as part of input handling and rendering operations. If you are using a -display widget that is time consuming to create you should consider caching it -to reduce its impact on performance. - -It is possible to create a widget that will behave as either a flow widget or -box widget depending on what is required: - -.. literalinclude:: wanat_multi.py - :linenos: - -MultiPudding will work in place of either Pudding or BoxPudding above. The -number of elements in the size tuple determines whether the containing widget -is expecting a flow widget or a box widget. - - -Creating a Selectable Widget ----------------------------- - -Selectable widgets such as Edit and Button widgets allow the user to interact -with the application. A widget is selectable if its selectable method returns -True. Selectable widgets must implement the keypress_ method to handle keyboard -input. - -.. _keypress: :meth:`Widget.keypress` - -.. literalinclude:: wsel.py - -The SelectablePudding widget will display its contents in uppercase when it is -in focus, and it allows the user to "eat" the pudding by pressing each of the -letters *P*, *U*, *D*, *D*, *I*, *N* and *G* on the keyboard. When the user has -"eaten" all the pudding the widget will reset to its initial state. - -Note that keys that are unhandled in the keypress method are returned so that -another widget may be able to handle them. This is a good convention to follow -unless you have a very good reason not to. In this case the *UP* and *DOWN* -keys are returned so that if this widget is in a -:class:`ListBox` the :class:`ListBox` will behave -as the user expects and change the focus or scroll the -:class:`ListBox`. - -Widget Displaying the Cursor ----------------------------- - -Widgets that display the cursor must implement the get_cursor_coords_ method. -Similar to the rows method for flow widgets, this method lets other widgets -make layout decisions without rendering the entire widget. The -:class:`ListBox` widget in particular uses get_cursor_coords to -make sure that the cursor is visible within its focus widget. - -.. _get_cursor_coords: :meth:`Widget.get_cursor_coords` - -.. literalinclude:: wcur1.py - :linenos: - -CursorPudding will let the user move the cursor through the widget by pressing -*LEFT* and *RIGHT*. The cursor must only be added to the canvas when the widget -is in focus. The get_cursor_coords method must always return the same cursor -coordinates that render does. - -A widget displaying a cursor may choose to implement get_pref_col. This method -returns the preferred column for the cursor, and is called when the focus is -moving up or down off this widget. - -.. _get_pref_col: :meth:`Widget.get_pref_col` - -Another optional method is move_cursor_to_coords_. This method allows other -widgets to try to position the cursor within this widget. The -:class:`ListBox` widget uses :meth:`move_cursor_to_coords` when -changing focus and when the user pressed *PAGE UP* or *PAGE DOWN*. This method -must return ``True`` on success and ``False`` on failure. If the cursor may be -placed at any position within the row specified (not only at the exact column -specified) then this method must move the cursor to that position and return -``True``. - -.. _move_cursor_to_coords: :meth:`Widget.move_cursor_to_coords` - -.. literalinclude:: wcur2.py - :linenos: diff --git a/docs/tutorial/wanat.py b/docs/tutorial/wanat.py deleted file mode 100644 index 723eb90..0000000 --- a/docs/tutorial/wanat.py +++ /dev/null @@ -1,22 +0,0 @@ -import urwid - -class Pudding(urwid.Widget): - _sizing = frozenset(['flow']) - - def rows(self, size, focus=False): - return 1 - - def render(self, size, focus=False): - (maxcol,) = size - num_pudding = maxcol / len("Pudding") - return urwid.TextCanvas(["Pudding" * num_pudding], maxcol=maxcol) - - -class BoxPudding(urwid.Widget): - _sizing = frozenset(['box']) - - def render(self, size, focus=False): - (maxcol, maxrow) = size - num_pudding = maxcol / len("Pudding") - return urwid.TextCanvas(["Pudding" * num_pudding] * maxrow, - maxcol=maxcol) diff --git a/docs/tutorial/wanat_multi.py b/docs/tutorial/wanat_multi.py deleted file mode 100644 index b6d6943..0000000 --- a/docs/tutorial/wanat_multi.py +++ /dev/null @@ -1,17 +0,0 @@ -import urwid - -class MultiPudding(urwid.Widget): - _sizing = frozenset(['flow', 'box']) - - def rows(self, size, focus=False): - return 1 - - def render(self, size, focus=False): - if len(size) == 1: - (maxcol,) = size - maxrow = 1 - else: - (maxcol, maxrow) = size - num_pudding = maxcol / len("Pudding") - return urwid.TextCanvas(["Pudding" * num_pudding] * maxrow, - maxcol=maxcol) diff --git a/docs/tutorial/wanat_new.py b/docs/tutorial/wanat_new.py deleted file mode 100644 index 5a597b9..0000000 --- a/docs/tutorial/wanat_new.py +++ /dev/null @@ -1,17 +0,0 @@ -import urwid - -class NewPudding(urwid.Widget): - _sizing = frozenset(['flow']) - - def rows(self, size, focus=False): - w = self.display_widget(size, focus) - return w.rows(size, focus) - - def render(self, size, focus=False): - w = self.display_widget(size, focus) - return w.render(size, focus) - - def display_widget(self, size, focus): - (maxcol,) = size - num_pudding = maxcol / len("Pudding") - return urwid.Text("Pudding" * num_pudding) diff --git a/docs/tutorial/wcur1.py b/docs/tutorial/wcur1.py deleted file mode 100644 index 6bf97f7..0000000 --- a/docs/tutorial/wcur1.py +++ /dev/null @@ -1,35 +0,0 @@ -import urwid - -class CursorPudding(urwid.Widget): - _sizing = frozenset(['flow']) - _selectable = True - - def __init__(self): - self.cursor_col = 0 - - def rows(self, size, focus=False): - return 1 - - def render(self, size, focus=False): - (maxcol,) = size - num_pudding = maxcol / len("Pudding") - cursor = None - if focus: - cursor = self.get_cursor_coords(size) - return urwid.TextCanvas(["Pudding" * num_pudding], [], cursor, maxcol) - - def get_cursor_coords(self, size): - (maxcol,) = size - col = min(self.cursor_col, maxcol - 1) - return col, 0 - - def keypress(self, size, key): - (maxcol, ) = size - if key == 'left': - col = self.cursor_col - 1 - elif key == 'right': - col = self.cursor_col + 1 - else: - return key - self.cursor_x = max(0, min(maxcol - 1, col)) - self._invalidate() diff --git a/docs/tutorial/wcur2.py b/docs/tutorial/wcur2.py deleted file mode 100644 index 1225419..0000000 --- a/docs/tutorial/wcur2.py +++ /dev/null @@ -1,7 +0,0 @@ - def get_pref_col(self, (maxcol,)): - return self.cursor_x - - def move_cursor_to_coords(self, (maxcol,), col, row): - assert row == 0 - self.cursor_x = col - return True diff --git a/docs/tutorial/wmod.py b/docs/tutorial/wmod.py deleted file mode 100644 index a066a49..0000000 --- a/docs/tutorial/wmod.py +++ /dev/null @@ -1,15 +0,0 @@ -import urwid - -class QuestionnaireItem(urwid.WidgetWrap): - def __init__(self): - self.options = [] - unsure = urwid.RadioButton(self.options, u"Unsure") - yes = urwid.RadioButton(self.options, u"Yes") - no = urwid.RadioButton(self.options, u"No") - display_widget = urwid.GridFlow([unsure, yes, no], 15, 3, 1, 'left') - urwid.WidgetWrap.__init__(self, display_widget) - - def get_state(self): - for o in self.options: - if o.get_state() is True: - return o.get_label() diff --git a/docs/tutorial/wsel.py b/docs/tutorial/wsel.py deleted file mode 100644 index 15683d8..0000000 --- a/docs/tutorial/wsel.py +++ /dev/null @@ -1,34 +0,0 @@ -import urwid - -class SelectablePudding(urwid.Widget): - _sizing = frozenset(['flow']) - _selectable = True - - def __init__(self): - self.pudding = "pudding" - - def rows(self, size, focus=False): - return 1 - - def render(self, size, focus=False): - (maxcol,) = size - num_pudding = maxcol / len(self.pudding) - pudding = self.pudding - if focus: - pudding = pudding.upper() - return urwid.TextCanvas([pudding * num_pudding], - maxcol=maxcol) - - def keypress(self, size, key): - (maxcol,) = size - if len(key) > 1: - return key - if key.lower() in self.pudding: - # remove letter from pudding - n = self.pudding.index(key.lower()) - self.pudding = self.pudding[:n] + self.pudding[n+1:] - if not self.pudding: - self.pudding = "pudding" - self._invalidate() - else: - return key |
