summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgignore11
-rw-r--r--.hgtags31
-rw-r--r--.travis.yml21
-rw-r--r--CHANGELOG883
-rw-r--r--MANIFEST.in2
-rw-r--r--README.rst24
-rwxr-xr-xbin/build-pages.sh15
-rwxr-xr-xbin/deps.py25
-rw-r--r--docs/Makefile2
-rw-r--r--docs/changelog.rst1095
-rw-r--r--docs/conf.py6
l---------docs/examples/bigtext.py1
-rw-r--r--docs/examples/bigtext.py.xdotool3
-rw-r--r--docs/examples/bigtext1.pngbin0 -> 3140 bytes
-rw-r--r--docs/examples/bigtext2.pngbin0 -> 3111 bytes
-rw-r--r--docs/examples/bigtext3.pngbin0 -> 3193 bytes
-rwxr-xr-xdocs/examples/browse.py6
-rw-r--r--docs/examples/browse.py.xdotool2
-rw-r--r--docs/examples/browse1.pngbin0 -> 3139 bytes
-rw-r--r--docs/examples/browse2.pngbin0 -> 3262 bytes
-rwxr-xr-xdocs/examples/edit.py8
-rw-r--r--docs/examples/edit.py.xdotool2
-rw-r--r--docs/examples/edit1.pngbin0 -> 7178 bytes
-rw-r--r--docs/examples/edit2.pngbin0 -> 7164 bytes
-rw-r--r--docs/examples/edit_text.txt11
l---------docs/examples/graph.py1
-rw-r--r--docs/examples/graph.py.xdotool2
-rw-r--r--docs/examples/graph1.pngbin0 -> 2325 bytes
-rw-r--r--docs/examples/graph2.pngbin0 -> 2432 bytes
-rw-r--r--docs/examples/index.rst80
l---------docs/examples/palette_test.py1
-rw-r--r--docs/examples/palette_test.py.xdotool2
-rw-r--r--docs/examples/palette_test1.pngbin0 -> 3428 bytes
-rw-r--r--docs/examples/palette_test2.pngbin0 -> 21625 bytes
l---------docs/examples/pop_up.py1
-rw-r--r--docs/examples/pop_up.py.xdotool2
-rw-r--r--docs/examples/pop_up1.pngbin0 -> 616 bytes
-rw-r--r--docs/examples/pop_up2.pngbin0 -> 1163 bytes
l---------docs/examples/real_browse.py1
l---------docs/examples/real_edit.py1
l---------docs/examples/subproc.py1
-rw-r--r--docs/examples/subproc.py.xdotool2
-rw-r--r--docs/examples/subproc1.pngbin0 -> 1517 bytes
-rw-r--r--docs/examples/subproc2.pngbin0 -> 2131 bytes
-rw-r--r--docs/examples/subproc2.py10
l---------docs/examples/tour.py1
-rw-r--r--docs/examples/tour.py.xdotool2
-rw-r--r--docs/examples/tour1.pngbin0 -> 4985 bytes
-rw-r--r--docs/examples/tour2.pngbin0 -> 4022 bytes
-rw-r--r--docs/index.rst2
-rwxr-xr-xdocs/manual/bright_combinations.py35
-rw-r--r--docs/manual/bright_combinations.py.xdotool1
-rw-r--r--docs/manual/bright_combinations1.pngbin0 -> 8511 bytes
-rw-r--r--docs/manual/canvascache.rst4
-rw-r--r--docs/manual/displayattributes.rst27
-rw-r--r--docs/manual/displaymodules.rst2
-rw-r--r--docs/manual/mainloop.rst22
-rwxr-xr-xdocs/manual/safe_combinations.py38
-rw-r--r--docs/manual/safe_combinations.py.xdotool1
-rw-r--r--docs/manual/safe_combinations1.pngbin0 -> 9289 bytes
-rw-r--r--docs/manual/widgets.rst4
-rw-r--r--docs/reference/main_loop.rst5
-rw-r--r--docs/reference/signals.rst8
-rwxr-xr-xdocs/tools/compile_pngs.sh3
-rwxr-xr-xdocs/tools/screenshots.sh1
-rw-r--r--docs/tools/templates/indexcontent.html86
-rw-r--r--docs/tools/templates/indexsidebar.html11
-rw-r--r--docs/tools/templates/layout.html5
-rw-r--r--docs/tools/templates/localtoc.html18
-rw-r--r--docs/tutorial/lbscr.py30
-rw-r--r--docs/tutorial/lbscr.py.xdotool9
-rw-r--r--docs/tutorial/lbscr1.pngbin627 -> 0 bytes
-rw-r--r--docs/tutorial/lbscr2.pngbin769 -> 0 bytes
-rw-r--r--docs/tutorial/lbscr3.pngbin666 -> 0 bytes
-rw-r--r--docs/tutorial/lbscr4.pngbin600 -> 0 bytes
-rw-r--r--docs/tutorial/lbscr5.pngbin595 -> 0 bytes
-rw-r--r--docs/tutorial/lbscr6.pngbin600 -> 0 bytes
-rw-r--r--docs/tutorial/lbscr7.pngbin718 -> 0 bytes
-rw-r--r--docs/tutorial/lbscr8.pngbin710 -> 0 bytes
-rw-r--r--docs/tutorial/lbscr9.pngbin714 -> 0 bytes
l---------docs/tutorial/urwid1
-rwxr-xr-xexamples/calc.py2
-rwxr-xr-xexamples/dialog.py2
-rwxr-xr-xexamples/fib.py2
-rwxr-xr-xexamples/lcd_cf635.py4
-rwxr-xr-xexamples/subproc.py5
-rwxr-xr-xexamples/tour.py2
-rw-r--r--examples/twisted_serve_ssh.py13
l---------examples/urwid1
-rw-r--r--setup.py36
-rw-r--r--urwid/__init__.py5
-rw-r--r--urwid/canvas.py2
-rwxr-xr-xurwid/container.py19
-rwxr-xr-xurwid/curses_display.py48
-rwxr-xr-xurwid/decoration.py8
-rwxr-xr-xurwid/display_common.py19
-rw-r--r--urwid/escape.py14
-rwxr-xr-xurwid/graphics.py2
-rwxr-xr-xurwid/html_fragment.py2
-rw-r--r--urwid/lcd_display.py2
-rw-r--r--urwid/listbox.py2
-rwxr-xr-xurwid/main_loop.py998
-rwxr-xr-xurwid/monitored_list.py4
-rwxr-xr-xurwid/old_str_util.py2
-rw-r--r--urwid/raw_display.py139
-rw-r--r--urwid/signals.py217
-rwxr-xr-xurwid/split_repr.py2
-rw-r--r--urwid/tests/test_container.py188
-rw-r--r--urwid/tests/test_decoration.py16
-rw-r--r--urwid/tests/test_doctests.py22
-rw-r--r--urwid/tests/test_event_loops.py131
-rw-r--r--urwid/tests/test_listbox.py16
-rw-r--r--urwid/tests/test_str_util.py2
-rw-r--r--urwid/tests/test_widget.py10
-rw-r--r--urwid/text_layout.py6
-rw-r--r--urwid/treetools.py2
-rw-r--r--urwid/util.py10
-rw-r--r--urwid/version.py2
-rw-r--r--urwid/vterm.py8
-rwxr-xr-xurwid/web_display.py2
-rw-r--r--urwid/widget.py2
-rwxr-xr-xurwid/wimp.py12
122 files changed, 2747 insertions, 1802 deletions
diff --git a/.hgignore b/.hgignore
deleted file mode 100644
index 951902a..0000000
--- a/.hgignore
+++ /dev/null
@@ -1,11 +0,0 @@
-syntax: glob
-
-*.pyc
-*.so
-urwid.egg-info
-.*.sw?
-tmp/
-tags
-
-syntax: regexp
-^build
diff --git a/.hgtags b/.hgtags
deleted file mode 100644
index 3b24199..0000000
--- a/.hgtags
+++ /dev/null
@@ -1,31 +0,0 @@
-00ecfc18e7a3e57d9ddfe2cf02dd596b9f1aecc9 release-0.9.0
-0836e10d9971aa20b04ab6ae17b8faeadd359f6d release-0.8.5
-20d334bf7f990ab83a4cf8182d7915038bab97c5 release-0.8.7
-3e41e992e58d5d8e1917b8c2e06ddb4875a0c3f1 release-0.9.3
-4a94ee84f10ace1d46bdaf64e7ec19d070e41960 release-0.8.6
-4d54f0d3d0921990cebde9f91897b30f50b1c1f0 release-0.9.0-pre3
-51ae19b2d0710ddaf2abed41173678f3ea353ba5 release-0.9.7
-55a4dbaf0f4c7864a0fd74b3399b5bb084225292 release-0.9.4
-6940f8e182d1cc5082c7e46f3701dce2559ad8f1 release-0.9.7.1
-724fa210dbd7c43670178baff9314b05b7dab420 release-0.9.5
-75e410b77de0675ec7ff8a595b97e21e21908e65 release-0.8.10
-77ca4911a81a9a6f6edd900c8759ccd5b775d41b release-0.8.0
-77fad9b941bd1b157d93bfaa29bcb1bc4f39b019 release-0.9.2
-80fb799e57f69d970c77e307d866f14d64c5e481 release-0.9.7.2
-8635df78aaf5eb3ca05f28380e3dc5c136c6566a release-0.8.1
-9488661c4e884ec28784e44da7f70d4917cb46b3 release-0.8.8
-9fc455b9845b892680efc2cfc94d08cbfc0ecaa6 release-0.8.4
-a32c3d617c3c03c5cbaab92d00087162bffbe393 release-0.9.0-pre2
-b08a823827c33811e7741e4d99aafc14c6204987 release-0.8.9
-bac46c2f40104277388c154a4554dabae08b967c release-0.8.3
-d8ccab6cab528c25c4701366094741c2b14ee96f release-0.9.0-pre1
-e985496ab59f788be7ff29b563aee92d2950bc81 release-0.9.6
-ea35d3af1d66baaac26816e1467cad3749c2e996 release-0.8.2
-f523745d15d437c58da48aaaef0b50b62f17216d release-0.9.1
-f71120f89f4a8ded108a98dc4a7a1fc16d3bee58 release-0.9.8
-167d78b81824d3533e64757172a979675f806c34 release-0.9.9
-f445b883a200348fdbfbd025628d4d8a347100ef release-0.9.9.1
-4701bc9ea3f18869ea536cbfec888e99c887a353 release-0.9.9.2
-36a3475de9564133c744f02d8bf4dd1694cc9599 release-1.0.0
-0b46d54a527e8e360274a5cac7c138f20908d679 release-1.0.1
-19012001f1522250d1e4b66541ffc055651f562d release-1.1.0
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..34a8809
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,21 @@
+language: python
+virtualenv:
+ system_site_packages: true
+python:
+ - "2.6"
+ - "2.7"
+ - "3.2"
+ - "3.3"
+ - "pypy"
+before_install:
+ - "sudo apt-get install python-gi python3-gi"
+install:
+ - "pip install --use-mirrors setuptools twisted tornado"
+script:
+ - "DEPS=\"$TRAVIS_PYTHON_VERSION `python bin/deps.py`\"; echo $DEPS"
+ - "[ \"$DEPS\" == '2.6 tornado twisted' -o
+ \"$DEPS\" == '2.7 pygobject tornado twisted' -o
+ \"$DEPS\" == '3.2 pygobject tornado twisted' -o
+ \"$DEPS\" == '3.3 tornado twisted' -o
+ \"$DEPS\" == 'pypy tornado twisted' ]"
+ - "python setup.py test"
diff --git a/CHANGELOG b/CHANGELOG
deleted file mode 100644
index 5115869..0000000
--- a/CHANGELOG
+++ /dev/null
@@ -1,883 +0,0 @@
- -*- coding: utf-8 -*-
-
-Urwid 1.1.0
-
- * New common container API: focus, focus_position, contents,
- options(), get_focus_path(), set_focus_path(), __getitem__,
- __iter__(), __reversed__() implemented across all included
- container widgets
-
- A full description doesn't fit here, see the Container Widgets
- section in the manual for details
-
- * New Sphinx-based documentation now included in source:
- Tutorial rewritten, manual revised and new reference based
- on updated docstrings (by Marco Giusti, Patrick Totzke)
-
- * New list walker SimpleFocusListWalker like SimpleListWalker but
- updates focus position as items are inserted or removed
-
- * New decoration widget WidgetDisable to disable interaction
- with the widgets it wraps
-
- * SelectableIcon selectable text widget used by button widgets is
- now documented (available since 0.9.9)
-
- * Columns widget now tries to keep column in focus visible, hiding
- columns on the left when necessary
-
- * Padding widget now defaults to ('relative', 100) instead of
- 'pack' so that left and right parameters are more useful and more
- child widgets are supported
-
- * New list walker "API Version 2" that is simpler for many list
- walker uses; "API Version 1" will still continue to be supported
-
- * List walkers may now allow iteration from the absolute top or
- bottom of the list if they provide a positions() method
-
- * raw_display now erases to the end of the line with EL escape
- sequence to improve copy+paste behavior for some terminals
-
- * Filler now has top and bottom parameters like Padding's left and
- right parameters and accepts 'pack' instead of None as a height
- value for widgets that calculate their own number of rows
-
- * Pile and Columns now accepts 'pack' instead of 'flow' for widgets
- that calculate their own number of rows or columns
-
- * Pile and Columns now accept 'given' instead of 'fixed' for
- cases where the number of rows or columns are specified by the
- container options
-
- * Pile and Columns widgets now accept any iterable to their
- __init__() methods
-
- * Widget now has a default focus_position property that raises
- an IndexError when read to be consistent with new common container
- API
-
- * GridFlow now supports multiple cell widths within the same widget
-
- * BoxWidget, FlowWidget and FixedWidget are deprecated, instead
- use the sizing() function or _sizing attribute to specify the
- supported sizing modes for your custom widgets
-
- * Some new shift+arrow and numpad input sequences from RXVT and
- xterm are now recognized
-
- * Fix for alarms when used with a screen event loop (e.g.
- curses_display)
-
- * Fix for raw_display when terminal width is 1 column
-
- * Fixes for a Columns.get_cursor_coords() regression and a
- SelectableIcon.get_cursor_coords() bug
-
- * Fixes for incorrect handling of box columns in a number of
- Columns methods when that column is selectable
-
- * Fix for Terminal widget input handling with Python 3
-
-
-Urwid 1.0.2
-
- * Fix for bug when entering Unicode text into Edit widget with
- bytes caption
-
- * Fix a regression when not running in UTF-8 mode
-
- * Fix for a MainLoop.remove_watch_pipe() bug
-
- * Fix for a bug when packing empty Edit widgets
-
- * Fix for a ListBox "contents too long" error with very large
- Edit widgets
-
- * Prevent ListBoxes from selecting 0-height selectable widgets
- when moving up or down
-
- * Fix a number of bugs caused by 0-height widgets in a ListBox
-
-
-Urwid 1.0.1
-
- * Fix for Terminal widget in BSD/OSX
-
- * Fix for a Filler mouse_event() position bug
-
- * Fix support for mouse positions up to x=255, y=255
-
- * Fixes for a number of string encoding issues under Python 3
-
- * Fix for a LineBox border __init__() parameters
-
- * Fix input input of UTF-8 in tour.py example by converting captions
- to unicode
-
- * Fix tutorial examples' use of TextCanvas and switch to using
- unicode literals
-
- * Prevent raw_display from calling tcseattr() or tcgetattr() on
- non-ttys
-
- * Disable curses_display external event loop support: screen resizing
- and gpm events are not properly supported
-
- * Mark PollingListWalker as deprecated
-
-
-Urwid 1.0.0
-
- * New support for Python 3.2 from the same 2.x code base,
- requires distribute instead of setuptools (by Kirk McDonald,
- Wendell, Marien Zwart) everything except TwistedEventLoop and
- GLibEventLoop is supported
-
- * New experimental Terminal widget with xterm emulation and
- terminal.py example program (by aszlig)
-
- * Edit widget now supports a mask (for passwords), has a
- insert_text_result() method for full-field validation and
- normalizes input text to Unicode or bytes based on the caption
- type used
-
- * New TreeWidget, TreeNode, ParentNode, TreeWalker
- and TreeListBox classes for lazy expanding/collapsing tree
- views factored out of browse.py example program, with new
- treesample.py example program (by Rob Lanphier)
-
- * MainLoop now calls draw_screen() just before going idle, so extra
- calls to draw_screen() in user code may now be removed
-
- * New MainLoop.watch_pipe() method for subprocess or threaded
- communication with the process/thread updating the UI, and new
- subproc.py example demonstrating its use
-
- * New PopUpLauncher and PopUpTarget widgets and MainLoop option
- for creating pop-ups and drop-downs, and new pop_up.py example
- program
-
- * New twisted_serve_ssh.py example (by Ali Afshar) that serves
- multiple displays over ssh from the same application using
- Twisted and the TwistedEventLoop
-
- * ListBox now includes a get_cursor_coords() method, allowing
- nested ListBox widgets
-
- * Columns widget contents may now be marked to always be treated
- as flow widgets for mixing flow and box widgets more easily
-
- * New lcd_display module with support for CF635 USB LCD panel and
- lcd_cf635.py example program with menus, slider controls and a custom
- font
-
- * Shared command_map instance is now stored as Widget._command_map
- class attribute and may be overridden in subclasses or individual
- widgets for more control over special keystrokes
-
- * Overlay widget parameters may now be adjusted after creation with
- set_overlay_parameters() method
-
- * New WidgetPlaceholder widget useful for swapping widgets without
- having to manipulate a container widget's contents
-
- * LineBox widgets may now include title text
-
- * ProgressBar text content and alignment may now be overridden
-
- * Use reactor.stop() in TwistedEventLoop and document that Twisted's
- reactor is not designed to be stopped then restarted
-
- * curses_display now supports AttrSpec and external event loops
- (Twisted or GLib) just like raw_display
-
- * raw_display and curses_display now support the IBMPC character
- set (currently only used by Terminal widget)
-
- * Fix for a gpm_mev bug preventing user input when on the console
-
- * Fix for leaks of None objects in str_util extension
-
- * Fix for WidgetWrap and AttrMap not working with fixed widgets
-
- * Fix for a lock up when attempting to wrap text containing wide
- characters into a single character column
-
-
-Urwid 0.9.9.2
-
- * Fix for an Overlay get_cursor_coords(), and Text top-widget bug
-
- * Fix for a Padding rows() bug when used with width=PACK
-
- * Fix for a bug with large flow widgets used in an Overlay
-
- * Fix for a gpm_mev bug
-
- * Fix for Pile and GraphVScale when rendered with no contents
-
- * Fix for a Python 2.3 incompatibility (0.9.9 is the last release
- to claim support Python 2.3)
-
-
-Urwid 0.9.9.1
-
- * Fix for ListBox snapping to selectable widgets taller than the
- ListBox itself
-
- * raw_display switching to alternate buffer now works properly with
- Terminal.app
-
- * Fix for BoxAdapter backwards incompatibility introduced in 0.9.9
-
- * Fix for a doctest failure under powerpc
-
- * Fix for systems with gpm_mev installed but not running gpm
-
-
-Urwid 0.9.9
-
- * New support for 256 and 88 color terminals with raw_display
- and html_fragment display modules
-
- * New palette_test example program to demonstrate high color
- modes
-
- * New AttrSpec class for specifying specific colors instead of
- using attributes defined in the screen's palette
-
- * New MainLoop class ties together widgets, user input, screen
- display and one of a number of new event loops, removing the
- need for tedious, error-prone boilerplate code
-
- * New GLibEventLoop allows running Urwid applications with GLib
- (makes D-Bus integration easier)
-
- * New TwistedEventLoop allows running Urwid with a Twisted reactor
-
- * Added new docstrings and doctests to many widget classes
-
- * New AttrMap widget supports mapping any attribute to any other
- attribute, replaces AttrWrap widget
-
- * New WidgetDecoration base class for AttrMap, BoxAdapter, Padding,
- Filler and LineBox widgets creates a common method for accessing
- and updating their contained widgets
-
- * New left and right values may be specified in Padding widgets
-
- * New command_map for specifying which keys cause actions such as
- clicking Button widgets and scrolling ListBox widgets
-
- * New tty_signal_keys() method of raw_display.Screen and
- curses_display.Screen allows changing or disabling the keys used
- to send signals to the application
-
- * Added helpful __repr__ for many widget classes
-
- * Updated all example programs to use MainLoop class
-
- * Updated tutorial with MainLoop usage and improved examples
-
- * Renamed WidgetWrap.w to _w, indicating its intended use as a way
- to implement a widget with other widgets, not necessarily as
- a container for other widgets
-
- * Replaced all tabs with 4 spaces, code is now more aerodynamic
- (and PEP 8 compliant)
-
- * Added saving of stdin and stdout in raw_display module allowing
- the originals to be redirected
-
- * Updated BigText widget's HalfBlock5x4Font
-
- * Fixed graph example CPU usage when animation is stopped
-
- * Fixed a memory leak related to objects listening for signals
-
- * Fixed a Popen3 deprecation warning
-
-
-Urwid 0.9.8.4
-
- * Fixed incompatibilities with Python 2.6 (by Friedrich Weber)
-
- * Fixed a SimpleListWalker with emptied list bug (found by Walter
- Mundt)
-
- * Fixed a curses_display stop()/start() bug (found by Christian
- Scharkus)
-
- * Fixed an is_wide_character() segfault on bad input data bug
- (by Andrew Psaltis)
-
- * Fixed a CanvasCache with render() used in both a widget and its
- superclass bug (found by Andrew Psaltis)
-
- * Fixed a ListBox.ends_visible() on empty list bug (found by Marc
- Hartstein)
-
- * Fixed a tutorial example bug (found by Kurtis D. Rader)
-
- * Fixed an Overlay.keypress() bug (found by Andreas Klöckner)
-
- * Fixed setuptools configuration (by Andreas Klöckner)
-
-
-Urwid 0.9.8.3
-
- * Fixed a canvas cache memory leak affecting 0.9.8, 0.9.8.1 and
- 0.9.8.2 (found by John Goodfellow)
-
- * Fixed a canvas fill_attr() bug (found by Joern Koerner)
-
-
-Urwid 0.9.8.2
-
- * Fixed incompatibilities with Python 2.3
-
- * Fixed Pile cursor pref_col bug, WidgetWrap rows caching bug, Button
- mouse_event with no callback bug, Filler body bug triggered by the
- tutorial and a LineBox lline parameter typo.
-
-
-Urwid 0.9.8.1
-
- * Fixed a Filler render() bug, a raw_display start()/stop() bug and a
- number of problems triggered by very small terminal window sizes.
-
-
-Urwid 0.9.8
-
- * Rendering is now significantly faster.
-
- * New Widget base class for all widgets. It includes automatic caching
- of rows() and render() methods. It also adds a new __super attribute
- for accessing methods in superclasses.
-
- Widgets must now call self._invalidate() to notify the cache when
- their content has changed.
-
- To disable caching in a widget set the class variable no_cache to a
- list that includes the string "render".
-
- * Canvas classes have been reorganized: Canvas has been renamed to
- TextCanvas and Canvas is now the base class for all canvases. New
- canvas classes include BlankCanvas, SolidCanvas and CompositeCanvas.
-
- * External event loops may now be used with the raw_display module. The
- new methods get_input_descriptors() and get_input_nonblocking()
- should be used instead of get_input() to allow input processing
- without blocking.
-
- * The Columns, Pile and ListBox widgets now choose their first
- selectable child widget as the focus widget by defaut.
-
- * New ListWalker base class for list walker classes.
-
- * New Signals class that will be used to improve the existing event
- callbacks. Currently it is used for ListWalker objects to notify
- their ListBox when their content has changed.
-
- * SimpleListWalker now behaves as a list and supports all list
- operations. This class now detects when changes are made to the list
- and notifies the ListBox object. New code should use this class to
- wrap lists of widgets before passing them to the ListBox
- constructor.
-
- * New PollingListWalker class is now the default list walker that is
- used when passing a simple list to the ListBox constructor. This
- class is intended for backwards compatibility only. When this class
- is used the ListBox object is unable to cache its render() method.
-
- * The curses_display module can now draw in the lower-right corner of
- the screen.
-
- * All display modules now have start() and stop() methods that may be
- used instead of calling run_wrapper().
-
- * The raw_display module now uses an alternate buffer so that the
- original screen can be restored on exit. The old behaviour is
- available by seting the alternate_buffer parameter of start() or
- run_wrapper() to False.
-
- * Many internal string processing functions have been rewritten in C to
- improve their performance.
-
- * Compatible with Python >= 2.2. Python 2.1 is no longer supported.
-
-
-Urwid 0.9.7.2
-
- * Improved performance in UTF-8 mode when ASCII text is used.
-
- * Fixed a UTF-8 input bug.
-
- * Added a clear() function to the the display modules to force the
- screen to be repainted on the next draw_screen() call.
-
-
-Urwid 0.9.7.1
-
- * Fixed bugs in Padding and Overlay widgets introduced in 0.9.7.
-
-
-Urwid 0.9.7
-
- * Added initial support for fixed widgets - widgets that have a fixed
- size on screen. Fixed widgets expect a size parameter equal to ().
- Fixed widgets must implement the pack(..) function to return their
- size.
-
- * New BigText class that draws text with fonts made of grids of
- character cells. BigText is a fixed widget and doesn't do any
- alignment or wrapping. It is intended for banners and number readouts
- that need to stand out on the screen.
-
- Fonts: Thin3x3Font, Thin4x3Font, Thin6x6Font (full ascii)
-
- UTF-8 only fonts: HalfBlock5x4Font, HalfBlock6x5Font,
- HalfBlockHeavy6x5Font, HalfBlock7x7Font (full ascii)
-
- New function get_all_fonts() may be used to get a list of the
- available fonts.
-
- * New example program bigtext.py demonstrates use of BigText.
-
- * Padding class now has a clipping mode that pads or clips fixed
- widgets to make them behave as flow widgets.
-
- * Overlay class can now accept a fixed widget as the widget to display
- "on top".
-
- * New Canvas functions: pad_trim() and pad_trim_left_right().
-
- * Fixed a bug in Filler.get_cursor_coords() that causes a crash if the
- contained widget's get_cursor_coords() function returns None.
-
- * Fixed a bug in Text.pack() that caused an infinite loop when the text
- contained a newline. This function is not currently used by Urwid.
-
- * Edit.__init__() now calls set_edit_text() to initialize its text.
-
- * Overlay.calculate_padding_filler() and Padding.padding_values() now
- include focus parameters.
-
-
-Urwid 0.9.6
-
- * Fixed Unicode conversion and locale issues when using Urwid with
- Python < 2.4. The graph.py example program should now work properly
- with older versions of Python.
-
- * The docgen_tutorial.py script can now write out the tutorial example
- programs as individual files.
-
- * Updated reference documentation table of contents to show which
- widgets are flow and/or box widgets.
-
- * Columns.set_focus(..) will now accept an integer or a widget as its
- parameter.
-
- * Added detection for rxvt's HOME and END escape sequences.
-
- * Added support for setuptools (improved distutils).
-
-
-Urwid 0.9.5
-
- * Some Unicode characters are now converted to use the G1 alternate
- character set with DEC special and line drawing characters. These
- Unicode characters should now "just work" in almost all terminals and
- encodings.
-
- When Urwid is run with the UTF-8 encoding the characters are left as
- UTF-8 and not converted.
-
- The characters converted are:
-
- \u00A3 (£), \u00B0 (°), \u00B1 (±), \u00B7 (·), \u03C0 (π),
- \u2260 (≠), \u2264 (≤), \u2265 (≥), \u23ba (⎺), \u23bb (⎻),
- \u23bc (⎼), \u23bd (⎽), \u2500 (─), \u2502 (│), \u250c (┌),
- \u2510 (┐), \u2514 (└), \u2518 (┘), \u251c (├), \u2524 (┤),
- \u252c (┬), \u2534 (┴), \u253c (┼), \u2592 (▒), \u25c6 (◆)
-
- * New SolidFill class for filling an area with a single character.
-
- * New LineBox class for wrapping widgets in a box made of line- drawing
- characters. May be used as a box widget or a flow widget.
-
- * New example program graph.py demonstrates use of BarGraph, LineBox,
- ProgressBar and SolidFill.
-
- * Pile class may now be used as a box widget and contain a mix of box
- and flow widgets.
-
- * Columns class may now contain a mix of box and flow widgets. The box
- widgets will take their height from the maximum height of the flow
- widgets.
-
- * Improved the smoothness of resizing with raw_display module. The
- module will now try to stop updating the screen when a resize event
- occurs during the update.
-
- * The Edit and IntEdit classes now use their set_edit_text() and
- set_edit_pos() functions when handling keypresses, so those functions
- may be overridden to catch text modification.
-
- * The set_state() functions in the CheckBox and RadioButton classes now
- have a do_callback parameter that determines if the callback function
- registered will be called.
-
- * Fixed a newly introduced incompatibility with python < 2.3.
-
- * Fixed a missing symbol in curses_display when python is linked
- against libcurses.
-
- * Fixed mouse handling bugs in the Frame and Overlay classes.
-
- * Fixed a Padding bug when the left or right has no padding.
-
-
-Urwid 0.9.4
-
- * Enabled mouse handling across the Urwid library.
-
- Added a new mouse_event() method to the Widget interface definition
- and to the following widgets: Edit, CheckBox, RadioButton, Button,
- GridFlow, Padding, Filler, Overlay, Frame, Pile, Columns, BoxAdapter
- and ListBox.
-
- Updated example programs browse.py, calc.py, dialog.py, edit.py and
- tour.py to support mouse input.
-
- * Released the files used to generate the reference and tutorial
- documentation: docgen_reference.py, docgen_tutorial.py and
- tmpl_tutorial.html. The "docgen" scripts write the documentation to
- stdout. docgen_tutorial.py requires the Templayer HTML templating
- library to run: http://excess.org/templayer/
-
- * Improved Widget and List Walker interface documentation.
-
- * Fixed a bug in the handling of invalid UTF-8 data. All invalid
- characters are now replaced with '?' characters when displayed.
-
-
-Urwid 0.9.3
-
- * Improved mouse reporting.
-
- The raw_display module now detects gpm mouse events by reading
- /usr/bin/mev output. The curses_display module already supports gpm
- directly.
-
- Mouse drag events are now reported by raw_display in terminals that
- provide button event tracking and on the console with gpm. Note that
- gpm may report coordinates off the screen if the user drags the mouse
- off the edge.
-
- Button release events now report which button was released if that
- information is available, currently only on the console with gpm.
-
- * Added display of raw keycodes to the input_test.py example program.
-
- * Fixed a text layout bug affecting clipped text with blank lines, and
- another related to wrapped text starting with a space character.
-
- * Fixed a Frame.keypress() bug that caused it to call keypress on
- unselectable widgets.
-
-
-Urwid 0.9.2
-
- * Preliminary mouse support was added to the raw_display and
- curses_display modules. A new Screen.set_mouse_tracking() method was
- added to enable mouse tracking. Mouse events are returned alongside
- keystrokes from the Screen.get_input() method.
-
- The widget interface does not yet include mouse handling. This will
- be addressed in the next release.
-
- * A new convenience function is_mouse_event() was added to help in
- separating mouse events from keystrokes.
-
- * Added a new example program input_test.py. This program displays the
- keyboard and mouse input it receives. It may be run as a CGI script
- or from the command line. On the command line it defaults to using
- the curses_display module, use input_test.py raw to use the
- raw_display module instead.
-
- * Fixed an Edit.render() bug that caused it to render the cursor in a
- different location than that reported by Edit.get_cursor_coords() in
- some circumstances.
-
- * Fixed a bug preventing use of UTF-8 characters with Divider widgets.
-
-
-Urwid 0.9.1
-
- * BarGraph and ProgressBar can now display data more accurately by
- using the UTF-8 vertical and horizontal eighth characters. This
- behavior will be enabled when the UTF-8 encoding is detected and
- "smoothed" attributes are passed to the BarGraph or ProgressBar
- constructors.
-
- * New get_encoding_mode() function to determine how Urwid will treat
- raw string data.
-
- * New raw_display.signal_init() and raw_display.signal_restore()
- methods that may be overridden by threaded applications that need to
- call signal.signal() from their main thread.
-
- * Fixed a bug that prevented the use of UTF-8 strings in text markup.
-
- * Removed some forgotten asserts that broke 8-bit and CJK input.
-
-
-Urwid 0.9.0
-
- * New support for UTF-8 encoding including input, display and editing
- of narrow and wide (CJK) characters.
-
- Preliminary combining (zero-width) character support is included, but
- full support will require terminal behavior detection.
-
- Right-to-Left input and display are not implemented.
-
- * New raw_display module that handles console display without relying
- on external libraries. This module was written as a work around for
- the lack of UTF-8 support in the standard version of ncurses.
-
- Eliminates "dead corner" in the bottom right of the screen.
-
- Avoids use of bold text in xterm and gnome-terminal for improved
- text legibility.
-
- * Fixed Overlay bug related to UTF-8 handling.
-
- * Fixed Edit.move_cursor_to_coords(..) bug related to wide characters
- in UTF-8 encoding.
-
-
-Urwid 0.9.0-pre3
-
- * Fixed Canvas attribute padding bug related to -pre1 changes.
-
-
-Urwid 0.9.0-pre2
-
- * Replaced the custom align and wrap modes in example program calc.py
- with a new layout class.
-
- * Fixed Overlay class call to Canvas.overlay() broken by -pre1 changes.
-
- * Fixed Padding bug related to Canvas -pre1 changes.
-
-
-Urwid 0.9.0-pre1
-
- * New support for UTF-8 encoding. Unicode strings may be used and will
- be converted to the current encoding when output. Regular strings in
- the current encoding may still be used.
-
- PLEASE NOTE: There are issues related to displaying UTF-8 characters
- with the curses_display module that have not yet been resolved.
-
- * New set_encoding() function replaces util.set_double_byte_encoding().
-
- * New supports_unicode() function to query if unicode strings with
- characters outside the ascii range may be used with the current
- encoding.
-
- * New TextLayout and StandardTextLayout classes to perform text
- wrapping and alignment. Text widgets now have a layout parameter to
- allow use of custom TextLayout objects.
-
- * New layout structure replaces line translation structure. Layout
- structure now allows arbitrary reordering/positioning of text
- segments, inclusion of UTF-8 characters and insertion of text not
- found in the original text string.
-
- * Removed util.register_align_mode() and util.register_wrap_mode().
- Their functionality has been replaced by the new layout classes.
-
-
-Urwid 0.8.10
-
- * Expanded tutorial to cover advanced ListBox usage, custom widget
- classes and the Pile, BoxAdapter, Columns, GridFlow and Overlay
- classes.
-
- * Added escape sequence for "shift tab" to curses_display.
-
- * Added ListBox.set_focus_valign() to allow positioning of the focus
- widget within the ListBox.
-
- * Added WidgetWrap class for extending existing widgets without
- inheriting their complete namespace.
-
- * Fixed web_display/mozilla breakage from 0.8.9. Fixed crash on invalid
- locale setting. Fixed ListBox slide-back bug. Fixed improper space
- trimming in calculate_alignment(). Fixed browse.py example program
- rows bug. Fixed sum definition, use of long ints for python2.1. Fixed
- warnings with python2.1. Fixed Padding.get_pref_col() bug. Fixed
- Overlay splitting CJK characters bug.
-
-
-Urwid 0.8.9
-
- * New Overlay class for drawing widgets that obscure parts of other
- widgets. May be used for drop down menus, combo boxes, overlapping
- "windows", caption text etc.
-
- * New BarGraph, GraphVScale and ProgressBar classes for graphical
- display of data in Urwid applications.
-
- * New method for configuring keyboard input timeouts and delays:
- curses_display.Screen.set_input_timeouts().
-
- * Fixed a ListBox.set_focus() bug.
-
-
-Urwid 0.8.8
-
- * New web_display module that emulates a console display within a web
- browser window. Application must be run as a CGI script under Apache.
-
- Supports font/window resizing, keepalive for long-lived connections,
- limiting maximum concurrent connections, polling and connected update
- methods. Tested with Mozilla Firefox and Internet Explorer.
-
- * New BoxAdapter class for using box widgets in places that usually
- expect flow widgets.
-
- * New curses_display input handling with better ESC key detection and
- broader escape code support.
-
- * Shortened resize timeout on gradual resize to improve responsiveness.
-
-
-Urwid 0.8.7
-
- * New widget classes: Button, RadioButton, CheckBox.
-
- * New layout widget classes: Padding, GridFlow.
-
- * New dialog.py example program that behaves like dialog(1) command.
-
- * Pile widgets now support selectable items, focus changing with up and
- down keys and setting the cursor position.
-
- * Frame widgets now support selectable items in the header and footer.
-
- * Columns widgets now support fixed width and relative width columns, a
- minimum width for all columns, selectable items within columns
- containing flow widgets (already supported for box widgets), focus
- changing with left and right keys and setting the cursor position.
-
- * Filler widgets may now wrap box widgets and have more alignment options.
-
- * Updated tour.py example program to show new widget types and
- features.
-
- * Avoid hogging cpu on gradual window resize and fix for slow resize
- with cygwin's broken curses implementation.
-
- * Fixed minor CJK problem and curs_set() crash under MacOSX and Cygwin.
-
- * Fixed crash when deleting cells in calc.py example program.
-
-
-Urwid 0.8.6
-
- * Improved support for CJK double-byte encodings: BIG5, UHC, GBK,
- GB2312, CN-GB, EUC-KR, EUC-CN, EUC-JP (JISX 0208 only) and EUC-TW
- (CNS 11643 plain 1 only)
-
- * Added support for ncurses' use_default_colors() function to
- curses_display module (Python >= 2.4).
-
- register_palette() and register_palette_entry() now accept "default"
- as foreground and/or background. If the terminal's default attributes
- cannot be detected black on light gray will be used to accomodate
- terminals with always-black cursors.
-
- "default" is now the default for text with no attributes. This means
- that areas with no attributes will change from light grey on black
- (curses default) to black on light gray or the terminal's default.
-
- * Modified examples to not use black as background of Edit widgets.
-
- * Fixed curses_display curs_set() call so that cursor is hidden when
- widget in focus has no cursor position.
-
-
-Urwid 0.8.5
-
- * New tutorial covering basic operation of: curses_display.Screen,
- Canvas, Text, FlowWidget, Filler, BoxWidget, AttrWrap, Edit, ListBox
- and Frame classes
-
- * New widget class: Filler
-
- * New ListBox functions: get_focus(), set_focus()
-
- * Debian packages for Python 2.4.
-
- * Fixed curses_display bug affecting text with no attributes.
-
-
-Urwid 0.8.4
-
- * Improved support for Cyrillic and other simple 8-bit encodings.
-
- * Added new functions to simplify taking screenshots:
- html_fragment.screenshot_init() and
- html_fragment.screenshot_collect()
-
- * Improved urwid/curses_display.py input debugging
-
- * Fixed cursor in screenshots of CJK text. Fixed "end" key in Edit
- boxes with CJK text.
-
-
-Urwid 0.8.3
-
- * Added support for CJK double-byte encodings.
-
- Word wrapping mode "space" will wrap on edges of double width
- characters. Wrapping and clipping will not split double width
- characters.
-
- curses_display.Screen.get_input() may now return double width
- characters. Text and Edit classes will work with a mix of regular and
- double width characters.
-
- * Use new method Edit.set_edit_text() instead of Edit.update_text().
-
- * Minor improvements to edit.py example program.
-
-
-Urwid 0.8.2
-
- * Re-released under GNU Lesser General Public License.
-
-
-Urwid 0.8.1
-
- * Added support for monochrome terminals. see
- curses_display.Screen.register_palette_entry() and example programs.
- set TERM=xterm-mono to test programs in monochrome mode.
-
- * Added unit testing code test_urwid.py to the examples.
-
- * Can now run urwid/curses_display.py to test your terminal's input and
- colour rendering.
-
- * Fixed an OSX browse.py compatibility issue. Added some OSX keycodes.
-
-
-Urwid 0.8.0
-
- * Initial Release
diff --git a/MANIFEST.in b/MANIFEST.in
index 597296f..796f967 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,3 @@
recursive-include examples *.py *.tac
recursive-include docs *.rst *.py *.py.xdotool *.png *.html *.sh Makefile
-include COPYING CHANGELOG
+include COPYING CHANGELOG README.rst
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..cfc4af8
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,24 @@
+.. image:: https://travis-ci.org/wardi/urwid.png?branch=master
+ :alt: build status
+ :target: https://travis-ci.org/wardi/urwid/
+
+`Development version documentation <http://urwid.readthedocs.org/en/latest/>`_
+
+.. content-start
+
+Urwid is a console user interface library for Python.
+It includes many features useful for text console application developers including:
+
+- Applcations resize quickly and smoothly
+- Automatic, programmable text alignment and wrapping
+- Simple markup for setting text attributes within blocks of text
+- Powerful list box with programmable content for scrolling all widget types
+- Your choice of event loops: Twisted, Glib, Tornado or select-based loop
+- Pre-built widgets include edit boxes, buttons, check boxes and radio buttons
+- Display modules include raw, curses, and experimental LCD and web displays
+- Support for UTF-8, simple 8-bit and CJK encodings
+- 256 and 88 color mode support
+- Compatible with Python 2.6, 2.7, 3.2+ and PyPy
+
+Home Page:
+ http://urwid.org/
diff --git a/bin/build-pages.sh b/bin/build-pages.sh
new file mode 100755
index 0000000..6e27158
--- /dev/null
+++ b/bin/build-pages.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -e
+
+cd "`dirname $0`/.."
+rm -r build/sphinx || true
+python setup.py build_sphinx
+
+git checkout gh-pages
+git rm `git ls-files`
+git checkout HEAD CNAME
+git checkout HEAD .nojekyll
+cp -r build/sphinx/html/. .
+git add `find build/sphinx/html | cut -c 19-`
+git status
diff --git a/bin/deps.py b/bin/deps.py
new file mode 100755
index 0000000..01489d6
--- /dev/null
+++ b/bin/deps.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+"""
+show optional Urwid dependencies installed
+"""
+deps = []
+
+try:
+ import gi.repository
+ deps.append("pygobject")
+except ImportError:
+ pass
+
+try:
+ import tornado
+ deps.append("tornado")
+except ImportError:
+ pass
+
+try:
+ import twisted
+ deps.append("twisted")
+except ImportError:
+ pass
+
+print(" ".join(deps))
diff --git a/docs/Makefile b/docs/Makefile
index 9c9ef20..7ec3bdb 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -1,5 +1,5 @@
-PYTHON_SOURCE=$(shell echo tutorial/*.py.xdotool)
+PYTHON_SOURCE=$(shell echo */*.py.xdotool)
TARGETS=${PYTHON_SOURCE:.py.xdotool=1.png}
TOOLS=tools/compile_pngs.sh tools/screenshots.sh
diff --git a/docs/changelog.rst b/docs/changelog.rst
new file mode 100644
index 0000000..0bfb7c5
--- /dev/null
+++ b/docs/changelog.rst
@@ -0,0 +1,1095 @@
+
+Changelog
+---------
+
+
+Urwid 1.2.1
+===========
+
+2014-04-04
+
+ * Fix false failures of event loop tests
+
+ * Remove extra newline generated on exit of raw_display
+
+ * Documentation fixes (by Paul Ivanov)
+
+
+Urwid 1.2.0
+===========
+
+2014-02-09
+
+ * Add support for PyPy, drop support for Python 2.4, 2.5
+
+ * Signals now support using weakly referenced arguments to help
+ avoid leaking objects when a signal consumer is no longer
+ referenced (by Matthijs Kooijman)
+
+ * Add TornadoEventLoop class (by Alexander Glyzov)
+
+ * Update GlibEventLoop to use python-gi for Python3 compatibility
+ (by Israel Garcia)
+
+ * Automate testing with Python 2.6, 2.7, 3.2, 3.3 and PyPy using
+ travis-ci
+
+ * New container method get_focus_widgets() (by Matthijs Kooijman)
+
+ * Add support for double and triple click mouse events
+ (by Igor Kotrasiński)
+
+ * Allow disabling and re-enabling of mouse tracking
+ (by Jim Garrison)
+
+ * Create section in docs for example program screenshots generated
+ as images like the tutorial examples
+
+ * Add suggested basic color combination images to manual
+
+ * Fall back to 80x24 if screen size detection fails
+
+ * Fix screen.stop(), screen.start() disabling mouse events
+
+ * Fix to make GridFlow v_sep argument behave as documented
+
+ * Fix for registering high palette entries in the form "hX" where
+ X > 15 so that basic colors are applied in 88-color mode
+
+ * Fix for raw_display clear-right escape not working with
+ standout attribute on some terminals
+
+ * Fix for Terminal widget select loop: retry when interrupted
+
+
+Urwid 1.1.2
+===========
+
+2013-12-30
+
+ * Move to urwid.org and use sphinx docs for generating whole site,
+ move changelog to docs/changelog.rst
+
+ * Fix encoding exceptions when unicode used on non-UTF-8 terminal
+
+ * Fix for suspend and resume applications with ^Z
+
+ * Fix for tmux and screen missing colors on right bug
+
+ * Fix Pile zero-weighted items and mouse_event when empty
+
+ * Fix Terminal select() not retrying when interrupted by signal
+
+ * Fix for Padding.align and width change not invalidating
+
+
+Urwid 1.1.1
+===========
+
+2012-11-15
+
+ * Fix for Pile not changing focus on mouse events
+
+ * Fix for Overlay.get_cursor_coords()
+
+
+Urwid 1.1.0
+===========
+
+2012-10-23
+
+ * New common container API: focus, focus_position, contents,
+ options(), get_focus_path(), set_focus_path(), __getitem__,
+ __iter__(), __reversed__() implemented across all included
+ container widgets
+
+ A full description doesn't fit here, see the Container Widgets
+ section in the manual for details
+
+ * New Sphinx-based documentation now included in source:
+ Tutorial rewritten, manual revised and new reference based
+ on updated docstrings (by Marco Giusti, Patrick Totzke)
+
+ * New list walker SimpleFocusListWalker like SimpleListWalker but
+ updates focus position as items are inserted or removed
+
+ * New decoration widget WidgetDisable to disable interaction
+ with the widgets it wraps
+
+ * SelectableIcon selectable text widget used by button widgets is
+ now documented (available since 0.9.9)
+
+ * Columns widget now tries to keep column in focus visible, hiding
+ columns on the left when necessary
+
+ * Padding widget now defaults to ('relative', 100) instead of
+ 'pack' so that left and right parameters are more useful and more
+ child widgets are supported
+
+ * New list walker "API Version 2" that is simpler for many list
+ walker uses; "API Version 1" will still continue to be supported
+
+ * List walkers may now allow iteration from the absolute top or
+ bottom of the list if they provide a positions() method
+
+ * raw_display now erases to the end of the line with EL escape
+ sequence to improve copy+paste behavior for some terminals
+
+ * Filler now has top and bottom parameters like Padding's left and
+ right parameters and accepts 'pack' instead of None as a height
+ value for widgets that calculate their own number of rows
+
+ * Pile and Columns now accepts 'pack' instead of 'flow' for widgets
+ that calculate their own number of rows or columns
+
+ * Pile and Columns now accept 'given' instead of 'fixed' for
+ cases where the number of rows or columns are specified by the
+ container options
+
+ * Pile and Columns widgets now accept any iterable to their
+ __init__() methods
+
+ * Widget now has a default focus_position property that raises
+ an IndexError when read to be consistent with new common container
+ API
+
+ * GridFlow now supports multiple cell widths within the same widget
+
+ * BoxWidget, FlowWidget and FixedWidget are deprecated, instead
+ use the sizing() function or _sizing attribute to specify the
+ supported sizing modes for your custom widgets
+
+ * Some new shift+arrow and numpad input sequences from RXVT and
+ xterm are now recognized
+
+ * Fix for alarms when used with a screen event loop (e.g.
+ curses_display)
+
+ * Fix for raw_display when terminal width is 1 column
+
+ * Fixes for a Columns.get_cursor_coords() regression and a
+ SelectableIcon.get_cursor_coords() bug
+
+ * Fixes for incorrect handling of box columns in a number of
+ Columns methods when that column is selectable
+
+ * Fix for Terminal widget input handling with Python 3
+
+
+Urwid 1.0.3
+===========
+
+2012-11-15
+
+ * Fix for alarms when used with a screen event loop (e.g.
+ curses_display)
+
+ * Fix for Overlay.get_cursor_coords()
+
+
+Urwid 1.0.2
+===========
+
+2012-07-13
+
+ * Fix for bug when entering Unicode text into Edit widget with
+ bytes caption
+
+ * Fix a regression when not running in UTF-8 mode
+
+ * Fix for a MainLoop.remove_watch_pipe() bug
+
+ * Fix for a bug when packing empty Edit widgets
+
+ * Fix for a ListBox "contents too long" error with very large
+ Edit widgets
+
+ * Prevent ListBoxes from selecting 0-height selectable widgets
+ when moving up or down
+
+ * Fix a number of bugs caused by 0-height widgets in a ListBox
+
+
+Urwid 1.0.1
+===========
+
+2011-11-28
+
+ * Fix for Terminal widget in BSD/OSX
+
+ * Fix for a Filler mouse_event() position bug
+
+ * Fix support for mouse positions up to x=255, y=255
+
+ * Fixes for a number of string encoding issues under Python 3
+
+ * Fix for a LineBox border __init__() parameters
+
+ * Fix input input of UTF-8 in tour.py example by converting captions
+ to unicode
+
+ * Fix tutorial examples' use of TextCanvas and switch to using
+ unicode literals
+
+ * Prevent raw_display from calling tcseattr() or tcgetattr() on
+ non-ttys
+
+ * Disable curses_display external event loop support: screen resizing
+ and gpm events are not properly supported
+
+ * Mark PollingListWalker as deprecated
+
+
+Urwid 1.0.0
+===========
+
+2011-09-22
+
+ * New support for Python 3.2 from the same 2.x code base,
+ requires distribute instead of setuptools (by Kirk McDonald,
+ Wendell, Marien Zwart) everything except TwistedEventLoop and
+ GLibEventLoop is supported
+
+ * New experimental Terminal widget with xterm emulation and
+ terminal.py example program (by aszlig)
+
+ * Edit widget now supports a mask (for passwords), has a
+ insert_text_result() method for full-field validation and
+ normalizes input text to Unicode or bytes based on the caption
+ type used
+
+ * New TreeWidget, TreeNode, ParentNode, TreeWalker
+ and TreeListBox classes for lazy expanding/collapsing tree
+ views factored out of browse.py example program, with new
+ treesample.py example program (by Rob Lanphier)
+
+ * MainLoop now calls draw_screen() just before going idle, so extra
+ calls to draw_screen() in user code may now be removed
+
+ * New MainLoop.watch_pipe() method for subprocess or threaded
+ communication with the process/thread updating the UI, and new
+ subproc.py example demonstrating its use
+
+ * New PopUpLauncher and PopUpTarget widgets and MainLoop option
+ for creating pop-ups and drop-downs, and new pop_up.py example
+ program
+
+ * New twisted_serve_ssh.py example (by Ali Afshar) that serves
+ multiple displays over ssh from the same application using
+ Twisted and the TwistedEventLoop
+
+ * ListBox now includes a get_cursor_coords() method, allowing
+ nested ListBox widgets
+
+ * Columns widget contents may now be marked to always be treated
+ as flow widgets for mixing flow and box widgets more easily
+
+ * New lcd_display module with support for CF635 USB LCD panel and
+ lcd_cf635.py example program with menus, slider controls and a custom
+ font
+
+ * Shared command_map instance is now stored as Widget._command_map
+ class attribute and may be overridden in subclasses or individual
+ widgets for more control over special keystrokes
+
+ * Overlay widget parameters may now be adjusted after creation with
+ set_overlay_parameters() method
+
+ * New WidgetPlaceholder widget useful for swapping widgets without
+ having to manipulate a container widget's contents
+
+ * LineBox widgets may now include title text
+
+ * ProgressBar text content and alignment may now be overridden
+
+ * Use reactor.stop() in TwistedEventLoop and document that Twisted's
+ reactor is not designed to be stopped then restarted
+
+ * curses_display now supports AttrSpec and external event loops
+ (Twisted or GLib) just like raw_display
+
+ * raw_display and curses_display now support the IBMPC character
+ set (currently only used by Terminal widget)
+
+ * Fix for a gpm_mev bug preventing user input when on the console
+
+ * Fix for leaks of None objects in str_util extension
+
+ * Fix for WidgetWrap and AttrMap not working with fixed widgets
+
+ * Fix for a lock up when attempting to wrap text containing wide
+ characters into a single character column
+
+
+Urwid 0.9.9.2
+=============
+
+2011-07-13
+
+ * Fix for an Overlay get_cursor_coords(), and Text top-widget bug
+
+ * Fix for a Padding rows() bug when used with width=PACK
+
+ * Fix for a bug with large flow widgets used in an Overlay
+
+ * Fix for a gpm_mev bug
+
+ * Fix for Pile and GraphVScale when rendered with no contents
+
+ * Fix for a Python 2.3 incompatibility (0.9.9 is the last release
+ to claim support Python 2.3)
+
+
+Urwid 0.9.9.1
+=============
+
+2010-01-25
+
+ * Fix for ListBox snapping to selectable widgets taller than the
+ ListBox itself
+
+ * raw_display switching to alternate buffer now works properly with
+ Terminal.app
+
+ * Fix for BoxAdapter backwards incompatibility introduced in 0.9.9
+
+ * Fix for a doctest failure under powerpc
+
+ * Fix for systems with gpm_mev installed but not running gpm
+
+
+Urwid 0.9.9
+===========
+
+2009-11-15
+
+ * New support for 256 and 88 color terminals with raw_display
+ and html_fragment display modules
+
+ * New palette_test example program to demonstrate high color
+ modes
+
+ * New AttrSpec class for specifying specific colors instead of
+ using attributes defined in the screen's palette
+
+ * New MainLoop class ties together widgets, user input, screen
+ display and one of a number of new event loops, removing the
+ need for tedious, error-prone boilerplate code
+
+ * New GLibEventLoop allows running Urwid applications with GLib
+ (makes D-Bus integration easier)
+
+ * New TwistedEventLoop allows running Urwid with a Twisted reactor
+
+ * Added new docstrings and doctests to many widget classes
+
+ * New AttrMap widget supports mapping any attribute to any other
+ attribute, replaces AttrWrap widget
+
+ * New WidgetDecoration base class for AttrMap, BoxAdapter, Padding,
+ Filler and LineBox widgets creates a common method for accessing
+ and updating their contained widgets
+
+ * New left and right values may be specified in Padding widgets
+
+ * New command_map for specifying which keys cause actions such as
+ clicking Button widgets and scrolling ListBox widgets
+
+ * New tty_signal_keys() method of raw_display.Screen and
+ curses_display.Screen allows changing or disabling the keys used
+ to send signals to the application
+
+ * Added helpful __repr__ for many widget classes
+
+ * Updated all example programs to use MainLoop class
+
+ * Updated tutorial with MainLoop usage and improved examples
+
+ * Renamed WidgetWrap.w to _w, indicating its intended use as a way
+ to implement a widget with other widgets, not necessarily as
+ a container for other widgets
+
+ * Replaced all tabs with 4 spaces, code is now more aerodynamic
+ (and PEP 8 compliant)
+
+ * Added saving of stdin and stdout in raw_display module allowing
+ the originals to be redirected
+
+ * Updated BigText widget's HalfBlock5x4Font
+
+ * Fixed graph example CPU usage when animation is stopped
+
+ * Fixed a memory leak related to objects listening for signals
+
+ * Fixed a Popen3 deprecation warning
+
+
+Urwid 0.9.8.4
+=============
+
+2009-03-13
+
+ * Fixed incompatibilities with Python 2.6 (by Friedrich Weber)
+
+ * Fixed a SimpleListWalker with emptied list bug (found by Walter
+ Mundt)
+
+ * Fixed a curses_display stop()/start() bug (found by Christian
+ Scharkus)
+
+ * Fixed an is_wide_character() segfault on bad input data bug
+ (by Andrew Psaltis)
+
+ * Fixed a CanvasCache with render() used in both a widget and its
+ superclass bug (found by Andrew Psaltis)
+
+ * Fixed a ListBox.ends_visible() on empty list bug (found by Marc
+ Hartstein)
+
+ * Fixed a tutorial example bug (found by Kurtis D. Rader)
+
+ * Fixed an Overlay.keypress() bug (found by Andreas Klöckner)
+
+ * Fixed setuptools configuration (by Andreas Klöckner)
+
+
+Urwid 0.9.8.3
+=============
+
+2008-07-14
+
+ * Fixed a canvas cache memory leak affecting 0.9.8, 0.9.8.1 and
+ 0.9.8.2 (found by John Goodfellow)
+
+ * Fixed a canvas fill_attr() bug (found by Joern Koerner)
+
+
+Urwid 0.9.8.2
+=============
+
+2008-05-19
+
+ * Fixed incompatibilities with Python 2.3
+
+ * Fixed Pile cursor pref_col bug, WidgetWrap rows caching bug, Button
+ mouse_event with no callback bug, Filler body bug triggered by the
+ tutorial and a LineBox lline parameter typo.
+
+
+Urwid 0.9.8.1
+=============
+
+2007-06-21
+
+ * Fixed a Filler render() bug, a raw_display start()/stop() bug and a
+ number of problems triggered by very small terminal window sizes.
+
+
+Urwid 0.9.8
+===========
+
+2007-03-23
+
+ * Rendering is now significantly faster.
+
+ * New Widget base class for all widgets. It includes automatic caching
+ of rows() and render() methods. It also adds a new __super attribute
+ for accessing methods in superclasses.
+
+ Widgets must now call self._invalidate() to notify the cache when
+ their content has changed.
+
+ To disable caching in a widget set the class variable no_cache to a
+ list that includes the string "render".
+
+ * Canvas classes have been reorganized: Canvas has been renamed to
+ TextCanvas and Canvas is now the base class for all canvases. New
+ canvas classes include BlankCanvas, SolidCanvas and CompositeCanvas.
+
+ * External event loops may now be used with the raw_display module. The
+ new methods get_input_descriptors() and get_input_nonblocking()
+ should be used instead of get_input() to allow input processing
+ without blocking.
+
+ * The Columns, Pile and ListBox widgets now choose their first
+ selectable child widget as the focus widget by defaut.
+
+ * New ListWalker base class for list walker classes.
+
+ * New Signals class that will be used to improve the existing event
+ callbacks. Currently it is used for ListWalker objects to notify
+ their ListBox when their content has changed.
+
+ * SimpleListWalker now behaves as a list and supports all list
+ operations. This class now detects when changes are made to the list
+ and notifies the ListBox object. New code should use this class to
+ wrap lists of widgets before passing them to the ListBox
+ constructor.
+
+ * New PollingListWalker class is now the default list walker that is
+ used when passing a simple list to the ListBox constructor. This
+ class is intended for backwards compatibility only. When this class
+ is used the ListBox object is unable to cache its render() method.
+
+ * The curses_display module can now draw in the lower-right corner of
+ the screen.
+
+ * All display modules now have start() and stop() methods that may be
+ used instead of calling run_wrapper().
+
+ * The raw_display module now uses an alternate buffer so that the
+ original screen can be restored on exit. The old behaviour is
+ available by seting the alternate_buffer parameter of start() or
+ run_wrapper() to False.
+
+ * Many internal string processing functions have been rewritten in C to
+ improve their performance.
+
+ * Compatible with Python >= 2.2. Python 2.1 is no longer supported.
+
+
+Urwid 0.9.7.2
+=============
+
+2007-01-03
+
+ * Improved performance in UTF-8 mode when ASCII text is used.
+
+ * Fixed a UTF-8 input bug.
+
+ * Added a clear() function to the the display modules to force the
+ screen to be repainted on the next draw_screen() call.
+
+
+Urwid 0.9.7.1
+=============
+
+2006-10-03
+
+ * Fixed bugs in Padding and Overlay widgets introduced in 0.9.7.
+
+
+Urwid 0.9.7
+===========
+
+2006-10-01
+
+ * Added initial support for fixed widgets - widgets that have a fixed
+ size on screen. Fixed widgets expect a size parameter equal to ().
+ Fixed widgets must implement the pack(..) function to return their
+ size.
+
+ * New BigText class that draws text with fonts made of grids of
+ character cells. BigText is a fixed widget and doesn't do any
+ alignment or wrapping. It is intended for banners and number readouts
+ that need to stand out on the screen.
+
+ Fonts: Thin3x3Font, Thin4x3Font, Thin6x6Font (full ascii)
+
+ UTF-8 only fonts: HalfBlock5x4Font, HalfBlock6x5Font,
+ HalfBlockHeavy6x5Font, HalfBlock7x7Font (full ascii)
+
+ New function get_all_fonts() may be used to get a list of the
+ available fonts.
+
+ * New example program bigtext.py demonstrates use of BigText.
+
+ * Padding class now has a clipping mode that pads or clips fixed
+ widgets to make them behave as flow widgets.
+
+ * Overlay class can now accept a fixed widget as the widget to display
+ "on top".
+
+ * New Canvas functions: pad_trim() and pad_trim_left_right().
+
+ * Fixed a bug in Filler.get_cursor_coords() that causes a crash if the
+ contained widget's get_cursor_coords() function returns None.
+
+ * Fixed a bug in Text.pack() that caused an infinite loop when the text
+ contained a newline. This function is not currently used by Urwid.
+
+ * Edit.__init__() now calls set_edit_text() to initialize its text.
+
+ * Overlay.calculate_padding_filler() and Padding.padding_values() now
+ include focus parameters.
+
+
+Urwid 0.9.6
+===========
+
+2006-08-22
+
+ * Fixed Unicode conversion and locale issues when using Urwid with
+ Python < 2.4. The graph.py example program should now work properly
+ with older versions of Python.
+
+ * The docgen_tutorial.py script can now write out the tutorial example
+ programs as individual files.
+
+ * Updated reference documentation table of contents to show which
+ widgets are flow and/or box widgets.
+
+ * Columns.set_focus(..) will now accept an integer or a widget as its
+ parameter.
+
+ * Added detection for rxvt's HOME and END escape sequences.
+
+ * Added support for setuptools (improved distutils).
+
+
+Urwid 0.9.5
+===========
+
+2006-06-14
+
+ * Some Unicode characters are now converted to use the G1 alternate
+ character set with DEC special and line drawing characters. These
+ Unicode characters should now "just work" in almost all terminals and
+ encodings.
+
+ When Urwid is run with the UTF-8 encoding the characters are left as
+ UTF-8 and not converted.
+
+ The characters converted are:
+
+ \u00A3 (£), \u00B0 (°), \u00B1 (±), \u00B7 (·), \u03C0 (π),
+ \u2260 (≠), \u2264 (≤), \u2265 (≥), \u23ba (⎺), \u23bb (⎻),
+ \u23bc (⎼), \u23bd (⎽), \u2500 (─), \u2502 (│), \u250c (┌),
+ \u2510 (┐), \u2514 (└), \u2518 (┘), \u251c (├), \u2524 (┤),
+ \u252c (┬), \u2534 (┴), \u253c (┼), \u2592 (▒), \u25c6 (◆)
+
+ * New SolidFill class for filling an area with a single character.
+
+ * New LineBox class for wrapping widgets in a box made of line- drawing
+ characters. May be used as a box widget or a flow widget.
+
+ * New example program graph.py demonstrates use of BarGraph, LineBox,
+ ProgressBar and SolidFill.
+
+ * Pile class may now be used as a box widget and contain a mix of box
+ and flow widgets.
+
+ * Columns class may now contain a mix of box and flow widgets. The box
+ widgets will take their height from the maximum height of the flow
+ widgets.
+
+ * Improved the smoothness of resizing with raw_display module. The
+ module will now try to stop updating the screen when a resize event
+ occurs during the update.
+
+ * The Edit and IntEdit classes now use their set_edit_text() and
+ set_edit_pos() functions when handling keypresses, so those functions
+ may be overridden to catch text modification.
+
+ * The set_state() functions in the CheckBox and RadioButton classes now
+ have a do_callback parameter that determines if the callback function
+ registered will be called.
+
+ * Fixed a newly introduced incompatibility with python < 2.3.
+
+ * Fixed a missing symbol in curses_display when python is linked
+ against libcurses.
+
+ * Fixed mouse handling bugs in the Frame and Overlay classes.
+
+ * Fixed a Padding bug when the left or right has no padding.
+
+
+Urwid 0.9.4
+===========
+
+2006-05-30
+
+ * Enabled mouse handling across the Urwid library.
+
+ Added a new mouse_event() method to the Widget interface definition
+ and to the following widgets: Edit, CheckBox, RadioButton, Button,
+ GridFlow, Padding, Filler, Overlay, Frame, Pile, Columns, BoxAdapter
+ and ListBox.
+
+ Updated example programs browse.py, calc.py, dialog.py, edit.py and
+ tour.py to support mouse input.
+
+ * Released the files used to generate the reference and tutorial
+ documentation: docgen_reference.py, docgen_tutorial.py and
+ tmpl_tutorial.html. The "docgen" scripts write the documentation to
+ stdout. docgen_tutorial.py requires the Templayer HTML templating
+ library to run: http://excess.org/templayer/
+
+ * Improved Widget and List Walker interface documentation.
+
+ * Fixed a bug in the handling of invalid UTF-8 data. All invalid
+ characters are now replaced with '?' characters when displayed.
+
+
+Urwid 0.9.3
+===========
+
+2006-05-14
+
+ * Improved mouse reporting.
+
+ The raw_display module now detects gpm mouse events by reading
+ /usr/bin/mev output. The curses_display module already supports gpm
+ directly.
+
+ Mouse drag events are now reported by raw_display in terminals that
+ provide button event tracking and on the console with gpm. Note that
+ gpm may report coordinates off the screen if the user drags the mouse
+ off the edge.
+
+ Button release events now report which button was released if that
+ information is available, currently only on the console with gpm.
+
+ * Added display of raw keycodes to the input_test.py example program.
+
+ * Fixed a text layout bug affecting clipped text with blank lines, and
+ another related to wrapped text starting with a space character.
+
+ * Fixed a Frame.keypress() bug that caused it to call keypress on
+ unselectable widgets.
+
+
+Urwid 0.9.2
+===========
+
+2006-03-18
+
+ * Preliminary mouse support was added to the raw_display and
+ curses_display modules. A new Screen.set_mouse_tracking() method was
+ added to enable mouse tracking. Mouse events are returned alongside
+ keystrokes from the Screen.get_input() method.
+
+ The widget interface does not yet include mouse handling. This will
+ be addressed in the next release.
+
+ * A new convenience function is_mouse_event() was added to help in
+ separating mouse events from keystrokes.
+
+ * Added a new example program input_test.py. This program displays the
+ keyboard and mouse input it receives. It may be run as a CGI script
+ or from the command line. On the command line it defaults to using
+ the curses_display module, use input_test.py raw to use the
+ raw_display module instead.
+
+ * Fixed an Edit.render() bug that caused it to render the cursor in a
+ different location than that reported by Edit.get_cursor_coords() in
+ some circumstances.
+
+ * Fixed a bug preventing use of UTF-8 characters with Divider widgets.
+
+
+Urwid 0.9.1
+===========
+
+2006-03-06
+
+ * BarGraph and ProgressBar can now display data more accurately by
+ using the UTF-8 vertical and horizontal eighth characters. This
+ behavior will be enabled when the UTF-8 encoding is detected and
+ "smoothed" attributes are passed to the BarGraph or ProgressBar
+ constructors.
+
+ * New get_encoding_mode() function to determine how Urwid will treat
+ raw string data.
+
+ * New raw_display.signal_init() and raw_display.signal_restore()
+ methods that may be overridden by threaded applications that need to
+ call signal.signal() from their main thread.
+
+ * Fixed a bug that prevented the use of UTF-8 strings in text markup.
+
+ * Removed some forgotten asserts that broke 8-bit and CJK input.
+
+
+Urwid 0.9.0
+===========
+
+2006-02-18
+
+ * New support for UTF-8 encoding including input, display and editing
+ of narrow and wide (CJK) characters.
+
+ Preliminary combining (zero-width) character support is included, but
+ full support will require terminal behavior detection.
+
+ Right-to-Left input and display are not implemented.
+
+ * New raw_display module that handles console display without relying
+ on external libraries. This module was written as a work around for
+ the lack of UTF-8 support in the standard version of ncurses.
+
+ Eliminates "dead corner" in the bottom right of the screen.
+
+ Avoids use of bold text in xterm and gnome-terminal for improved
+ text legibility.
+
+ * Fixed Overlay bug related to UTF-8 handling.
+
+ * Fixed Edit.move_cursor_to_coords(..) bug related to wide characters
+ in UTF-8 encoding.
+
+
+Urwid 0.9.0-pre3
+================
+
+2006-02-13
+
+ * Fixed Canvas attribute padding bug related to -pre1 changes.
+
+
+Urwid 0.9.0-pre2
+================
+
+2006-02-10
+
+ * Replaced the custom align and wrap modes in example program calc.py
+ with a new layout class.
+
+ * Fixed Overlay class call to Canvas.overlay() broken by -pre1 changes.
+
+ * Fixed Padding bug related to Canvas -pre1 changes.
+
+
+Urwid 0.9.0-pre1
+================
+
+2006-02-08
+
+ * New support for UTF-8 encoding. Unicode strings may be used and will
+ be converted to the current encoding when output. Regular strings in
+ the current encoding may still be used.
+
+ PLEASE NOTE: There are issues related to displaying UTF-8 characters
+ with the curses_display module that have not yet been resolved.
+
+ * New set_encoding() function replaces util.set_double_byte_encoding().
+
+ * New supports_unicode() function to query if unicode strings with
+ characters outside the ascii range may be used with the current
+ encoding.
+
+ * New TextLayout and StandardTextLayout classes to perform text
+ wrapping and alignment. Text widgets now have a layout parameter to
+ allow use of custom TextLayout objects.
+
+ * New layout structure replaces line translation structure. Layout
+ structure now allows arbitrary reordering/positioning of text
+ segments, inclusion of UTF-8 characters and insertion of text not
+ found in the original text string.
+
+ * Removed util.register_align_mode() and util.register_wrap_mode().
+ Their functionality has been replaced by the new layout classes.
+
+
+Urwid 0.8.10
+============
+
+2005-11-27
+
+ * Expanded tutorial to cover advanced ListBox usage, custom widget
+ classes and the Pile, BoxAdapter, Columns, GridFlow and Overlay
+ classes.
+
+ * Added escape sequence for "shift tab" to curses_display.
+
+ * Added ListBox.set_focus_valign() to allow positioning of the focus
+ widget within the ListBox.
+
+ * Added WidgetWrap class for extending existing widgets without
+ inheriting their complete namespace.
+
+ * Fixed web_display/mozilla breakage from 0.8.9. Fixed crash on invalid
+ locale setting. Fixed ListBox slide-back bug. Fixed improper space
+ trimming in calculate_alignment(). Fixed browse.py example program
+ rows bug. Fixed sum definition, use of long ints for python2.1. Fixed
+ warnings with python2.1. Fixed Padding.get_pref_col() bug. Fixed
+ Overlay splitting CJK characters bug.
+
+
+Urwid 0.8.9
+===========
+
+2005-10-21
+
+ * New Overlay class for drawing widgets that obscure parts of other
+ widgets. May be used for drop down menus, combo boxes, overlapping
+ "windows", caption text etc.
+
+ * New BarGraph, GraphVScale and ProgressBar classes for graphical
+ display of data in Urwid applications.
+
+ * New method for configuring keyboard input timeouts and delays:
+ curses_display.Screen.set_input_timeouts().
+
+ * Fixed a ListBox.set_focus() bug.
+
+
+Urwid 0.8.8
+===========
+
+2005-06-13
+
+ * New web_display module that emulates a console display within a web
+ browser window. Application must be run as a CGI script under Apache.
+
+ Supports font/window resizing, keepalive for long-lived connections,
+ limiting maximum concurrent connections, polling and connected update
+ methods. Tested with Mozilla Firefox and Internet Explorer.
+
+ * New BoxAdapter class for using box widgets in places that usually
+ expect flow widgets.
+
+ * New curses_display input handling with better ESC key detection and
+ broader escape code support.
+
+ * Shortened resize timeout on gradual resize to improve responsiveness.
+
+
+Urwid 0.8.7
+===========
+
+2005-05-21
+
+ * New widget classes: Button, RadioButton, CheckBox.
+
+ * New layout widget classes: Padding, GridFlow.
+
+ * New dialog.py example program that behaves like dialog(1) command.
+
+ * Pile widgets now support selectable items, focus changing with up and
+ down keys and setting the cursor position.
+
+ * Frame widgets now support selectable items in the header and footer.
+
+ * Columns widgets now support fixed width and relative width columns, a
+ minimum width for all columns, selectable items within columns
+ containing flow widgets (already supported for box widgets), focus
+ changing with left and right keys and setting the cursor position.
+
+ * Filler widgets may now wrap box widgets and have more alignment options.
+
+ * Updated tour.py example program to show new widget types and
+ features.
+
+ * Avoid hogging cpu on gradual window resize and fix for slow resize
+ with cygwin's broken curses implementation.
+
+ * Fixed minor CJK problem and curs_set() crash under MacOSX and Cygwin.
+
+ * Fixed crash when deleting cells in calc.py example program.
+
+
+Urwid 0.8.6
+===========
+
+2005-01-03
+
+ * Improved support for CJK double-byte encodings: BIG5, UHC, GBK,
+ GB2312, CN-GB, EUC-KR, EUC-CN, EUC-JP (JISX 0208 only) and EUC-TW
+ (CNS 11643 plain 1 only)
+
+ * Added support for ncurses' use_default_colors() function to
+ curses_display module (Python >= 2.4).
+
+ register_palette() and register_palette_entry() now accept "default"
+ as foreground and/or background. If the terminal's default attributes
+ cannot be detected black on light gray will be used to accomodate
+ terminals with always-black cursors.
+
+ "default" is now the default for text with no attributes. This means
+ that areas with no attributes will change from light grey on black
+ (curses default) to black on light gray or the terminal's default.
+
+ * Modified examples to not use black as background of Edit widgets.
+
+ * Fixed curses_display curs_set() call so that cursor is hidden when
+ widget in focus has no cursor position.
+
+
+Urwid 0.8.5
+===========
+
+2004-12-15
+
+ * New tutorial covering basic operation of: curses_display.Screen,
+ Canvas, Text, FlowWidget, Filler, BoxWidget, AttrWrap, Edit, ListBox
+ and Frame classes
+
+ * New widget class: Filler
+
+ * New ListBox functions: get_focus(), set_focus()
+
+ * Debian packages for Python 2.4.
+
+ * Fixed curses_display bug affecting text with no attributes.
+
+
+Urwid 0.8.4
+===========
+
+2004-11-20
+
+ * Improved support for Cyrillic and other simple 8-bit encodings.
+
+ * Added new functions to simplify taking screenshots:
+ html_fragment.screenshot_init() and
+ html_fragment.screenshot_collect()
+
+ * Improved urwid/curses_display.py input debugging
+
+ * Fixed cursor in screenshots of CJK text. Fixed "end" key in Edit
+ boxes with CJK text.
+
+
+Urwid 0.8.3
+===========
+
+2004-11-15
+
+ * Added support for CJK double-byte encodings.
+
+ Word wrapping mode "space" will wrap on edges of double width
+ characters. Wrapping and clipping will not split double width
+ characters.
+
+ curses_display.Screen.get_input() may now return double width
+ characters. Text and Edit classes will work with a mix of regular and
+ double width characters.
+
+ * Use new method Edit.set_edit_text() instead of Edit.update_text().
+
+ * Minor improvements to edit.py example program.
+
+
+Urwid 0.8.2
+===========
+
+2004-11-08
+
+ * Re-released under GNU Lesser General Public License.
+
+
+Urwid 0.8.1
+===========
+
+2004-10-29
+
+ * Added support for monochrome terminals. see
+ curses_display.Screen.register_palette_entry() and example programs.
+ set TERM=xterm-mono to test programs in monochrome mode.
+
+ * Added unit testing code test_urwid.py to the examples.
+
+ * Can now run urwid/curses_display.py to test your terminal's input and
+ colour rendering.
+
+ * Fixed an OSX browse.py compatibility issue. Added some OSX keycodes.
+
+
+Urwid 0.8.0
+===========
+
+2004-10-17
+
+ * Initial Release
diff --git a/docs/conf.py b/docs/conf.py
index e612f89..302dff0 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -41,7 +41,7 @@ master_doc = 'index'
# General information about the project.
project = u'Urwid'
-copyright = u'2012, Ian Ward'
+copyright = u'2014, Ian Ward et al'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -119,7 +119,7 @@ html_theme_options = {
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
-html_title = "Urwid %s Documentation" % (release,)
+html_title = "Urwid %s" % (release,)
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
@@ -268,7 +268,7 @@ texinfo_documents = [
epub_title = u'Urwid'
epub_author = u'Ian Ward'
epub_publisher = u'Ian Ward'
-epub_copyright = u'2012, Ian Ward'
+epub_copyright = u'2014, Ian Ward et al'
# The language of the text. It defaults to the language option
# or en if the language is not set.
diff --git a/docs/examples/bigtext.py b/docs/examples/bigtext.py
new file mode 120000
index 0000000..119650d
--- /dev/null
+++ b/docs/examples/bigtext.py
@@ -0,0 +1 @@
+../../examples/bigtext.py \ No newline at end of file
diff --git a/docs/examples/bigtext.py.xdotool b/docs/examples/bigtext.py.xdotool
new file mode 100644
index 0000000..d245b08
--- /dev/null
+++ b/docs/examples/bigtext.py.xdotool
@@ -0,0 +1,3 @@
+windowsize --usehints $RXVTWINDOWID 39 21
+key --window $RXVTWINDOWID BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace e x a m p l e Down Down Down Down Down Down Return
+key --window $RXVTWINDOWID Up Up Up Return
diff --git a/docs/examples/bigtext1.png b/docs/examples/bigtext1.png
new file mode 100644
index 0000000..2598916
--- /dev/null
+++ b/docs/examples/bigtext1.png
Binary files differ
diff --git a/docs/examples/bigtext2.png b/docs/examples/bigtext2.png
new file mode 100644
index 0000000..1e9906b
--- /dev/null
+++ b/docs/examples/bigtext2.png
Binary files differ
diff --git a/docs/examples/bigtext3.png b/docs/examples/bigtext3.png
new file mode 100644
index 0000000..5ab0e37
--- /dev/null
+++ b/docs/examples/bigtext3.png
Binary files differ
diff --git a/docs/examples/browse.py b/docs/examples/browse.py
new file mode 100755
index 0000000..2bbcc34
--- /dev/null
+++ b/docs/examples/browse.py
@@ -0,0 +1,6 @@
+#!/usr/bin/env python
+
+import real_browse
+import os
+os.chdir('/usr/share/doc/python')
+real_browse.main()
diff --git a/docs/examples/browse.py.xdotool b/docs/examples/browse.py.xdotool
new file mode 100644
index 0000000..c722435
--- /dev/null
+++ b/docs/examples/browse.py.xdotool
@@ -0,0 +1,2 @@
+windowsize --usehints $RXVTWINDOWID 79 19
+key --window $RXVTWINDOWID Down plus Down space Down space Down Down Down
diff --git a/docs/examples/browse1.png b/docs/examples/browse1.png
new file mode 100644
index 0000000..857903a
--- /dev/null
+++ b/docs/examples/browse1.png
Binary files differ
diff --git a/docs/examples/browse2.png b/docs/examples/browse2.png
new file mode 100644
index 0000000..3dc5ba2
--- /dev/null
+++ b/docs/examples/browse2.png
Binary files differ
diff --git a/docs/examples/edit.py b/docs/examples/edit.py
new file mode 100755
index 0000000..7eacc86
--- /dev/null
+++ b/docs/examples/edit.py
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+
+import sys
+import os
+import real_edit
+
+real_edit.EditDisplay(os.path.join(
+ os.path.dirname(sys.argv[0]), 'edit_text.txt')).main()
diff --git a/docs/examples/edit.py.xdotool b/docs/examples/edit.py.xdotool
new file mode 100644
index 0000000..70ba7a8
--- /dev/null
+++ b/docs/examples/edit.py.xdotool
@@ -0,0 +1,2 @@
+windowsize --usehints $RXVTWINDOWID 39 20
+key --window $RXVTWINDOWID Return Return a d d i n g space s o m e space t e x t space
diff --git a/docs/examples/edit1.png b/docs/examples/edit1.png
new file mode 100644
index 0000000..6a3ba42
--- /dev/null
+++ b/docs/examples/edit1.png
Binary files differ
diff --git a/docs/examples/edit2.png b/docs/examples/edit2.png
new file mode 100644
index 0000000..165695f
--- /dev/null
+++ b/docs/examples/edit2.png
Binary files differ
diff --git a/docs/examples/edit_text.txt b/docs/examples/edit_text.txt
new file mode 100644
index 0000000..c0947b3
--- /dev/null
+++ b/docs/examples/edit_text.txt
@@ -0,0 +1,11 @@
+替洼渎溏潺瀚灯烫虫调达逯遘醋长闫阚顺驼髓
+공곽껫끓뇽늙등뗍뛴룸많맹뫘볶
+ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.
+ ╔══════════════════════╗
+ ║ • “smart quotes” ║
+ ║ ╭─────────╮ ║
+ ║ • euro: │ € 14.95 │ ║
+ ║ ╰─────────╯ ║
+ ╚══════════════════════╝
+Math: ∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β)
+
diff --git a/docs/examples/graph.py b/docs/examples/graph.py
new file mode 120000
index 0000000..ffbbea0
--- /dev/null
+++ b/docs/examples/graph.py
@@ -0,0 +1 @@
+../../examples/graph.py \ No newline at end of file
diff --git a/docs/examples/graph.py.xdotool b/docs/examples/graph.py.xdotool
new file mode 100644
index 0000000..0abaa5a
--- /dev/null
+++ b/docs/examples/graph.py.xdotool
@@ -0,0 +1,2 @@
+windowsize --usehints $RXVTWINDOWID 79 24
+key --window $RXVTWINDOWID Down Down Down Return
diff --git a/docs/examples/graph1.png b/docs/examples/graph1.png
new file mode 100644
index 0000000..1cf14fa
--- /dev/null
+++ b/docs/examples/graph1.png
Binary files differ
diff --git a/docs/examples/graph2.png b/docs/examples/graph2.png
new file mode 100644
index 0000000..888c610
--- /dev/null
+++ b/docs/examples/graph2.png
Binary files differ
diff --git a/docs/examples/index.rst b/docs/examples/index.rst
new file mode 100644
index 0000000..0141b64
--- /dev/null
+++ b/docs/examples/index.rst
@@ -0,0 +1,80 @@
+.. _example-programs:
+
+********************
+ Example Programs
+********************
+
+.. currentmodule:: urwid
+
+These example programs may be found in the examples directory of your
+Urwid distribution.
+
+tour.py
+-------
+
+.. image:: tour1.png
+.. image:: tour2.png
+
+Show how many of the standard widgets may be arranged
+on the screen
+
+
+graph.py
+--------
+
+.. image:: graph2.png
+
+Demonstrate BarGraph widget and alarms used for animation
+
+
+edit.py
+-------
+
+.. image:: edit1.png
+.. image:: edit2.png
+
+A simple text editor with lazy loading
+
+
+browse.py
+---------
+
+.. image:: browse2.png
+
+A lazy directory browser with file selection, tree-view, custom widgets
+and list walker
+
+
+subproc.py
+----------
+
+.. image:: subproc1.png
+.. image:: subproc2.png
+
+Monitor and display results from a subprocess
+
+
+palette_test.py
+---------------
+
+.. image:: palette_test2.png
+
+Show available colors in various screen modes
+
+
+pop_up.py
+---------
+
+.. image:: pop_up1.png
+.. image:: pop_up2.png
+
+Create a pop-up/drop-down/window anchored to another widget
+
+
+bigtext.py
+----------
+
+.. image:: bigtext2.png
+.. image:: bigtext3.png
+
+demonstrate the BigText widget
diff --git a/docs/examples/palette_test.py b/docs/examples/palette_test.py
new file mode 120000
index 0000000..0ab2544
--- /dev/null
+++ b/docs/examples/palette_test.py
@@ -0,0 +1 @@
+../../examples/palette_test.py \ No newline at end of file
diff --git a/docs/examples/palette_test.py.xdotool b/docs/examples/palette_test.py.xdotool
new file mode 100644
index 0000000..ed97821
--- /dev/null
+++ b/docs/examples/palette_test.py.xdotool
@@ -0,0 +1,2 @@
+windowsize --usehints $RXVTWINDOWID 79 34
+key --window $RXVTWINDOWID Down Down Down Return Right Down Return
diff --git a/docs/examples/palette_test1.png b/docs/examples/palette_test1.png
new file mode 100644
index 0000000..4a5c392
--- /dev/null
+++ b/docs/examples/palette_test1.png
Binary files differ
diff --git a/docs/examples/palette_test2.png b/docs/examples/palette_test2.png
new file mode 100644
index 0000000..64a9e46
--- /dev/null
+++ b/docs/examples/palette_test2.png
Binary files differ
diff --git a/docs/examples/pop_up.py b/docs/examples/pop_up.py
new file mode 120000
index 0000000..0e32494
--- /dev/null
+++ b/docs/examples/pop_up.py
@@ -0,0 +1 @@
+../../examples/pop_up.py \ No newline at end of file
diff --git a/docs/examples/pop_up.py.xdotool b/docs/examples/pop_up.py.xdotool
new file mode 100644
index 0000000..486ab8d
--- /dev/null
+++ b/docs/examples/pop_up.py.xdotool
@@ -0,0 +1,2 @@
+windowsize --usehints $RXVTWINDOWID 39 18
+key --window $RXVTWINDOWID space
diff --git a/docs/examples/pop_up1.png b/docs/examples/pop_up1.png
new file mode 100644
index 0000000..9371d54
--- /dev/null
+++ b/docs/examples/pop_up1.png
Binary files differ
diff --git a/docs/examples/pop_up2.png b/docs/examples/pop_up2.png
new file mode 100644
index 0000000..ac0bd53
--- /dev/null
+++ b/docs/examples/pop_up2.png
Binary files differ
diff --git a/docs/examples/real_browse.py b/docs/examples/real_browse.py
new file mode 120000
index 0000000..a817177
--- /dev/null
+++ b/docs/examples/real_browse.py
@@ -0,0 +1 @@
+../../examples/browse.py \ No newline at end of file
diff --git a/docs/examples/real_edit.py b/docs/examples/real_edit.py
new file mode 120000
index 0000000..16bf719
--- /dev/null
+++ b/docs/examples/real_edit.py
@@ -0,0 +1 @@
+../../examples/edit.py \ No newline at end of file
diff --git a/docs/examples/subproc.py b/docs/examples/subproc.py
new file mode 120000
index 0000000..b3410f7
--- /dev/null
+++ b/docs/examples/subproc.py
@@ -0,0 +1 @@
+../../examples/subproc.py \ No newline at end of file
diff --git a/docs/examples/subproc.py.xdotool b/docs/examples/subproc.py.xdotool
new file mode 100644
index 0000000..4f8e186
--- /dev/null
+++ b/docs/examples/subproc.py.xdotool
@@ -0,0 +1,2 @@
+windowsize --usehints $RXVTWINDOWID 39 18
+key --window $RXVTWINDOWID a n y t h i n g question
diff --git a/docs/examples/subproc1.png b/docs/examples/subproc1.png
new file mode 100644
index 0000000..6e515fa
--- /dev/null
+++ b/docs/examples/subproc1.png
Binary files differ
diff --git a/docs/examples/subproc2.png b/docs/examples/subproc2.png
new file mode 100644
index 0000000..86fb0ec
--- /dev/null
+++ b/docs/examples/subproc2.png
Binary files differ
diff --git a/docs/examples/subproc2.py b/docs/examples/subproc2.py
new file mode 100644
index 0000000..76c8090
--- /dev/null
+++ b/docs/examples/subproc2.py
@@ -0,0 +1,10 @@
+
+import time
+time.sleep(1)
+
+print """factor: 1
+factor: 1000003
+factor: 2000029
+factor: 3000073
+factor: 4000159
+factor: 5000101"""
diff --git a/docs/examples/tour.py b/docs/examples/tour.py
new file mode 120000
index 0000000..84d7931
--- /dev/null
+++ b/docs/examples/tour.py
@@ -0,0 +1 @@
+../../examples/tour.py \ No newline at end of file
diff --git a/docs/examples/tour.py.xdotool b/docs/examples/tour.py.xdotool
new file mode 100644
index 0000000..8c1ddea
--- /dev/null
+++ b/docs/examples/tour.py.xdotool
@@ -0,0 +1,2 @@
+windowsize --usehints $RXVTWINDOWID 39 25
+key --window $RXVTWINDOWID Return Page_Down Page_Down Page_Down Page_Down Page_Down
diff --git a/docs/examples/tour1.png b/docs/examples/tour1.png
new file mode 100644
index 0000000..d97c8b8
--- /dev/null
+++ b/docs/examples/tour1.png
Binary files differ
diff --git a/docs/examples/tour2.png b/docs/examples/tour2.png
new file mode 100644
index 0000000..d5c4ab8
--- /dev/null
+++ b/docs/examples/tour2.png
Binary files differ
diff --git a/docs/index.rst b/docs/index.rst
index 4a6bb3d..66050c9 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -4,6 +4,8 @@ Urwid |release| Documentation
.. toctree::
+ examples/index
tutorial/index
manual/index
reference/index
+ changelog
diff --git a/docs/manual/bright_combinations.py b/docs/manual/bright_combinations.py
new file mode 100755
index 0000000..4ff350c
--- /dev/null
+++ b/docs/manual/bright_combinations.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+
+import urwid
+
+RED_FGS = ('black', 'light gray', 'white', 'light cyan', 'light red',
+ 'light green', 'yellow', 'light magenta')
+GREEN_FGS = ('black', 'light gray', 'dark blue', 'white', 'light cyan',
+ 'light red', 'light green', 'yellow')
+BROWN_FGS = ('black', 'dark blue', 'dark gray', 'white',
+ 'light blue', 'light cyan', 'light red', 'light green', 'yellow')
+MAGENTA_FGS = ('black', 'light gray', 'dark blue', 'white', 'light cyan',
+ 'light red', 'light green', 'yellow', 'light magenta')
+
+BG_FGS =[
+ ('dark red', RED_FGS),
+ ('dark green', GREEN_FGS),
+ ('brown', BROWN_FGS),
+ ('dark magenta', MAGENTA_FGS),
+ ]
+
+body = urwid.SimpleFocusListWalker([])
+
+for bg, fgs in BG_FGS:
+ spec = urwid.AttrSpec(fgs[0], bg)
+ def s(w):
+ return urwid.AttrMap(w, spec)
+ body.append(s(urwid.Divider()))
+ body.append(s(
+ urwid.GridFlow(
+ [urwid.AttrMap(urwid.Text("'{0}' on '{1}'".format(fg, bg)),
+ urwid.AttrSpec(fg, bg)) for fg in fgs],
+ 35, 0, 0, 'left')))
+ body.append(s(urwid.Divider()))
+
+urwid.MainLoop(urwid.ListBox(body)).run()
diff --git a/docs/manual/bright_combinations.py.xdotool b/docs/manual/bright_combinations.py.xdotool
new file mode 100644
index 0000000..fd929d8
--- /dev/null
+++ b/docs/manual/bright_combinations.py.xdotool
@@ -0,0 +1 @@
+windowsize --usehints $RXVTWINDOWID 70 26
diff --git a/docs/manual/bright_combinations1.png b/docs/manual/bright_combinations1.png
new file mode 100644
index 0000000..d46c920
--- /dev/null
+++ b/docs/manual/bright_combinations1.png
Binary files differ
diff --git a/docs/manual/canvascache.rst b/docs/manual/canvascache.rst
index 0dd4271..bb5f094 100644
--- a/docs/manual/canvascache.rst
+++ b/docs/manual/canvascache.rst
@@ -12,7 +12,7 @@ visible, unchanged canvases so that not all of the visible widgets need to be
rendered for each update.
The :class:`Widget` base class uses some metaclass magic to
-capture the canvas objects returned :meth:`Widget.render` is called and return
+capture the canvas objects returned when :meth:`Widget.render` is called and return
them the next time :meth:`Widget.render` is called again with the same parameters. The
:meth:`Widget._invalidate` method is provided as a way to remove cached widgets so
that changes to the widget are visible the next time the screen is redrawn.
@@ -26,7 +26,7 @@ Composite Canvases
When container and decoration widgets are rendered, they collect the canvases
returned by their children and arrange them into a composite canvas. Composite
-canvases may are nested to form a tree with the topmost widget's :meth:`Widget.render`
+canvases are nested to form a tree with the topmost widget's :meth:`Widget.render`
method returning the root of the tree. That canvas is sent to the display
module to be rendered on the screen.
diff --git a/docs/manual/displayattributes.rst b/docs/manual/displayattributes.rst
index 9492869..44b7753 100644
--- a/docs/manual/displayattributes.rst
+++ b/docs/manual/displayattributes.rst
@@ -334,3 +334,30 @@ terminal, so this feature shouldn't be relied on.
:meth:`raw_display.Screen.reset_default_terminal_palette` is used to
reset the palette in the ``palette_test.py`` example program when switching modes.
+
+Recommended Combinations
+========================
+
+Neutral Backgrounds
+-------------------
+
+.. image:: safe_combinations1.png
+
+Choose colors that are fairly neutral with medium contrast for most of
+your application. It is good to use one background as a default for text,
+another for edit boxes and a third for selected edit boxes.
+
+Foreground colors shown here in bold text will appear as bold text on
+many terminals. Bold fonts are often more difficult to read so those
+foreground colours should be used sparingly.
+
+Bright Backgrounds
+------------------
+
+.. image:: bright_combinations1.png
+
+Use bright colors to draw attention to small areas with important
+information. They are good for buttons and selected widgets (other than
+edit boxes).
+
+
diff --git a/docs/manual/displaymodules.rst b/docs/manual/displaymodules.rst
index c31d417..eba84c8 100644
--- a/docs/manual/displaymodules.rst
+++ b/docs/manual/displaymodules.rst
@@ -92,7 +92,7 @@ Screenshot Display Module ``html_fragment``
Screenshots of Urwid interfaces can be rendered in plain HTML. The
:class:`html_fragment.HtmlGenerator` display module lets you do this by simulating user input
and capturing the screen as fragments of HTML each time
-:meth:`html_fragemnt.HtmlGenerator.draw_screen` is
+:meth:`html_fragment.HtmlGenerator.draw_screen` is
called.
These fragments may be included in HTML documents. They will be rendered
diff --git a/docs/manual/mainloop.rst b/docs/manual/mainloop.rst
index e34c6c5..ee3f62a 100644
--- a/docs/manual/mainloop.rst
+++ b/docs/manual/mainloop.rst
@@ -59,7 +59,8 @@ Event Loops
Urwid's event loop classes handle waiting for things for the
:class:`MainLoop`. The different event loops allow you to
-integrate with Twisted_ or Glib_ libraries, or use a simple ``select``-based
+integrate with Twisted_, Glib_, Tornado_ libraries,
+or use a simple ``select``-based
loop. Event loop classes abstract the particulars of waiting for input and
calling functions as a result of timeouts.
@@ -69,10 +70,12 @@ you have more than one :class:`MainLoop` running.
You can add your own files to watch to your event loop, with the
:meth:`watch_file() <SelectEventLoop.watch_file>` method.
Using this interface gives you the special handling
-of :exc:`ExitMainLoop` and other exceptions when using Glib_ or Twisted_.
+of :exc:`ExitMainLoop` and other exceptions when using Glib_, Twisted_ or
+Tornado_ callbacks.
.. _Twisted: http://twistedmatrix.com/trac/
.. _Glib: http://developer.gnome.org/glib/stable/
+.. _Tornado: http://www.tornadoweb.org/
``SelectEventLoop``
-------------------
@@ -120,3 +123,18 @@ application on Twisted.
.. seealso::
:class:`GLibEventLoop reference <GLibEventLoop>`
+
+``TornadoEventLoop``
+--------------------
+
+This event loop integrates with Tornado.
+
+::
+
+ from tornado.ioloop import IOLoop
+ evl = urwid.TornadoEventLoop(IOLoop()
+ loop = urwid.MainLoop(widget, event_loop=evl)
+
+.. seealso::
+
+ :class`TornadoEventLoop reference <TornadoEventLoop>`
diff --git a/docs/manual/safe_combinations.py b/docs/manual/safe_combinations.py
new file mode 100755
index 0000000..5a02159
--- /dev/null
+++ b/docs/manual/safe_combinations.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+import urwid
+
+BLACK_FGS = ('light gray', 'dark cyan', 'dark red', 'dark green',
+ 'dark magenta', 'white', 'light blue', 'light cyan', 'light red',
+ 'light green', 'yellow', 'light magenta')
+GRAY_FGS = ('black', 'dark blue', 'dark cyan', 'dark red', 'dark green',
+ 'dark magenta', 'white', 'light red', 'yellow',
+ 'light magenta')
+BLUE_FGS = ('light gray', 'dark cyan', 'white',
+ 'light cyan', 'light red', 'light green', 'yellow',
+ 'light magenta')
+CYAN_FGS = ('black', 'light gray', 'dark blue', 'white', 'light cyan',
+ 'light green', 'yellow')
+
+BG_FGS =[
+ ('black', BLACK_FGS),
+ ('light gray', GRAY_FGS),
+ ('dark blue', BLUE_FGS),
+ ('dark cyan', CYAN_FGS),
+ ]
+
+body = urwid.SimpleFocusListWalker([])
+
+for bg, fgs in BG_FGS:
+ spec = urwid.AttrSpec(fgs[0], bg)
+ def s(w):
+ return urwid.AttrMap(w, spec)
+ body.append(s(urwid.Divider()))
+ body.append(s(
+ urwid.GridFlow(
+ [urwid.AttrMap(urwid.Text("'{0}' on '{1}'".format(fg, bg)),
+ urwid.AttrSpec(fg, bg)) for fg in fgs],
+ 35, 0, 0, 'left')))
+ body.append(s(urwid.Divider()))
+
+urwid.MainLoop(urwid.ListBox(body)).run()
diff --git a/docs/manual/safe_combinations.py.xdotool b/docs/manual/safe_combinations.py.xdotool
new file mode 100644
index 0000000..c1b7590
--- /dev/null
+++ b/docs/manual/safe_combinations.py.xdotool
@@ -0,0 +1 @@
+windowsize --usehints $RXVTWINDOWID 70 27
diff --git a/docs/manual/safe_combinations1.png b/docs/manual/safe_combinations1.png
new file mode 100644
index 0000000..a5c7d7e
--- /dev/null
+++ b/docs/manual/safe_combinations1.png
Binary files differ
diff --git a/docs/manual/widgets.rst b/docs/manual/widgets.rst
index 0e42c52..44fe7d3 100644
--- a/docs/manual/widgets.rst
+++ b/docs/manual/widgets.rst
@@ -29,7 +29,7 @@ screen. When we render the topmost widget:
7. *(a)* possibly modifies the canvas from *(b)* and returns it
Widgets *(a)*, *(b)* and *(e)* are called container widgets because they
-contain other widgets. Container widgets choose the size and position their
+contain other widgets. Container widgets choose the size and position of their
contained widgets.
Container widgets must also keep track of which one of their contained widgets
@@ -315,7 +315,7 @@ 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
+If you want to use a flow widget for the top widget, first wrap the flow
widget with a :class:`Filler` widget.
diff --git a/docs/reference/main_loop.rst b/docs/reference/main_loop.rst
index dfde825..48b1c4f 100644
--- a/docs/reference/main_loop.rst
+++ b/docs/reference/main_loop.rst
@@ -23,3 +23,8 @@ TwistedEventLoop
.. autoclass:: TwistedEventLoop
+TornadoEventLoop
+----------------
+
+.. autoclass:: TornadoEventLoop
+
diff --git a/docs/reference/signals.rst b/docs/reference/signals.rst
index cbcbf37..9e10f94 100644
--- a/docs/reference/signals.rst
+++ b/docs/reference/signals.rst
@@ -7,11 +7,15 @@ The :func:`urwid.\*_signal` functions use a shared Signals object instance
for tracking registered and connected signals. There is no reason to
instantiate your own Signals object.
-.. function:: connect_signal(obj, name, callback, user_arg=None)
+.. function:: connect_signal(obj, name, callback, user_arg=None, weak_args=None, user_args=None)
.. automethod:: Signals.connect
-.. function:: disconnect_signal(obj, name, callback, user_arg=None)
+.. function:: disconnect_by_key(obj, name, key)
+
+.. automethod:: Signals.disconnect_by_key
+
+.. function:: disconnect_signal(obj, name, callback, user_arg=None, weak_args=None, user_args=None)
.. automethod:: Signals.disconnect
diff --git a/docs/tools/compile_pngs.sh b/docs/tools/compile_pngs.sh
index 1d078a8..f9135e9 100755
--- a/docs/tools/compile_pngs.sh
+++ b/docs/tools/compile_pngs.sh
@@ -2,7 +2,7 @@
# args: scripts to capture
-DISPLAYNUM=1
+DISPLAYNUM=5
SCREENSHOTS=`dirname $0`/screenshots.sh
XVFB=$(which Xvfb)
@@ -14,6 +14,7 @@ if [ -n $XVFB ]; then
fi
for script in $@; do
+ echo "doing $script"
if [ -f "${script}.xdotool" ]; then
"$SCREENSHOTS" "$script" < "${script}.xdotool"
fi
diff --git a/docs/tools/screenshots.sh b/docs/tools/screenshots.sh
index 5d2b04d..db7ca11 100755
--- a/docs/tools/screenshots.sh
+++ b/docs/tools/screenshots.sh
@@ -20,6 +20,7 @@ c=1
while read -r line; do
# the echo trick is needed to expand RXVTWINDOWID variable
echo $line | xdotool -
+ echo "sending $line"
import -window "$RXVTWINDOWID" "${image}$c.png"
(( c++ ))
done
diff --git a/docs/tools/templates/indexcontent.html b/docs/tools/templates/indexcontent.html
index 13dc2b5..784cd83 100644
--- a/docs/tools/templates/indexcontent.html
+++ b/docs/tools/templates/indexcontent.html
@@ -1,29 +1,73 @@
{% extends "!defindex.html" %}
{% block body %}
-<h1>Urwid {{ release }} Documentation</h1>
+<h1>Urwid{% if 'dev' in release %} development version{% endif %}</h1>
+<div style="float: left; width: 500px;">
+<p>Console user interface library for Python</p>
-<div class="section" id="tutorial">
-<h2><a class="reference internal" href="tutorial/index.html#urwid-tutorial"><em>Tutorial</em></a><a class="headerlink" href="#tutorial" title="Permalink to this headline">¶</a></h2>
-<a class="reference external image-reference" href="tutorial/index.html#urwid-tutorial"><img alt="_images/highcolors1.png" src="_images/highcolors1.png" width="200" align="left" style="padding-right: 15px" /></a>
-<p>The <a class="reference internal" href="tutorial/index.html#urwid-tutorial"><em>Urwid Tutorial</em></a> covers example Urwid applications
-from basic to moderate complexity. Each new concept is explained along the way.</p>
+<ul>
+{% if 'dev' in release %}<li>Stable release available at <a href="http://urwid.org/">http://urwid.org</a></li>
+{% else %}
+<li><a href="https://pypi.python.org/packages/source/u/urwid/urwid-{{ release }}.tar.gz"
+>Download: urwid-{{ release }}.tar.gz</a></li>
+{% endif %}
+<li><a href="changelog.html">Changelog</a>
+</ul>
+
+<div class="hilight"><pre>git clone https://github.com/wardi/urwid.git</pre></div>
+
+<ul>
+<li><a href="http://github.com/wardi/urwid">Github page</a></li>
+<li><a href="http://github.com/wardi/urwid/issues">Issues</a></li>
+<li><a href="http://webchat.oftc.net/?channels=%23urwid">IRC: <code>#urwid on irc.oftc.net</code></a></li>
+<li><a href="http://lists.excess.org/mailman/listinfo/urwid">Mailing list</a>
+(<a href="http://dir.gmane.org/gmane.comp.lib.urwid">gmane</a>)</li>
+</ul>
+
+<p>Wiki:</p>
+
+<ul>
+<li><a href="http://github.com/wardi/urwid/wiki/Installation-instructions">Installation instructions</a></li>
+<li><a href="http://github.com/wardi/urwid/wiki/FAQ">FAQ</a></li>
+<li><a href="http://github.com/wardi/urwid/wiki/Application-list">Applications built with Urwid</a></li>
+<li><a href="http://github.com/wardi/urwid/wiki/How-you-can-help">How you can help</a></li>
+<li><a href="http://github.com/wardi/urwid/wiki/Coding-style">Coding style</a></li>
+<li><a href="http://github.com/wardi/urwid/wiki/Planned-development">Planned development</a></li>
+</ul>
+
+<div class="section" id="requirements">
+<h3>Requirements</h3>
+<ul>
+<li>Python 2.6, 2.7, 3.2+ or PyPy</li>
+<li>Linux, OSX, Cygwin or other unix-like OS</li>
+<li>python-gi for GlibEventLoop (optional)</li>
+<li>Twisted for TwistedEventLoop (optional)</li>
+<li>Tornado for TornadoEventLoop (optional)</li>
+<li>Apache for web_display module (optional)</li>
+<li>ncurses for curses_display module (optional)</li>
+</ul>
+</div>
+
+<div class="section" id="similar">
+<h3>Similar projects</h3>
+<ul>
+<li><a href="http://www.npcole.com/npyscreen/">npyscreen</a></li>
+</ul>
</div>
-<div class="section" id="manual" style="clear:both; padding-top: 8px">
-<h2><a class="reference internal" href="manual/index.html#urwid-manual"><em>Manual</em></a><a class="headerlink" href="#manual" title="Permalink to this headline">¶</a></h2>
-<a class="reference external image-reference" href="manual/index.html#urwid-manual"><img alt="_images/introduction.png" src="_images/introduction.png" width="200" align="left" style="padding-right: 15px" /></a>
-<p>The <a class="reference internal" href="manual/index.html#urwid-manual"><em>Urwid Manual</em></a> discusses each part of the library and how
-everything fits together.</p>
</div>
-<div class="section" id="reference" style="clear:both; padding-top: 8px">
-<h2><a class="reference internal" href="reference/index.html#urwid-reference"><em>Reference</em></a><a class="headerlink" href="#reference" title="Permalink to this headline">¶</a></h2>
-<a class="reference external image-reference" href="reference/index.html#urwid-reference"><img alt="_images/urwid_widgets_1.png" src="_images/urwid_widgets_1.png" width="200" align="left" style="padding-right: 15px" /></a>
-<p>The <a class="reference internal" href="reference/index.html#urwid-reference"><em>Urwid Reference</em></a> documents all the classes and functions in the
-library individually.</p>
+
+<div style="padding-left: 560px;">
+<div class="section" id="big-buttons">
+<p><a class="reference external image-reference" href="examples/index.html"><img alt="_images/graph2.png" src="_images/graph2.png" width="200" style="padding-bottom: 5px" /></br>
+Examples</a></p>
+<p><a class="reference external image-reference" href="tutorial/index.html"><img alt="_images/highcolors1.png" src="_images/highcolors1.png" width="200" style="padding-bottom: 5px" /></br>
+Tutorial</a></p>
+<p><a class="reference external image-reference" href="manual/index.html"><img alt="_images/introduction.png" src="_images/introduction.png" width="200" style="padding-bottom: 5px" /></br>
+Manual</a></p>
+<p><a class="reference external image-reference" href="reference/index.html"><img alt="_images/urwid_widgets_1.png" src="_images/urwid_widgets_1.png" width="200" style="padding-bottom: 5px" /></br>
+Reference</a></p>
+<p><a class="reference internal" href="genindex.html">Index</a></p>
+<p><a class="reference internal" href="search.html">Search Page</a></p>
</div>
-<div class="section" id="indices-and-tables" style="clear: both; padding-top: 8px">
-<h2>Indices and tables<a class="headerlink" href="#indices-and-tables" title="Permalink to this headline">¶</a></h2>
-<ul class="simple">
-<li><a class="reference internal" href="genindex.html"><em>Index</em></a></li>
-<li><a class="reference internal" href="search.html"><em>Search Page</em></a></li>
</div>
+<div style="clear:both">&nbsp;</div>
{% endblock %}
diff --git a/docs/tools/templates/indexsidebar.html b/docs/tools/templates/indexsidebar.html
index cdff25a..252d115 100644
--- a/docs/tools/templates/indexsidebar.html
+++ b/docs/tools/templates/indexsidebar.html
@@ -1,7 +1,8 @@
-<h3>Table of Contents</h4>
+<h3>Documentation</h3>
<div style="padding: 10px 0 16px 10px">
-<h4><a class="reference internal" href="tutorial/index.html#urwid-tutorial">Tutorial</a></h4>
-<h4><a class="reference internal" href="manual/index.html#urwid-manual">Manual</a></h4>
-<h4><a class="reference internal" href="reference/index.html#urwid-reference">Reference</a></h4>
-<h4 style="padding-left: 8px"><a class="reference internal" href="genindex.html"><em>Index</em></a></h4>
+<h4><a class="reference internal" href="examples/index.html">Examples</a></h4>
+<h4><a class="reference internal" href="tutorial/index.html">Tutorial</a></h4>
+<h4><a class="reference internal" href="manual/index.html">Manual</a></h4>
+<h4><a class="reference internal" href="reference/index.html">Reference</a></h4>
+<h4><a class="reference internal" href="genindex.html">Index</a></h4>
</div>
diff --git a/docs/tools/templates/layout.html b/docs/tools/templates/layout.html
deleted file mode 100644
index bba7e42..0000000
--- a/docs/tools/templates/layout.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{% extends "!layout.html" %}
-{% block rootrellink %}
- <li><a href="http://excess.org/urwid/">Urwid Homepage</a> &raquo;</li>
- {{ super() }}
-{% endblock %}
diff --git a/docs/tools/templates/localtoc.html b/docs/tools/templates/localtoc.html
new file mode 100644
index 0000000..0e9bf8a
--- /dev/null
+++ b/docs/tools/templates/localtoc.html
@@ -0,0 +1,18 @@
+{%- if pagename.startswith('manual/') -%}
+ <h3>Table of contents</a></h3>
+ <ul><li><a href="index.html">Manual</a></li>
+ {%- if display_toc -%}
+ {{ toc }}
+ {%- endif %}
+ </ul>
+{%- elif pagename.startswith('reference/') -%}
+ <h3>Table of contents</a></h3>
+ <ul><li><a href="index.html">Reference</a></li>
+ {%- if display_toc -%}
+ {{ toc }}
+ {%- endif %}
+ </ul>
+{%- elif display_toc -%}
+ <h3>Table of contents</a></h3>
+ {{ toc }}
+{% endif %}
diff --git a/docs/tutorial/lbscr.py b/docs/tutorial/lbscr.py
deleted file mode 100644
index 9536549..0000000
--- a/docs/tutorial/lbscr.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import urwid
-
-def show_all_input(keys, raw):
- """make keys pressed visible to the user"""
- show_key.set_text(u"Pressed: " + u" ".join([
- unicode(k) for k in keys]))
- return keys
-
-def exit_on_cr(key):
- if key == 'enter':
- raise urwid.ExitMainLoop()
-
-palette = [('header', 'white', 'black'),
- ('reveal focus', 'black', 'dark cyan', 'standout'),]
-
-div = urwid.Divider(u"-")
-content = urwid.SimpleListWalker([
- urwid.AttrMap(w, None, 'reveal focus') for w in [
- urwid.Text(u"This is a text string that is fairly long"),
- urwid.Divider(u"-"),] + [
- urwid.Text(u"Numbers %d" % i) for i in range(40)] + [
- urwid.Divider(u"-"),
- urwid.Text(u"The end."),]])
-listbox = urwid.ListBox(content)
-show_key = urwid.Text(u"", wrap='clip')
-head = urwid.AttrMap(show_key, 'header')
-top = urwid.Frame(listbox, head)
-loop = urwid.MainLoop(top, palette, input_filter=show_all_input,
- unhandled_input=exit_on_cr)
-loop.run()
diff --git a/docs/tutorial/lbscr.py.xdotool b/docs/tutorial/lbscr.py.xdotool
deleted file mode 100644
index 9f733dd..0000000
--- a/docs/tutorial/lbscr.py.xdotool
+++ /dev/null
@@ -1,9 +0,0 @@
-windowsize --usehints $RXVTWINDOWID 15 7
-key --window $RXVTWINDOWID Down
-key --window $RXVTWINDOWID Down
-key --window $RXVTWINDOWID Down
-key --window $RXVTWINDOWID Up
-key --window $RXVTWINDOWID Down
-windowsize --usehints $RXVTWINDOWID 20 9
-windowsize --usehints $RXVTWINDOWID 25 7
-windowsize --usehints $RXVTWINDOWID 11 13
diff --git a/docs/tutorial/lbscr1.png b/docs/tutorial/lbscr1.png
deleted file mode 100644
index a938a66..0000000
--- a/docs/tutorial/lbscr1.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorial/lbscr2.png b/docs/tutorial/lbscr2.png
deleted file mode 100644
index 130032e..0000000
--- a/docs/tutorial/lbscr2.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorial/lbscr3.png b/docs/tutorial/lbscr3.png
deleted file mode 100644
index 8adf3d9..0000000
--- a/docs/tutorial/lbscr3.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorial/lbscr4.png b/docs/tutorial/lbscr4.png
deleted file mode 100644
index 81aa77d..0000000
--- a/docs/tutorial/lbscr4.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorial/lbscr5.png b/docs/tutorial/lbscr5.png
deleted file mode 100644
index 9861422..0000000
--- a/docs/tutorial/lbscr5.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorial/lbscr6.png b/docs/tutorial/lbscr6.png
deleted file mode 100644
index 81aa77d..0000000
--- a/docs/tutorial/lbscr6.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorial/lbscr7.png b/docs/tutorial/lbscr7.png
deleted file mode 100644
index 9ccac3d..0000000
--- a/docs/tutorial/lbscr7.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorial/lbscr8.png b/docs/tutorial/lbscr8.png
deleted file mode 100644
index 61e7752..0000000
--- a/docs/tutorial/lbscr8.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorial/lbscr9.png b/docs/tutorial/lbscr9.png
deleted file mode 100644
index 9a5f337..0000000
--- a/docs/tutorial/lbscr9.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorial/urwid b/docs/tutorial/urwid
deleted file mode 120000
index 2657fa1..0000000
--- a/docs/tutorial/urwid
+++ /dev/null
@@ -1 +0,0 @@
-../../urwid \ No newline at end of file
diff --git a/examples/calc.py b/examples/calc.py
index a40259f..f132172 100755
--- a/examples/calc.py
+++ b/examples/calc.py
@@ -734,7 +734,7 @@ class CalcDisplay:
# keep only the non-children
self.col_list[i:] = keep_right_cols
- # fix the letter assigmnents
+ # fix the letter assignments
for j in range(i, len(self.col_list)):
col = self.col_list[j]
# fix the column heading
diff --git a/examples/dialog.py b/examples/dialog.py
index 7de86d5..2de61c0 100755
--- a/examples/dialog.py
+++ b/examples/dialog.py
@@ -206,7 +206,7 @@ class ListDialogDisplay(DialogDisplay):
class CheckListDialogDisplay(ListDialogDisplay):
def on_exit(self, exitcode):
"""
- Mimick dialog(1)'s --checklist exit.
+ Mimic dialog(1)'s --checklist exit.
Put each checked item in double quotes with a trailing space.
"""
if exitcode != 0:
diff --git a/examples/fib.py b/examples/fib.py
index 4439129..7f8d4b7 100755
--- a/examples/fib.py
+++ b/examples/fib.py
@@ -32,7 +32,7 @@ import urwid
class FibonacciWalker(urwid.ListWalker):
"""ListWalker-compatible class for browsing fibonacci set.
- positions returned are (value at position-1, value at poistion) tuples.
+ positions returned are (value at position-1, value at position) tuples.
"""
def __init__(self):
self.focus = (0L,1L)
diff --git a/examples/lcd_cf635.py b/examples/lcd_cf635.py
index e01ead2..77e8f17 100755
--- a/examples/lcd_cf635.py
+++ b/examples/lcd_cf635.py
@@ -104,7 +104,7 @@ class LCDProgressBar(urwid.FlowWidget):
Update and return the value one step +ve or -ve, based on
the size of the displayed bar.
- directon -- 1 for +ve, 0 for -ve
+ direction -- 1 for +ve, 0 for -ve
"""
steps = self.get_steps(size)
filled = urwid.int_scale(self.value, self.range, steps)
@@ -249,7 +249,7 @@ def build_menus():
urwid.Text("This is a demo of Urwid's CF635Display "
"module. If you need an interface for a limited "
"character display device this should serve as a "
- "good example for implmenting your own display "
+ "good example for implementing your own display "
"module and menu-driven application."),
])
]
diff --git a/examples/subproc.py b/examples/subproc.py
index 28f5497..64eb072 100755
--- a/examples/subproc.py
+++ b/examples/subproc.py
@@ -2,8 +2,11 @@
import subprocess
import urwid
+import os
+import sys
factor_me = 362923067964327863989661926737477737673859044111968554257667
+run_me = os.path.join(os.path.dirname(sys.argv[0]), 'subproc2.py')
output_widget = urwid.Text("Factors of %d:\n" % factor_me)
edit_widget = urwid.Edit("Type anything or press enter to exit:")
@@ -22,7 +25,7 @@ def received_output(data):
write_fd = loop.watch_pipe(received_output)
proc = subprocess.Popen(
- ['python', '-u', 'subproc2.py', str(factor_me)],
+ ['python', '-u', run_me, str(factor_me)],
stdout=write_fd,
close_fds=True)
diff --git a/examples/tour.py b/examples/tour.py
index d0a9a48..fedfb59 100755
--- a/examples/tour.py
+++ b/examples/tour.py
@@ -122,7 +122,7 @@ def main():
text_cb_list = [u"Wax", u"Wash", u"Buff", u"Clear Coat", u"Dry",
u"Racing Stripe"]
text_rb_list = [u"Morning", u"Afternoon", u"Evening", u"Weekend"]
- text_listbox = [u"All these widgets have been diplayed "
+ text_listbox = [u"All these widgets have been displayed "
u"with the help of a ", ('important', u"ListBox"), u" widget. "
u"ListBox widgets handle scrolling and changing focus. A ",
('important', u"Frame"), u" widget is used to keep the "
diff --git a/examples/twisted_serve_ssh.py b/examples/twisted_serve_ssh.py
index 9562bc6..36396dd 100644
--- a/examples/twisted_serve_ssh.py
+++ b/examples/twisted_serve_ssh.py
@@ -168,7 +168,7 @@ class TwistedScreen(urwid.BaseScreen):
1. Input
2. Output
- Input is achieved in normal urwid by passing a lsit of available readable
+ Input is achieved in normal urwid by passing a list of available readable
file descriptors to the event loop for polling/selecting etc. In the
Twisted situation, this is not necessary because Twisted polls the input
descriptors itself. Urwid allows this by being driven using the main loop
@@ -221,14 +221,17 @@ class TwistedScreen(urwid.BaseScreen):
self.terminal.cursorPosition(*cursor)
# XXX from base screen
- def set_mouse_tracking(self):
+ def set_mouse_tracking(self, enable=True):
"""
- Enable mouse tracking.
+ Enable (or disable) mouse tracking.
After calling this function get_input will include mouse
click events along with keystrokes.
"""
- self.write(urwid.escape.MOUSE_TRACKING_ON)
+ if enable:
+ self.write(urwid.escape.MOUSE_TRACKING_ON)
+ else:
+ self.write(urwid.escape.MOUSE_TRACKING_OFF)
# twisted handles polling, so we don't need the loop to do it, we just
# push what we get to the loop from dataReceived.
@@ -262,7 +265,7 @@ class TwistedScreen(urwid.BaseScreen):
# Private
def _on_update_palette_entry(self, name, *attrspecs):
- # copy the attribute to a dictionary containing the escape seqences
+ # copy the attribute to a dictionary containing the escape sequences
self._pal_escape[name] = self._attrspec_to_escape(
attrspecs[{16:0,1:1,88:2,256:3}[self.colors]])
diff --git a/examples/urwid b/examples/urwid
deleted file mode 120000
index 1272936..0000000
--- a/examples/urwid
+++ /dev/null
@@ -1 +0,0 @@
-../urwid \ No newline at end of file
diff --git a/setup.py b/setup.py
index 60161bc..6a4d61b 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
#
# Urwid setup.py exports the useful bits
-# Copyright (C) 2004-2012 Ian Ward
+# Copyright (C) 2004-2014 Ian Ward
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -44,37 +44,13 @@ setup_d = {
'author':"Ian Ward",
'author_email':"ian@excess.org",
'ext_modules':[Extension('urwid.str_util', sources=['source/str_util.c'])],
- 'packages':['urwid'],
- 'url':"http://excess.org/urwid/",
- 'download_url':"http://excess.org/urwid/urwid-%s.tar.gz"%release,
+ 'packages':['urwid', 'urwid.tests'],
+ 'url':"http://urwid.org/",
'license':"LGPL",
'keywords':"curses ui widget scroll listbox user interface text layout console ncurses",
'platforms':"unix-like",
'description': "A full-featured console (xterm et al.) user interface library",
- 'long_description':"""
-Urwid is a console user interface library. It includes many features
-useful for text console application developers including:
-
-- Applcations resize quickly and smoothly
-- Automatic, programmable text alignment and wrapping
-- Simple markup for setting text attributes within blocks of text
-- Powerful list box with programmable content for scrolling all widget types
-- Your choice of event loops: Twisted, Glib or built-in select-based loop
-- Pre-built widgets include edit boxes, buttons, check boxes and radio buttons
-- Display modules include raw, curses, and experimental LCD and web displays
-- Support for UTF-8, simple 8-bit and CJK encodings
-- 256 and 88 color mode support
-- Python 3.2 support
-
-Home Page:
- http://excess.org/urwid/
-
-Documentation:
- http://excess.org/urwid/docs/
-
-Example Program Screenshots:
- http://excess.org/urwid/examples.html
-"""[1:],
+ 'long_description':open("README.rst").read().split('.. content-start\n',1)[1],
'classifiers':[
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
@@ -87,13 +63,13 @@ Example Program Screenshots:
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Widget Sets",
"Programming Language :: Python :: 2",
- "Programming Language :: Python :: 2.4",
- "Programming Language :: Python :: 2.5",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.2",
"Programming Language :: Python :: 3.3",
+ "Programming Language :: Python :: 3.4",
+ "Programming Language :: Python :: Implementation :: PyPy",
],
}
diff --git a/urwid/__init__.py b/urwid/__init__.py
index 899a4da..a6cacf8 100644
--- a/urwid/__init__.py
+++ b/urwid/__init__.py
@@ -52,9 +52,10 @@ from urwid.command_map import (CommandMap, command_map,
REDRAW_SCREEN, CURSOR_UP, CURSOR_DOWN, CURSOR_LEFT, CURSOR_RIGHT,
CURSOR_PAGE_UP, CURSOR_PAGE_DOWN, CURSOR_MAX_LEFT, CURSOR_MAX_RIGHT,
ACTIVATE)
-from urwid.main_loop import ExitMainLoop, MainLoop, SelectEventLoop
+from urwid.main_loop import (ExitMainLoop, MainLoop, SelectEventLoop,
+ GLibEventLoop, TornadoEventLoop)
try:
- from urwid.main_loop import GLibEventLoop, TwistedEventLoop
+ from urwid.main_loop import TwistedEventLoop
except ImportError:
pass
from urwid.text_layout import (TextLayout, StandardTextLayout, default_layout,
diff --git a/urwid/canvas.py b/urwid/canvas.py
index 593b252..d27e7ab 100644
--- a/urwid/canvas.py
+++ b/urwid/canvas.py
@@ -972,7 +972,7 @@ def shards_trim_top(shards, top):
shard_tail = shard_body_tail(num_rows, sbody)
top -= num_rows
else:
- raise CanvasError("tried to trim shards out of existance")
+ raise CanvasError("tried to trim shards out of existence")
sbody = shard_body(cviews, shard_tail, False)
shard_tail = shard_body_tail(num_rows, sbody)
diff --git a/urwid/container.py b/urwid/container.py
index e0fede7..aa81614 100755
--- a/urwid/container.py
+++ b/urwid/container.py
@@ -360,6 +360,10 @@ class GridFlow(WidgetWrap, WidgetContainerMixin, WidgetContainerListContentsMixi
pad.original_widget=w
pad.width = used_space - self.h_sep
+ if self.v_sep:
+ # remove first divider
+ del p.contents[:1]
+
return p
def _set_focus_from_display_widget(self):
@@ -976,7 +980,7 @@ class Frame(Widget, WidgetContainerMixin):
This object may be used to read or update the contents of the Frame.
- The values are similar to the the list-like .contents objects used
+ The values are similar to the list-like .contents objects used
in other containers with (:class:`Widget`, options) tuples, but are
constrained to keys for each of the three usual parts of a Frame.
When other keys are used a :exc:`KeyError` will be raised.
@@ -1477,9 +1481,11 @@ class Pile(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
elif f == GIVEN:
l.append(height)
remaining -= height
- else:
+ elif height:
l.append(None)
wtotal += height
+ else:
+ l.append(0) # zero-weighted items treated as ('given', 0)
if wtotal == 0:
raise PileError, "No weighted widgets found for Pile treated as a box widget"
@@ -1661,6 +1667,8 @@ class Pile(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
if wrow + r > row:
break
wrow += r
+ else:
+ return False
focus = focus and self.focus_item == w
if is_mouse_press(event) and button == 1:
@@ -1716,7 +1724,7 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
are treated as box widgets, and *box_columns* is ignored.
If the Columns widget is treated as a flow widget then the rows
- are calcualated as the largest rows() returned from all columns
+ are calculated as the largest rows() returned from all columns
except the ones listed in *box_columns*. The box widgets in
*box_columns* will be displayed with this calculated number of rows,
filling the full height.
@@ -1964,7 +1972,7 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
raise IndexError, "No Columns child widget at position %s" % (position,)
self.contents.focus = position
focus_position = property(_get_focus_position, _set_focus_position, doc="""
- index of child widget in focus. Raises IndexError if read when
+ index of child widget in focus. Raises :exc:`IndexError` if read when
Columns is empty, or when set to an invalid index.
""")
@@ -1985,7 +1993,8 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
maxcol = size[0]
# FIXME: get rid of this check and recalculate only when
# a 'pack' widget has been modified.
- if maxcol == self._cache_maxcol and not PACK in self.column_types:
+ if maxcol == self._cache_maxcol and not any(
+ t == PACK for w, (t, n, b) in self.contents):
return self._cache_column_widths
widths = []
diff --git a/urwid/curses_display.py b/urwid/curses_display.py
index dba940b..b7f1cea 100755
--- a/urwid/curses_display.py
+++ b/urwid/curses_display.py
@@ -70,23 +70,37 @@ class Screen(BaseScreen, RealTerminal):
self.prev_input_resize = 0
self.set_input_timeouts()
self.last_bstate = 0
+ self._mouse_tracking_enabled = False
self.register_palette_entry(None, 'default','default')
- def set_mouse_tracking(self):
+ def set_mouse_tracking(self, enable=True):
"""
Enable mouse tracking.
After calling this function get_input will include mouse
click events along with keystrokes.
"""
- curses.mousemask(0
- | curses.BUTTON1_PRESSED | curses.BUTTON1_RELEASED
- | curses.BUTTON2_PRESSED | curses.BUTTON2_RELEASED
- | curses.BUTTON3_PRESSED | curses.BUTTON3_RELEASED
- | curses.BUTTON4_PRESSED | curses.BUTTON4_RELEASED
- | curses.BUTTON_SHIFT | curses.BUTTON_ALT
- | curses.BUTTON_CTRL)
+ enable = bool(enable)
+ if enable == self._mouse_tracking_enabled:
+ return
+
+ if enable:
+ curses.mousemask(0
+ | curses.BUTTON1_PRESSED | curses.BUTTON1_RELEASED
+ | curses.BUTTON2_PRESSED | curses.BUTTON2_RELEASED
+ | curses.BUTTON3_PRESSED | curses.BUTTON3_RELEASED
+ | curses.BUTTON4_PRESSED | curses.BUTTON4_RELEASED
+ | curses.BUTTON1_DOUBLE_CLICKED | curses.BUTTON1_TRIPLE_CLICKED
+ | curses.BUTTON2_DOUBLE_CLICKED | curses.BUTTON2_TRIPLE_CLICKED
+ | curses.BUTTON3_DOUBLE_CLICKED | curses.BUTTON3_TRIPLE_CLICKED
+ | curses.BUTTON4_DOUBLE_CLICKED | curses.BUTTON4_TRIPLE_CLICKED
+ | curses.BUTTON_SHIFT | curses.BUTTON_ALT
+ | curses.BUTTON_CTRL)
+ else:
+ raise NotImplementedError()
+
+ self._mouse_tracking_enabled = enable
def start(self):
"""
@@ -397,6 +411,24 @@ class Screen(BaseScreen, RealTerminal):
append_button( 64 + escape.MOUSE_RELEASE_FLAG )
next &= ~ 8
+ if bstate & curses.BUTTON1_DOUBLE_CLICKED:
+ append_button( 0 + escape.MOUSE_MULTIPLE_CLICK_FLAG )
+ if bstate & curses.BUTTON2_DOUBLE_CLICKED:
+ append_button( 1 + escape.MOUSE_MULTIPLE_CLICK_FLAG )
+ if bstate & curses.BUTTON3_DOUBLE_CLICKED:
+ append_button( 2 + escape.MOUSE_MULTIPLE_CLICK_FLAG )
+ if bstate & curses.BUTTON4_DOUBLE_CLICKED:
+ append_button( 64 + escape.MOUSE_MULTIPLE_CLICK_FLAG )
+
+ if bstate & curses.BUTTON1_TRIPLE_CLICKED:
+ append_button( 0 + escape.MOUSE_MULTIPLE_CLICK_FLAG*2 )
+ if bstate & curses.BUTTON2_TRIPLE_CLICKED:
+ append_button( 1 + escape.MOUSE_MULTIPLE_CLICK_FLAG*2 )
+ if bstate & curses.BUTTON3_TRIPLE_CLICKED:
+ append_button( 2 + escape.MOUSE_MULTIPLE_CLICK_FLAG*2 )
+ if bstate & curses.BUTTON4_TRIPLE_CLICKED:
+ append_button( 64 + escape.MOUSE_MULTIPLE_CLICK_FLAG*2 )
+
self.last_bstate = next
return l
diff --git a/urwid/decoration.py b/urwid/decoration.py
index 190c402..731eb91 100755
--- a/urwid/decoration.py
+++ b/urwid/decoration.py
@@ -310,7 +310,7 @@ class AttrWrap(AttrMap):
"""
Call getattr on wrapped widget. This has been the longstanding
behaviour of AttrWrap, but is discouraged. New code should be
- using AttrMap and .base_widget or .original_widget instad.
+ using AttrMap and .base_widget or .original_widget instead.
"""
return getattr(self._original_widget, name)
@@ -447,7 +447,7 @@ class Padding(WidgetDecoration):
:param left: a fixed number of columns to pad on the left
:type left: int
- :param right: a fixed number of columns to pad on thr right
+ :param right: a fixed number of columns to pad on the right
:type right: int
Clipping Mode: (width= ``'clip'``)
@@ -537,11 +537,12 @@ class Padding(WidgetDecoration):
"""
self._align_type, self._align_amount = normalize_align(align,
PaddingError)
+ self._invalidate()
align = property(_get_align, _set_align)
def _get_width(self):
"""
- Return the padding widthment setting.
+ Return the padding width.
"""
return simplify_width(self._width_type, self._width_amount)
def _set_width(self, width):
@@ -550,6 +551,7 @@ class Padding(WidgetDecoration):
"""
self._width_type, self._width_amount = normalize_width(width,
PaddingError)
+ self._invalidate()
width = property(_get_width, _set_width)
def render(self, size, focus=False):
diff --git a/urwid/display_common.py b/urwid/display_common.py
index 8e8c0a8..cbe433f 100755
--- a/urwid/display_common.py
+++ b/urwid/display_common.py
@@ -657,7 +657,7 @@ class RealTerminal(object):
def tty_signal_keys(self, intr=None, quit=None, start=None,
stop=None, susp=None, fileno=None):
"""
- Read and/or set the tty's signal charater settings.
+ Read and/or set the tty's signal character settings.
This function returns the current settings as a tuple.
Use the string 'undefined' to unmap keys from their signals.
@@ -820,18 +820,29 @@ class BaseScreen(object):
if mono is None:
mono = DEFAULT
mono = AttrSpec(mono, DEFAULT, 1)
-
+
if foreground_high is None:
foreground_high = foreground
if background_high is None:
background_high = background
- high_88 = AttrSpec(foreground_high, background_high, 88)
high_256 = AttrSpec(foreground_high, background_high, 256)
+ # 'hX' where X > 15 are different in 88/256 color, use
+ # basic colors for 88-color mode if high colors are specified
+ # in this way (also avoids crash when X > 87)
+ def large_h(desc):
+ if not desc.startswith('h'):
+ return False
+ num = int(desc[1:], 10)
+ return num > 15
+ if large_h(foreground_high) or large_h(background_high):
+ high_88 = basic
+ else:
+ high_88 = AttrSpec(foreground_high, background_high, 88)
+
signals.emit_signal(self, UPDATE_PALETTE_ENTRY,
name, basic, mono, high_88, high_256)
self._palette[name] = (basic, mono, high_88, high_256)
-
def _test():
diff --git a/urwid/escape.py b/urwid/escape.py
index 6df3d86..bf34921 100644
--- a/urwid/escape.py
+++ b/urwid/escape.py
@@ -180,6 +180,8 @@ class KeyqueueTrie(object):
if b & 4: prefix = prefix + "shift "
if b & 8: prefix = prefix + "meta "
if b & 16: prefix = prefix + "ctrl "
+ if (b & MOUSE_MULTIPLE_CLICK_MASK)>>9 == 1: prefix = prefix + "double "
+ if (b & MOUSE_MULTIPLE_CLICK_MASK)>>9 == 2: prefix = prefix + "triple "
# 0->1, 1->2, 2->3, 64->4, 65->5
button = ((b&64)/64*3) + (b & 3) + 1
@@ -191,6 +193,8 @@ class KeyqueueTrie(object):
action = "release"
elif b & MOUSE_DRAG_FLAG:
action = "drag"
+ elif b & MOUSE_MULTIPLE_CLICK_MASK:
+ action = "click"
else:
action = "press"
@@ -251,6 +255,16 @@ class KeyqueueTrie(object):
# and raw_display when we know which button was released. NON-STANDARD
MOUSE_RELEASE_FLAG = 2048
+# This 2-bit mask is used to check if the mouse release from curses or gpm
+# is a double or triple release. 00 means single click, 01 double,
+# 10 triple. NON-STANDARD
+MOUSE_MULTIPLE_CLICK_MASK = 1536
+
+# This is added to button value at mouse release to differentiate between
+# single, double and triple press. Double release adds this times one,
+# triple release adds this times two. NON-STANDARD
+MOUSE_MULTIPLE_CLICK_FLAG = 512
+
# xterm adds this to the button value to signal a mouse drag event
MOUSE_DRAG_FLAG = 32
diff --git a/urwid/graphics.py b/urwid/graphics.py
index adcff72..d3dd277 100755
--- a/urwid/graphics.py
+++ b/urwid/graphics.py
@@ -802,7 +802,7 @@ class ProgressBar(Widget):
def __init__(self, normal, complete, current=0, done=100, satt=None):
"""
- :param normal: display attribute for uncomplete part of progress bar
+ :param normal: display attribute for incomplete part of progress bar
:param complete: display attribute for complete part of progress bar
:param current: current progress
:param done: progress amount at 100%
diff --git a/urwid/html_fragment.py b/urwid/html_fragment.py
index 9aba906..7ba835e 100755
--- a/urwid/html_fragment.py
+++ b/urwid/html_fragment.py
@@ -66,7 +66,7 @@ class HtmlGenerator(BaseScreen):
self.bright_is_bold = bright_is_bold
self.has_underline = has_underline
- def set_mouse_tracking(self):
+ def set_mouse_tracking(self, enable=True):
"""Not yet implemented"""
pass
diff --git a/urwid/lcd_display.py b/urwid/lcd_display.py
index c66338d..71a1cfa 100644
--- a/urwid/lcd_display.py
+++ b/urwid/lcd_display.py
@@ -30,7 +30,7 @@ class LCDScreen(BaseScreen):
has_underline=None):
pass
- def set_mouse_tracking(self):
+ def set_mouse_tracking(self, enable=True):
pass
def start(self):
diff --git a/urwid/listbox.py b/urwid/listbox.py
index e954f72..5370e23 100644
--- a/urwid/listbox.py
+++ b/urwid/listbox.py
@@ -821,7 +821,7 @@ class ListBox(Widget, WidgetContainerMixin):
of the focus widget is aligned with the top edge of the
listbox (default if unspecified)
:type offset_inset: int
- :param coming_from: eiter 'above', 'below' or unspecified `None`
+ :param coming_from: either 'above', 'below' or unspecified `None`
:type coming_from: str
:param cursor_coords: (x, y) tuple indicating the desired
column and row for the cursor, a (x,) tuple indicating only
diff --git a/urwid/main_loop.py b/urwid/main_loop.py
index 4e83113..cfc8f20 100755
--- a/urwid/main_loop.py
+++ b/urwid/main_loop.py
@@ -26,6 +26,8 @@ import time
import heapq
import select
import os
+from functools import wraps
+from weakref import WeakKeyDictionary
try:
import fcntl
@@ -163,7 +165,7 @@ class MainLoop(object):
def set_alarm_at(self, tm, callback, user_data=None):
"""
Schedule an alarm at *tm* time that will call *callback* from the
- within the :meth`run` function. Returns a handle that may be passed to
+ within the :meth:`run` function. Returns a handle that may be passed to
:meth:`remove_alarm`.
:param tm: time to call callback e.g. ``time.time() + 5``
@@ -568,9 +570,6 @@ class MainLoop(object):
self.screen.draw_screen(self.screen_size, canvas)
-
-
-
class SelectEventLoop(object):
"""
Event loop based on :func:`select.select`
@@ -582,24 +581,6 @@ class SelectEventLoop(object):
self._idle_handle = 0
self._idle_callbacks = {}
- def _test_event_loop(self):
- """
- >>> import os
- >>> rd, wr = os.pipe()
- >>> evl = SelectEventLoop()
- >>> def step1():
- ... print "writing"
- ... os.write(wr, "hi".encode('ascii'))
- >>> def step2():
- ... print os.read(rd, 2).decode('ascii')
- ... raise ExitMainLoop
- >>> handle = evl.alarm(0, step1)
- >>> handle = evl.watch_file(rd, step2)
- >>> evl.run()
- writing
- hi
- """
-
def alarm(self, seconds, callback):
"""
Call callback() given time from from now. No parameters are
@@ -627,16 +608,6 @@ class SelectEventLoop(object):
except ValueError:
return False
- def _test_remove_alarm(self):
- """
- >>> evl = SelectEventLoop()
- >>> handle = evl.alarm(50, lambda: None)
- >>> evl.remove_alarm(handle)
- True
- >>> evl.remove_alarm(handle)
- False
- """
-
def watch_file(self, fd, callback):
"""
Call callback() when fd has some data to read. No parameters
@@ -661,16 +632,6 @@ class SelectEventLoop(object):
return True
return False
- def _test_remove_watch_file(self):
- """
- >>> evl = SelectEventLoop()
- >>> handle = evl.watch_file(5, lambda: None)
- >>> evl.remove_watch_file(handle)
- True
- >>> evl.remove_watch_file(handle)
- False
- """
-
def enter_idle(self, callback):
"""
Add a callback for entering idle.
@@ -717,48 +678,6 @@ class SelectEventLoop(object):
except ExitMainLoop:
pass
- def _test_run(self):
- """
- >>> import os
- >>> rd, wr = os.pipe()
- >>> os.write(wr, "data".encode('ascii')) # something to read from rd
- 4
- >>> evl = SelectEventLoop()
- >>> def say_hello():
- ... print "hello"
- >>> def say_waiting():
- ... print "waiting"
- >>> def exit_clean():
- ... print "clean exit"
- ... raise ExitMainLoop
- >>> def exit_error():
- ... 1/0
- >>> handle = evl.alarm(0.01, exit_clean)
- >>> handle = evl.alarm(0.005, say_hello)
- >>> evl.enter_idle(say_waiting)
- 1
- >>> evl.run()
- waiting
- hello
- waiting
- clean exit
- >>> handle = evl.watch_file(rd, exit_clean)
- >>> evl.run()
- clean exit
- >>> evl.remove_watch_file(handle)
- True
- >>> handle = evl.alarm(0, exit_error)
- >>> evl.run()
- Traceback (most recent call last):
- ...
- ZeroDivisionError: integer division or modulo by zero
- >>> handle = evl.watch_file(rd, exit_error)
- >>> evl.run()
- Traceback (most recent call last):
- ...
- ZeroDivisionError: integer division or modulo by zero
- """
-
def _loop(self):
"""
A single iteration of the event loop
@@ -792,484 +711,493 @@ class SelectEventLoop(object):
self._did_something = True
-if not PYTHON3:
- class GLibEventLoop(object):
+class GLibEventLoop(object):
+ """
+ Event loop based on GLib.MainLoop
+ """
+
+ def __init__(self):
+ from gi.repository import GLib
+ self.GLib = GLib
+ self._alarms = []
+ self._watch_files = {}
+ self._idle_handle = 0
+ self._glib_idle_enabled = False # have we called glib.idle_add?
+ self._idle_callbacks = {}
+ self._loop = GLib.MainLoop()
+ self._exc_info = None
+ self._enable_glib_idle()
+
+ def alarm(self, seconds, callback):
+ """
+ Call callback() given time from from now. No parameters are
+ passed to callback.
+
+ Returns a handle that may be passed to remove_alarm()
+
+ seconds -- floating point time to wait before calling callback
+ callback -- function to call from event loop
"""
- Event loop based on gobject.MainLoop
+ @self.handle_exit
+ def ret_false():
+ callback()
+ self._enable_glib_idle()
+ return False
+ fd = self.GLib.timeout_add(int(seconds*1000), ret_false)
+ self._alarms.append(fd)
+ return (fd, callback)
+
+ def remove_alarm(self, handle):
"""
+ Remove an alarm.
- def __init__(self):
- import gobject
- self.gobject = gobject
- self._alarms = []
- self._watch_files = {}
- self._idle_handle = 0
- self._glib_idle_enabled = False # have we called glib.idle_add?
- self._idle_callbacks = {}
- self._loop = self.gobject.MainLoop()
- self._exc_info = None
+ Returns True if the alarm exists, False otherwise
+ """
+ try:
+ self._alarms.remove(handle[0])
+ self.GLib.source_remove(handle[0])
+ return True
+ except ValueError:
+ return False
+
+ def watch_file(self, fd, callback):
+ """
+ Call callback() when fd has some data to read. No parameters
+ are passed to callback.
+
+ Returns a handle that may be passed to remove_watch_file()
+
+ fd -- file descriptor to watch for input
+ callback -- function to call when input is available
+ """
+ @self.handle_exit
+ def io_callback(source, cb_condition):
+ callback()
self._enable_glib_idle()
+ return True
+ self._watch_files[fd] = \
+ self.GLib.io_add_watch(fd,self.GLib.IO_IN,io_callback)
+ return fd
- def _test_event_loop(self):
- """
- >>> import os
- >>> rd, wr = os.pipe()
- >>> evl = GLibEventLoop()
- >>> def step1():
- ... print "writing"
- ... os.write(wr, "hi")
- >>> def step2():
- ... print os.read(rd, 2)
- ... raise ExitMainLoop
- >>> handle = evl.alarm(0, step1)
- >>> handle = evl.watch_file(rd, step2)
- >>> evl.run()
- writing
- hi
- """
-
- def alarm(self, seconds, callback):
- """
- Call callback() given time from from now. No parameters are
- passed to callback.
-
- Returns a handle that may be passed to remove_alarm()
-
- seconds -- floating point time to wait before calling callback
- callback -- function to call from event loop
- """
- @self.handle_exit
- def ret_false():
- callback()
- self._enable_glib_idle()
- return False
- fd = self.gobject.timeout_add(int(seconds*1000), ret_false)
- self._alarms.append(fd)
- return (fd, callback)
-
- def remove_alarm(self, handle):
- """
- Remove an alarm.
-
- Returns True if the alarm exists, False otherwise
- """
+ def remove_watch_file(self, handle):
+ """
+ Remove an input file.
+
+ Returns True if the input file exists, False otherwise
+ """
+ if handle in self._watch_files:
+ self.GLib.source_remove(self._watch_files[handle])
+ del self._watch_files[handle]
+ return True
+ return False
+
+ def enter_idle(self, callback):
+ """
+ Add a callback for entering idle.
+
+ Returns a handle that may be passed to remove_enter_idle()
+ """
+ self._idle_handle += 1
+ self._idle_callbacks[self._idle_handle] = callback
+ return self._idle_handle
+
+ def _enable_glib_idle(self):
+ if self._glib_idle_enabled:
+ return
+ self.GLib.idle_add(self._glib_idle_callback)
+ self._glib_idle_enabled = True
+
+ def _glib_idle_callback(self):
+ for callback in self._idle_callbacks.values():
+ callback()
+ self._glib_idle_enabled = False
+ return False # ask glib not to call again (or we would be called
+
+ def remove_enter_idle(self, handle):
+ """
+ Remove an idle callback.
+
+ Returns True if the handle was removed.
+ """
+ try:
+ del self._idle_callbacks[handle]
+ except KeyError:
+ return False
+ return True
+
+ def run(self):
+ """
+ Start the event loop. Exit the loop when any callback raises
+ an exception. If ExitMainLoop is raised, exit cleanly.
+ """
+ try:
+ self._loop.run()
+ finally:
+ if self._loop.is_running():
+ self._loop.quit()
+ if self._exc_info:
+ # An exception caused us to exit, raise it now
+ exc_info = self._exc_info
+ self._exc_info = None
+ raise exc_info[0], exc_info[1], exc_info[2]
+
+ def handle_exit(self,f):
+ """
+ Decorator that cleanly exits the :class:`GLibEventLoop` if
+ :exc:`ExitMainLoop` is thrown inside of the wrapped function. Store the
+ exception info if some other exception occurs, it will be reraised after
+ the loop quits.
+
+ *f* -- function to be wrapped
+ """
+ def wrapper(*args,**kargs):
try:
- self._alarms.remove(handle[0])
- self.gobject.source_remove(handle[0])
- return True
- except ValueError:
- return False
-
- def _test_remove_alarm(self):
- """
- >>> evl = GLibEventLoop()
- >>> handle = evl.alarm(50, lambda: None)
- >>> evl.remove_alarm(handle)
- True
- >>> evl.remove_alarm(handle)
- False
- """
-
- def watch_file(self, fd, callback):
- """
- Call callback() when fd has some data to read. No parameters
- are passed to callback.
-
- Returns a handle that may be passed to remove_watch_file()
-
- fd -- file descriptor to watch for input
- callback -- function to call when input is available
- """
- @self.handle_exit
- def io_callback(source, cb_condition):
- callback()
- self._enable_glib_idle()
- return True
- self._watch_files[fd] = \
- self.gobject.io_add_watch(fd,self.gobject.IO_IN,io_callback)
- return fd
-
- def remove_watch_file(self, handle):
- """
- Remove an input file.
-
- Returns True if the input file exists, False otherwise
- """
- if handle in self._watch_files:
- self.gobject.source_remove(self._watch_files[handle])
- del self._watch_files[handle]
- return True
+ return f(*args,**kargs)
+ except ExitMainLoop:
+ self._loop.quit()
+ except:
+ import sys
+ self._exc_info = sys.exc_info()
+ if self._loop.is_running():
+ self._loop.quit()
return False
+ return wrapper
- def _test_remove_watch_file(self):
- """
- >>> evl = GLibEventLoop()
- >>> handle = evl.watch_file(1, lambda: None)
- >>> evl.remove_watch_file(handle)
- True
- >>> evl.remove_watch_file(handle)
- False
- """
-
- def enter_idle(self, callback):
- """
- Add a callback for entering idle.
-
- Returns a handle that may be passed to remove_enter_idle()
- """
- self._idle_handle += 1
- self._idle_callbacks[self._idle_handle] = callback
- return self._idle_handle
-
- def _enable_glib_idle(self):
- if self._glib_idle_enabled:
- return
- self.gobject.idle_add(self._glib_idle_callback)
- self._glib_idle_enabled = True
-
- def _glib_idle_callback(self):
- for callback in self._idle_callbacks.values():
- callback()
- self._glib_idle_enabled = False
- return False # ask glib not to call again (or we would be called
- def remove_enter_idle(self, handle):
- """
- Remove an idle callback.
+class TornadoEventLoop(object):
+ """ This is an Urwid-specific event loop to plug into its MainLoop.
+ It acts as an adaptor for Tornado's IOLoop which does all
+ heavy lifting except idle-callbacks.
- Returns True if the handle was removed.
- """
+ Notice, since Tornado has no concept of idle callbacks we
+ monkey patch ioloop._impl.poll() function to be able to detect
+ potential idle periods.
+ """
+ _ioloop_registry = WeakKeyDictionary() # {<ioloop> : {<handle> : <idle_func>}}
+ _max_idle_handle = 0
+
+ class PollProxy(object):
+ """ A simple proxy for a Python's poll object that wraps the .poll() method
+ in order to detect idle periods and call Urwid callbacks
+ """
+ def __init__(self, poll_obj, idle_map):
+ self.__poll_obj = poll_obj
+ self.__idle_map = idle_map
+ self._idle_done = False
+ self._prev_timeout = 0
+
+ def __getattr__(self, name):
+ return getattr(self.__poll_obj, name)
+
+ def poll(self, timeout):
+ if timeout > self._prev_timeout:
+ # if timeout increased we assume a timer event was handled
+ self._idle_done = False
+ self._prev_timeout = timeout
+ start = time.time()
+
+ # any IO pending wins
+ events = self.__poll_obj.poll(0)
+ if events:
+ self._idle_done = False
+ return events
+
+ # our chance to enter idle
+ if not self._idle_done:
+ for callback in self.__idle_map.values():
+ callback()
+ self._idle_done = True
+
+ # then complete the actual request (adjusting timeout)
+ timeout = max(0, min(timeout, timeout + start - time.time()))
+ events = self.__poll_obj.poll(timeout)
+ if events:
+ self._idle_done = False
+ return events
+
+ @classmethod
+ def _patch_poll_impl(cls, ioloop):
+ """ Wraps original poll object in the IOLoop's poll object
+ """
+ if ioloop in cls._ioloop_registry:
+ return # we already patched this instance
+
+ cls._ioloop_registry[ioloop] = idle_map = {}
+ ioloop._impl = cls.PollProxy(ioloop._impl, idle_map)
+
+ def __init__(self, ioloop=None):
+ if not ioloop:
+ from tornado.ioloop import IOLoop
+ ioloop = IOLoop.instance()
+ self._ioloop = ioloop
+ self._patch_poll_impl(ioloop)
+ self._pending_alarms = {}
+ self._watch_handles = {} # {<watch_handle> : <file_descriptor>}
+ self._max_watch_handle = 0
+ self._exception = None
+
+ def alarm(self, secs, callback):
+ ioloop = self._ioloop
+ def wrapped():
try:
- del self._idle_callbacks[handle]
+ del self._pending_alarms[handle]
except KeyError:
- return False
+ pass
+ self.handle_exit(callback)()
+ handle = ioloop.add_timeout(ioloop.time() + secs, wrapped)
+ self._pending_alarms[handle] = 1
+ return handle
+
+ def remove_alarm(self, handle):
+ self._ioloop.remove_timeout(handle)
+ try:
+ del self._pending_alarms[handle]
+ except KeyError:
+ return False
+ else:
return True
+ def watch_file(self, fd, callback):
+ from tornado.ioloop import IOLoop
+ handler = lambda fd,events: self.handle_exit(callback)()
+ self._ioloop.add_handler(fd, handler, IOLoop.READ)
+ self._max_watch_handle += 1
+ handle = self._max_watch_handle
+ self._watch_handles[handle] = fd
+ return handle
- def run(self):
- """
- Start the event loop. Exit the loop when any callback raises
- an exception. If ExitMainLoop is raised, exit cleanly.
- """
+ def remove_watch_file(self, handle):
+ fd = self._watch_handles.pop(handle, None)
+ if fd is None:
+ return False
+ else:
+ self._ioloop.remove_handler(fd)
+ return True
+
+ def enter_idle(self, callback):
+ self._max_idle_handle += 1
+ handle = self._max_idle_handle
+ idle_map = self._ioloop_registry[self._ioloop]
+ idle_map[handle] = callback
+ return handle
+
+ def remove_enter_idle(self, handle):
+ idle_map = self._ioloop_registry[self._ioloop]
+ cb = idle_map.pop(handle, None)
+ return cb is not None
+
+ def handle_exit(self, func):
+ @wraps(func)
+ def wrapper(*args, **kw):
try:
- self._loop.run()
- finally:
- if self._loop.is_running():
- self._loop.quit()
- if self._exc_info:
- # An exception caused us to exit, raise it now
- exc_info = self._exc_info
- self._exc_info = None
- raise exc_info[0], exc_info[1], exc_info[2]
-
- def _test_run(self):
- """
- >>> import os
- >>> rd, wr = os.pipe()
- >>> os.write(wr, "data") # something to read from rd
- 4
- >>> evl = GLibEventLoop()
- >>> def say_hello():
- ... print "hello"
- >>> def say_waiting():
- ... print "waiting"
- >>> def exit_clean():
- ... print "clean exit"
- ... raise ExitMainLoop
- >>> def exit_error():
- ... 1/0
- >>> handle = evl.alarm(0.01, exit_clean)
- >>> handle = evl.alarm(0.005, say_hello)
- >>> evl.enter_idle(say_waiting)
- 1
- >>> evl.run()
- waiting
- hello
- waiting
- clean exit
- >>> handle = evl.watch_file(rd, exit_clean)
- >>> evl.run()
- clean exit
- >>> evl.remove_watch_file(handle)
- True
- >>> handle = evl.alarm(0, exit_error)
- >>> evl.run()
- Traceback (most recent call last):
- ...
- ZeroDivisionError: integer division or modulo by zero
- >>> handle = evl.watch_file(rd, exit_error)
- >>> evl.run()
- Traceback (most recent call last):
- ...
- ZeroDivisionError: integer division or modulo by zero
- """
-
- def handle_exit(self,f):
- """
- Decorator that cleanly exits the :class:`GLibEventLoop` if
- :exc:`ExitMainLoop` is thrown inside of the wrapped function. Store the
- exception info if some other exception occurs, it will be reraised after
- the loop quits.
-
- *f* -- function to be wrapped
- """
- def wrapper(*args,**kargs):
- try:
- return f(*args,**kargs)
- except ExitMainLoop:
- self._loop.quit()
- except:
- import sys
- self._exc_info = sys.exc_info()
- if self._loop.is_running():
- self._loop.quit()
- return False
- return wrapper
-
-
- try:
- from twisted.internet.abstract import FileDescriptor
- except ImportError:
- FileDescriptor = object
-
- class TwistedInputDescriptor(FileDescriptor):
- def __init__(self, reactor, fd, cb):
- self._fileno = fd
- self.cb = cb
- FileDescriptor.__init__(self, reactor)
-
- def fileno(self):
- return self._fileno
-
- def doRead(self):
- return self.cb()
-
-
-
- class TwistedEventLoop(object):
- """
- Event loop based on Twisted_
- """
- _idle_emulation_delay = 1.0/256 # a short time (in seconds)
-
- def __init__(self, reactor=None, manage_reactor=True):
- """
- :param reactor: reactor to use
- :type reactor: :class:`twisted.internet.reactor`.
- :param: manage_reactor: `True` if you want this event loop to run
- and stop the reactor.
- :type manage_reactor: boolean
-
- .. WARNING::
- Twisted's reactor doesn't like to be stopped and run again. If you
- need to stop and run your :class:`MainLoop`, consider setting
- ``manage_reactor=False`` and take care of running/stopping the reactor
- at the beginning/ending of your program yourself.
-
- .. _Twisted: http://twistedmatrix.com/trac/
- """
- if reactor is None:
- import twisted.internet.reactor
- reactor = twisted.internet.reactor
- self.reactor = reactor
- self._alarms = []
- self._watch_files = {}
- self._idle_handle = 0
- self._twisted_idle_enabled = False
- self._idle_callbacks = {}
- self._exc_info = None
- self.manage_reactor = manage_reactor
- self._enable_twisted_idle()
+ return func(*args, **kw)
+ except ExitMainLoop:
+ self._ioloop.stop()
+ except Exception as exc:
+ self._exception = exc
+ self._ioloop.stop()
+ return False
+ return wrapper
- def alarm(self, seconds, callback):
- """
- Call callback() given time from from now. No parameters are
- passed to callback.
+ def run(self):
+ self._ioloop.start()
+ if self._exception:
+ exc, self._exception = self._exception, None
+ raise exc
- Returns a handle that may be passed to remove_alarm()
- seconds -- floating point time to wait before calling callback
- callback -- function to call from event loop
- """
- handle = self.reactor.callLater(seconds, self.handle_exit(callback))
- return handle
+try:
+ from twisted.internet.abstract import FileDescriptor
+except ImportError:
+ FileDescriptor = object
- def remove_alarm(self, handle):
- """
- Remove an alarm.
+class TwistedInputDescriptor(FileDescriptor):
+ def __init__(self, reactor, fd, cb):
+ self._fileno = fd
+ self.cb = cb
+ FileDescriptor.__init__(self, reactor)
- Returns True if the alarm exists, False otherwise
- """
- from twisted.internet.error import AlreadyCancelled, AlreadyCalled
- try:
- handle.cancel()
- return True
- except AlreadyCancelled:
- return False
- except AlreadyCalled:
- return False
-
- def _test_remove_alarm(self):
- """
- >>> evl = TwistedEventLoop()
- >>> handle = evl.alarm(50, lambda: None)
- >>> evl.remove_alarm(handle)
- True
- >>> evl.remove_alarm(handle)
- False
- """
-
- def watch_file(self, fd, callback):
- """
- Call callback() when fd has some data to read. No parameters
- are passed to callback.
-
- Returns a handle that may be passed to remove_watch_file()
-
- fd -- file descriptor to watch for input
- callback -- function to call when input is available
- """
- ind = TwistedInputDescriptor(self.reactor, fd,
- self.handle_exit(callback))
- self._watch_files[fd] = ind
- self.reactor.addReader(ind)
- return fd
-
- def remove_watch_file(self, handle):
- """
- Remove an input file.
-
- Returns True if the input file exists, False otherwise
- """
- if handle in self._watch_files:
- self.reactor.removeReader(self._watch_files[handle])
- del self._watch_files[handle]
- return True
+ def fileno(self):
+ return self._fileno
+
+ def doRead(self):
+ return self.cb()
+
+
+class TwistedEventLoop(object):
+ """
+ Event loop based on Twisted_
+ """
+ _idle_emulation_delay = 1.0/256 # a short time (in seconds)
+
+ def __init__(self, reactor=None, manage_reactor=True):
+ """
+ :param reactor: reactor to use
+ :type reactor: :class:`twisted.internet.reactor`.
+ :param: manage_reactor: `True` if you want this event loop to run
+ and stop the reactor.
+ :type manage_reactor: boolean
+
+ .. WARNING::
+ Twisted's reactor doesn't like to be stopped and run again. If you
+ need to stop and run your :class:`MainLoop`, consider setting
+ ``manage_reactor=False`` and take care of running/stopping the reactor
+ at the beginning/ending of your program yourself.
+
+ .. _Twisted: http://twistedmatrix.com/trac/
+ """
+ if reactor is None:
+ import twisted.internet.reactor
+ reactor = twisted.internet.reactor
+ self.reactor = reactor
+ self._alarms = []
+ self._watch_files = {}
+ self._idle_handle = 0
+ self._twisted_idle_enabled = False
+ self._idle_callbacks = {}
+ self._exc_info = None
+ self.manage_reactor = manage_reactor
+ self._enable_twisted_idle()
+
+ def alarm(self, seconds, callback):
+ """
+ Call callback() given time from from now. No parameters are
+ passed to callback.
+
+ Returns a handle that may be passed to remove_alarm()
+
+ seconds -- floating point time to wait before calling callback
+ callback -- function to call from event loop
+ """
+ handle = self.reactor.callLater(seconds, self.handle_exit(callback))
+ return handle
+
+ def remove_alarm(self, handle):
+ """
+ Remove an alarm.
+
+ Returns True if the alarm exists, False otherwise
+ """
+ from twisted.internet.error import AlreadyCancelled, AlreadyCalled
+ try:
+ handle.cancel()
+ return True
+ except AlreadyCancelled:
+ return False
+ except AlreadyCalled:
return False
- def _test_remove_watch_file(self):
- """
- >>> evl = TwistedEventLoop()
- >>> handle = evl.watch_file(1, lambda: None)
- >>> evl.remove_watch_file(handle)
- True
- >>> evl.remove_watch_file(handle)
- False
- """
-
- def enter_idle(self, callback):
- """
- Add a callback for entering idle.
-
- Returns a handle that may be passed to remove_enter_idle()
- """
- self._idle_handle += 1
- self._idle_callbacks[self._idle_handle] = callback
- return self._idle_handle
-
- def _enable_twisted_idle(self):
- """
- Twisted's reactors don't have an idle or enter-idle callback
- so the best we can do for now is to set a timer event in a very
- short time to approximate an enter-idle callback.
-
- .. WARNING::
- This will perform worse than the other event loops until we can find a
- fix or workaround
- """
- if self._twisted_idle_enabled:
- return
- self.reactor.callLater(self._idle_emulation_delay,
- self.handle_exit(self._twisted_idle_callback, enable_idle=False))
- self._twisted_idle_enabled = True
-
- def _twisted_idle_callback(self):
- for callback in self._idle_callbacks.values():
- callback()
- self._twisted_idle_enabled = False
+ def watch_file(self, fd, callback):
+ """
+ Call callback() when fd has some data to read. No parameters
+ are passed to callback.
- def remove_enter_idle(self, handle):
- """
- Remove an idle callback.
+ Returns a handle that may be passed to remove_watch_file()
- Returns True if the handle was removed.
- """
- try:
- del self._idle_callbacks[handle]
- except KeyError:
- return False
+ fd -- file descriptor to watch for input
+ callback -- function to call when input is available
+ """
+ ind = TwistedInputDescriptor(self.reactor, fd,
+ self.handle_exit(callback))
+ self._watch_files[fd] = ind
+ self.reactor.addReader(ind)
+ return fd
+
+ def remove_watch_file(self, handle):
+ """
+ Remove an input file.
+
+ Returns True if the input file exists, False otherwise
+ """
+ if handle in self._watch_files:
+ self.reactor.removeReader(self._watch_files[handle])
+ del self._watch_files[handle]
return True
+ return False
- def run(self):
- """
- Start the event loop. Exit the loop when any callback raises
- an exception. If ExitMainLoop is raised, exit cleanly.
- """
- if not self.manage_reactor:
- return
- self.reactor.run()
- if self._exc_info:
- # An exception caused us to exit, raise it now
- exc_info = self._exc_info
- self._exc_info = None
- raise exc_info[0], exc_info[1], exc_info[2]
-
- def _test_run(self):
- """
- >>> import os
- >>> rd, wr = os.pipe()
- >>> os.write(wr, "data") # something to read from rd
- 4
- >>> evl = TwistedEventLoop()
- >>> def say_hello_data():
- ... print "hello data"
- ... os.read(rd, 4)
- >>> def say_waiting():
- ... print "waiting"
- >>> def say_hello():
- ... print "hello"
- >>> handle = evl.watch_file(rd, say_hello_data)
- >>> def say_being_twisted():
- ... print "oh I'm messed up"
- ... raise ExitMainLoop
- >>> handle = evl.alarm(0.0625, say_being_twisted)
- >>> handle = evl.alarm(0.03125, say_hello)
- >>> evl.enter_idle(say_waiting)
- 1
- >>> evl.run()
- hello data
- waiting
- hello
- waiting
- oh I'm messed up
- """
-
- def handle_exit(self, f, enable_idle=True):
- """
- Decorator that cleanly exits the :class:`TwistedEventLoop` if
- :class:`ExitMainLoop` is thrown inside of the wrapped function. Store the
- exception info if some other exception occurs, it will be reraised after
- the loop quits.
-
- *f* -- function to be wrapped
- """
- def wrapper(*args,**kargs):
- rval = None
- try:
- rval = f(*args,**kargs)
- except ExitMainLoop:
- if self.manage_reactor:
- self.reactor.stop()
- except:
- import sys
- print sys.exc_info()
- self._exc_info = sys.exc_info()
- if self.manage_reactor:
- self.reactor.crash()
- if enable_idle:
- self._enable_twisted_idle()
- return rval
- return wrapper
+ def enter_idle(self, callback):
+ """
+ Add a callback for entering idle.
+
+ Returns a handle that may be passed to remove_enter_idle()
+ """
+ self._idle_handle += 1
+ self._idle_callbacks[self._idle_handle] = callback
+ return self._idle_handle
+
+ def _enable_twisted_idle(self):
+ """
+ Twisted's reactors don't have an idle or enter-idle callback
+ so the best we can do for now is to set a timer event in a very
+ short time to approximate an enter-idle callback.
+
+ .. WARNING::
+ This will perform worse than the other event loops until we can find a
+ fix or workaround
+ """
+ if self._twisted_idle_enabled:
+ return
+ self.reactor.callLater(self._idle_emulation_delay,
+ self.handle_exit(self._twisted_idle_callback, enable_idle=False))
+ self._twisted_idle_enabled = True
+
+ def _twisted_idle_callback(self):
+ for callback in self._idle_callbacks.values():
+ callback()
+ self._twisted_idle_enabled = False
+
+ def remove_enter_idle(self, handle):
+ """
+ Remove an idle callback.
+
+ Returns True if the handle was removed.
+ """
+ try:
+ del self._idle_callbacks[handle]
+ except KeyError:
+ return False
+ return True
+
+ def run(self):
+ """
+ Start the event loop. Exit the loop when any callback raises
+ an exception. If ExitMainLoop is raised, exit cleanly.
+ """
+ if not self.manage_reactor:
+ return
+ self.reactor.run()
+ if self._exc_info:
+ # An exception caused us to exit, raise it now
+ exc_info = self._exc_info
+ self._exc_info = None
+ raise exc_info[0], exc_info[1], exc_info[2]
+
+ def handle_exit(self, f, enable_idle=True):
+ """
+ Decorator that cleanly exits the :class:`TwistedEventLoop` if
+ :class:`ExitMainLoop` is thrown inside of the wrapped function. Store the
+ exception info if some other exception occurs, it will be reraised after
+ the loop quits.
+
+ *f* -- function to be wrapped
+ """
+ def wrapper(*args,**kargs):
+ rval = None
+ try:
+ rval = f(*args,**kargs)
+ except ExitMainLoop:
+ if self.manage_reactor:
+ self.reactor.stop()
+ except:
+ import sys
+ print sys.exc_info()
+ self._exc_info = sys.exc_info()
+ if self.manage_reactor:
+ self.reactor.crash()
+ if enable_idle:
+ self._enable_twisted_idle()
+ return rval
+ return wrapper
diff --git a/urwid/monitored_list.py b/urwid/monitored_list.py
index 1a098c8..86d749e 100755
--- a/urwid/monitored_list.py
+++ b/urwid/monitored_list.py
@@ -139,7 +139,9 @@ class MonitoredFocusList(MonitoredList):
the index passed is ignored.
This function may call self._focus_changed when the focus
- is actually modified. That method may be overridden on the
+ is modified, passing the new focus position to the
+ callback just before changing the old focus setting.
+ That method may be overridden on the
instance with set_focus_changed_callback().
>>> ml = MonitoredFocusList([9, 10, 11])
diff --git a/urwid/old_str_util.py b/urwid/old_str_util.py
index f2b9287..bb993f6 100755
--- a/urwid/old_str_util.py
+++ b/urwid/old_str_util.py
@@ -297,7 +297,7 @@ def within_double_byte(text, line_start, pos):
Return values:
0 -- not within dbe char, or double_byte_encoding == False
1 -- pos is on the 1st half of a dbe char
- 2 -- pos is on the 2nd half og a dbe char
+ 2 -- pos is on the 2nd half of a dbe char
"""
assert isinstance(text, bytes)
v = ord2(text[pos])
diff --git a/urwid/raw_display.py b/urwid/raw_display.py
index f267e4f..58b3112 100644
--- a/urwid/raw_display.py
+++ b/urwid/raw_display.py
@@ -54,6 +54,7 @@ class Screen(BaseScreen, RealTerminal):
"""
super(Screen, self).__init__()
self._pal_escape = {}
+ self._pal_attrspec = {}
signals.connect_signal(self, UPDATE_PALETTE_ENTRY,
self._on_update_palette_entry)
self.colors = 16 # FIXME: detect this
@@ -68,11 +69,14 @@ class Screen(BaseScreen, RealTerminal):
self.maxrow = None
self.gpm_mev = None
self.gpm_event_pending = False
+ self._mouse_tracking_enabled = False
self.last_bstate = 0
self._setup_G1_done = False
self._rows_used = None
self._cy = 0
- self.bright_is_bold = os.environ.get('TERM',None) != "xterm"
+ term = os.environ.get('TERM', '')
+ self.bright_is_bold = not term.startswith("xterm")
+ self.back_color_erase = not term.startswith("screen")
self._next_timeout = None
self._term_output_file = sys.stdout
self._term_input_file = sys.stdin
@@ -82,8 +86,9 @@ class Screen(BaseScreen, RealTerminal):
def _on_update_palette_entry(self, name, *attrspecs):
# copy the attribute to a dictionary containing the escape seqences
- self._pal_escape[name] = self._attrspec_to_escape(
- attrspecs[{16:0,1:1,88:2,256:3}[self.colors]])
+ a = attrspecs[{16:0,1:1,88:2,256:3}[self.colors]]
+ self._pal_attrspec[name] = a
+ self._pal_escape[name] = self._attrspec_to_escape(a)
def set_input_timeouts(self, max_wait=None, complete_wait=0.125,
resize_wait=0.125):
@@ -115,38 +120,56 @@ class Screen(BaseScreen, RealTerminal):
os.write(self._resize_pipe_wr, B('R'))
self._resized = True
self.screen_buf = None
-
+
+ def _sigcont_handler(self, signum, frame):
+ self.stop()
+ self.start()
+ self._sigwinch_handler(None, None)
+
def signal_init(self):
"""
- Called in the startup of run wrapper to set the SIGWINCH
- signal handler to self._sigwinch_handler.
+ Called in the startup of run wrapper to set the SIGWINCH
+ and SIGCONT signal handlers.
Override this function to call from main thread in threaded
applications.
"""
signal.signal(signal.SIGWINCH, self._sigwinch_handler)
-
+ signal.signal(signal.SIGCONT, self._sigcont_handler)
+
def signal_restore(self):
"""
Called in the finally block of run wrapper to restore the
- SIGWINCH handler to the default handler.
+ SIGWINCH and SIGCONT signal handlers.
Override this function to call from main thread in threaded
applications.
"""
+ signal.signal(signal.SIGCONT, signal.SIG_DFL)
signal.signal(signal.SIGWINCH, signal.SIG_DFL)
-
- def set_mouse_tracking(self):
+
+ def set_mouse_tracking(self, enable=True):
"""
- Enable mouse tracking.
-
+ Enable (or disable) mouse tracking.
+
After calling this function get_input will include mouse
click events along with keystrokes.
"""
- self._term_output_file.write(escape.MOUSE_TRACKING_ON)
+ enable = bool(enable)
+ if enable == self._mouse_tracking_enabled:
+ return
+
+ self._mouse_tracking(enable)
+ self._mouse_tracking_enabled = enable
+
+ def _mouse_tracking(self, enable):
+ if enable:
+ self._term_output_file.write(escape.MOUSE_TRACKING_ON)
+ self._start_gpm_tracking()
+ else:
+ self._term_output_file.write(escape.MOUSE_TRACKING_OFF)
+ self._stop_gpm_tracking()
- self._start_gpm_tracking()
-
def _start_gpm_tracking(self):
if not os.path.isfile("/usr/bin/mev"):
return
@@ -158,12 +181,14 @@ class Screen(BaseScreen, RealTerminal):
close_fds=True)
fcntl.fcntl(m.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
self.gpm_mev = m
-
+
def _stop_gpm_tracking(self):
+ if not self.gpm_mev:
+ return
os.kill(self.gpm_mev.pid, signal.SIGINT)
os.waitpid(self.gpm_mev.pid, 0)
self.gpm_mev = None
-
+
def start(self, alternate_buffer=True):
"""
Initialize the screen and input mode.
@@ -193,7 +218,8 @@ class Screen(BaseScreen, RealTerminal):
super(Screen, self).start()
signals.emit_signal(self, INPUT_DESCRIPTORS_CHANGED)
-
+ # restore mouse tracking to previous state
+ self._mouse_tracking(self._mouse_tracking_enabled)
def stop(self):
"""
@@ -212,19 +238,19 @@ class Screen(BaseScreen, RealTerminal):
termios.tcsetattr(fd, termios.TCSADRAIN,
self._old_termios_settings)
+ self._mouse_tracking(False)
+
move_cursor = ""
- if self.gpm_mev:
- self._stop_gpm_tracking()
if self._alternate_buffer:
move_cursor = escape.RESTORE_NORMAL_BUFFER
elif self.maxrow is not None:
- move_cursor = escape.set_cursor_position(
+ move_cursor = escape.set_cursor_position(
0, self.maxrow)
- self._term_output_file.write(self._attrspec_to_escape(AttrSpec('',''))
+ self._term_output_file.write(
+ self._attrspec_to_escape(AttrSpec('',''))
+ escape.SI
- + escape.MOUSE_TRACKING_OFF
- + escape.SHOW_CURSOR
- + move_cursor + "\n" + escape.SHOW_CURSOR )
+ + move_cursor
+ + escape.SHOW_CURSOR)
self._input_iter = self._fake_input_iter()
if self._old_signal_keys:
@@ -235,7 +261,7 @@ class Screen(BaseScreen, RealTerminal):
def run_wrapper(self, fn, alternate_buffer=True):
"""
- Call start to initialize screen, then call fn.
+ Call start to initialize screen, then call fn.
When fn exits call stop to restore the screen to normal.
alternate_buffer -- use alternate screen buffer and restore
@@ -328,7 +354,7 @@ class Screen(BaseScreen, RealTerminal):
Return a list of integer file descriptors that should be
polled in external event loops to check for user input.
- Use this method if you are implementing yout own event loop.
+ Use this method if you are implementing your own event loop.
"""
if not self._started:
return []
@@ -357,7 +383,7 @@ class Screen(BaseScreen, RealTerminal):
def _run_input_iter(self):
def empty_resize_pipe():
# clean out the pipe used to signal external event loops
- # that a resize has occured
+ # that a resize has occurred
try:
while True: os.read(self._resize_pipe_rd, 1)
except OSError:
@@ -484,7 +510,18 @@ class Screen(BaseScreen, RealTerminal):
b |= mod
l.extend([ 27, ord('['), ord('M'), b+32, x+32, y+32 ])
- if ev == 20: # press
+ def determine_button_release( flag ):
+ if b & 4 and last & 1:
+ append_button( 0 + flag )
+ next |= 1
+ if b & 2 and last & 2:
+ append_button( 1 + flag )
+ next |= 2
+ if b & 1 and last & 4:
+ append_button( 2 + flag )
+ next |= 4
+
+ if ev == 20 or ev == 36 or ev == 52: # press
if b & 4 and last & 1 == 0:
append_button( 0 )
next |= 1
@@ -511,7 +548,21 @@ class Screen(BaseScreen, RealTerminal):
if b & 1 and last & 4:
append_button( 2 + escape.MOUSE_RELEASE_FLAG )
next &= ~ 4
-
+ if ev == 40: # double click (release)
+ if b & 4 and last & 1:
+ append_button( 0 + escape.MOUSE_MULTIPLE_CLICK_FLAG )
+ if b & 2 and last & 2:
+ append_button( 1 + escape.MOUSE_MULTIPLE_CLICK_FLAG )
+ if b & 1 and last & 4:
+ append_button( 2 + escape.MOUSE_MULTIPLE_CLICK_FLAG )
+ elif ev == 52:
+ if b & 4 and last & 1:
+ append_button( 0 + escape.MOUSE_MULTIPLE_CLICK_FLAG*2 )
+ if b & 2 and last & 2:
+ append_button( 1 + escape.MOUSE_MULTIPLE_CLICK_FLAG*2 )
+ if b & 1 and last & 4:
+ append_button( 2 + escape.MOUSE_MULTIPLE_CLICK_FLAG*2 )
+
self.last_bstate = next
return l
@@ -521,8 +572,14 @@ class Screen(BaseScreen, RealTerminal):
def get_cols_rows(self):
"""Return the terminal dimensions (num columns, num rows)."""
- buf = fcntl.ioctl(0, termios.TIOCGWINSZ, ' '*4)
- y, x = struct.unpack('hh', buf)
+ y, x = 80, 24
+ try:
+ buf = fcntl.ioctl(self._term_output_file.fileno(),
+ termios.TIOCGWINSZ, ' '*4)
+ y, x = struct.unpack('hh', buf)
+ except IOError:
+ # Term size could not be determined
+ pass
self.maxrow = y
return x, y
@@ -616,6 +673,10 @@ class Screen(BaseScreen, RealTerminal):
return self._attrspec_to_escape(
AttrSpec('default','default'))
+ def using_standout(a):
+ a = self._pal_attrspec.get(a, a)
+ return isinstance(a, AttrSpec) and a.standout
+
ins = None
o.append(set_cursor_home())
cy = 0
@@ -644,12 +705,14 @@ class Screen(BaseScreen, RealTerminal):
cy = y
whitespace_at_end = False
- if row and row[-1][2][-1:] == B(' '):
- whitespace_at_end = True
+ if row:
a, cs, run = row[-1]
- row = row[:-1] + [(a, cs, run.rstrip(B(' ')))]
- elif y == maxrow-1 and maxcol>1:
- row, back, ins = self._last_row(row)
+ if (run[-1:] == B(' ') and self.back_color_erase
+ and not using_standout(a)):
+ whitespace_at_end = True
+ row = row[:-1] + [(a, cs, run.rstrip(B(' ')))]
+ elif y == maxrow-1 and maxcol > 1:
+ row, back, ins = self._last_row(row)
first = True
lasta = lastcs = None
@@ -872,7 +935,7 @@ class Screen(BaseScreen, RealTerminal):
"""
entries - list of (index, red, green, blue) tuples.
- Attempt to set part of the terminal pallette (this does not work
+ Attempt to set part of the terminal palette (this does not work
on all terminals.) The changes are sent as a single escape
sequence so they should all take effect at the same time.
diff --git a/urwid/signals.py b/urwid/signals.py
index f360b27..80b6646 100644
--- a/urwid/signals.py
+++ b/urwid/signals.py
@@ -20,6 +20,8 @@
# Urwid web site: http://excess.org/urwid/
+import itertools
+import weakref
class MetaSignals(type):
@@ -43,6 +45,12 @@ def setdefaultattr(obj, name, value):
setattr(obj, name, value)
return value
+class Key(object):
+ """
+ Minimal class, whose only purpose is to produce objects with a
+ unique hash
+ """
+ __slots__ = []
class Signals(object):
_signal_attr = '_urwid_signals' # attribute to attach to signal senders
@@ -63,7 +71,7 @@ class Signals(object):
"""
self._supported[sig_cls] = signals
- def connect(self, obj, name, callback, user_arg=None):
+ def connect(self, obj, name, callback, user_arg=None, weak_args=None, user_args=None):
"""
:param obj: the object sending a signal
:type obj: object
@@ -71,32 +79,169 @@ class Signals(object):
:type name: signal name
:param callback: the function to call when that signal is sent
:type callback: function
- :param user_arg: optional additional argument to callback, if None
- no arguments will be added
+ :param user_arg: deprecated additional argument to callback (appended
+ after the arguments passed when the signal is
+ emitted). If None no arguments will be added.
+ Don't use this argument, use user_args instead.
+ :param weak_args: additional arguments passed to the callback
+ (before any arguments passed when the signal
+ is emitted and before any user_args).
+
+ These arguments are stored as weak references
+ (but converted back into their original value
+ before passing them to callback) to prevent
+ any objects referenced (indirectly) from
+ weak_args from being kept alive just because
+ they are referenced by this signal handler.
+
+ Use this argument only as a keyword argument,
+ since user_arg might be removed in the future.
+ :type weak_args: iterable
+ :param user_args: additional arguments to pass to the callback,
+ (before any arguments passed when the signal
+ is emitted but after any weak_args).
+
+ Use this argument only as a keyword argument,
+ since user_arg might be removed in the future.
+ :type user_args: iterable
+
+ When a matching signal is sent, callback will be called. The
+ arguments it receives will be the user_args passed at connect
+ time (as individual arguments) followed by all the positional
+ parameters sent with the signal.
+
+ As an example of using weak_args, consider the following snippet:
+
+ >>> import urwid
+ >>> debug = urwid.Text('')
+ >>> def handler(widget, newtext):
+ ... debug.set_text("Edit widget changed to %s" % newtext)
+ >>> edit = urwid.Edit('')
+ >>> key = urwid.connect_signal(edit, 'change', handler)
+
+ If you now build some interface using "edit" and "debug", the
+ "debug" widget will show whatever you type in the "edit" widget.
+ However, if you remove all references to the "debug" widget, it
+ will still be kept alive by the signal handler. This because the
+ signal handler is a closure that (implicitly) references the
+ "edit" widget. If you want to allow the "debug" widget to be
+ garbage collected, you can create a "fake" or "weak" closure
+ (it's not really a closure, since it doesn't reference any
+ outside variables, so it's just a dynamic function):
+
+ >>> debug = urwid.Text('')
+ >>> def handler(weak_debug, widget, newtext):
+ ... weak_debug.set_text("Edit widget changed to %s" % newtext)
+ >>> edit = urwid.Edit('')
+ >>> key = urwid.connect_signal(edit, 'change', handler, weak_args=[debug])
- When a matching signal is sent, callback will be called with
- all the positional parameters sent with the signal. If user_arg
- is not None it will be sent added to the end of the positional
- parameters sent to callback.
+ Here the weak_debug parameter in print_debug is the value passed
+ in the weak_args list to connect_signal. Note that the
+ weak_debug value passed is not a weak reference anymore, the
+ signals code transparently dereferences the weakref parameter
+ before passing it to print_debug.
+
+ Returns a key associated by this signal handler, which can be
+ used to disconnect the signal later on using
+ urwid.disconnect_signal_by_key. Alternatively, the signal
+ handler can also be disconnected by calling
+ urwid.disconnect_signal, which doesn't need this key.
"""
+
sig_cls = obj.__class__
if not name in self._supported.get(sig_cls, []):
raise NameError, "No such signal %r for object %r" % \
(name, obj)
- d = setdefaultattr(obj, self._signal_attr, {})
- d.setdefault(name, []).append((callback, user_arg))
- def disconnect(self, obj, name, callback, user_arg=None):
+ # Just generate an arbitrary (but unique) key
+ key = Key()
+
+ signals = setdefaultattr(obj, self._signal_attr, {})
+ handlers = signals.setdefault(name, [])
+
+ # Remove the signal handler when any of the weakref'd arguments
+ # are garbage collected. Note that this means that the handlers
+ # dictionary can be modified _at any time_, so it should never
+ # be iterated directly (e.g. iterate only over .keys() and
+ # .items(), never over .iterkeys(), .iteritems() or the object
+ # itself).
+ # We let the callback keep a weakref to the object as well, to
+ # prevent a circular reference between the handler and the
+ # object (via the weakrefs, which keep strong references to
+ # their callbacks) from existing.
+ obj_weak = weakref.ref(obj)
+ def weakref_callback(weakref):
+ o = obj_weak()
+ if o:
+ try:
+ del getattr(o, self._signal_attr, {})[name][key]
+ except KeyError:
+ pass
+
+ user_args = self._prepare_user_args(weak_args, user_args, weakref_callback)
+ handlers.append((key, callback, user_arg, user_args))
+
+ return key
+
+ def _prepare_user_args(self, weak_args, user_args, callback = None):
+ # Turn weak_args into weakrefs and prepend them to user_args
+ return [weakref.ref(a, callback) for a in (weak_args or [])] + (user_args or [])
+
+
+ def disconnect(self, obj, name, callback, user_arg=None, weak_args=None, user_args=None):
"""
+ :param obj: the object to disconnect the signal from
+ :type obj: object
+ :param name: the signal to disconnect, typically a string
+ :type name: signal name
+ :param callback: the callback function passed to connect_signal
+ :type callback: function
+ :param user_arg: the user_arg parameter passed to connect_signal
+ :param weak_args: the weak_args parameter passed to connect_signal
+ :param user_args: the weak_args parameter passed to connect_signal
+
This function will remove a callback from the list connected
- to a signal with connect_signal().
+ to a signal with connect_signal(). The arguments passed should
+ be exactly the same as those passed to connect_signal().
+
+ If the callback is not connected or already disconnected, this
+ function will simply do nothing.
"""
- d = setdefaultattr(obj, self._signal_attr, {})
- if name not in d:
- return
- if (callback, user_arg) not in d[name]:
+ signals = setdefaultattr(obj, self._signal_attr, {})
+ if name not in signals:
return
- d[name].remove((callback, user_arg))
+
+ handlers = signals[name]
+
+ # Do the same processing as in connect, so we can compare the
+ # resulting tuple.
+ user_args = self._prepare_user_args(weak_args, user_args)
+
+ # Remove the given handler
+ for h in handlers:
+ if h[1:] == (callback, user_arg, user_args):
+ return self.disconnect_by_key(obj, name, h[0])
+
+ def disconnect_by_key(self, obj, name, key):
+ """
+ :param obj: the object to disconnect the signal from
+ :type obj: object
+ :param name: the signal to disconnect, typically a string
+ :type name: signal name
+ :param key: the key for this signal handler, as returned by
+ connect_signal().
+ :type key: Key
+
+ This function will remove a callback from the list connected
+ to a signal with connect_signal(). The key passed should be the
+ value returned by connect_signal().
+
+ If the callback is not connected or already disconnected, this
+ function will simply do nothing.
+ """
+ signals = setdefaultattr(obj, self._signal_attr, {})
+ handlers = signals.get(name, [])
+ handlers[:] = [h for h in handlers if h[0] is not key]
def emit(self, obj, name, *args):
"""
@@ -113,17 +258,45 @@ class Signals(object):
This function returns True if any of the callbacks returned True.
"""
result = False
- d = getattr(obj, self._signal_attr, {})
- for callback, user_arg in d.get(name, []):
- args_copy = args
- if user_arg is not None:
- args_copy = args + (user_arg,)
- result |= bool(callback(*args_copy))
+ signals = getattr(obj, self._signal_attr, {})
+ handlers = signals.get(name, [])
+ for key, callback, user_arg, user_args in handlers:
+ result |= self._call_callback(callback, user_arg, user_args, args)
return result
+ def _call_callback(self, callback, user_arg, user_args, emit_args):
+ if user_args:
+ args_to_pass = []
+ for arg in user_args:
+ if isinstance(arg, weakref.ReferenceType):
+ arg = arg()
+ if arg is None:
+ # If the weakref is None, the referenced object
+ # was cleaned up. We just skip the entire
+ # callback in this case. The weakref cleanup
+ # handler will have removed the callback when
+ # this happens, so no need to actually remove
+ # the callback here.
+ return False
+ args_to_pass.append(arg)
+
+ args_to_pass.extend(emit_args)
+ else:
+ # Optimization: Don't create a new list when there are
+ # no user_args
+ args_to_pass = emit_args
+
+ # The deprecated user_arg argument was added to the end
+ # instead of the beginning.
+ if user_arg is not None:
+ args_to_pass = itertools.chain(args_to_pass, (user_arg,))
+
+ return bool(callback(*args_to_pass))
+
_signals = Signals()
emit_signal = _signals.emit
register_signal = _signals.register
connect_signal = _signals.connect
disconnect_signal = _signals.disconnect
+disconnect_signal_by_key = _signals.disconnect_by_key
diff --git a/urwid/split_repr.py b/urwid/split_repr.py
index 1438995..8b5e24c 100755
--- a/urwid/split_repr.py
+++ b/urwid/split_repr.py
@@ -128,7 +128,7 @@ def remove_defaults(d, fn):
if varargs:
del args[-1]
- # create adictionary of args with default values
+ # create a dictionary of args with default values
ddict = dict(zip(args[len(args) - len(defaults):], defaults))
for k, v in d.items():
diff --git a/urwid/tests/test_container.py b/urwid/tests/test_container.py
index caa0ef4..6ddb909 100644
--- a/urwid/tests/test_container.py
+++ b/urwid/tests/test_container.py
@@ -118,9 +118,21 @@ class PileTest(unittest.TestCase):
def test_change_focus_with_mouse(self):
p = urwid.Pile([urwid.Edit(), urwid.Edit()])
- self.assertEquals(p.focus_position, 0)
+ self.assertEqual(p.focus_position, 0)
p.mouse_event((10,), 'button press', 1, 1, 1, True)
- self.assertEquals(p.focus_position, 1)
+ self.assertEqual(p.focus_position, 1)
+
+ def test_zero_weight(self):
+ p = urwid.Pile([
+ urwid.SolidFill('a'),
+ ('weight', 0, urwid.SolidFill('d')),
+ ])
+ p.render((5, 4))
+
+ def test_mouse_event_in_empty_pile(self):
+ p = urwid.Pile([])
+ p.mouse_event((5,), 'button press', 1, 1, 1, False)
+ p.mouse_event((5,), 'button press', 1, 1, 1, True)
class ColumnsTest(unittest.TestCase):
@@ -228,9 +240,9 @@ class ColumnsTest(unittest.TestCase):
def test_old_attributes(self):
c = urwid.Columns([urwid.Text(u'a'), urwid.SolidFill(u'x')],
box_columns=[1])
- self.assertEquals(c.box_columns, [1])
+ self.assertEqual(c.box_columns, [1])
c.box_columns=[]
- self.assertEquals(c.box_columns, [])
+ self.assertEqual(c.box_columns, [])
def test_box_column(self):
c = urwid.Columns([urwid.Filler(urwid.Edit()),urwid.Text('')],
@@ -248,18 +260,18 @@ class OverlayTest(unittest.TestCase):
o1 = urwid.Overlay(urwid.SolidFill(u'X'), urwid.SolidFill(u'O'),
('fixed left', 5), ('fixed right', 4),
('fixed top', 3), ('fixed bottom', 2),)
- self.assertEquals(o1.contents[1][1], (
+ self.assertEqual(o1.contents[1][1], (
'left', None, 'relative', 100, None, 5, 4,
'top', None, 'relative', 100, None, 3, 2))
o2 = urwid.Overlay(urwid.SolidFill(u'X'), urwid.SolidFill(u'O'),
('fixed right', 5), ('fixed left', 4),
('fixed bottom', 3), ('fixed top', 2),)
- self.assertEquals(o2.contents[1][1], (
+ self.assertEqual(o2.contents[1][1], (
'right', None, 'relative', 100, None, 4, 5,
'bottom', None, 'relative', 100, None, 2, 3))
def test_get_cursor_coords(self):
- self.assertEquals(urwid.Overlay(urwid.Filler(urwid.Edit()),
+ self.assertEqual(urwid.Overlay(urwid.Filler(urwid.Edit()),
urwid.SolidFill(u'B'),
'right', 1, 'bottom', 1).get_cursor_coords((2,2)), (1,1))
@@ -267,11 +279,15 @@ class OverlayTest(unittest.TestCase):
class GridFlowTest(unittest.TestCase):
def test_cell_width(self):
gf = urwid.GridFlow([], 5, 0, 0, 'left')
- self.assertEquals(gf.cell_width, 5)
+ self.assertEqual(gf.cell_width, 5)
def test_basics(self):
repr(urwid.GridFlow([], 5, 0, 0, 'left')) # should not fail
+ def test_v_sep(self):
+ gf = urwid.GridFlow([urwid.Text("test")], 10, 3, 1, "center")
+ self.assertEqual(gf.rows((40,), False), 1)
+
class WidgetSquishTest(unittest.TestCase):
def wstest(self, w):
@@ -352,7 +368,7 @@ class CommonContainerTest(unittest.TestCase):
t3 = urwid.Text(u'three')
sf = urwid.SolidFill('x')
p = urwid.Pile([])
- self.assertEquals(p.focus, None)
+ self.assertEqual(p.focus, None)
self.assertRaises(IndexError, lambda: getattr(p, 'focus_position'))
self.assertRaises(IndexError, lambda: setattr(p, 'focus_position',
None))
@@ -361,20 +377,20 @@ class CommonContainerTest(unittest.TestCase):
(sf, ('given', 3)), (t3, ('pack', None))]
p.focus_position = 1
del p.contents[0]
- self.assertEquals(p.focus_position, 0)
+ self.assertEqual(p.focus_position, 0)
p.contents[0:0] = [(t3, ('pack', None)), (t2, ('pack', None))]
p.contents.insert(3, (t1, ('pack', None)))
- self.assertEquals(p.focus_position, 2)
+ self.assertEqual(p.focus_position, 2)
self.assertRaises(urwid.PileError, lambda: p.contents.append(t1))
self.assertRaises(urwid.PileError, lambda: p.contents.append((t1, None)))
self.assertRaises(urwid.PileError, lambda: p.contents.append((t1, 'given')))
p = urwid.Pile([t1, t2])
- self.assertEquals(p.focus, t1)
- self.assertEquals(p.focus_position, 0)
+ self.assertEqual(p.focus, t1)
+ self.assertEqual(p.focus_position, 0)
p.focus_position = 1
- self.assertEquals(p.focus, t2)
- self.assertEquals(p.focus_position, 1)
+ self.assertEqual(p.focus, t2)
+ self.assertEqual(p.focus_position, 1)
p.focus_position = 0
self.assertRaises(IndexError, lambda: setattr(p, 'focus_position', -1))
self.assertRaises(IndexError, lambda: setattr(p, 'focus_position', 2))
@@ -383,27 +399,27 @@ class CommonContainerTest(unittest.TestCase):
self.assertRaises(IndexError, lambda: p.set_focus(-1))
self.assertRaises(IndexError, lambda: p.set_focus(2))
p.set_focus(t2)
- self.assertEquals(p.focus_position, 1)
+ self.assertEqual(p.focus_position, 1)
self.assertRaises(ValueError, lambda: p.set_focus('nonexistant'))
- self.assertEquals(p.widget_list, [t1, t2])
- self.assertEquals(p.item_types, [('weight', 1), ('weight', 1)])
+ self.assertEqual(p.widget_list, [t1, t2])
+ self.assertEqual(p.item_types, [('weight', 1), ('weight', 1)])
p.widget_list = [t2, t1]
- self.assertEquals(p.widget_list, [t2, t1])
- self.assertEquals(p.contents, [(t2, ('weight', 1)), (t1, ('weight', 1))])
- self.assertEquals(p.focus_position, 1) # focus unchanged
+ self.assertEqual(p.widget_list, [t2, t1])
+ self.assertEqual(p.contents, [(t2, ('weight', 1)), (t1, ('weight', 1))])
+ self.assertEqual(p.focus_position, 1) # focus unchanged
p.item_types = [('flow', None), ('weight', 2)]
- self.assertEquals(p.item_types, [('flow', None), ('weight', 2)])
- self.assertEquals(p.contents, [(t2, ('pack', None)), (t1, ('weight', 2))])
- self.assertEquals(p.focus_position, 1) # focus unchanged
+ self.assertEqual(p.item_types, [('flow', None), ('weight', 2)])
+ self.assertEqual(p.contents, [(t2, ('pack', None)), (t1, ('weight', 2))])
+ self.assertEqual(p.focus_position, 1) # focus unchanged
p.widget_list = [t1]
- self.assertEquals(len(p.contents), 1)
- self.assertEquals(p.focus_position, 0)
+ self.assertEqual(len(p.contents), 1)
+ self.assertEqual(p.focus_position, 0)
p.widget_list.extend([t2, t1])
- self.assertEquals(len(p.contents), 3)
- self.assertEquals(p.item_types, [
+ self.assertEqual(len(p.contents), 3)
+ self.assertEqual(p.item_types, [
('flow', None), ('weight', 1), ('weight', 1)])
p.item_types[:] = [('weight', 2)]
- self.assertEquals(len(p.contents), 1)
+ self.assertEqual(len(p.contents), 1)
def test_columns(self):
t1 = urwid.Text(u'one')
@@ -411,7 +427,7 @@ class CommonContainerTest(unittest.TestCase):
t3 = urwid.Text(u'three')
sf = urwid.SolidFill('x')
c = urwid.Columns([])
- self.assertEquals(c.focus, None)
+ self.assertEqual(c.focus, None)
self.assertRaises(IndexError, lambda: getattr(c, 'focus_position'))
self.assertRaises(IndexError, lambda: setattr(c, 'focus_position',
None))
@@ -423,22 +439,22 @@ class CommonContainerTest(unittest.TestCase):
(t3, ('given', 10, False))]
c.focus_position = 1
del c.contents[0]
- self.assertEquals(c.focus_position, 0)
+ self.assertEqual(c.focus_position, 0)
c.contents[0:0] = [
(t3, ('given', 10, False)),
(t2, ('weight', 1, False))]
c.contents.insert(3, (t1, ('pack', None, False)))
- self.assertEquals(c.focus_position, 2)
+ self.assertEqual(c.focus_position, 2)
self.assertRaises(urwid.ColumnsError, lambda: c.contents.append(t1))
self.assertRaises(urwid.ColumnsError, lambda: c.contents.append((t1, None)))
self.assertRaises(urwid.ColumnsError, lambda: c.contents.append((t1, 'given')))
c = urwid.Columns([t1, t2])
- self.assertEquals(c.focus, t1)
- self.assertEquals(c.focus_position, 0)
+ self.assertEqual(c.focus, t1)
+ self.assertEqual(c.focus_position, 0)
c.focus_position = 1
- self.assertEquals(c.focus, t2)
- self.assertEquals(c.focus_position, 1)
+ self.assertEqual(c.focus, t2)
+ self.assertEqual(c.focus_position, 1)
c.focus_position = 0
self.assertRaises(IndexError, lambda: setattr(c, 'focus_position', -1))
self.assertRaises(IndexError, lambda: setattr(c, 'focus_position', 2))
@@ -448,47 +464,47 @@ class CommonContainerTest(unittest.TestCase):
self.assertRaises(IndexError, lambda: c.set_focus(-1))
self.assertRaises(IndexError, lambda: c.set_focus(3))
c.set_focus(t2)
- self.assertEquals(c.focus_position, 1)
+ self.assertEqual(c.focus_position, 1)
self.assertRaises(ValueError, lambda: c.set_focus('nonexistant'))
- self.assertEquals(c.widget_list, [t1, t2, sf])
- self.assertEquals(c.column_types, [
+ self.assertEqual(c.widget_list, [t1, t2, sf])
+ self.assertEqual(c.column_types, [
('weight', 1), ('weight', 3), ('weight', 1)])
- self.assertEquals(c.box_columns, [2])
+ self.assertEqual(c.box_columns, [2])
c.widget_list = [t2, t1, sf]
- self.assertEquals(c.widget_list, [t2, t1, sf])
- self.assertEquals(c.box_columns, [2])
+ self.assertEqual(c.widget_list, [t2, t1, sf])
+ self.assertEqual(c.box_columns, [2])
- self.assertEquals(c.contents, [
+ self.assertEqual(c.contents, [
(t2, ('weight', 1, False)),
(t1, ('weight', 3, False)),
(sf, ('weight', 1, True))])
- self.assertEquals(c.focus_position, 1) # focus unchanged
+ self.assertEqual(c.focus_position, 1) # focus unchanged
c.column_types = [
('flow', None), # use the old name
('weight', 2),
('fixed', 5)]
- self.assertEquals(c.column_types, [
+ self.assertEqual(c.column_types, [
('flow', None),
('weight', 2),
('fixed', 5)])
- self.assertEquals(c.contents, [
+ self.assertEqual(c.contents, [
(t2, ('pack', None, False)),
(t1, ('weight', 2, False)),
(sf, ('given', 5, True))])
- self.assertEquals(c.focus_position, 1) # focus unchanged
+ self.assertEqual(c.focus_position, 1) # focus unchanged
c.widget_list = [t1]
- self.assertEquals(len(c.contents), 1)
- self.assertEquals(c.focus_position, 0)
+ self.assertEqual(len(c.contents), 1)
+ self.assertEqual(c.focus_position, 0)
c.widget_list.extend([t2, t1])
- self.assertEquals(len(c.contents), 3)
- self.assertEquals(c.column_types, [
+ self.assertEqual(len(c.contents), 3)
+ self.assertEqual(c.column_types, [
('flow', None), ('weight', 1), ('weight', 1)])
c.column_types[:] = [('weight', 2)]
- self.assertEquals(len(c.contents), 1)
+ self.assertEqual(len(c.contents), 1)
def test_list_box(self):
lb = urwid.ListBox(urwid.SimpleFocusListWalker([]))
- self.assertEquals(lb.focus, None)
+ self.assertEqual(lb.focus, None)
self.assertRaises(IndexError, lambda: getattr(lb, 'focus_position'))
self.assertRaises(IndexError, lambda: setattr(lb, 'focus_position',
None))
@@ -497,39 +513,39 @@ class CommonContainerTest(unittest.TestCase):
t1 = urwid.Text(u'one')
t2 = urwid.Text(u'two')
lb = urwid.ListBox(urwid.SimpleListWalker([t1, t2]))
- self.assertEquals(lb.focus, t1)
- self.assertEquals(lb.focus_position, 0)
+ self.assertEqual(lb.focus, t1)
+ self.assertEqual(lb.focus_position, 0)
lb.focus_position = 1
- self.assertEquals(lb.focus, t2)
- self.assertEquals(lb.focus_position, 1)
+ self.assertEqual(lb.focus, t2)
+ self.assertEqual(lb.focus_position, 1)
lb.focus_position = 0
self.assertRaises(IndexError, lambda: setattr(lb, 'focus_position', -1))
self.assertRaises(IndexError, lambda: setattr(lb, 'focus_position', 2))
def test_grid_flow(self):
gf = urwid.GridFlow([], 5, 1, 0, 'left')
- self.assertEquals(gf.focus, None)
- self.assertEquals(gf.contents, [])
+ self.assertEqual(gf.focus, None)
+ self.assertEqual(gf.contents, [])
self.assertRaises(IndexError, lambda: getattr(gf, 'focus_position'))
self.assertRaises(IndexError, lambda: setattr(gf, 'focus_position',
None))
self.assertRaises(IndexError, lambda: setattr(gf, 'focus_position', 0))
- self.assertEquals(gf.options(), ('given', 5))
- self.assertEquals(gf.options(width_amount=9), ('given', 9))
+ self.assertEqual(gf.options(), ('given', 5))
+ self.assertEqual(gf.options(width_amount=9), ('given', 9))
self.assertRaises(urwid.GridFlowError, lambda: gf.options(
'pack', None))
t1 = urwid.Text(u'one')
t2 = urwid.Text(u'two')
gf = urwid.GridFlow([t1, t2], 5, 1, 0, 'left')
- self.assertEquals(gf.focus, t1)
- self.assertEquals(gf.focus_position, 0)
- self.assertEquals(gf.contents, [(t1, ('given', 5)), (t2, ('given', 5))])
+ self.assertEqual(gf.focus, t1)
+ self.assertEqual(gf.focus_position, 0)
+ self.assertEqual(gf.contents, [(t1, ('given', 5)), (t2, ('given', 5))])
gf.focus_position = 1
- self.assertEquals(gf.focus, t2)
- self.assertEquals(gf.focus_position, 1)
+ self.assertEqual(gf.focus, t2)
+ self.assertEqual(gf.focus_position, 1)
gf.contents.insert(0, (t2, ('given', 5)))
- self.assertEquals(gf.focus_position, 2)
+ self.assertEqual(gf.focus_position, 2)
self.assertRaises(urwid.GridFlowError, lambda: gf.contents.append(()))
self.assertRaises(urwid.GridFlowError, lambda: gf.contents.insert(1,
(t1, ('pack', None))))
@@ -541,7 +557,7 @@ class CommonContainerTest(unittest.TestCase):
self.assertRaises(IndexError, lambda: gf.set_focus(-1))
self.assertRaises(IndexError, lambda: gf.set_focus(3))
gf.set_focus(t1)
- self.assertEquals(gf.focus_position, 1)
+ self.assertEqual(gf.focus_position, 1)
self.assertRaises(ValueError, lambda: gf.set_focus('nonexistant'))
def test_overlay(self):
@@ -549,15 +565,15 @@ class CommonContainerTest(unittest.TestCase):
s2 = urwid.SolidFill(u'2')
o = urwid.Overlay(s1, s2,
'center', ('relative', 50), 'middle', ('relative', 50))
- self.assertEquals(o.focus, s1)
- self.assertEquals(o.focus_position, 1)
+ self.assertEqual(o.focus, s1)
+ self.assertEqual(o.focus_position, 1)
self.assertRaises(IndexError, lambda: setattr(o, 'focus_position',
None))
self.assertRaises(IndexError, lambda: setattr(o, 'focus_position', 2))
- self.assertEquals(o.contents[0], (s2,
+ self.assertEqual(o.contents[0], (s2,
urwid.Overlay._DEFAULT_BOTTOM_OPTIONS))
- self.assertEquals(o.contents[1], (s1, (
+ self.assertEqual(o.contents[1], (s1, (
'center', None, 'relative', 50, None, 0, 0,
'middle', None, 'relative', 50, None, 0, 0)))
@@ -565,8 +581,8 @@ class CommonContainerTest(unittest.TestCase):
s1 = urwid.SolidFill(u'1')
f = urwid.Frame(s1)
- self.assertEquals(f.focus, s1)
- self.assertEquals(f.focus_position, 'body')
+ self.assertEqual(f.focus, s1)
+ self.assertEqual(f.focus_position, 'body')
self.assertRaises(IndexError, lambda: setattr(f, 'focus_position',
None))
self.assertRaises(IndexError, lambda: setattr(f, 'focus_position',
@@ -576,19 +592,19 @@ class CommonContainerTest(unittest.TestCase):
t2 = urwid.Text(u'two')
t3 = urwid.Text(u'three')
f = urwid.Frame(s1, t1, t2, 'header')
- self.assertEquals(f.focus, t1)
- self.assertEquals(f.focus_position, 'header')
+ self.assertEqual(f.focus, t1)
+ self.assertEqual(f.focus_position, 'header')
f.focus_position = 'footer'
- self.assertEquals(f.focus, t2)
- self.assertEquals(f.focus_position, 'footer')
+ self.assertEqual(f.focus, t2)
+ self.assertEqual(f.focus_position, 'footer')
self.assertRaises(IndexError, lambda: setattr(f, 'focus_position', -1))
self.assertRaises(IndexError, lambda: setattr(f, 'focus_position', 2))
del f.contents['footer']
- self.assertEquals(f.footer, None)
- self.assertEquals(f.focus_position, 'body')
+ self.assertEqual(f.footer, None)
+ self.assertEqual(f.focus_position, 'body')
f.contents.update(footer=(t3, None), header=(t2, None))
- self.assertEquals(f.header, t2)
- self.assertEquals(f.footer, t3)
+ self.assertEqual(f.header, t2)
+ self.assertEqual(f.footer, t3)
def set1():
f.contents['body'] = t1
self.assertRaises(urwid.FrameError, set1)
@@ -611,12 +627,12 @@ class CommonContainerTest(unittest.TestCase):
g.focus_position = 4
f = urwid.Frame(lb, header=t, footer=g)
- self.assertEquals(f.get_focus_path(), ['body', 1, 2, 1])
+ self.assertEqual(f.get_focus_path(), ['body', 1, 2, 1])
f.set_focus_path(['footer']) # same as f.focus_position = 'footer'
- self.assertEquals(f.get_focus_path(), ['footer', 4])
+ self.assertEqual(f.get_focus_path(), ['footer', 4])
f.set_focus_path(['body', 1, 2, 2])
- self.assertEquals(f.get_focus_path(), ['body', 1, 2, 2])
+ self.assertEqual(f.get_focus_path(), ['body', 1, 2, 2])
self.assertRaises(IndexError, lambda: f.set_focus_path([0, 1, 2]))
self.assertRaises(IndexError, lambda: f.set_focus_path(['body', 2, 2]))
f.set_focus_path(['body', 2]) # focus the overlay
- self.assertEquals(f.get_focus_path(), ['body', 2, 1])
+ self.assertEqual(f.get_focus_path(), ['body', 2, 1])
diff --git a/urwid/tests/test_decoration.py b/urwid/tests/test_decoration.py
index 5add164..8ab1acd 100644
--- a/urwid/tests/test_decoration.py
+++ b/urwid/tests/test_decoration.py
@@ -78,16 +78,16 @@ class PaddingTest(unittest.TestCase):
# though, so this might not get fixed for a while
p = urwid.Padding(urwid.Edit(u'',u''), width='pack', left=4)
- self.assertEquals(p.render((10,), True).cursor, None)
- self.assertEquals(p.get_cursor_coords((10,)), None)
- self.assertEquals(p.render((4,), True).cursor, None)
- self.assertEquals(p.get_cursor_coords((4,)), None)
+ self.assertEqual(p.render((10,), True).cursor, None)
+ self.assertEqual(p.get_cursor_coords((10,)), None)
+ self.assertEqual(p.render((4,), True).cursor, None)
+ self.assertEqual(p.get_cursor_coords((4,)), None)
p = urwid.Padding(urwid.Edit(u'',u''), width=('relative', 100), left=4)
- self.assertEquals(p.render((10,), True).cursor, (4, 0))
- self.assertEquals(p.get_cursor_coords((10,)), (4, 0))
- self.assertEquals(p.render((4,), True).cursor, None)
- self.assertEquals(p.get_cursor_coords((4,)), None)
+ self.assertEqual(p.render((10,), True).cursor, (4, 0))
+ self.assertEqual(p.get_cursor_coords((10,)), (4, 0))
+ self.assertEqual(p.render((4,), True).cursor, None)
+ self.assertEqual(p.get_cursor_coords((4,)), None)
class FillerTest(unittest.TestCase):
diff --git a/urwid/tests/test_doctests.py b/urwid/tests/test_doctests.py
new file mode 100644
index 0000000..1720a48
--- /dev/null
+++ b/urwid/tests/test_doctests.py
@@ -0,0 +1,22 @@
+import unittest
+import doctest
+
+import urwid
+
+def load_tests(loader, tests, ignore):
+ module_doctests = [
+ urwid.widget,
+ urwid.wimp,
+ urwid.decoration,
+ urwid.display_common,
+ urwid.main_loop,
+ urwid.monitored_list,
+ urwid.raw_display,
+ 'urwid.split_repr', # override function with same name
+ urwid.util,
+ urwid.signals,
+ ]
+ for m in module_doctests:
+ tests.addTests(doctest.DocTestSuite(m,
+ optionflags=doctest.ELLIPSIS | doctest.IGNORE_EXCEPTION_DETAIL))
+ return tests
diff --git a/urwid/tests/test_event_loops.py b/urwid/tests/test_event_loops.py
new file mode 100644
index 0000000..0793602
--- /dev/null
+++ b/urwid/tests/test_event_loops.py
@@ -0,0 +1,131 @@
+import os
+import unittest
+import platform
+
+import urwid
+from urwid.compat import PYTHON3
+
+
+class EventLoopTestMixin(object):
+ def test_event_loop(self):
+ rd, wr = os.pipe()
+ evl = self.evl
+ out = []
+ def step1():
+ out.append("writing")
+ os.write(wr, "hi".encode('ascii'))
+ def step2():
+ out.append(os.read(rd, 2).decode('ascii'))
+ raise urwid.ExitMainLoop
+ handle = evl.alarm(0, step1)
+ handle = evl.watch_file(rd, step2)
+ evl.run()
+ self.assertEqual(out, ["writing", "hi"])
+
+ def test_remove_alarm(self):
+ evl = self.evl
+ handle = evl.alarm(50, lambda: None)
+ self.assertTrue(evl.remove_alarm(handle))
+ self.assertFalse(evl.remove_alarm(handle))
+
+ def test_remove_watch_file(self):
+ evl = self.evl
+ handle = evl.watch_file(5, lambda: None)
+ self.assertTrue(evl.remove_watch_file(handle))
+ self.assertFalse(evl.remove_watch_file(handle))
+
+ def test_run(self):
+ evl = self.evl
+ out = []
+ rd, wr = os.pipe()
+ self.assertEqual(os.write(wr, "data".encode('ascii')), 4)
+ def say_hello():
+ out.append("hello")
+ def say_waiting():
+ out.append("waiting")
+ def exit_clean():
+ out.append("clean exit")
+ raise urwid.ExitMainLoop
+ def exit_error():
+ 1/0
+ handle = evl.alarm(0.01, exit_clean)
+ handle = evl.alarm(0.005, say_hello)
+ self.assertEqual(evl.enter_idle(say_waiting), 1)
+ evl.run()
+ self.assertTrue("hello" in out, out)
+ self.assertTrue("clean exit"in out, out)
+ handle = evl.watch_file(rd, exit_clean)
+ del out[:]
+ evl.run()
+ self.assertEqual(out, ["clean exit"])
+ self.assertTrue(evl.remove_watch_file(handle))
+ handle = evl.alarm(0, exit_error)
+ self.assertRaises(ZeroDivisionError, evl.run)
+ handle = evl.watch_file(rd, exit_error)
+ self.assertRaises(ZeroDivisionError, evl.run)
+
+
+class SelectEventLoopTest(unittest.TestCase, EventLoopTestMixin):
+ def setUp(self):
+ self.evl = urwid.SelectEventLoop()
+
+
+try:
+ import gi.repository
+except ImportError:
+ pass
+else:
+ class GLibEventLoopTest(unittest.TestCase, EventLoopTestMixin):
+ def setUp(self):
+ self.evl = urwid.GLibEventLoop()
+
+
+try:
+ import tornado
+except ImportError:
+ pass
+else:
+ class TornadoEventLoopTest(unittest.TestCase, EventLoopTestMixin):
+ def setUp(self):
+ from tornado.ioloop import IOLoop
+ self.evl = urwid.TornadoEventLoop(IOLoop())
+
+
+try:
+ import twisted
+except ImportError:
+ pass
+else:
+ class TwistedEventLoopTest(unittest.TestCase, EventLoopTestMixin):
+ def setUp(self):
+ self.evl = urwid.TwistedEventLoop()
+
+ # can't restart twisted reactor, so use shortened tests
+ def test_event_loop(self):
+ pass
+
+ def test_run(self):
+ evl = self.evl
+ out = []
+ rd, wr = os.pipe()
+ self.assertEqual(os.write(wr, "data".encode('ascii')), 4)
+ def step2():
+ out.append(os.read(rd, 2).decode('ascii'))
+ def say_hello():
+ out.append("hello")
+ def say_waiting():
+ out.append("waiting")
+ def exit_clean():
+ out.append("clean exit")
+ raise urwid.ExitMainLoop
+ def exit_error():
+ 1/0
+ handle = evl.watch_file(rd, step2)
+ handle = evl.alarm(0.01, exit_clean)
+ handle = evl.alarm(0.005, say_hello)
+ self.assertEqual(evl.enter_idle(say_waiting), 1)
+ evl.run()
+ self.assertTrue("da" in out, out)
+ self.assertTrue("ta" in out, out)
+ self.assertTrue("hello" in out, out)
+ self.assertTrue("clean exit" in out, out)
diff --git a/urwid/tests/test_listbox.py b/urwid/tests/test_listbox.py
index d796dc6..8afeb97 100644
--- a/urwid/tests/test_listbox.py
+++ b/urwid/tests/test_listbox.py
@@ -770,27 +770,27 @@ class ZeroHeightContentsTest(unittest.TestCase):
lb = urwid.ListBox(urwid.SimpleListWalker(
[urwid.Text(u'above'), urwid.Pile([])]))
lb.keypress((40,10), 'page down')
- self.assertEquals(lb.get_focus()[1], 0)
+ self.assertEqual(lb.get_focus()[1], 0)
lb.keypress((40,10), 'page down') # second one caused ListBox failure
- self.assertEquals(lb.get_focus()[1], 0)
+ self.assertEqual(lb.get_focus()[1], 0)
def test_listbox_text_pile_page_up(self):
lb = urwid.ListBox(urwid.SimpleListWalker(
[urwid.Pile([]), urwid.Text(u'below')]))
lb.set_focus(1)
lb.keypress((40,10), 'page up')
- self.assertEquals(lb.get_focus()[1], 1)
+ self.assertEqual(lb.get_focus()[1], 1)
lb.keypress((40,10), 'page up') # second one caused pile failure
- self.assertEquals(lb.get_focus()[1], 1)
+ self.assertEqual(lb.get_focus()[1], 1)
def test_listbox_text_pile_down(self):
sp = urwid.Pile([])
sp.selectable = lambda: True # abuse our Pile
lb = urwid.ListBox(urwid.SimpleListWalker([urwid.Text(u'above'), sp]))
lb.keypress((40,10), 'down')
- self.assertEquals(lb.get_focus()[1], 0)
+ self.assertEqual(lb.get_focus()[1], 0)
lb.keypress((40,10), 'down')
- self.assertEquals(lb.get_focus()[1], 0)
+ self.assertEqual(lb.get_focus()[1], 0)
def test_listbox_text_pile_up(self):
sp = urwid.Pile([])
@@ -798,7 +798,7 @@ class ZeroHeightContentsTest(unittest.TestCase):
lb = urwid.ListBox(urwid.SimpleListWalker([sp, urwid.Text(u'below')]))
lb.set_focus(1)
lb.keypress((40,10), 'up')
- self.assertEquals(lb.get_focus()[1], 1)
+ self.assertEqual(lb.get_focus()[1], 1)
lb.keypress((40,10), 'up')
- self.assertEquals(lb.get_focus()[1], 1)
+ self.assertEqual(lb.get_focus()[1], 1)
diff --git a/urwid/tests/test_str_util.py b/urwid/tests/test_str_util.py
index 0a0b61c..f7ad1d9 100644
--- a/urwid/tests/test_str_util.py
+++ b/urwid/tests/test_str_util.py
@@ -1,7 +1,7 @@
import unittest
from urwid.compat import B
-from urwid import str_util
+from urwid.escape import str_util
class DecodeOneTest(unittest.TestCase):
diff --git a/urwid/tests/test_widget.py b/urwid/tests/test_widget.py
index 1f6dfbe..3ae9a93 100644
--- a/urwid/tests/test_widget.py
+++ b/urwid/tests/test_widget.py
@@ -32,6 +32,12 @@ class TextTest(unittest.TestCase):
got = self.t.render((18,))._text
assert got == expected, "got: %r expected: %r" % (got, expected)
+ def test5_encode_error(self):
+ urwid.set_encoding("ascii")
+ expected = [B("? ")]
+ got = urwid.Text(u'û').render((3,))._text
+ assert got == expected, "got: %r expected: %r" % (got, expected)
+
class EditTest(unittest.TestCase):
def setUp(self):
@@ -86,9 +92,9 @@ class EditTest(unittest.TestCase):
urwid.set_encoding("utf-8")
self.t1.set_edit_text('')
self.t1.keypress((12,), u'û')
- self.assertEquals(self.t1.edit_text, u'û'.encode('utf-8'))
+ self.assertEqual(self.t1.edit_text, u'û'.encode('utf-8'))
self.t4.keypress((12,), u'û')
- self.assertEquals(self.t4.edit_text, u'û')
+ self.assertEqual(self.t4.edit_text, u'û')
class EditRenderTest(unittest.TestCase):
diff --git a/urwid/text_layout.py b/urwid/text_layout.py
index 4a76cc8..302c504 100644
--- a/urwid/text_layout.py
+++ b/urwid/text_layout.py
@@ -125,7 +125,7 @@ class StandardTextLayout(TextLayout):
width - number of available screen columns
wrap - wrapping mode used
- Returns a layout structure without aligmnent applied.
+ Returns a layout structure without alignment applied.
"""
nl, nl_o, sp_o = "\n", "\n", " "
if PYTHON3 and isinstance(text, bytes):
@@ -320,7 +320,7 @@ def line_width( segs ):
Return the screen column width of one line of a text layout structure.
This function ignores any existing shift applied to the line,
- represended by an (amount, None) tuple at the start of the line.
+ represented by an (amount, None) tuple at the start of the line.
"""
sc = 0
seglist = segs
@@ -353,7 +353,7 @@ def shift_line( segs, amount ):
def trim_line( segs, text, start, end ):
"""
Return a trimmed line of a text layout structure.
- text -- text to which this layout structre applies
+ text -- text to which this layout structure applies
start -- starting screen column
end -- ending screen column
"""
diff --git a/urwid/treetools.py b/urwid/treetools.py
index 2f65e49..26f0914 100644
--- a/urwid/treetools.py
+++ b/urwid/treetools.py
@@ -69,7 +69,7 @@ class TreeWidget(urwid.WidgetWrap):
def update_expanded_icon(self):
"""Update display widget text for parent widgets"""
- # icon is first element in colums indented widget
+ # icon is first element in columns indented widget
self._w.base_widget.widget_list[0] = [
self.unexpanded_icon, self.expanded_icon][self.expanded]
diff --git a/urwid/util.py b/urwid/util.py
index e7acdd3..12becca 100644
--- a/urwid/util.py
+++ b/urwid/util.py
@@ -23,6 +23,8 @@
from urwid import escape
from urwid.compat import bytes
+import codecs
+
str_util = escape.str_util
# bring str_util functions into our namespace
@@ -115,10 +117,10 @@ def apply_target_encoding( s ):
for c, alt in zip(escape.DEC_SPECIAL_CHARS,
escape.ALT_DEC_SPECIAL_CHARS):
s = s.replace( c, escape.SO+alt+escape.SI )
-
+
if type(s) == unicode:
- s = s.replace( escape.SI+escape.SO, u"" ) # remove redundant shifts
- s = s.encode( _target_encoding )
+ s = s.replace(escape.SI+escape.SO, u"") # remove redundant shifts
+ s = codecs.encode(s, _target_encoding, 'replace')
assert isinstance(s, bytes)
SO = escape.SO.encode('ascii')
@@ -437,7 +439,7 @@ class MetaSuper(type):
def int_scale(val, val_range, out_range):
"""
Scale val in the range [0, val_range-1] to an integer in the range
- [0, out_range-1]. This implementaton uses the "round-half-up" rounding
+ [0, out_range-1]. This implementation uses the "round-half-up" rounding
method.
>>> "%x" % int_scale(0x7, 0x10, 0x10000)
diff --git a/urwid/version.py b/urwid/version.py
index 3d2d3a1..0cfb892 100644
--- a/urwid/version.py
+++ b/urwid/version.py
@@ -1,5 +1,5 @@
-VERSION = (1, 2, 0, 'dev')
+VERSION = (1, 2, 2, 'dev')
__version__ = ''.join(['-.'[type(x) == int]+str(x) for x in VERSION])[1:]
diff --git a/urwid/vterm.py b/urwid/vterm.py
index c473d65..212094c 100644
--- a/urwid/vterm.py
+++ b/urwid/vterm.py
@@ -1518,7 +1518,13 @@ class Terminal(Widget):
return True
def wait_and_feed(self, timeout=1.0):
- select.select([self.master], [], [], timeout)
+ while True:
+ try:
+ select.select([self.master], [], [], timeout)
+ break
+ except select.error, e:
+ if e.args[0] != 4:
+ raise
self.feed()
def feed(self):
diff --git a/urwid/web_display.py b/urwid/web_display.py
index f22e104..69aa1ad 100755
--- a/urwid/web_display.py
+++ b/urwid/web_display.py
@@ -614,7 +614,7 @@ class Screen:
background = "light gray"
self.palette[name] = (foreground, background, mono)
- def set_mouse_tracking(self):
+ def set_mouse_tracking(self, enable=True):
"""Not yet implemented"""
pass
diff --git a/urwid/widget.py b/urwid/widget.py
index d1f3c33..44abd2c 100644
--- a/urwid/widget.py
+++ b/urwid/widget.py
@@ -353,7 +353,7 @@ class Widget(object):
:param size: See :meth:`Widget.render` for details.
:type size: widget size
:param event: Values such as ``'mouse press'``, ``'ctrl mouse press'``,
- ``'mouse relase'``, ``'meta mouse release'``,
+ ``'mouse release'``, ``'meta mouse release'``,
``'mouse drag'``; see :ref:`mouse-input`
:type event: mouse event
:param button: 1 through 5 for press events, often 0 for release events
diff --git a/urwid/wimp.py b/urwid/wimp.py
index d8a47c6..03a3613 100755
--- a/urwid/wimp.py
+++ b/urwid/wimp.py
@@ -208,8 +208,8 @@ class CheckBox(WidgetWrap):
>>> def callback_b(cb, state):
... changes.append("B %r" % state)
>>> cb = CheckBox('test', False, False)
- >>> connect_signal(cb, 'change', callback_a, "user_a")
- >>> connect_signal(cb, 'change', callback_b)
+ >>> key1 = connect_signal(cb, 'change', callback_a, "user_a")
+ >>> key2 = connect_signal(cb, 'change', callback_b)
>>> cb.set_state(True) # both callbacks will be triggered
>>> cb.state
True
@@ -390,7 +390,7 @@ class RadioButton(CheckBox):
(False, True, False)
>>> def relabel_button(radio_button, new_state):
... radio_button.set_label(u"Think Harder!")
- >>> connect_signal(b3, 'change', relabel_button)
+ >>> key = connect_signal(b3, 'change', relabel_button)
>>> b3
<RadioButton selectable flow widget 'Unsure' state=False>
>>> b3.set_state(True) # this will trigger the callback
@@ -523,7 +523,7 @@ class Button(WidgetWrap):
>>> clicked_buttons = []
>>> def handle_click(button):
... clicked_buttons.append(button.label)
- >>> connect_signal(b, 'click', handle_click)
+ >>> key = connect_signal(b, 'click', handle_click)
>>> b.keypress(size, 'enter')
>>> b.keypress(size, ' ')
>>> clicked_buttons # ... = u in Python 2
@@ -543,7 +543,7 @@ class Button(WidgetWrap):
>>> clicked_buttons = []
>>> def handle_click(button):
... clicked_buttons.append(button.label)
- >>> connect_signal(b, 'click', handle_click)
+ >>> key = connect_signal(b, 'click', handle_click)
>>> b.mouse_event(size, 'mouse press', 1, 4, 0, True)
True
>>> b.mouse_event(size, 'mouse press', 2, 4, 0, True) # ignored
@@ -566,7 +566,7 @@ class PopUpLauncher(delegate_to_widget_mixin('_original_widget'),
def create_pop_up(self):
"""
- Subclass must override this method and have is return a widget
+ Subclass must override this method and return a widget
to be used for the pop-up. This method is called once each time
the pop-up is opened.
"""