diff options
-rw-r--r-- | reference.html | 96 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rwxr-xr-x | test_urwid.py | 24 | ||||
-rw-r--r-- | urwid/escape.py | 2 | ||||
-rwxr-xr-x | urwid/graphics.py | 340 | ||||
-rw-r--r-- | urwid/raw_display.py | 26 | ||||
-rw-r--r-- | urwid/util.py | 25 |
7 files changed, 412 insertions, 103 deletions
diff --git a/reference.html b/reference.html index d306e44..acaa3f3 100644 --- a/reference.html +++ b/reference.html @@ -20,7 +20,7 @@ Reference </div> <br> -<table width="100%"><tr><td width="33%" valign="top"><div class="l1">User interface wrappers</div><div class="l2"><a href="#raw_display.Screen">raw_display.Screen</a></div><div class="l2"><a href="#curses_display.Screen">curses_display.Screen</a></div><div class="l1">Top-level widgets</div><div class="l2"><a href="#BoxWidget">BoxWidget</a></div><div class="l2"><a href="#Frame">Frame</a></div><div class="l2"><a href="#Filler">Filler</a></div><div class="l2"><a href="#ListBox">ListBox</a></div><div class="l2"><a href="#SimpleListWalker">SimpleListWalker</a></div><div class="l1">Decorations</div><div class="l2"><a href="#WidgetWrap">WidgetWrap</a></div><div class="l2"><a href="#AttrWrap">AttrWrap</a></div><div class="l2"><a href="#Padding">Padding</a></div><div class="l2"><a href="#Divider">Divider</a></div><div class="l1">Composite widgets</div><div class="l2"><a href="#Columns">Columns</a></div><div class="l2"><a href="#Pile">Pile</a></div><div class="l2"><a href="#GridFlow">GridFlow</a></div><div class="l2"><a href="#BoxAdapter">BoxAdapter</a></div><div class="l2"><a href="#Overlay">Overlay</a></div></td><td width="33%" valign="top"><div class="l1">Content widgets</div><div class="l2"><a href="#FlowWidget">FlowWidget</a></div><div class="l2"><a href="#Text">Text</a></div><div class="l2"><a href="#Edit">Edit</a></div><div class="l2"><a href="#IntEdit">IntEdit</a></div><div class="l2"><a href="#Button">Button</a></div><div class="l2"><a href="#CheckBox">CheckBox</a></div><div class="l2"><a href="#RadioButton">RadioButton</a></div><div class="l1">Graphics</div><div class="l2"><a href="#BarGraph">BarGraph</a></div><div class="l2"><a href="#GraphVScale">GraphVScale</a></div><div class="l2"><a href="#ProgressBar">ProgressBar</a></div><div class="l1">Urwid class interfaces</div><div class="l2"><a href="#Widget_interface_definition">Widget interface definition</a></div><div class="l2"><a href="#List_Walker_interface_definition">List Walker interface definition</a></div><div class="l1">Canvas painting</div><div class="l2"><a href="#Canvas">Canvas</a></div><div class="l2"><a href="#CanvasCombine">CanvasCombine</a></div><div class="l2"><a href="#CanvasJoin">CanvasJoin</a></div></td><td width="33%" valign="top"><div class="l1">Custom formatting rules</div><div class="l2"><a href="#TextLayout">TextLayout</a></div><div class="l2"><a href="#StandardTextLayout">StandardTextLayout</a></div><div class="l1">Character encoding</div><div class="l2"><a href="#set_encoding">set_encoding</a></div><div class="l2"><a href="#supports_unicode">supports_unicode</a></div><div class="l1">Screen capture</div><div class="l2"><a href="#html_fragment.screenshot_init">html_fragment.screenshot_init</a></div><div class="l2"><a href="#html_fragment.screenshot_collect">html_fragment.screenshot_collect</a></div><div class="l2"><a href="#html_fragment.HtmlGenerator">html_fragment.HtmlGenerator</a></div><div class="l1">Web Application Interface</div><div class="l2"><a href="#web_display.is_web_request">web_display.is_web_request</a></div><div class="l2"><a href="#web_display.set_preferences">web_display.set_preferences</a></div><div class="l2"><a href="#web_display.handle_short_request">web_display.handle_short_request</a></div><div class="l2"><a href="#web_display.Screen">web_display.Screen</a></div></td></tr></table><h2>User interface wrappers</h2><h3><a name="raw_display.Screen"></a><strong>raw_display.Screen</strong> <span style="font-size:small; padding-left: 20px">[<a href="#top">back to top</a>]</span></h3>Methods defined here:<br> +<table width="100%"><tr><td width="33%" valign="top"><div class="l1">User interface wrappers</div><div class="l2"><a href="#raw_display.Screen">raw_display.Screen</a></div><div class="l2"><a href="#curses_display.Screen">curses_display.Screen</a></div><div class="l1">Top-level widgets</div><div class="l2"><a href="#BoxWidget">BoxWidget</a></div><div class="l2"><a href="#Frame">Frame</a></div><div class="l2"><a href="#Filler">Filler</a></div><div class="l2"><a href="#ListBox">ListBox</a></div><div class="l2"><a href="#SimpleListWalker">SimpleListWalker</a></div><div class="l1">Decorations</div><div class="l2"><a href="#WidgetWrap">WidgetWrap</a></div><div class="l2"><a href="#AttrWrap">AttrWrap</a></div><div class="l2"><a href="#Padding">Padding</a></div><div class="l2"><a href="#Divider">Divider</a></div><div class="l1">Composite widgets</div><div class="l2"><a href="#Columns">Columns</a></div><div class="l2"><a href="#Pile">Pile</a></div><div class="l2"><a href="#GridFlow">GridFlow</a></div><div class="l2"><a href="#BoxAdapter">BoxAdapter</a></div><div class="l2"><a href="#Overlay">Overlay</a></div></td><td width="33%" valign="top"><div class="l1">Content widgets</div><div class="l2"><a href="#FlowWidget">FlowWidget</a></div><div class="l2"><a href="#Text">Text</a></div><div class="l2"><a href="#Edit">Edit</a></div><div class="l2"><a href="#IntEdit">IntEdit</a></div><div class="l2"><a href="#Button">Button</a></div><div class="l2"><a href="#CheckBox">CheckBox</a></div><div class="l2"><a href="#RadioButton">RadioButton</a></div><div class="l1">Graphics</div><div class="l2"><a href="#BarGraph">BarGraph</a></div><div class="l2"><a href="#GraphVScale">GraphVScale</a></div><div class="l2"><a href="#ProgressBar">ProgressBar</a></div><div class="l1">Urwid class interfaces</div><div class="l2"><a href="#Widget_interface_definition">Widget interface definition</a></div><div class="l2"><a href="#List_Walker_interface_definition">List Walker interface definition</a></div><div class="l1">Canvas painting</div><div class="l2"><a href="#Canvas">Canvas</a></div><div class="l2"><a href="#CanvasCombine">CanvasCombine</a></div><div class="l2"><a href="#CanvasJoin">CanvasJoin</a></div></td><td width="33%" valign="top"><div class="l1">Custom formatting rules</div><div class="l2"><a href="#TextLayout">TextLayout</a></div><div class="l2"><a href="#StandardTextLayout">StandardTextLayout</a></div><div class="l1">Character encoding</div><div class="l2"><a href="#set_encoding">set_encoding</a></div><div class="l2"><a href="#get_encoding_mode">get_encoding_mode</a></div><div class="l2"><a href="#supports_unicode">supports_unicode</a></div><div class="l1">Screen capture</div><div class="l2"><a href="#html_fragment.screenshot_init">html_fragment.screenshot_init</a></div><div class="l2"><a href="#html_fragment.screenshot_collect">html_fragment.screenshot_collect</a></div><div class="l2"><a href="#html_fragment.HtmlGenerator">html_fragment.HtmlGenerator</a></div><div class="l1">Web Application Interface</div><div class="l2"><a href="#web_display.is_web_request">web_display.is_web_request</a></div><div class="l2"><a href="#web_display.set_preferences">web_display.set_preferences</a></div><div class="l2"><a href="#web_display.handle_short_request">web_display.handle_short_request</a></div><div class="l2"><a href="#web_display.Screen">web_display.Screen</a></div></td></tr></table><h2>User interface wrappers</h2><h3><a name="raw_display.Screen"></a><strong>raw_display.Screen</strong> <span style="font-size:small; padding-left: 20px">[<a href="#top">back to top</a>]</span></h3>Methods defined here:<br> <dl><dt><a name="Screen-__init__"><strong>__init__</strong></a>(self)</dt></dl> <dl><dt><a name="Screen-draw_screen"><strong>draw_screen</strong></a>(self, (maxcol, maxrow), r)</dt><dd><tt>Paint screen with rendered canvas.</tt></dd></dl> @@ -97,6 +97,18 @@ resize_wait -- amount of time in seconds to&n stop Urwid from consuming 100% cpu during a gradual<br> window resize operation</tt></dd></dl> +<dl><dt><a name="Screen-signal_init"><strong>signal_init</strong></a>(self)</dt><dd><tt>Called in the startup of run wrapper to set the SIGWINCH <br> +signal handler to self.<strong>_sigwinch_handler</strong>.<br> + <br> +Override this function to call from main thread in threaded<br> +applications.</tt></dd></dl> + +<dl><dt><a name="Screen-signal_restore"><strong>signal_restore</strong></a>(self)</dt><dd><tt>Called in the finally block of run wrapper to restore the<br> +SIGWINCH handler to the default handler.<br> + <br> +Override this function to call from main thread in threaded<br> +applications.</tt></dd></dl> + <h3><a name="curses_display.Screen"></a><strong>curses_display.Screen</strong> <span style="font-size:small; padding-left: 20px">[<a href="#top">back to top</a>]</span></h3>Methods defined here:<br> <dl><dt><a name="Screen-__init__"><strong>__init__</strong></a>(self)</dt></dl> @@ -884,22 +896,16 @@ Data and other attributes defined here:<br> <dl><dt><strong>states</strong> = {False: <urwid.widget.SelectableIcon instance>, True: <urwid.widget.SelectableIcon instance>, 'mixed': <urwid.widget.SelectableIcon instance>}</dl> <h2>Graphics</h2><h3><a name="BarGraph">class <strong>BarGraph</strong></a>(BoxWidget) <span style="font-size:small; padding-left: 20px">[<a href="#top">back to top</a>]</span></h3>Methods defined here:<br> -<dl><dt><a name="BarGraph-__init__"><strong>__init__</strong></a>(self, attlist, hatt<font color="#909090">=None</font>)</dt><dd><tt>BarGraph( [bg, fg, ...], hatt )<br> -bg -- attribute or (attribute, character) tuple for background<br> -fg -- attribute or (attribute, character) tuple for bars<br> -... -- further attributes or tuples for multi-segment bars<br> -hatt -- attribute or (attribute, character) tuple for horizontal<br> - lines<br> - <br> -see set_segment_attributes for description of above parameters.<br> - <br> -character defaults to space ' ' if only attribute is given.</tt></dd></dl> +<dl><dt><a name="BarGraph-__init__"><strong>__init__</strong></a>(self, attlist, hatt<font color="#909090">=None</font>, satt<font color="#909090">=None</font>)</dt><dd><tt>Create a bar graph with the passed display characteristics.<br> +see set_segment_attributes for a description of the parameters.</tt></dd></dl> <dl><dt><a name="BarGraph-calculate_bar_widths"><strong>calculate_bar_widths</strong></a>(self, (maxcol, maxrow), bardata)</dt><dd><tt>Return a list of bar widths, one for each bar in data.<br> <br> If self.<strong>bar_width</strong> is None this implementation will stretch <br> the bars across the available space specified by maxcol.</tt></dd></dl> +<dl><dt><a name="BarGraph-calculate_display"><strong>calculate_display</strong></a>(self, (maxcol, maxrow))</dt><dd><tt>Calculate display data.</tt></dd></dl> + <dl><dt><a name="BarGraph-get_data"><strong>get_data</strong></a>(self, (maxcol, maxrow))</dt><dd><tt>Return (bardata, top, hlines)<br> <br> This function is called by render to retrieve the data for<br> @@ -908,6 +914,13 @@ the graph. It may be overloaded to create&nbs This implementation will truncate the bardata list returned <br> if not all bars will fit within maxcol.</tt></dd></dl> +<dl><dt><a name="BarGraph-hlines_display"><strong>hlines_display</strong></a>(self, disp, top, hlines, maxrow)</dt><dd><tt>Add hlines to display structure represented as bar_type tuple<br> +values:<br> +(bg, 0-5)<br> +bg is the segment that has the hline on it<br> +0-5 is the hline graphic to use where 0 is a regular underscore<br> +and 1-5 are the UTF-8 horizontal scan line characters.</tt></dd></dl> + <dl><dt><a name="BarGraph-render"><strong>render</strong></a>(self, (maxcol, maxrow), focus<font color="#909090">=False</font>)</dt><dd><tt>Render BarGraph.</tt></dd></dl> <dl><dt><a name="BarGraph-selectable"><strong>selectable</strong></a>(self)</dt><dd><tt>Return False.</tt></dd></dl> @@ -932,15 +945,28 @@ Eg: if top is 100 and there is a ba the top of this bar will be at 80% of full height of the graph<br> and it will have a second segment that starts at 30%.</tt></dd></dl> -<dl><dt><a name="BarGraph-set_segment_attributes"><strong>set_segment_attributes</strong></a>(self, attlist, hatt<font color="#909090">=None</font>)</dt><dd><tt>set_segment_attributes( [bg, fg, ...], hatt )<br> -bg -- attribute or (attribute, character) tuple for background<br> -fg -- attribute or (attribute, character) tuple for bars<br> -... -- further attributes or tuples for multi-segment bars<br> -hatt -- attribute or (attribute, character) tuple for horizontal<br> - lines or None<br> - <br> -If hatt is None it will default to (bg's attribute, "_").<br> -If hatt does not specidy the character it will default to "_".<br> +<dl><dt><a name="BarGraph-set_segment_attributes"><strong>set_segment_attributes</strong></a>(self, attlist, hatt<font color="#909090">=None</font>, satt<font color="#909090">=None</font>)</dt><dd><tt>attlist -- list containing attribute or (attribute, character)<br> + tuple for background, first segment, and optionally<br> + following segments. ie. len(attlist) == num segments+1<br> + character defaults to ' ' if not specified.<br> +hatt -- list containing attributes for horizontal lines. First <br> + lement is for lines on background, second is for lines<br> + on first segment, third is for lines on second segment<br> + etc..<br> +satt -- dictionary containing attributes for smoothed <br> + transitions of bars in UTF-8 display mode. The values<br> + are in the form:<br> + (fg,bg) : attr<br> + fg and bg are integers where 0 is the graph background,<br> + 1 is the first segment, 2 is the second, ... <br> + fg > bg in all values. attr is an attribute with a <br> + foreground corresponding to fg and a background <br> + corresponding to bg.<br> + <br> +If satt is not None and the bar graph is being displayed in<br> +a terminal using the UTF-8 encoding then the character cell<br> +that is shared between the segments specified will be smoothed<br> +with using the UTF-8 vertical eighth characters.<br> <br> eg: set_segment_attributes( ['no', ('unsure',"?"), 'yes'] )<br> will use the attribute 'no' for the background (the area from<br> @@ -949,6 +975,21 @@ with the attribute 'unsure' will be used for& segment of the bar, and the attribute 'yes' will be used for<br> the bottom segment of the bar.</tt></dd></dl> +<dl><dt><a name="BarGraph-smooth_display"><strong>smooth_display</strong></a>(self, disp)</dt><dd><tt>smooth (col, row*8) display into (col, row) display using<br> +UTF vertical eighth characters represented as bar_type<br> +tuple values:<br> +( fg, bg, 1-7 )<br> +where fg is the lower segment, bg is the upper segment and<br> +1-7 is the vertical eighth character to use.</tt></dd></dl> + +<dl><dt><a name="BarGraph-use_smoothed"><strong>use_smoothed</strong></a>(self)</dt></dl> + +<hr> +Data and other attributes defined here:<br> +<dl><dt><strong>eighths</strong> = u' <font color="#c040c0">\u2581\u2582\u2583\u2584\u2585\u2586\u2587</font>'</dl> + +<dl><dt><strong>hlines</strong> = u'_<font color="#c040c0">\u23ba\u23bb\u2500\u23bc\u23bd</font>'</dl> + <h3><a name="GraphVScale">class <strong>GraphVScale</strong></a>(BoxWidget) <span style="font-size:small; padding-left: 20px">[<a href="#top">back to top</a>]</span></h3>Methods defined here:<br> <dl><dt><a name="GraphVScale-__init__"><strong>__init__</strong></a>(self, labels, top)</dt><dd><tt>GraphVScale( [(label1 position, label1 markup),...], top )<br> label position -- 0 < position < top for the y position<br> @@ -968,10 +1009,14 @@ label markup -- text markup for this label<br top -- top y position</tt></dd></dl> <h3><a name="ProgressBar">class <strong>ProgressBar</strong></a>(FlowWidget) <span style="font-size:small; padding-left: 20px">[<a href="#top">back to top</a>]</span></h3>Methods defined here:<br> -<dl><dt><a name="ProgressBar-__init__"><strong>__init__</strong></a>(self, normal, complete, current<font color="#909090">=0</font>, done<font color="#909090">=100</font>)</dt><dd><tt>normal -- attribute for uncomplete part of progress bar<br> +<dl><dt><a name="ProgressBar-__init__"><strong>__init__</strong></a>(self, normal, complete, current<font color="#909090">=0</font>, done<font color="#909090">=100</font>, satt<font color="#909090">=None</font>)</dt><dd><tt>normal -- attribute for uncomplete part of progress bar<br> complete -- attribute for complete part of progress bar<br> current -- current progress<br> -done -- progress amount at 100%</tt></dd></dl> +done -- progress amount at 100%<br> +satt -- attribute for smoothed part of bar where the foreground<br> + of satt corresponds to the normal part and the<br> + background corresponds to the complete part. If satt<br> + is None then no smoothing will be done.</tt></dd></dl> <dl><dt><a name="ProgressBar-render"><strong>render</strong></a>(self, (maxcol,), focus<font color="#909090">=False</font>)</dt><dd><tt>Render the progress bar.</tt></dd></dl> @@ -980,6 +1025,10 @@ done -- progress amount at 100%</tt></dd></dl> <dl><dt><a name="ProgressBar-set_completion"><strong>set_completion</strong></a>(self, current)</dt><dd><tt>current -- current progress</tt></dd></dl> <hr> +Data and other attributes defined here:<br> +<dl><dt><strong>eighths</strong> = u' <font color="#c040c0">\u258f\u258e\u258d\u258c\u258b\u258a\u2589</font>'</dl> + +<hr> Methods inherited from FlowWidget:<br> <dl><dt><a name="ProgressBar-selectable"><strong>selectable</strong></a>(self)</dt><dd><tt>Return False. Not selectable by default.</tt></dd></dl> @@ -1127,6 +1176,9 @@ Returns a layout structure without aligmnent appli <h2>Character encoding</h2><a name="set_encoding"></a><h3>function set_encoding <span style="font-size:small; padding-left: 20px">[<a href="#top">back to top</a>]</span></h3><dl><dt><a name="-set_encoding"><strong>set_encoding</strong></a>(encoding)</dt><dd><tt>Set the byte encoding to assume when processing strings and the<br> encoding to use when converting unicode strings.</tt></dd></dl> +<a name="get_encoding_mode"></a><h3>function get_encoding_mode <span style="font-size:small; padding-left: 20px">[<a href="#top">back to top</a>]</span></h3><dl><dt><a name="-get_encoding_mode"><strong>get_encoding_mode</strong></a>()</dt><dd><tt>Get the mode Urwid is using when processing text strings.<br> +Returns 'narrow' for 8-bit encodings, 'wide' for CJK encodings<br> +or 'utf8' for UTF-8 encodings.</tt></dd></dl> <a name="supports_unicode"></a><h3>function supports_unicode <span style="font-size:small; padding-left: 20px">[<a href="#top">back to top</a>]</span></h3><dl><dt><a name="-supports_unicode"><strong>supports_unicode</strong></a>()</dt><dd><tt>Return True if python is able to convert non-ascii unicode strings<br> to the current encoding.</tt></dd></dl> <h2>Screen capture</h2><a name="html_fragment.screenshot_init"></a><h3>function html_fragment.screenshot_init <span style="font-size:small; padding-left: 20px">[<a href="#top">back to top</a>]</span></h3><dl><dt><a name="-html_fragment.screenshot_init"><strong>html_fragment.screenshot_init</strong></a> = screenshot_init(sizes, keys)</dt><dd><tt>Replace curses_display.Screen and raw_display.Screen class with <br> @@ -23,7 +23,7 @@ from distutils.core import setup import os -release = "0.9.0" +release = "0.9.1" setup_d = { 'name':"urwid", diff --git a/test_urwid.py b/test_urwid.py index 3c19a93..d40e19e 100755 --- a/test_urwid.py +++ b/test_urwid.py @@ -1,4 +1,5 @@ #!/usr/bin/python +# -* coding: utf-8 -*- # # Urwid unit testing .. ok, ok, ok # Copyright (C) 2004-2006 Ian Ward @@ -1638,6 +1639,28 @@ class BarGraphTest(unittest.TestCase): [(1,[(3,1),(0,1),(1,1)]),(1,[(3,2),(2,1)]), (1,[(3,3)]) ] ) +class SmoothBarGraphTest(unittest.TestCase): + def sbgtest(self, desc, data, top, exp ): + urwid.set_encoding('utf-8') + g = urwid.BarGraph( ['black','red','blue'], + None, {(1,0):'red/black', (2,1):'blue/red'}) + g.set_data( data, top ) + rval, ignore = g.calculate_display((5,3)) + assert rval == exp, "%s expected %s, got %s"%(desc,`exp`,`rval`) + + def test1(self): + self.sbgtest('simple', [[3]], 5, + [(1, [(0, 5)]), (1, [((1, 0, 6), 5)]), (1, [(1, 5)])] ) + self.sbgtest('boring', [[4,2]], 6, + [(1, [(0, 5)]), (1, [(1, 5)]), (1, [(2,5)]) ] ) + self.sbgtest('two', [[4],[2]], 6, + [(1, [(0, 5)]), (1, [(1, 3), (0, 2)]), (1, [(1, 5)]) ] ) + self.sbgtest('twos', [[3],[4]], 6, + [(1, [(0, 5)]), (1, [((1,0,4), 3), (1, 2)]), (1, [(1,5)]) ] ) + self.sbgtest('twof', [[4],[3]], 6, + [(1, [(0, 5)]), (1, [(1,3), ((1,0,4), 2)]), (1, [(1,5)]) ] ) + + class CanvasJoinTest(unittest.TestCase): def cjtest(self, desc, l, et, ea): result = urwid.CanvasJoin( l ) @@ -1744,6 +1767,7 @@ def test_main(): PileTest, ColumnsTest, BarGraphTest, + SmoothBarGraphTest, CanvasJoinTest, CanvasOverlayTest, ]: diff --git a/urwid/escape.py b/urwid/escape.py index e1b5497..793fcf5 100644 --- a/urwid/escape.py +++ b/urwid/escape.py @@ -161,7 +161,6 @@ def process_keyqueue(keys, more_fn): if (util.byte_encoding == 'wide' and code < 256 and util.within_double_byte(chr(code),0,0)): - assert 0, code if not keys: key = more_fn() if key >= 0: keys.append(key) @@ -198,7 +197,6 @@ def process_keyqueue(keys, more_fn): return ["<%d>"%code],keys if code >127 and code <256: - assert 0, code key = chr(code) return [key],keys if code != 27: diff --git a/urwid/graphics.py b/urwid/graphics.py index b44c3bf..bca1a81 100755 --- a/urwid/graphics.py +++ b/urwid/graphics.py @@ -1,4 +1,5 @@ #!/usr/bin/python +# -* coding: utf-8 -*- # # Urwid graphics widgets # Copyright (C) 2004-2006 Ian Ward @@ -23,38 +24,52 @@ from util import * from canvas import * from widget import * +from __future__ import nested_scopes + try: True # old python? except: False, True = 0, 1 +class BarGraphError(Exception): + pass + class BarGraph(BoxWidget): - def __init__(self, attlist, hatt=None): + eighths = u" ▁▂▃▄▅▆▇" + hlines = u"_⎺⎻─⎼⎽" + + def __init__(self, attlist, hatt=None, satt=None): """ - BarGraph( [bg, fg, ...], hatt ) - bg -- attribute or (attribute, character) tuple for background - fg -- attribute or (attribute, character) tuple for bars - ... -- further attributes or tuples for multi-segment bars - hatt -- attribute or (attribute, character) tuple for horizontal - lines - - see set_segment_attributes for description of above parameters. - - character defaults to space ' ' if only attribute is given. + Create a bar graph with the passed display characteristics. + see set_segment_attributes for a description of the parameters. """ - self.set_segment_attributes( attlist, hatt ) + + self.set_segment_attributes( attlist, hatt, satt ) self.set_data([], 1, None) self.set_bar_width(None) - def set_segment_attributes(self, attlist, hatt=None ): - """ - set_segment_attributes( [bg, fg, ...], hatt ) - bg -- attribute or (attribute, character) tuple for background - fg -- attribute or (attribute, character) tuple for bars - ... -- further attributes or tuples for multi-segment bars - hatt -- attribute or (attribute, character) tuple for horizontal - lines or None - - If hatt is None it will default to (bg's attribute, "_"). - If hatt does not specidy the character it will default to "_". + def set_segment_attributes(self, attlist, hatt=None, satt=None ): + """ + attlist -- list containing attribute or (attribute, character) + tuple for background, first segment, and optionally + following segments. ie. len(attlist) == num segments+1 + character defaults to ' ' if not specified. + hatt -- list containing attributes for horizontal lines. First + lement is for lines on background, second is for lines + on first segment, third is for lines on second segment + etc.. + satt -- dictionary containing attributes for smoothed + transitions of bars in UTF-8 display mode. The values + are in the form: + (fg,bg) : attr + fg and bg are integers where 0 is the graph background, + 1 is the first segment, 2 is the second, ... + fg > bg in all values. attr is an attribute with a + foreground corresponding to fg and a background + corresponding to bg. + + If satt is not None and the bar graph is being displayed in + a terminal using the UTF-8 encoding then the character cell + that is shared between the segments specified will be smoothed + with using the UTF-8 vertical eighth characters. eg: set_segment_attributes( ['no', ('unsure',"?"), 'yes'] ) will use the attribute 'no' for the background (the area from @@ -65,6 +80,8 @@ class BarGraph(BoxWidget): """ self.attr = [] self.char = [] + if len(attlist) < 2: + raise BarGraphError, "attlist must include at least background and seg1: %s" % `attlist` assert len(attlist) >= 2, 'must at least specify bg and fg!' for a in attlist: if type(a)!=type(()): @@ -74,12 +91,29 @@ class BarGraph(BoxWidget): attr, ch = a self.attr.append(attr) self.char.append(ch) + + self.hatt = [] if hatt is None: - self.hattr = self.attr[0], "_" - elif type(hatt)!=type(()): - self.hattr = hatt, "_" - else: - self.hattr = hatt + hatt = [self.attr[0]] + elif type(hatt)!=type([]): + hatt = [hatt] + self.hatt = hatt + + if satt is None: + satt = {} + for i in satt.items(): + try: + (fg,bg), attr = i + except: + raise BarGraphError, "satt not in (fg,bg:attr) form: %s"%`i` + if type(fg) != type(0) or fg >= len(attlist): + raise BarGraphError, "fg not valid integer: %s"%`fg` + if type(bg) != type(0) or bg >= len(attlist): + raise BarGraphError, "bg not valid integer: %s"%`fg` + if fg<=bg: + raise BarGraphError, "fg (%s) not > bg (%s)" %(fg,bg) + self.satt = satt + @@ -165,55 +199,206 @@ class BarGraph(BoxWidget): """ return False - def render(self, (maxcol, maxrow), focus=False): + def use_smoothed(self): + return self.satt and get_encoding_mode()=="utf8" + + def calculate_display(self, (maxcol, maxrow) ): """ - Render BarGraph. + Calculate display data. """ bardata, top, hlines = self.get_data( (maxcol, maxrow) ) widths = self.calculate_bar_widths( (maxcol, maxrow), bardata ) - disp = calculate_bargraph_display(bardata, top, widths, maxrow ) + + if self.use_smoothed(): + disp = calculate_bargraph_display(bardata, top, widths, + maxrow * 8 ) + disp = self.smooth_display( disp ) + + else: + disp = calculate_bargraph_display(bardata, top, widths, + maxrow ) + + if hlines: + disp = self.hlines_display( disp, top, hlines, maxrow ) - shl = [ maxrow+1 ] # ie. never - if hlines is not None: - shl = scale_bar_values( hlines, top, maxrow ) - shl = [x-1 for x in shl if x > 0] - shl.append( maxrow+1 ) - shl.reverse() - #assert 0, `shl` - nexthl = shl.pop() + return disp + + def hlines_display(self, disp, top, hlines, maxrow ): + """ + Add hlines to display structure represented as bar_type tuple + values: + (bg, 0-5) + bg is the segment that has the hline on it + 0-5 is the hline graphic to use where 0 is a regular underscore + and 1-5 are the UTF-8 horizontal scan line characters. + """ + if self.use_smoothed(): + shiftr = 0 + r = [ (0.2, 1), + (0.4, 2), + (0.6, 3), + (0.8, 4), + (1.0, 5),] + else: + shiftr = 0.5 + r = [ (1.0, 0), ] + + # reverse the hlines to match screen ordering + rhl = [] + for h in hlines: + rh = float(top-h) * maxrow / top - shiftr + if rh < 0: + continue + rhl.append(rh) + + # build a list of rows that will have hlines + hrows = [] + last_i = -1 + for rh in rhl: + i = int(rh) + if i == last_i: + continue + f = rh-i + for spl, chnum in r: + if f < spl: + hrows.append( (i, chnum) ) + break + last_i = i + + # fill hlines into disp data + def fill_row( row, chnum ): + rout = [] + for bar_type, width in row: + if (type(bar_type) == type(0) and + len(self.hatt) > bar_type ): + rout.append( ((bar_type, chnum), width)) + continue + rout.append( (bar_type, width)) + return rout + + o = [] + k = 0 + rnum = 0 + for y_count, row in disp: + if k >= len(hrows): + o.append( (y_count, row) ) + continue + end_block = rnum + y_count + while k < len(hrows) and hrows[k][0] < end_block: + i, chnum = hrows[k] + if i-rnum > 0: + o.append( (i-rnum, row) ) + o.append( (1, fill_row( row, chnum ) ) ) + rnum = i+1 + k += 1 + if rnum < end_block: + o.append( (end_block-rnum, row) ) + rnum = end_block + + #assert 0, o + return o + + + def smooth_display(self, disp): + """ + smooth (col, row*8) display into (col, row) display using + UTF vertical eighth characters represented as bar_type + tuple values: + ( fg, bg, 1-7 ) + where fg is the lower segment, bg is the upper segment and + 1-7 is the vertical eighth character to use. + """ + o = [] + r = 0 # row remainder + def seg_combine( (bt1,w1), (bt2,w2) ): + if (bt1,w1) == (bt2,w2): + return (bt1,w1), None, None + wmin = min(w1,w2) + l1 = l2 = None + if w1>w2: + l1 = (bt1, w1-w2) + elif w2>w1: + l2 = (bt2, w2-w1) + if type(bt1)==type(()): + return (bt1,wmin), l1, l2 + if not self.satt.has_key( (bt2, bt1) ): + if r<4: + return (bt2,wmin), l1, l2 + return (bt1,wmin), l1, l2 + return ((bt2, bt1, 8-r), wmin), l1, l2 + + def row_combine_last( count, row ): + o_count, o_row = o[-1] + row = row[:] # shallow copy, so we don't destroy orig. + o_row = o_row[:] + l = [] + while row: + (bt, w), l1, l2 = seg_combine( + o_row.pop(0), row.pop(0) ) + if l and l[-1][0] == bt: + l[-1] = (bt, l[-1][1]+w) + else: + l.append((bt, w)) + if l1: + o_row = [l1]+o_row + if l2: + row = [l2]+row + assert not o_row + o[-1] = ( o_count + count, l ) + + + # regroup into actual rows (8 disp rows == 1 actual row) + for y_count, row in disp: + if r: + count = min( 8-r, y_count ) + row_combine_last( count, row ) + y_count -= count + r += count + r = r % 8 + if not y_count: + continue + assert r == 0 + # copy whole blocks + if y_count > 7: + o.append( (y_count/8*8 , row) ) + y_count = y_count %8 + if not y_count: + continue + o.append( (y_count, row) ) + r = y_count + return [(y/8, row) for (y,row) in o] + + + def render(self, (maxcol, maxrow), focus=False): + """ + Render BarGraph. + """ + disp = self.calculate_display( (maxcol,maxrow) ) + #assert 0, disp + r = [] for y_count, row in disp: l = [] - lhl = [] - has_hl = nexthl<len(r)+y_count for bar_type, width in row: - a = self.attr[bar_type] - t = self.char[bar_type] * width + if type(bar_type) == type(()): + if len(bar_type) == 3: + # vertical eighths + fg,bg,k = bar_type + a = self.satt[(fg,bg)] + t = self.eighths[k] * width + else: + # horizontal lines + bg,k = bar_type + a = self.hatt[bg] + t = self.hlines[k] * width + else: + a = self.attr[bar_type] + t = self.char[bar_type] * width l.append( (a, t) ) - if has_hl: - if bar_type == 0: - a,t = self.hattr - t *= width - lhl.append( (a, t) ) c = Text(l).render( (maxcol,) ) assert c.rows() == 1, "Invalid characters in BarGraph!" - if not has_hl: - r += [c] * y_count - continue - - chl = Text(lhl).render( (maxcol,) ) - assert chl.rows() == 1, "Invalid characters in hline!" - while y_count: - if len(r)==nexthl: - r.append(chl) - y_count -=1 - while nexthl < len(r): - nexthl = shl.pop() - continue - y_run = min( nexthl-len(r), y_count ) - y_count -= y_run - r += [c] * y_run + r += [c] * y_count return CanvasCombine(r) @@ -437,17 +622,23 @@ def scale_bar_values( bar, top, maxrow ): class ProgressBar( FlowWidget ): - def __init__(self, normal, complete, current=0, done=100): + eighths = u" ▏▎▍▌▋▊▉" + def __init__(self, normal, complete, current=0, done=100, satt=None): """ normal -- attribute for uncomplete part of progress bar complete -- attribute for complete part of progress bar current -- current progress done -- progress amount at 100% + satt -- attribute for smoothed part of bar where the foreground + of satt corresponds to the normal part and the + background corresponds to the complete part. If satt + is None then no smoothing will be done. """ self.normal = normal self.complete = complete self.current = current self.done = done + self.satt = satt def set_completion(self, current ): """ @@ -472,11 +663,26 @@ class ProgressBar( FlowWidget ): txt=Text( str(percent)+" %", 'center', 'clip' ) c = txt.render((maxcol,)) - ccol = int( self.current*maxcol/self.done ) - if ccol <= 0: + cf = float( self.current ) * maxcol / self.done + ccol = int( cf ) + cs = 0 + if self.satt is not None: + cs = int((cf - ccol) * 8) + if ccol < 0 or (ccol == 0 and cs == 0): c.attr = [[(self.normal,maxcol)]] elif ccol >= maxcol: c.attr = [[(self.complete,maxcol)]] + elif cs and c.text[0][ccol] == " ": + t = c.text[0] + cenc = self.eighths[cs].encode("utf-8") + c.text[0] = t[:ccol]+cenc+t[ccol+1:] + a = [] + if ccol > 0: + a.append( (self.complete, ccol) ) + a.append((self.satt,len(cenc))) + if maxcol-ccol-1 > 0: + a.append( (self.normal, maxcol-ccol-1) ) + c.attr = [a] else: c.attr = [[(self.complete,ccol), (self.normal,maxcol-ccol)]] diff --git a/urwid/raw_display.py b/urwid/raw_display.py index 760c49c..ce91df3 100644 --- a/urwid/raw_display.py +++ b/urwid/raw_display.py @@ -124,17 +124,37 @@ class Screen: def _sigwinch_handler(self, signum, frame): self.resized = True - + + def signal_init(self): + """ + Called in the startup of run wrapper to set the SIGWINCH + signal handler to self._sigwinch_handler. + + Override this function to call from main thread in threaded + applications. + """ + signal.signal(signal.SIGWINCH, self._sigwinch_handler) + + def signal_restore(self): + """ + Called in the finally block of run wrapper to restore the + SIGWINCH handler to the default handler. + + Override this function to call from main thread in threaded + applications. + """ + signal.signal(signal.SIGWINCH, signal.SIG_DFL) + def run_wrapper(self,fn): """ Call fn and reset terminal on exit. """ old_settings = termios.tcgetattr(0) try: - signal.signal(signal.SIGWINCH, self._sigwinch_handler) + self.signal_init() tty.setcbreak(sys.stdin.fileno()) fn() finally: - signal.signal(signal.SIGWINCH, signal.SIG_DFL) + self.signal_restore() termios.tcsetattr(0, termios.TCSADRAIN, old_settings) move_cursor = "" if self.maxrow is not None: diff --git a/urwid/util.py b/urwid/util.py index 011891e..a31525d 100644 --- a/urwid/util.py +++ b/urwid/util.py @@ -52,7 +52,7 @@ def set_encoding( encoding ): global byte_encoding, target_encoding - if encoding in ( 'utf-8', 'utf8' ): + if encoding in ( 'utf-8', 'utf8', 'utf' ): byte_encoding = "utf8" elif encoding in ( 'euc-jp' # JISX 0208 only , 'euc-kr', 'euc-cn', 'euc-tw' # CNS 11643 plain 1 only @@ -71,6 +71,15 @@ def set_encoding( encoding ): target_encoding = encoding except LookupError: pass +def get_encoding_mode(): + """ + Get the mode Urwid is using when processing text strings. + Returns 'narrow' for 8-bit encodings, 'wide' for CJK encodings + or 'utf8' for UTF-8 encodings. + """ + return byte_encoding + + ###################################################################### # Try to set the encoding using the one detected by the locale module set_encoding( detected_encoding ) @@ -781,14 +790,14 @@ def trim_attr( attr, start, end ): drawing_charmap = { u"◆" : "`", u"▒" : "a", - u"␉" : "b", - u"␌" : "c", - u"␍" : "d", - u"␊" : "e", +# u"␉" : "b", +# u"␌" : "c", +# u"␍" : "d", +# u"␊" : "e", u"°" : "f", u"±" : "g", - u"" : "h", - u"␋" : "i", +# u"" : "h", +# u"␋" : "i", u"┘" : "j", u"┐" : "k", u"┌" : "l", @@ -831,7 +840,7 @@ def _tagmarkup_recurse( tm, attr ): tm -- tagmarkup attr -- current attribute or None""" - if type(tm) == type(""): + if type(tm) == type("") or type(tm) == type( u"" ): # text return [tm], [(attr, len(tm))] |