Imported Upstream version 0.1.5+dfsg1
SVN-Git Migration
8 years ago
0 | syntax: glob | |
1 | *.pyc | |
2 | *.pyo | |
3 | *.pyc | |
4 | *.o | |
5 | *.so | |
6 | *~ | |
7 | *.rej | |
8 | *.orig | |
9 | *.mo | |
10 | ||
11 | .figleaf* | |
12 | *.egg-info | |
13 | *.egg | |
14 | build | |
15 | deps | |
16 | local_settings.py | |
17 | .build | |
18 | dist | |
19 | pip-log.txt | |
20 | ||
21 | syntax: regexp | |
22 | (.*/)?\#[^/]*\#$ |
0 | f0b97fffad309fd8dea60efd7d989fff4ba3058a 0.1 | |
1 | f0b97fffad309fd8dea60efd7d989fff4ba3058a 0.1 | |
2 | c6a2348d3455d5b25849d50d9fe497192dd62deb 0.1 | |
3 | 2a1837cbbaa1b99ff7caeae5b320584ffda3c8a9 0.1.1 | |
4 | dfc04f2f18769e08d3d8b5f32bfabc20970e6c6b 0.1.2 | |
5 | 48ffde688a2e85ec80d72b59eb6107f46c8d39e4 0.1.3 | |
6 | a7cfbe73acd2a137607bb29abbcb250f8b465571 0.1.4 |
0 | Copyright (c) 2010 J.A. Roberts Tunney | |
1 | ||
2 | Permission is hereby granted, free of charge, to any person obtaining | |
3 | a copy of this software and associated documentation files (the | |
4 | "Software"), to deal in the Software without restriction, including | |
5 | without limitation the rights to use, copy, modify, merge, publish, | |
6 | distribute, sublicense, and/or sell copies of the Software, and to | |
7 | permit persons to whom the Software is furnished to do so, subject to | |
8 | the following conditions: | |
9 | ||
10 | The above copyright notice and this permission notice shall be | |
11 | included in all copies or substantial portions of the Software. | |
12 | ||
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |
17 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
18 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
19 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
0 | Metadata-Version: 1.0 | |
1 | Name: fabulous | |
2 | Version: 0.1.5 | |
3 | Summary: Makes your terminal output totally fabulous | |
4 | Home-page: http://lobstertech.com/fabulous.html | |
5 | Author: J.A. Roberts Tunney | |
6 | Author-email: jtunney@lobstertech.com | |
7 | License: MIT | |
8 | Download-URL: http://lobstertech.com/media/file/fabulous/fabulous-0.1.5.tar.gz | |
9 | Description: .. -*-restructuredtext-*- | |
10 | ||
11 | ========== | |
12 | Fabulous | |
13 | ========== | |
14 | ||
15 | --------------------------------------------- | |
16 | Makes Your Terminal Output Totally Fabulous | |
17 | --------------------------------------------- | |
18 | ||
19 | :Version: 0.1 | |
20 | :Date: 2009-12-07 | |
21 | :Copyright: Copyright (c) 2009 Lobstertech, Inc. | |
22 | :Manual section: 3 | |
23 | :Manual group: Library Calls | |
24 | ||
25 | ||
26 | Getting Started | |
27 | =============== | |
28 | ||
29 | Download and extract the latest version:: | |
30 | ||
31 | sudo apt-get install gcc python-imaging python-setuptools | |
32 | sudo python setup.py install | |
33 | ||
34 | Run the demo to see what's available:: | |
35 | ||
36 | python -m fabulous.demo | |
37 | ||
38 | ||
39 | Basic Examples | |
40 | ============== | |
41 | ||
42 | Colors | |
43 | ------ | |
44 | ||
45 | 4-bit color. These colors and styles are standard and work almost | |
46 | everywhere. They are useful in helping make your program output | |
47 | easier to read:: | |
48 | ||
49 | from fabulous import bold, magenta, highlight_red | |
50 | ||
51 | print bold(magenta('hello kitty')) | |
52 | print highlight_red('DANGER DANGER!') | |
53 | ||
54 | print bold('hello') + ' ' + magenta( kitty') | |
55 | ||
56 | assert len(bold('test')) == 4 | |
57 | ||
58 | 8-bit color. If you want to spice things up a bit, Fabulous supports | |
59 | xterm256 colors:: | |
60 | ||
61 | from fabulous import fg256, bg256 | |
62 | print fg256('#F0F', 'hello kitty') | |
63 | print fg256('magenta', 'hello kitty') | |
64 | ||
65 | ||
66 | Fancy Text | |
67 | ---------- | |
68 | ||
69 | Way cool text. This is something neat you can use when you program | |
70 | starts up to display its name with style:: | |
71 | ||
72 | from fabulous import text | |
73 | print text.Text("Fabulous", color='#0099ff', shadow=True, scew=5) | |
74 | ||
75 | ||
76 | Images | |
77 | ------ | |
78 | ||
79 | Fabulous lets you print images, which is more fun than useful. | |
80 | Fabulous' unique method of printing images really shines when used | |
81 | with semi-transparent PNG files. When blending backgrounds, Fabulous | |
82 | assumes by default that your terminal has a black background. Don't | |
83 | worry if your image is huge, it'll be resized by default to fit your | |
84 | terminal:: | |
85 | ||
86 | from fabulous import utils, image | |
87 | print image.Image("balls.png") | |
88 | ||
89 | # adjust for a white background | |
90 | utils.term.bgcolor = 'white' | |
91 | print image.Image("balls.png") | |
92 | ||
93 | It's scriptable too (like img2txt) :: | |
94 | ||
95 | python -m fabulous.image balls.png >balls.txt | |
96 | cat balls.txt | |
97 | ||
98 | ||
99 | Transient Logging | |
100 | ----------------- | |
101 | ||
102 | This is very useful tool for monitoring what your Python scripts are | |
103 | doing. It allows you to have full verbosity without drowning out | |
104 | important error messages:: | |
105 | ||
106 | import time, logging | |
107 | from fabulous import logs | |
108 | logs.basicConfig(level='WARNING') | |
109 | ||
110 | for n in range(20): | |
111 | logging.debug("verbose stuff you don't care about") | |
112 | time.sleep(0.1) | |
113 | logging.warning("something bad happened!") | |
114 | for n in range(20): | |
115 | logging.debug("verbose stuff you don't care about") | |
116 | time.sleep(0.1) | |
117 | ||
118 | ||
119 | Why Fabulous? | |
120 | ============= | |
121 | ||
122 | Here's how Fabulous compares to other similar libraries: | |
123 | ||
124 | - fabulous_: Licensed MIT. Focuses on delivering useful features in | |
125 | the simplest, most user-friendly way possible (without a repulsive | |
126 | name.) Written in pure-python but will attempt to auto-magically | |
127 | compile/link a speedup library. ~1,000 lines of code. | |
128 | ||
129 | - libcaca_: WTFPL. This is the established and respected standard for | |
130 | doing totally insane things with ascii art (ever wanted to watch a | |
131 | movie on the command line?) Weighing in at ~72k lines of C, this | |
132 | project is a monster. It uses an older, more complex | |
133 | text/dithering-based rendering method. Compared to fabulous, some | |
134 | images look better, some worse. I found the docs somewhat difficult | |
135 | to follow and couldn't find support for transparency or 256-colors. | |
136 | ||
137 | - asciiporn_: GPL. Similar to libcaca but has an interesting feature | |
138 | for drawing math graphs to the terminal... Needs to compile C code, | |
139 | requires numpy/python2.6, and I couldn't get the darn thing to work. | |
140 | Aprox 17k lines of code. | |
141 | ||
142 | - pygments_: BSD. Has *excellent* support for terminal syntax highlighting. | |
143 | ||
144 | - termcolor_: GPL. Only supports 4-bit ANSI colors. | |
145 | ||
146 | .. _fabulous: http://pypi.python.org/pypi/fabulous | |
147 | .. _libcaca: http://caca.zoy.org/ | |
148 | .. _termcolor: http://pypi.python.org/pypi/termcolor | |
149 | .. _pygments: http://pygments.org/ | |
150 | .. _asciiporn: http://pypi.python.org/pypi/asciiporn/2009.05.01 | |
151 | ||
152 | ||
153 | ToDo | |
154 | ==== | |
155 | ||
156 | - <http://www.burgaud.com/bring-colors-to-the-windows-console-with-python/> | |
157 | ||
158 | Platform: UNKNOWN | |
159 | Classifier: Development Status :: 2 - Pre-Alpha | |
160 | Classifier: License :: OSI Approved :: MIT License | |
161 | Classifier: Environment :: Console | |
162 | Classifier: Intended Audience :: Developers | |
163 | Classifier: Programming Language :: C | |
164 | Classifier: Programming Language :: Python | |
165 | Classifier: Topic :: Utilities | |
166 | Classifier: Topic :: Artistic Software | |
167 | Classifier: Topic :: System :: Logging | |
168 | Classifier: Topic :: Multimedia :: Graphics |
0 | .. -*-restructuredtext-*- | |
1 | ||
2 | ========== | |
3 | Fabulous | |
4 | ========== | |
5 | ||
6 | --------------------------------------------- | |
7 | Makes Your Terminal Output Totally Fabulous | |
8 | --------------------------------------------- | |
9 | ||
10 | :Version: 0.1 | |
11 | :Date: 2009-12-07 | |
12 | :Copyright: Copyright (c) 2009 Lobstertech, Inc. | |
13 | :Manual section: 3 | |
14 | :Manual group: Library Calls | |
15 | ||
16 | ||
17 | Getting Started | |
18 | =============== | |
19 | ||
20 | Download and extract the latest version:: | |
21 | ||
22 | sudo apt-get install gcc python-imaging python-setuptools | |
23 | sudo python setup.py install | |
24 | ||
25 | Run the demo to see what's available:: | |
26 | ||
27 | python -m fabulous.demo | |
28 | ||
29 | ||
30 | Basic Examples | |
31 | ============== | |
32 | ||
33 | Colors | |
34 | ------ | |
35 | ||
36 | 4-bit color. These colors and styles are standard and work almost | |
37 | everywhere. They are useful in helping make your program output | |
38 | easier to read:: | |
39 | ||
40 | from fabulous import bold, magenta, highlight_red | |
41 | ||
42 | print bold(magenta('hello kitty')) | |
43 | print highlight_red('DANGER DANGER!') | |
44 | ||
45 | print bold('hello') + ' ' + magenta( kitty') | |
46 | ||
47 | assert len(bold('test')) == 4 | |
48 | ||
49 | 8-bit color. If you want to spice things up a bit, Fabulous supports | |
50 | xterm256 colors:: | |
51 | ||
52 | from fabulous import fg256, bg256 | |
53 | print fg256('#F0F', 'hello kitty') | |
54 | print fg256('magenta', 'hello kitty') | |
55 | ||
56 | ||
57 | Fancy Text | |
58 | ---------- | |
59 | ||
60 | Way cool text. This is something neat you can use when you program | |
61 | starts up to display its name with style:: | |
62 | ||
63 | from fabulous import text | |
64 | print text.Text("Fabulous", color='#0099ff', shadow=True, scew=5) | |
65 | ||
66 | ||
67 | Images | |
68 | ------ | |
69 | ||
70 | Fabulous lets you print images, which is more fun than useful. | |
71 | Fabulous' unique method of printing images really shines when used | |
72 | with semi-transparent PNG files. When blending backgrounds, Fabulous | |
73 | assumes by default that your terminal has a black background. Don't | |
74 | worry if your image is huge, it'll be resized by default to fit your | |
75 | terminal:: | |
76 | ||
77 | from fabulous import utils, image | |
78 | print image.Image("balls.png") | |
79 | ||
80 | # adjust for a white background | |
81 | utils.term.bgcolor = 'white' | |
82 | print image.Image("balls.png") | |
83 | ||
84 | It's scriptable too (like img2txt) :: | |
85 | ||
86 | python -m fabulous.image balls.png >balls.txt | |
87 | cat balls.txt | |
88 | ||
89 | ||
90 | Transient Logging | |
91 | ----------------- | |
92 | ||
93 | This is very useful tool for monitoring what your Python scripts are | |
94 | doing. It allows you to have full verbosity without drowning out | |
95 | important error messages:: | |
96 | ||
97 | import time, logging | |
98 | from fabulous import logs | |
99 | logs.basicConfig(level='WARNING') | |
100 | ||
101 | for n in range(20): | |
102 | logging.debug("verbose stuff you don't care about") | |
103 | time.sleep(0.1) | |
104 | logging.warning("something bad happened!") | |
105 | for n in range(20): | |
106 | logging.debug("verbose stuff you don't care about") | |
107 | time.sleep(0.1) | |
108 | ||
109 | ||
110 | Why Fabulous? | |
111 | ============= | |
112 | ||
113 | Here's how Fabulous compares to other similar libraries: | |
114 | ||
115 | - fabulous_: Licensed MIT. Focuses on delivering useful features in | |
116 | the simplest, most user-friendly way possible (without a repulsive | |
117 | name.) Written in pure-python but will attempt to auto-magically | |
118 | compile/link a speedup library. ~1,000 lines of code. | |
119 | ||
120 | - libcaca_: WTFPL. This is the established and respected standard for | |
121 | doing totally insane things with ascii art (ever wanted to watch a | |
122 | movie on the command line?) Weighing in at ~72k lines of C, this | |
123 | project is a monster. It uses an older, more complex | |
124 | text/dithering-based rendering method. Compared to fabulous, some | |
125 | images look better, some worse. I found the docs somewhat difficult | |
126 | to follow and couldn't find support for transparency or 256-colors. | |
127 | ||
128 | - asciiporn_: GPL. Similar to libcaca but has an interesting feature | |
129 | for drawing math graphs to the terminal... Needs to compile C code, | |
130 | requires numpy/python2.6, and I couldn't get the darn thing to work. | |
131 | Aprox 17k lines of code. | |
132 | ||
133 | - pygments_: BSD. Has *excellent* support for terminal syntax highlighting. | |
134 | ||
135 | - termcolor_: GPL. Only supports 4-bit ANSI colors. | |
136 | ||
137 | .. _fabulous: http://pypi.python.org/pypi/fabulous | |
138 | .. _libcaca: http://caca.zoy.org/ | |
139 | .. _termcolor: http://pypi.python.org/pypi/termcolor | |
140 | .. _pygments: http://pygments.org/ | |
141 | .. _asciiporn: http://pypi.python.org/pypi/asciiporn/2009.05.01 | |
142 | ||
143 | ||
144 | ToDo | |
145 | ==== | |
146 | ||
147 | - <http://www.burgaud.com/bring-colors-to-the-windows-console-with-python/> |
0 | # Makefile for Sphinx documentation | |
1 | # | |
2 | ||
3 | # You can set these variables from the command line. | |
4 | SPHINXOPTS = | |
5 | SPHINXBUILD = sphinx-build | |
6 | PAPER = | |
7 | BUILDDIR = _build | |
8 | ||
9 | # Internal variables. | |
10 | PAPEROPT_a4 = -D latex_paper_size=a4 | |
11 | PAPEROPT_letter = -D latex_paper_size=letter | |
12 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . | |
13 | ||
14 | .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest | |
15 | ||
16 | help: | |
17 | @echo "Please use \`make <target>' where <target> is one of" | |
18 | @echo " html to make standalone HTML files" | |
19 | @echo " dirhtml to make HTML files named index.html in directories" | |
20 | @echo " pickle to make pickle files" | |
21 | @echo " json to make JSON files" | |
22 | @echo " htmlhelp to make HTML files and a HTML help project" | |
23 | @echo " qthelp to make HTML files and a qthelp project" | |
24 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" | |
25 | @echo " changes to make an overview of all changed/added/deprecated items" | |
26 | @echo " linkcheck to check all external links for integrity" | |
27 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" | |
28 | ||
29 | clean: | |
30 | -rm -rf $(BUILDDIR)/* | |
31 | ||
32 | html: | |
33 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html | |
34 | @echo | |
35 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." | |
36 | ||
37 | dirhtml: | |
38 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml | |
39 | @echo | |
40 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." | |
41 | ||
42 | pickle: | |
43 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle | |
44 | @echo | |
45 | @echo "Build finished; now you can process the pickle files." | |
46 | ||
47 | json: | |
48 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json | |
49 | @echo | |
50 | @echo "Build finished; now you can process the JSON files." | |
51 | ||
52 | htmlhelp: | |
53 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp | |
54 | @echo | |
55 | @echo "Build finished; now you can run HTML Help Workshop with the" \ | |
56 | ".hhp project file in $(BUILDDIR)/htmlhelp." | |
57 | ||
58 | qthelp: | |
59 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp | |
60 | @echo | |
61 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ | |
62 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" | |
63 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/nemesis.qhcp" | |
64 | @echo "To view the help file:" | |
65 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/nemesis.qhc" | |
66 | ||
67 | latex: | |
68 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex | |
69 | @echo | |
70 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." | |
71 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ | |
72 | "run these through (pdf)latex." | |
73 | ||
74 | changes: | |
75 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes | |
76 | @echo | |
77 | @echo "The overview file is in $(BUILDDIR)/changes." | |
78 | ||
79 | linkcheck: | |
80 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck | |
81 | @echo | |
82 | @echo "Link check complete; look for any errors in the above output " \ | |
83 | "or in $(BUILDDIR)/linkcheck/output.txt." | |
84 | ||
85 | doctest: | |
86 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest | |
87 | @echo "Testing of doctests in the sources finished, look at the " \ | |
88 | "results in $(BUILDDIR)/doctest/output.txt." |
0 | # -*- coding: utf-8 -*- | |
1 | # | |
2 | # fabulous documentation build configuration file, created by | |
3 | # sphinx-quickstart on Tue Apr 20 02:12:28 2010. | |
4 | # | |
5 | # This file is execfile()d with the current directory set to its containing dir. | |
6 | # | |
7 | # Note that not all possible configuration values are present in this | |
8 | # autogenerated file. | |
9 | # | |
10 | # All configuration values have a default; values that are commented out | |
11 | # serve to show the default. | |
12 | ||
13 | import sys, os | |
14 | import fabulous | |
15 | ||
16 | # If extensions (or modules to document with autodoc) are in another directory, | |
17 | # add these directories to sys.path here. If the directory is relative to the | |
18 | # documentation root, use os.path.abspath to make it absolute, like shown here. | |
19 | # sys.path.append(os.path.abspath('..')) | |
20 | ||
21 | # -- General configuration ----------------------------------------------------- | |
22 | ||
23 | # Add any Sphinx extension module names here, as strings. They can be extensions | |
24 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. | |
25 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.pngmath'] | |
26 | ||
27 | # Add any paths that contain templates here, relative to this directory. | |
28 | templates_path = ['_templates'] | |
29 | ||
30 | # The suffix of source filenames. | |
31 | source_suffix = '.rst' | |
32 | ||
33 | # The encoding of source files. | |
34 | #source_encoding = 'utf-8' | |
35 | ||
36 | # The master toctree document. | |
37 | master_doc = 'index' | |
38 | ||
39 | # General information about the project. | |
40 | project = u'fabulous' | |
41 | copyright = u'2010, J.A. Roberts Tunney' | |
42 | ||
43 | # The version info for the project you're documenting, acts as replacement for | |
44 | # |version| and |release|, also used in various other places throughout the | |
45 | # built documents. | |
46 | # | |
47 | # The short X.Y version. | |
48 | version = fabulous.__version__ | |
49 | # The full version, including alpha/beta/rc tags. | |
50 | release = fabulous.__version__ | |
51 | ||
52 | # The language for content autogenerated by Sphinx. Refer to documentation | |
53 | # for a list of supported languages. | |
54 | #language = None | |
55 | ||
56 | # There are two options for replacing |today|: either, you set today to some | |
57 | # non-false value, then it is used: | |
58 | #today = '' | |
59 | # Else, today_fmt is used as the format for a strftime call. | |
60 | #today_fmt = '%B %d, %Y' | |
61 | ||
62 | # List of documents that shouldn't be included in the build. | |
63 | #unused_docs = [] | |
64 | ||
65 | # List of directories, relative to source directory, that shouldn't be searched | |
66 | # for source files. | |
67 | exclude_trees = ['_build'] | |
68 | ||
69 | # The reST default role (used for this markup: `text`) to use for all documents. | |
70 | #default_role = None | |
71 | ||
72 | # If true, '()' will be appended to :func: etc. cross-reference text. | |
73 | #add_function_parentheses = True | |
74 | ||
75 | # If true, the current module name will be prepended to all description | |
76 | # unit titles (such as .. function::). | |
77 | #add_module_names = True | |
78 | ||
79 | # If true, sectionauthor and moduleauthor directives will be shown in the | |
80 | # output. They are ignored by default. | |
81 | #show_authors = False | |
82 | ||
83 | # The name of the Pygments (syntax highlighting) style to use. | |
84 | pygments_style = 'sphinx' | |
85 | ||
86 | # A list of ignored prefixes for module index sorting. | |
87 | #modindex_common_prefix = [] | |
88 | ||
89 | ||
90 | # -- Options for HTML output --------------------------------------------------- | |
91 | ||
92 | # The theme to use for HTML and HTML Help pages. Major themes that come with | |
93 | # Sphinx are currently 'default' and 'sphinxdoc'. | |
94 | html_theme = 'default' | |
95 | ||
96 | # Theme options are theme-specific and customize the look and feel of a theme | |
97 | # further. For a list of options available for each theme, see the | |
98 | # documentation. | |
99 | #html_theme_options = {} | |
100 | ||
101 | # Add any paths that contain custom themes here, relative to this directory. | |
102 | #html_theme_path = [] | |
103 | ||
104 | # The name for this set of Sphinx documents. If None, it defaults to | |
105 | # "<project> v<release> documentation". | |
106 | #html_title = None | |
107 | ||
108 | # A shorter title for the navigation bar. Default is the same as html_title. | |
109 | #html_short_title = None | |
110 | ||
111 | # The name of an image file (relative to this directory) to place at the top | |
112 | # of the sidebar. | |
113 | #html_logo = None | |
114 | ||
115 | # The name of an image file (within the static path) to use as favicon of the | |
116 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 | |
117 | # pixels large. | |
118 | #html_favicon = None | |
119 | ||
120 | # Add any paths that contain custom static files (such as style sheets) here, | |
121 | # relative to this directory. They are copied after the builtin static files, | |
122 | # so a file named "default.css" will overwrite the builtin "default.css". | |
123 | html_static_path = ['_static'] | |
124 | ||
125 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, | |
126 | # using the given strftime format. | |
127 | #html_last_updated_fmt = '%b %d, %Y' | |
128 | ||
129 | # If true, SmartyPants will be used to convert quotes and dashes to | |
130 | # typographically correct entities. | |
131 | #html_use_smartypants = True | |
132 | ||
133 | # Custom sidebar templates, maps document names to template names. | |
134 | #html_sidebars = {} | |
135 | ||
136 | # Additional templates that should be rendered to pages, maps page names to | |
137 | # template names. | |
138 | #html_additional_pages = {} | |
139 | ||
140 | # If false, no module index is generated. | |
141 | #html_use_modindex = True | |
142 | ||
143 | # If false, no index is generated. | |
144 | #html_use_index = True | |
145 | ||
146 | # If true, the index is split into individual pages for each letter. | |
147 | #html_split_index = False | |
148 | ||
149 | # If true, links to the reST sources are added to the pages. | |
150 | #html_show_sourcelink = True | |
151 | ||
152 | # If true, an OpenSearch description file will be output, and all pages will | |
153 | # contain a <link> tag referring to it. The value of this option must be the | |
154 | # base URL from which the finished HTML is served. | |
155 | #html_use_opensearch = '' | |
156 | ||
157 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). | |
158 | #html_file_suffix = '' | |
159 | ||
160 | # Output file base name for HTML help builder. | |
161 | htmlhelp_basename = 'fabulousdoc' | |
162 | ||
163 | ||
164 | # -- Options for LaTeX output -------------------------------------------------- | |
165 | ||
166 | # The paper size ('letter' or 'a4'). | |
167 | #latex_paper_size = 'letter' | |
168 | ||
169 | # The font size ('10pt', '11pt' or '12pt'). | |
170 | #latex_font_size = '10pt' | |
171 | ||
172 | # Grouping the document tree into LaTeX files. List of tuples | |
173 | # (source start file, target name, title, author, documentclass [howto/manual]). | |
174 | latex_documents = [ | |
175 | ('index', 'fabulous.tex', u'Fabulous Documentation', | |
176 | u'J.A. Roberts Tunney', 'manual'), | |
177 | ] | |
178 | ||
179 | # The name of an image file (relative to this directory) to place at the top of | |
180 | # the title page. | |
181 | #latex_logo = None | |
182 | ||
183 | # For "manual" documents, if this is true, then toplevel headings are parts, | |
184 | # not chapters. | |
185 | #latex_use_parts = False | |
186 | ||
187 | # Additional stuff for the LaTeX preamble. | |
188 | #latex_preamble = '' | |
189 | ||
190 | # Documents to append as an appendix to all manuals. | |
191 | #latex_appendices = [] | |
192 | ||
193 | # If false, no module index is generated. | |
194 | #latex_use_modindex = True | |
195 | ||
196 | ||
197 | # Example configuration for intersphinx: refer to the Python standard library. | |
198 | intersphinx_mapping = {'http://docs.python.org/': None} |
0 | .. fabulous documentation master file, created by | |
1 | sphinx-quickstart on Tue Apr 20 02:12:28 2010. | |
2 | You can adapt this file completely to your liking, but it should at least | |
3 | contain the root `toctree` directive. | |
4 | ||
5 | ========== | |
6 | Fabulous | |
7 | ========== | |
8 | ||
9 | .. toctree:: | |
10 | :maxdepth: 1 | |
11 | ||
12 | :Version: 0.1 | |
13 | :Copyright: Copyright (c) 2010 J.A. Roberts Tunney | |
14 | ||
15 | ||
16 | Installation | |
17 | ============ | |
18 | ||
19 | Run the following commands:: | |
20 | ||
21 | sudo apt-get install python-imaging | |
22 | sudo python setup.py install | |
23 | ||
24 | ||
25 | Modules | |
26 | ======= | |
27 | ||
28 | .. automodule:: fabulous.color | |
29 | :members: | |
30 | .. automodule:: fabulous.xterm256 | |
31 | :members: | |
32 | .. automodule:: fabulous.logs | |
33 | :members: | |
34 | .. automodule:: fabulous.text | |
35 | :members: | |
36 | .. automodule:: fabulous.image | |
37 | :members: | |
38 | .. automodule:: fabulous.utils | |
39 | :members: | |
40 | .. automodule:: fabulous.gotham | |
41 | :members: | |
42 | .. automodule:: fabulous.rotating_cube | |
43 | :members: | |
44 | .. automodule:: fabulous.debug | |
45 | :members: |
0 | #!python | |
1 | """Bootstrap setuptools installation | |
2 | ||
3 | If you want to use setuptools in your package's setup.py, just include this | |
4 | file in the same directory with it, and add this to the top of your setup.py:: | |
5 | ||
6 | from ez_setup import use_setuptools | |
7 | use_setuptools() | |
8 | ||
9 | If you want to require a specific version of setuptools, set a download | |
10 | mirror, or use an alternate download directory, you can do so by supplying | |
11 | the appropriate options to ``use_setuptools()``. | |
12 | ||
13 | This file can also be run as a script to install or upgrade setuptools. | |
14 | """ | |
15 | import sys | |
16 | DEFAULT_VERSION = "0.6c11" | |
17 | DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] | |
18 | ||
19 | md5_data = { | |
20 | 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', | |
21 | 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', | |
22 | 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', | |
23 | 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', | |
24 | 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', | |
25 | 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', | |
26 | 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', | |
27 | 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', | |
28 | 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', | |
29 | 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', | |
30 | 'setuptools-0.6c10-py2.3.egg': 'ce1e2ab5d3a0256456d9fc13800a7090', | |
31 | 'setuptools-0.6c10-py2.4.egg': '57d6d9d6e9b80772c59a53a8433a5dd4', | |
32 | 'setuptools-0.6c10-py2.5.egg': 'de46ac8b1c97c895572e5e8596aeb8c7', | |
33 | 'setuptools-0.6c10-py2.6.egg': '58ea40aef06da02ce641495523a0b7f5', | |
34 | 'setuptools-0.6c11-py2.3.egg': '2baeac6e13d414a9d28e7ba5b5a596de', | |
35 | 'setuptools-0.6c11-py2.4.egg': 'bd639f9b0eac4c42497034dec2ec0c2b', | |
36 | 'setuptools-0.6c11-py2.5.egg': '64c94f3bf7a72a13ec83e0b24f2749b2', | |
37 | 'setuptools-0.6c11-py2.6.egg': 'bfa92100bd772d5a213eedd356d64086', | |
38 | 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', | |
39 | 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', | |
40 | 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', | |
41 | 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', | |
42 | 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', | |
43 | 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', | |
44 | 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', | |
45 | 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', | |
46 | 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', | |
47 | 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', | |
48 | 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', | |
49 | 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', | |
50 | 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', | |
51 | 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', | |
52 | 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', | |
53 | 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', | |
54 | 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', | |
55 | 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902', | |
56 | 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de', | |
57 | 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b', | |
58 | 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03', | |
59 | 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a', | |
60 | 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6', | |
61 | 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a', | |
62 | } | |
63 | ||
64 | import sys, os | |
65 | try: from hashlib import md5 | |
66 | except ImportError: from md5 import md5 | |
67 | ||
68 | def _validate_md5(egg_name, data): | |
69 | if egg_name in md5_data: | |
70 | digest = md5(data).hexdigest() | |
71 | if digest != md5_data[egg_name]: | |
72 | print >>sys.stderr, ( | |
73 | "md5 validation of %s failed! (Possible download problem?)" | |
74 | % egg_name | |
75 | ) | |
76 | sys.exit(2) | |
77 | return data | |
78 | ||
79 | def use_setuptools( | |
80 | version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, | |
81 | download_delay=15 | |
82 | ): | |
83 | """Automatically find/download setuptools and make it available on sys.path | |
84 | ||
85 | `version` should be a valid setuptools version number that is available | |
86 | as an egg for download under the `download_base` URL (which should end with | |
87 | a '/'). `to_dir` is the directory where setuptools will be downloaded, if | |
88 | it is not already available. If `download_delay` is specified, it should | |
89 | be the number of seconds that will be paused before initiating a download, | |
90 | should one be required. If an older version of setuptools is installed, | |
91 | this routine will print a message to ``sys.stderr`` and raise SystemExit in | |
92 | an attempt to abort the calling script. | |
93 | """ | |
94 | was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules | |
95 | def do_download(): | |
96 | egg = download_setuptools(version, download_base, to_dir, download_delay) | |
97 | sys.path.insert(0, egg) | |
98 | import setuptools; setuptools.bootstrap_install_from = egg | |
99 | try: | |
100 | import pkg_resources | |
101 | except ImportError: | |
102 | return do_download() | |
103 | try: | |
104 | pkg_resources.require("setuptools>="+version); return | |
105 | except pkg_resources.VersionConflict, e: | |
106 | if was_imported: | |
107 | print >>sys.stderr, ( | |
108 | "The required version of setuptools (>=%s) is not available, and\n" | |
109 | "can't be installed while this script is running. Please install\n" | |
110 | " a more recent version first, using 'easy_install -U setuptools'." | |
111 | "\n\n(Currently using %r)" | |
112 | ) % (version, e.args[0]) | |
113 | sys.exit(2) | |
114 | else: | |
115 | del pkg_resources, sys.modules['pkg_resources'] # reload ok | |
116 | return do_download() | |
117 | except pkg_resources.DistributionNotFound: | |
118 | return do_download() | |
119 | ||
120 | def download_setuptools( | |
121 | version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, | |
122 | delay = 15 | |
123 | ): | |
124 | """Download setuptools from a specified location and return its filename | |
125 | ||
126 | `version` should be a valid setuptools version number that is available | |
127 | as an egg for download under the `download_base` URL (which should end | |
128 | with a '/'). `to_dir` is the directory where the egg will be downloaded. | |
129 | `delay` is the number of seconds to pause before an actual download attempt. | |
130 | """ | |
131 | import urllib2, shutil | |
132 | egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) | |
133 | url = download_base + egg_name | |
134 | saveto = os.path.join(to_dir, egg_name) | |
135 | src = dst = None | |
136 | if not os.path.exists(saveto): # Avoid repeated downloads | |
137 | try: | |
138 | from distutils import log | |
139 | if delay: | |
140 | log.warn(""" | |
141 | --------------------------------------------------------------------------- | |
142 | This script requires setuptools version %s to run (even to display | |
143 | help). I will attempt to download it for you (from | |
144 | %s), but | |
145 | you may need to enable firewall access for this script first. | |
146 | I will start the download in %d seconds. | |
147 | ||
148 | (Note: if this machine does not have network access, please obtain the file | |
149 | ||
150 | %s | |
151 | ||
152 | and place it in this directory before rerunning this script.) | |
153 | ---------------------------------------------------------------------------""", | |
154 | version, download_base, delay, url | |
155 | ); from time import sleep; sleep(delay) | |
156 | log.warn("Downloading %s", url) | |
157 | src = urllib2.urlopen(url) | |
158 | # Read/write all in one block, so we don't create a corrupt file | |
159 | # if the download is interrupted. | |
160 | data = _validate_md5(egg_name, src.read()) | |
161 | dst = open(saveto,"wb"); dst.write(data) | |
162 | finally: | |
163 | if src: src.close() | |
164 | if dst: dst.close() | |
165 | return os.path.realpath(saveto) | |
166 | ||
167 | ||
168 | ||
169 | ||
170 | ||
171 | ||
172 | ||
173 | ||
174 | ||
175 | ||
176 | ||
177 | ||
178 | ||
179 | ||
180 | ||
181 | ||
182 | ||
183 | ||
184 | ||
185 | ||
186 | ||
187 | ||
188 | ||
189 | ||
190 | ||
191 | ||
192 | ||
193 | ||
194 | ||
195 | ||
196 | ||
197 | ||
198 | ||
199 | ||
200 | ||
201 | ||
202 | def main(argv, version=DEFAULT_VERSION): | |
203 | """Install or upgrade setuptools and EasyInstall""" | |
204 | try: | |
205 | import setuptools | |
206 | except ImportError: | |
207 | egg = None | |
208 | try: | |
209 | egg = download_setuptools(version, delay=0) | |
210 | sys.path.insert(0,egg) | |
211 | from setuptools.command.easy_install import main | |
212 | return main(list(argv)+[egg]) # we're done here | |
213 | finally: | |
214 | if egg and os.path.exists(egg): | |
215 | os.unlink(egg) | |
216 | else: | |
217 | if setuptools.__version__ == '0.0.1': | |
218 | print >>sys.stderr, ( | |
219 | "You have an obsolete version of setuptools installed. Please\n" | |
220 | "remove it from your system entirely before rerunning this script." | |
221 | ) | |
222 | sys.exit(2) | |
223 | ||
224 | req = "setuptools>="+version | |
225 | import pkg_resources | |
226 | try: | |
227 | pkg_resources.require(req) | |
228 | except pkg_resources.VersionConflict: | |
229 | try: | |
230 | from setuptools.command.easy_install import main | |
231 | except ImportError: | |
232 | from easy_install import main | |
233 | main(list(argv)+[download_setuptools(delay=0)]) | |
234 | sys.exit(0) # try to force an exit | |
235 | else: | |
236 | if argv: | |
237 | from setuptools.command.easy_install import main | |
238 | main(argv) | |
239 | else: | |
240 | print "Setuptools version",version,"or greater has been installed." | |
241 | print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' | |
242 | ||
243 | def update_md5(filenames): | |
244 | """Update our built-in md5 registry""" | |
245 | ||
246 | import re | |
247 | ||
248 | for name in filenames: | |
249 | base = os.path.basename(name) | |
250 | f = open(name,'rb') | |
251 | md5_data[base] = md5(f.read()).hexdigest() | |
252 | f.close() | |
253 | ||
254 | data = [" %r: %r,\n" % it for it in md5_data.items()] | |
255 | data.sort() | |
256 | repl = "".join(data) | |
257 | ||
258 | import inspect | |
259 | srcfile = inspect.getsourcefile(sys.modules[__name__]) | |
260 | f = open(srcfile, 'rb'); src = f.read(); f.close() | |
261 | ||
262 | match = re.search("\nmd5_data = {\n([^}]+)}", src) | |
263 | if not match: | |
264 | print >>sys.stderr, "Internal error!" | |
265 | sys.exit(2) | |
266 | ||
267 | src = src[:match.start(1)] + repl + src[match.end(1):] | |
268 | f = open(srcfile,'w') | |
269 | f.write(src) | |
270 | f.close() | |
271 | ||
272 | ||
273 | if __name__=='__main__': | |
274 | if len(sys.argv)>2 and sys.argv[1]=='--md5update': | |
275 | update_md5(sys.argv[2:]) | |
276 | else: | |
277 | main(sys.argv[1:]) | |
278 | ||
279 | ||
280 | ||
281 | ||
282 |
0 | /** | |
1 | * Optimized Code For Quantizing Colors to xterm256 | |
2 | * | |
3 | * These functions are equivalent to the ones found in xterm256.py but | |
4 | * orders of a magnitude faster and should compile quickly (fractions | |
5 | * of a second) on most systems with very little risk of | |
6 | * complications. | |
7 | * | |
8 | * Color quantization is very complex. This works by treating RGB | |
9 | * values as 3D euclidean space and brute-force searching for the | |
10 | * nearest neighbor. | |
11 | */ | |
12 | ||
13 | typedef struct { | |
14 | int r; | |
15 | int g; | |
16 | int b; | |
17 | } rgb_t; | |
18 | ||
19 | int CUBE_STEPS[] = { 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF }; | |
20 | rgb_t BASIC16[] = { { 0, 0, 0 }, { 205, 0, 0}, { 0, 205, 0 }, | |
21 | { 205, 205, 0 }, { 0, 0, 238}, { 205, 0, 205 }, | |
22 | { 0, 205, 205 }, { 229, 229, 229}, { 127, 127, 127 }, | |
23 | { 255, 0, 0 }, { 0, 255, 0}, { 255, 255, 0 }, | |
24 | { 92, 92, 255 }, { 255, 0, 255}, { 0, 255, 255 }, | |
25 | { 255, 255, 255 } }; | |
26 | rgb_t COLOR_TABLE[256]; | |
27 | ||
28 | ||
29 | rgb_t xterm_to_rgb(int xcolor) | |
30 | { | |
31 | rgb_t res; | |
32 | if (xcolor < 16) { | |
33 | res = BASIC16[xcolor]; | |
34 | } else if (16 <= xcolor && xcolor <= 231) { | |
35 | xcolor -= 16; | |
36 | res.r = CUBE_STEPS[(xcolor / 36) % 6]; | |
37 | res.g = CUBE_STEPS[(xcolor / 6) % 6]; | |
38 | res.b = CUBE_STEPS[xcolor % 6]; | |
39 | } else if (232 <= xcolor && xcolor <= 255) { | |
40 | res.r = res.g = res.b = 8 + (xcolor - 232) * 0x0A; | |
41 | } | |
42 | return res; | |
43 | } | |
44 | ||
45 | /** | |
46 | * This function provides a quick and dirty way to serialize an rgb_t | |
47 | * struct to an int which can be decoded by our Python code using | |
48 | * ctypes. | |
49 | */ | |
50 | int xterm_to_rgb_i(int xcolor) | |
51 | { | |
52 | rgb_t res = xterm_to_rgb(xcolor); | |
53 | return (res.r << 16) | (res.g << 8) | (res.b << 0); | |
54 | } | |
55 | ||
56 | #define sqr(x) ((x) * (x)) | |
57 | ||
58 | /** | |
59 | * Quantize RGB values to an xterm 256-color ID | |
60 | */ | |
61 | int rgb_to_xterm(int r, int g, int b) | |
62 | { | |
63 | int best_match = 0; | |
64 | int smallest_distance = 1000000000; | |
65 | int c, d; | |
66 | for (c = 16; c < 256; c++) { | |
67 | d = sqr(COLOR_TABLE[c].r - r) + | |
68 | sqr(COLOR_TABLE[c].g - g) + | |
69 | sqr(COLOR_TABLE[c].b - b); | |
70 | if (d < smallest_distance) { | |
71 | smallest_distance = d; | |
72 | best_match = c; | |
73 | } | |
74 | } | |
75 | return best_match; | |
76 | } | |
77 | ||
78 | /* int rgb_to_xterm(int r, int g, int b) */ | |
79 | /* { */ | |
80 | /* int best_match = 0; */ | |
81 | /* int smallest_distance = 1000000000; */ | |
82 | /* int c, d; */ | |
83 | /* for (c = 0; c < 16; c++) { */ | |
84 | /* d = sqr(BASIC16[c].r - r) + */ | |
85 | /* sqr(BASIC16[c].g - g) + */ | |
86 | /* sqr(BASIC16[c].b - b); */ | |
87 | /* if (d < smallest_distance) { */ | |
88 | /* smallest_distance = d; */ | |
89 | /* best_match = c; */ | |
90 | /* } */ | |
91 | /* } */ | |
92 | /* return best_match; */ | |
93 | /* } */ | |
94 | ||
95 | int init() | |
96 | { | |
97 | int c; | |
98 | for (c = 0; c < 256; c++) { | |
99 | COLOR_TABLE[c] = xterm_to_rgb(c); | |
100 | } | |
101 | return 0; | |
102 | } |
0 | # -*- coding: utf-8 -*- | |
1 | """ | |
2 | fabulous.color | |
3 | ~~~~~~~~~~~~~~ | |
4 | ||
5 | I implement support for standard 16-color color terminals. | |
6 | ||
7 | """ | |
8 | ||
9 | import sys | |
10 | import functools | |
11 | ||
12 | from fabulous import utils, xterm256 | |
13 | ||
14 | import grapefruit | |
15 | ||
16 | ||
17 | OVERLINE = u'\u203e' | |
18 | ||
19 | ||
20 | def esc(*codes): | |
21 | return "\x1b[%sm" % (";".join([str(c) for c in codes])) | |
22 | ||
23 | ||
24 | class ColorString(object): | |
25 | r"""A colorized string-like object that gives correct length | |
26 | ||
27 | If anyone knows a way to be able to make this behave like a string | |
28 | object without creating a bug minefield let me know:: | |
29 | ||
30 | >>> str(red("hello")) | |
31 | '\x1b[31mhello\x1b[39m' | |
32 | >>> len(red("hello")) | |
33 | 5 | |
34 | >>> len(str(red("hello"))) | |
35 | 15 | |
36 | >>> str(bold(red("hello"))) | |
37 | '\x1b[1m\x1b[31mhello\x1b[39m\x1b[22m' | |
38 | >>> len(bold(red("hello"))) | |
39 | 5 | |
40 | >>> len(bold("hello ", red("world"))) | |
41 | 11 | |
42 | ||
43 | """ | |
44 | sep = "" | |
45 | fmt = "%s" | |
46 | ||
47 | def __init__(self, *items): | |
48 | self.items = items | |
49 | ||
50 | def __str__(self): | |
51 | return self.fmt % (self.sep.join([unicode(s) for s in self.items])) | |
52 | ||
53 | def __repr__(self): | |
54 | return repr(unicode(self)) | |
55 | ||
56 | def __len__(self): | |
57 | return sum([len(item) for item in self.items]) | |
58 | ||
59 | def __add__(self, cs): | |
60 | if not isinstance(cs, (basestring, ColorString)): | |
61 | msg = "Concatenatation failed: %r + %r (Not a ColorString or str)" | |
62 | raise TypeError(msg % (type(cs), type(self))) | |
63 | return ColorString(self, cs) | |
64 | ||
65 | def __radd__(self, cs): | |
66 | if not isinstance(cs, (basestring, ColorString)): | |
67 | msg = "Concatenatation failed: %r + %r (Not a ColorString or str)" | |
68 | raise TypeError(msg % (type(self), type(cs))) | |
69 | return ColorString(cs, self) | |
70 | ||
71 | @property | |
72 | def as_utf8(self): | |
73 | """A more readable way to say ``unicode(color).encode('utf8')`` | |
74 | """ | |
75 | return unicode(self).encode('utf8') | |
76 | ||
77 | ||
78 | class ColorString256(ColorString): | |
79 | def __init__(self, color, *items): | |
80 | (r, g, b) = parse_color(color) | |
81 | self.color = xterm256.rgb_to_xterm(r, g, b) | |
82 | self.items = items | |
83 | ||
84 | def __str__(self): | |
85 | return self.fmt % ( | |
86 | self.color, self.sep.join([unicode(s) for s in self.items])) | |
87 | ||
88 | ||
89 | class plain(ColorString): | |
90 | r"""A passive wrapper that preserves proper length reporting | |
91 | ||
92 | >>> len(plain("hello ", bold("kitty"))) | |
93 | 11 | |
94 | """ | |
95 | pass | |
96 | ||
97 | ||
98 | class bold(ColorString): | |
99 | fmt = esc(1) + "%s" + esc(22) | |
100 | class italic(ColorString): | |
101 | fmt = esc(3) + "%s" + esc(23) | |
102 | class underline(ColorString): | |
103 | fmt = esc(4) + "%s" + esc(24) | |
104 | class underline2(ColorString): | |
105 | fmt = esc(21) + "%s" + esc(24) | |
106 | class strike(ColorString): | |
107 | fmt = esc(9) + "%s" + esc(29) | |
108 | class blink(ColorString): | |
109 | fmt = esc(5) + "%s" + esc(25) | |
110 | class flip(ColorString): | |
111 | fmt = esc(7) + "%s" + esc(27) | |
112 | ||
113 | ||
114 | class black(ColorString): | |
115 | fmt = esc(30) + "%s" + esc(39) | |
116 | class red(ColorString): | |
117 | fmt = esc(31) + "%s" + esc(39) | |
118 | class green(ColorString): | |
119 | fmt = esc(32) + "%s" + esc(39) | |
120 | class yellow(ColorString): | |
121 | fmt = esc(33) + "%s" + esc(39) | |
122 | class blue(ColorString): | |
123 | fmt = esc(34) + "%s" + esc(39) | |
124 | class magenta(ColorString): | |
125 | fmt = esc(35) + "%s" + esc(39) | |
126 | class cyan(ColorString): | |
127 | fmt = esc(36) + "%s" + esc(39) | |
128 | class white(ColorString): | |
129 | fmt = esc(37) + "%s" + esc(39) | |
130 | ||
131 | ||
132 | class highlight_black(ColorString): | |
133 | fmt = esc(1, 30, 7) + "%s" + esc(22, 27, 39) | |
134 | class highlight_red(ColorString): | |
135 | fmt = esc(1, 31, 7) + "%s" + esc(22, 27, 39) | |
136 | class highlight_green(ColorString): | |
137 | fmt = esc(1, 32, 7) + "%s" + esc(22, 27, 39) | |
138 | class highlight_yellow(ColorString): | |
139 | fmt = esc(1, 33, 7) + "%s" + esc(22, 27, 39) | |
140 | class highlight_blue(ColorString): | |
141 | fmt = esc(1, 34, 7) + "%s" + esc(22, 27, 39) | |
142 | class highlight_magenta(ColorString): | |
143 | fmt = esc(1, 35, 7) + "%s" + esc(22, 27, 39) | |
144 | class highlight_cyan(ColorString): | |
145 | fmt = esc(1, 36, 7) + "%s" + esc(22, 27, 39) | |
146 | class highlight_white(ColorString): | |
147 | fmt = esc(1, 37, 7) + "%s" + esc(22, 27, 39) | |
148 | ||
149 | ||
150 | class black_bg(ColorString): | |
151 | fmt = esc(40) + "%s" + esc(49) | |
152 | class red_bg(ColorString): | |
153 | fmt = esc(41) + "%s" + esc(49) | |
154 | class green_bg(ColorString): | |
155 | fmt = esc(42) + "%s" + esc(49) | |
156 | class yellow_bg(ColorString): | |
157 | fmt = esc(43) + "%s" + esc(49) | |
158 | class blue_bg(ColorString): | |
159 | fmt = esc(44) + "%s" + esc(49) | |
160 | class magenta_bg(ColorString): | |
161 | fmt = esc(45) + "%s" + esc(49) | |
162 | class cyan_bg(ColorString): | |
163 | fmt = esc(46) + "%s" + esc(49) | |
164 | class white_bg(ColorString): | |
165 | fmt = esc(47) + "%s" + esc(49) | |
166 | ||
167 | ||
168 | class fg256(ColorString256): | |
169 | fmt = esc(38, 5, "%d") + "%s" + esc(39) | |
170 | ||
171 | ||
172 | class bg256(ColorString256): | |
173 | fmt = esc(48, 5, "%d") + "%s" + esc(49) | |
174 | ||
175 | ||
176 | class highlight256(ColorString256): | |
177 | fmt = esc(1, 38, 5, "%d", 7) + "%s" + esc(27, 39, 22) | |
178 | ||
179 | ||
180 | class complement256(ColorString256): | |
181 | fmt = esc(1, 38, 5, "%d", 48, 5, "%d") + "%s" + esc(49, 39, 22) | |
182 | ||
183 | def __init__(self, color, *items): | |
184 | self.bg = xterm256.rgb_to_xterm(*parse_color(color)) | |
185 | self.fg = xterm256.rgb_to_xterm(*complement(color)) | |
186 | self.items = items | |
187 | ||
188 | def __str__(self): | |
189 | return self.fmt % ( | |
190 | self.fg, self.bg, | |
191 | self.sep.join([unicode(s) for s in self.items])) | |
192 | ||
193 | ||
194 | def h1(title, line=OVERLINE): | |
195 | width = utils.term.width | |
196 | print bold(title.center(width)).as_utf8 | |
197 | print bold((line * width)[:width]).as_utf8 | |
198 | ||
199 | ||
200 | def parse_color(color): | |
201 | r"""Turns a color into an (r, g, b) tuple | |
202 | ||
203 | >>> parse_color('white') | |
204 | (255, 255, 255) | |
205 | >>> parse_color('#ff0000') | |
206 | (255, 0, 0) | |
207 | >>> parse_color('#f00') | |
208 | (255, 0, 0) | |
209 | >>> parse_color((255, 0, 0)) | |
210 | (255, 0, 0) | |
211 | >>> import grapefruit | |
212 | >>> parse_color(grapefruit.Color((0.0, 1.0, 0.0))) | |
213 | (0, 255, 0) | |
214 | """ | |
215 | if isinstance(color, basestring): | |
216 | color = grapefruit.Color.NewFromHtml(color) | |
217 | if isinstance(color, int): | |
218 | (r, g, b) = xterm256.xterm_to_rgb(color) | |
219 | elif hasattr(color, 'rgb'): | |
220 | (r, g, b) = [int(c * 255.0) for c in color.rgb] | |
221 | else: | |
222 | (r, g, b) = color | |
223 | assert isinstance(r, int) and 0 <= r <= 255 | |
224 | assert isinstance(g, int) and 0 <= g <= 255 | |
225 | assert isinstance(b, int) and 0 <= b <= 255 | |
226 | return (r, g, b) | |
227 | ||
228 | ||
229 | def complement(color): | |
230 | r"""Gives you the polar opposite of your color | |
231 | ||
232 | This isn't guaranteed to look good >_> (especially with | |
233 | brighter, higher intensity colors.) | |
234 | ||
235 | >>> complement('red') | |
236 | (0, 255, 76) | |
237 | >>> complement((0, 100, 175)) | |
238 | (175, 101, 0) | |
239 | """ | |
240 | (r, g, b) = parse_color(color) | |
241 | gcolor = grapefruit.Color((r / 255.0, g / 255.0, b / 255.0)) | |
242 | complement = gcolor.ComplementaryColor() | |
243 | (r, g, b) = [int(c * 255.0) for c in complement.rgb] | |
244 | return (r, g, b) | |
245 | ||
246 | ||
247 | def section(title, bar=OVERLINE, strm=sys.stdout): | |
248 | """Helper function for testing demo routines | |
249 | """ | |
250 | width = utils.term.width | |
251 | print >>strm, bold(title.center(width)).as_utf8 | |
252 | print >>strm, bold((bar * width)[:width]).as_utf8 | |
253 | ||
254 | ||
255 | def main(args): | |
256 | """I provide a command-line interface for this module | |
257 | """ | |
258 | section("Fabulous 4-Bit Colors") | |
259 | ||
260 | print ("style(...): " + | |
261 | bold("bold") +" "+ | |
262 | underline("underline") +" "+ | |
263 | flip("flip") + | |
264 | " (YMMV: " + italic("italic") +" "+ | |
265 | underline2("underline2") +" "+ | |
266 | strike("strike") +" "+ | |
267 | blink("blink") + ")\n").as_utf8 | |
268 | ||
269 | print ("color(...) " + | |
270 | black("black") +" "+ | |
271 | red("red") +" "+ | |
272 | green("green") +" "+ | |
273 | yellow("yellow") +" "+ | |
274 | blue("blue") +" "+ | |
275 | magenta("magenta") +" "+ | |
276 | cyan("cyan") +" "+ | |
277 | white("white")).as_utf8 | |
278 | ||
279 | print ("bold(color(...)) " + | |
280 | bold(black("black") +" "+ | |
281 | red("red") +" "+ | |
282 | green("green") +" "+ | |
283 | yellow("yellow") +" "+ | |
284 | blue("blue") +" "+ | |
285 | magenta("magenta") +" "+ | |
286 | cyan("cyan") +" "+ | |
287 | white("white"))).as_utf8 | |
288 | ||
289 | print plain( | |
290 | 'highlight_color(...) ', | |
291 | highlight_black('black'), ' ', highlight_red('red'), ' ', | |
292 | highlight_green('green'), ' ', highlight_yellow('yellow'), ' ', | |
293 | highlight_blue('blue'), ' ', highlight_magenta('magenta'), ' ', | |
294 | highlight_cyan('cyan'), ' ', highlight_white('white')).as_utf8 | |
295 | ||
296 | print ("bold(color_bg(...)) " + | |
297 | bold(black_bg("black") +" "+ | |
298 | red_bg("red") +" "+ | |
299 | green_bg("green") +" "+ | |
300 | yellow_bg("yellow") +" "+ | |
301 | blue_bg("blue") +" "+ | |
302 | magenta_bg("magenta") +" "+ | |
303 | cyan_bg("cyan") +" "+ | |
304 | white_bg("white"))).as_utf8 | |
305 | ||
306 | section("Fabulous 8-Bit Colors") | |
307 | ||
308 | for code in ["bold(fg256('red', ' lorem ipsum '))", | |
309 | "bold(bg256('#ff0000', ' lorem ipsum '))", | |
310 | "highlight256((255, 0, 0), ' lorem ipsum ')", | |
311 | "highlight256('#09a', ' lorem ipsum ')", | |
312 | "highlight256('green', ' lorem ipsum ')", | |
313 | "highlight256('magenta', ' lorem ipsum ')", | |
314 | "highlight256('indigo', ' lorem ipsum ')", | |
315 | "highlight256('orange', ' lorem ipsum ')", | |
316 | "highlight256('orangered', ' lorem ipsum ')"]: | |
317 | print "%-42s %s" % (code, eval(code)) | |
318 | print '' | |
319 | ||
320 | # grayscales | |
321 | line = " " | |
322 | for xc in range(232, 256): | |
323 | line += bg256(xc, ' ') | |
324 | print line | |
325 | line = " " | |
326 | for xc in range(232, 256)[::-1]: | |
327 | line += bg256(xc, ' ') | |
328 | print line | |
329 | print '' | |
330 | ||
331 | cube_color = lambda x,y,z: 16 + x + y*6 + z*6*6 | |
332 | for y in range(6): | |
333 | line = " " | |
334 | for z in range(6): | |
335 | for x in range(6): | |
336 | line += bg256(cube_color(x, y, z), ' ') | |
337 | line += " " | |
338 | print line.as_utf8 | |
339 |
0 | """ | |
1 | fabulous.debug | |
2 | ~~~~~~~~~~~~~~ | |
3 | ||
4 | """ | |
5 | ||
6 | import sys | |
7 | import itertools | |
8 | ||
9 | from fabulous import image | |
10 | ||
11 | ||
12 | class DebugImage(image.Image): | |
13 | """Visualize optimization techniques used by :class:`Image` | |
14 | """ | |
15 | ||
16 | def reduce(self, colors): | |
17 | need_reset = False | |
18 | line = '' | |
19 | for color, items in itertools.groupby(colors): | |
20 | if color is None: | |
21 | if need_reset: | |
22 | line = line[:-1] + ">" | |
23 | need_reset = False | |
24 | line += 'T' + (self.pad * len(list(items)))[1:] | |
25 | elif color == "EOL": | |
26 | if need_reset: | |
27 | line = line[:-1] + ">" | |
28 | need_reset = False | |
29 | yield line.rstrip(' T') | |
30 | else: | |
31 | yield line.rstrip(' T') | |
32 | line = '' | |
33 | else: | |
34 | need_reset = True | |
35 | line += '<' + (self.pad * len(list(items)))[1:] | |
36 | ||
37 | ||
38 | def main(args): | |
39 | """I provide a command-line interface for this module | |
40 | """ | |
41 | for imgpath in sys.argv[1:]: | |
42 | for line in DebugImage(imgpath): | |
43 | print line | |
44 | ||
45 | ||
46 | if __name__ == '__main__': | |
47 | main(sys.argv) |
0 | ||
1 | import os | |
2 | import fabulous | |
3 | from fabulous.color import * | |
4 | from fabulous import text, utils, image, debug, xterm256 | |
5 | ||
6 | ||
7 | def wait(): | |
8 | raw_input("\nPress " + bold("enter") + " for more fun... ") | |
9 | print "" | |
10 | ||
11 | ||
12 | def demo_image(): | |
13 | section("Semi-Transparent PNG") | |
14 | imp = " from fabulous import image\n " | |
15 | print bold(imp + 'print image.Image("balls.png")\n') | |
16 | ||
17 | balls = 'balls.png' | |
18 | fabdir = os.path.dirname(fabulous.__file__) | |
19 | ||
20 | for fn in ['balls.png', | |
21 | 'fabulous/balls.png', | |
22 | os.path.join(fabdir, 'balls.png')]: | |
23 | if os.path.exists(fn): | |
24 | balls = fn | |
25 | break | |
26 | ||
27 | if not os.path.exists(balls): | |
28 | import urllib | |
29 | ugh = urllib.urlopen('http://lobstertech.com/media/img/balls.png') | |
30 | open('balls.png', 'w').write(ugh.read()) | |
31 | balls = 'balls.png' | |
32 | ||
33 | for line in image.Image(balls): | |
34 | print line | |
35 | wait() | |
36 | ||
37 | section("Yes the output is optimized (JELLY-FISH)") | |
38 | imp = " from fabulous import debug\n " | |
39 | print bold(imp + 'print debug.DebugImage("balls.png")\n') | |
40 | for line in debug.DebugImage(balls): | |
41 | print line | |
42 | wait() | |
43 | ||
44 | ||
45 | def demo_text(): | |
46 | section('Fabulous Text Rendering') | |
47 | ||
48 | imp = " from fabulous import text\n " | |
49 | # print bold(imp + 'print text.Text("Fabulous")\n') | |
50 | # print text.Text("Fabulous") | |
51 | # wait() | |
52 | ||
53 | print bold(imp + 'print text.Text("Fabulous", shadow=True, skew=5)\n') | |
54 | print text.Text("Fabulous", shadow=True, skew=5) | |
55 | wait() | |
56 | ||
57 | ||
58 | def demo_color_4bit(): | |
59 | section("Fabulous 4-Bit Colors") | |
60 | ||
61 | print ("style(...): " + | |
62 | bold("bold") +" "+ | |
63 | underline("underline") +" "+ | |
64 | flip("flip") + | |
65 | " (YMMV: " + italic("italic") +" "+ | |
66 | underline2("underline2") +" "+ | |
67 | strike("strike") +" "+ | |
68 | blink("blink") + ")\n").as_utf8 | |
69 | ||
70 | print ("color(...) " + | |
71 | black("black") +" "+ | |
72 | red("red") +" "+ | |
73 | green("green") +" "+ | |
74 | yellow("yellow") +" "+ | |
75 | blue("blue") +" "+ | |
76 | magenta("magenta") +" "+ | |
77 | cyan("cyan") +" "+ | |
78 | white("white")).as_utf8 | |
79 | ||
80 | print ("bold(color(...)) " + | |
81 | bold(black("black") +" "+ | |
82 | red("red") +" "+ | |
83 | green("green") +" "+ | |
84 | yellow("yellow") +" "+ | |
85 | blue("blue") +" "+ | |
86 | magenta("magenta") +" "+ | |
87 | cyan("cyan") +" "+ | |
88 | white("white"))).as_utf8 | |
89 | ||
90 | print plain( | |
91 | 'highlight_color(...) ', | |
92 | highlight_black('black'), ' ', highlight_red('red'), ' ', | |
93 | highlight_green('green'), ' ', highlight_yellow('yellow'), ' ', | |
94 | highlight_blue('blue'), ' ', highlight_magenta('magenta'), ' ', | |
95 | highlight_cyan('cyan'), ' ', highlight_white('white')).as_utf8 | |
96 | ||
97 | print ("bold(color_bg(...)) " + | |
98 | bold(black_bg("black") +" "+ | |
99 | red_bg("red") +" "+ | |
100 | green_bg("green") +" "+ | |
101 | yellow_bg("yellow") +" "+ | |
102 | blue_bg("blue") +" "+ | |
103 | magenta_bg("magenta") +" "+ | |
104 | cyan_bg("cyan") +" "+ | |
105 | white_bg("white"))).as_utf8 | |
106 | ||
107 | wait() | |
108 | ||
109 | ||
110 | def demo_color_8bit(): | |
111 | section("Fabulous 8-Bit Colors") | |
112 | ||
113 | for code in ["bold(fg256('red', ' lorem ipsum '))", | |
114 | "bold(bg256('#ff0000', ' lorem ipsum '))", | |
115 | "highlight256((255, 0, 0), ' lorem ipsum ')", | |
116 | "highlight256('#09a', ' lorem ipsum ')", | |
117 | "highlight256('green', ' lorem ipsum ')", | |
118 | "highlight256('magenta', ' lorem ipsum ')", | |
119 | "highlight256('indigo', ' lorem ipsum ')", | |
120 | "highlight256('orange', ' lorem ipsum ')", | |
121 | "highlight256('orangered', ' lorem ipsum ')"]: | |
122 | print "%-42s %s" % (code, eval(code)) | |
123 | print '' | |
124 | ||
125 | # grayscales | |
126 | line = " " | |
127 | for xc in range(232, 256): | |
128 | line += bg256(xc, ' ') | |
129 | print line | |
130 | line = " " | |
131 | for xc in range(232, 256)[::-1]: | |
132 | line += bg256(xc, ' ') | |
133 | print line | |
134 | print '' | |
135 | ||
136 | cube_color = lambda x,y,z: 16 + x + y*6 + z*6*6 | |
137 | for y in range(6): | |
138 | line = " " | |
139 | for z in range(6): | |
140 | for x in range(6): | |
141 | line += bg256(cube_color(x, y, z), ' ') | |
142 | line += " " | |
143 | print line.as_utf8 | |
144 | ||
145 | wait() | |
146 | ||
147 | ||
148 | def full_chart(): | |
149 | # grayscales | |
150 | line = " " | |
151 | for xc in range(232, 256): | |
152 | line += bg256(xc, ' ') | |
153 | print line | |
154 | line = " " | |
155 | for xc in range(232, 256)[::-1]: | |
156 | line += bg256(xc, ' ') | |
157 | print line | |
158 | print '' | |
159 | ||
160 | # cube | |
161 | print "" | |
162 | cube_color = lambda x,y,z: 16 + x + y*6 + z*6*6 | |
163 | for y in range(6): | |
164 | line = " " | |
165 | for z in range(6): | |
166 | for x in range(6): | |
167 | line += bg256(cube_color(x, y, z), ' ') | |
168 | line += " " | |
169 | print line.as_utf8 | |
170 | print "" | |
171 | ||
172 | def f(xc): | |
173 | s = highlight256(xc, "color %03d" % (xc)) | |
174 | rgb = xterm256.xterm_to_rgb(xc) | |
175 | rgbs = ' (%3d, %3d, %3d)' % rgb | |
176 | if rgb[0] == rgb[1] == rgb[2]: | |
177 | s += bold(rgbs) | |
178 | else: | |
179 | s += rgbs | |
180 | s += ' (%08d, %08d, %08d)' % tuple([int(bin(n)[2:]) for n in rgb]) | |
181 | return s | |
182 | ||
183 | def l(c1, c2): | |
184 | c1, c2 = f(c1), f(c2) | |
185 | assert len(c1) == len(c2) | |
186 | half = width // 2 | |
187 | assert half > len(c1) | |
188 | pad = " " * ((width // 2 - len(c1)) // 2) | |
189 | print "%(pad)s%(c1)s%(pad)s%(pad)s%(c2)s" % {'pad': pad, 'c1': c1, 'c2': c2} | |
190 | ||
191 | width = utils.term.width | |
192 | for z1, z2 in zip((0, 2, 4), (1, 3, 5)): | |
193 | for y1, y2 in zip(range(6), range(6)): | |
194 | for x1, x2 in zip(range(6), range(6)): | |
195 | l(cube_color(x1, y1, z1), cube_color(x2, y2, z2)) | |
196 | print "" | |
197 | ||
198 | ||
199 | if __name__ == '__main__': | |
200 | # full_chart() | |
201 | demo_color_4bit() | |
202 | demo_color_8bit() | |
203 | demo_text() | |
204 | demo_image() | |
205 | ||
206 | ||
207 | # if __name__ == '__main__': | |
208 | # def sect(s): | |
209 | # print "\n" * term_height() | |
210 | # print bold("=" * term_width()) | |
211 | # pad = " " * (term_width() // 2 - len(s) // 2) | |
212 | # print bold(pad + s) | |
213 | # print bold("=" * term_width()) | |
214 | # print "" | |
215 | ||
216 | # sect("Agent Orange") | |
217 | # for line in Image('orange.png'): | |
218 | # print line | |
219 | # wait() | |
220 | ||
221 | # sect("LOL Cat (JPEGs are not its forte)") | |
222 | # for line in Image('lolcat.jpg'): | |
223 | # print line | |
224 | # wait() | |
225 | ||
226 | # sect("Semi-Transparent PNG") | |
227 | # for line in Image('balls.png'): | |
228 | # print line | |
229 | # wait() | |
230 | ||
231 | # sect("Yes the output is optimized (JELLY-FISH)") | |
232 | # for line in DebugImage('balls.png'): | |
233 | # print line | |
234 | # wait() | |
235 | ||
236 | # basicConfig(level=logging.WARNING) | |
237 | ||
238 | # sect("Transient Style Logging (Uses standard python logger)") | |
239 | # test_transientlogger() | |
240 | ||
241 | # sect("More Transient Logging (Teen Goth Poetry Engine Included)") | |
242 | # test_transientlogger2() |
0 | ||
1 | from __future__ import with_statement | |
2 | ||
3 | import time | |
4 | import curses | |
5 | ||
6 | ||
7 | class Canvas(object): | |
8 | def __init__(self, encoding='UTF-8'): | |
9 | self.encoding = encoding | |
10 | ||
11 | def __enter__(self): | |
12 | self.win = curses.initscr() | |
13 | curses.start_color() | |
14 | curses.init_color(200, 1000, 300, 0) | |
15 | curses.init_pair(1, 200, curses.COLOR_WHITE) | |
16 | return self | |
17 | ||
18 | def __exit__(self, type_, value, traceback): | |
19 | curses.endwin() | |
20 | ||
21 | def __setitem__(self, xy, val): | |
22 | self.win.attron(curses.color_pair(1)) | |
23 | (x, y) = xy | |
24 | self.win.addch(x, y, val) | |
25 | ||
26 | ||
27 | if __name__ == '__main__': | |
28 | import locale | |
29 | locale.setlocale(locale.LC_ALL, '') | |
30 | encoding = locale.getpreferredencoding() | |
31 | with Canvas(encoding=encoding) as canvas: | |
32 | canvas[5, 5] = 'Y' | |
33 | canvas.win.refresh() | |
34 | time.sleep(5.0) |
0 | """ | |
1 | fabulous.gotham | |
2 | ~~~~~~~~~~~~~~~ | |
3 | ||
4 | I implement functions to satisfy your darker side. | |
5 | ||
6 | """ | |
7 | ||
8 | import sys | |
9 | import random | |
10 | import itertools | |
11 | ||
12 | ||
13 | them = ['angels', 'mourners', 'shadows', 'storm clouds', 'memories', 'condemned', | |
14 | 'hand of Heaven', 'stroke of death', 'damned', 'witches', 'corpses'] | |
15 | them_verb = ['follow', 'hover close', 'approach', 'loom', 'taunt', | |
16 | 'laugh', 'surround', 'compell', 'scour'] | |
17 | adj = ['cold', 'dead', 'dark', 'frozen', 'angry', 'ghastly', 'unholy', 'cunning', 'deep', | |
18 | 'morose', 'maligned', 'rotting', 'sickly'] | |
19 | me_part = ['neck', 'heart', 'head', 'eyes', 'soul', 'blood', 'essence', 'wisdom'] | |
20 | feeling = ['pain', 'horror', 'frenzy', 'agony', 'numbness', 'fear', 'love', | |
21 | 'terror', 'madness', 'torment', 'bitterness', 'misery'] | |
22 | angst = ['care', 'understand', 'question'] | |
23 | me_verb = ['flee', 'dance', 'flail madly', 'fall limply', 'hang my head', 'try to run', | |
24 | 'cry out', 'call your name', 'beg forgiveness', 'bleed', 'tremble', 'hear'] | |
25 | action = ['sever', 'crush', 'mutilate', 'slay', 'wound', 'smite', 'drip', | |
26 | 'melt', 'cast', 'mourn', 'avenge'] | |
27 | place = ['the witching hour', 'the gates of hell', 'the door', 'the path', 'death', | |
28 | 'my doom', 'oblivion', 'the end of life', 'Hell', 'nothingness', 'purgatory', | |
29 | 'void', 'earth', 'tomb', 'broken ground', 'barren land', 'swirling dust'] | |
30 | ||
31 | ||
32 | def lorem_gotham(): | |
33 | """Cheesy Gothic Poetry Generator | |
34 | ||
35 | Uses Python generators to yield eternal angst. | |
36 | ||
37 | When you need to generate random verbiage to test your code or | |
38 | typographic design, let's face it... Lorem Ipsum and "the quick | |
39 | brown fox" are old and boring! | |
40 | ||
41 | What you need is something with *flavor*, the kind of thing a | |
42 | depressed teenager with a lot of black makeup would write. | |
43 | """ | |
44 | w = lambda l: l[random.randrange(len(l))] | |
45 | er = lambda w: w[:-1]+'ier' if w.endswith('y') else (w+'r' if w.endswith('e') else w+'er') | |
46 | s = lambda w: w+'s' | |
47 | punc = lambda c, *l: " ".join(l)+c | |
48 | sentence = lambda *l: lambda: " ".join(l) | |
49 | pick = lambda *l: (l[random.randrange(len(l))])() | |
50 | while True: | |
51 | yield pick( | |
52 | sentence('the',w(adj),w(them),'and the',w(them),w(them_verb)), | |
53 | sentence('delivering me to',w(place)), | |
54 | sentence('they',w(action),'my',w(me_part),'and',w(me_verb),'with all my',w(feeling)), | |
55 | sentence('in the',w(place),'my',w(feeling),'shall',w(me_verb)), | |
56 | sentence(punc(',', er(w(adj)),'than the a petty',w(feeling))), | |
57 | sentence(er(w(adj)),'than',w(them),'in',w(place)), | |
58 | sentence(punc('!','oh my',w(me_part)),punc('!','the',w(feeling))), | |
59 | sentence('no one',s(w(angst)),'why the',w(them),w(them_verb + me_verb))) | |
60 | ||
61 | ||
62 | def lorem_gotham_title(): | |
63 | """Names your poem | |
64 | """ | |
65 | w = lambda l: l[random.randrange(len(l))] | |
66 | sentence = lambda *l: lambda: " ".join(l) | |
67 | pick = lambda *l: (l[random.randrange(len(l))])() | |
68 | return pick( | |
69 | sentence('why i',w(me_verb)), | |
70 | sentence(w(place)), | |
71 | sentence('a',w(adj),w(adj),w(place)), | |
72 | sentence('the',w(them))) | |
73 | ||
74 | ||
75 | def main(args): | |
76 | """I provide a command-line interface for this module | |
77 | """ | |
78 | ||
79 | print "-~*~--~*~--~*~--~*~--~*~--~*~--~*~--~*~--~*~--~*~-" | |
80 | print lorem_gotham_title().center(50) | |
81 | print "-~*~--~*~--~*~--~*~--~*~--~*~--~*~--~*~--~*~--~*~-" | |
82 | ||
83 | poem = lorem_gotham() | |
84 | for n in range(16): | |
85 | if n in (4, 8, 12): | |
86 | ||
87 | print poem.next() | |
88 | ||
89 | ||
90 | ||
91 | if __name__ == '__main__': | |
92 | main(sys.argv) |
0 | """ | |
1 | fabulous.image | |
2 | ~~~~~~~~~~~~~~ | |
3 | ||
4 | """ | |
5 | ||
6 | import sys | |
7 | import itertools | |
8 | ||
9 | import grapefruit as gf | |
10 | ||
11 | from fabulous import utils, xterm256 | |
12 | ||
13 | ||
14 | class Image(object): | |
15 | """Printing image files to a terminal | |
16 | ||
17 | I use :mod:`PIL` to turn your image file into a bitmap, resize it | |
18 | so it'll fit inside your terminal, and implement methods so I can | |
19 | behave like a string or iterable. | |
20 | ||
21 | When resizing, I'll assume that a single character on the terminal | |
22 | display is one pixel wide and two pixels tall. For most fonts | |
23 | this is the best way to preserve the aspect ratio of your image. | |
24 | ||
25 | All colors are are quantized by :mod:`fabulous.xterm256` to the | |
26 | 256 colors supported by modern terminals. When quantizing | |
27 | semi-transparant pixels (common in text or PNG files) I'll ask | |
28 | :class:`TerminalInfo` for the background color I should use to | |
29 | solidify the color. Fully transparent pixels will be rendered as | |
30 | a blank space without color so we don't need to mix in a | |
31 | background color. | |
32 | ||
33 | I also put a lot of work into optimizing the output line-by-line | |
34 | so it needs as few ANSI escape sequences as possible. If your | |
35 | terminal is kinda slow, you're gonna want to buy me a drink ;) You | |
36 | can use :class:`DebugImage` to visualize these optimizations. | |
37 | ||
38 | The generated output will only include spaces with different | |
39 | background colors. In the future routines will be provided to | |
40 | overlay text on top of these images. | |
41 | ||
42 | """ | |
43 | ||
44 | pad = ' ' | |
45 | ||
46 | def __init__(self, path, width=None): | |
47 | utils.pil_check() | |
48 | from PIL import Image as PillsPillsPills | |
49 | self.img = PillsPillsPills.open(path) | |
50 | # when reading pixels, gifs will return colors corresponding | |
51 | # to a palette if we don't do this :\ | |
52 | self.img = self.img.convert("RGBA") | |
53 | self.resize(width) | |
54 | ||
55 | def __iter__(self): | |
56 | """I allow Image to behave as an iterable | |
57 | ||
58 | By using me with a for loop, you can use each line as they're | |
59 | created. When printing a large image, this helps you not have | |
60 | to wait for the whole thing to be converted. | |
61 | ||
62 | :return: Yields lines of text (without line end character) | |
63 | """ | |
64 | # strip out blank lines | |
65 | for line in self.reduce(self.convert()): | |
66 | if line.strip(): | |
67 | yield line | |
68 | yield "" | |
69 | ||
70 | def __str__(self): | |
71 | """I return the entire image as one big string | |
72 | ||
73 | Unlike the iteration approach, you have to wait for the entire | |
74 | image to be converted. | |
75 | ||
76 | :return: String containing all lines joined together. | |
77 | """ | |
78 | return "\n".join(self) | |
79 | ||
80 | @property | |
81 | def size(self): | |
82 | """Returns size of image | |
83 | """ | |
84 | return self.img.size | |
85 | ||
86 | def resize(self, width=None): | |
87 | """Resizes image to fit inside terminal | |
88 | ||
89 | Called by the constructor automatically. | |
90 | """ | |
91 | (iw, ih) = self.size | |
92 | if width is None: | |
93 | width = min(iw, utils.term.width) | |
94 | elif isinstance(width, basestring): | |
95 | percents = dict([(pct, '%s%%' % (pct)) for pct in range(101)]) | |
96 | width = percents[width] | |
97 | height = int(float(ih) * (float(width) / float(iw))) | |
98 | height //= 2 | |
99 | self.img = self.img.resize((width, height)) | |
100 | ||
101 | def reduce(self, colors): | |
102 | """Converts color codes into optimized text | |
103 | ||
104 | This optimizer works by merging adjacent colors so we don't | |
105 | have to repeat the same escape codes for each pixel. There is | |
106 | no loss of information. | |
107 | ||
108 | :param colors: Iterable yielding an xterm color code for each | |
109 | pixel, None to indicate a transparent pixel, or | |
110 | ``'EOL'`` to indicate th end of a line. | |
111 | ||
112 | :return: Yields lines of optimized text. | |
113 | ||
114 | """ | |
115 | need_reset = False | |
116 | line = [] | |
117 | for color, items in itertools.groupby(colors): | |
118 | if color is None: | |
119 | if need_reset: | |
120 | line.append("\x1b[49m") | |
121 | need_reset = False | |
122 | line.append(self.pad * len(list(items))) | |
123 | elif color == "EOL": | |
124 | if need_reset: | |
125 | line.append("\x1b[49m") | |
126 | need_reset = False | |
127 | yield "".join(line) | |
128 | else: | |
129 | line.pop() | |
130 | yield "".join(line) | |
131 | line = [] | |
132 | else: | |
133 | need_reset = True | |
134 | line.append("\x1b[48;5;%dm%s" % ( | |
135 | color, self.pad * len(list(items)))) | |
136 | ||
137 | def convert(self): | |
138 | """Yields xterm color codes for each pixel in image | |
139 | """ | |
140 | (width, height) = self.img.size | |
141 | bgcolor = utils.term.bgcolor | |
142 | self.img.load() | |
143 | for y in xrange(height): | |
144 | for x in xrange(width): | |
145 | rgba = self.img.getpixel((x, y)) | |
146 | if len(rgba) == 4 and rgba[3] == 0: | |
147 | yield None | |
148 | elif len(rgba) == 3 or rgba[3] == 255: | |
149 | yield xterm256.rgb_to_xterm(*rgba[:3]) | |
150 | else: | |
151 | color = gf.Color.NewFromRgb(*[c / 255.0 for c in rgba]) | |
152 | rgba = gf.Color.AlphaBlend(color, bgcolor).rgb | |
153 | yield xterm256.rgb_to_xterm( | |
154 | *[int(c * 255.0) for c in rgba]) | |
155 | yield "EOL" | |
156 | ||
157 | ||
158 | def main(args): | |
159 | """I provide a command-line interface for this module | |
160 | """ | |
161 | for imgpath in args: | |
162 | for line in Image(imgpath): | |
163 | print line | |
164 | ||
165 | ||
166 | if __name__ == '__main__': | |
167 | main(sys.argv[1:]) |
0 | """ | |
1 | fabulous.logs | |
2 | ~~~~~~~~~~~~~ | |
3 | ||
4 | I provide utilities for making your logs look fabulous. | |
5 | ||
6 | """ | |
7 | ||
8 | import sys | |
9 | import logging | |
10 | ||
11 | from fabulous import utils | |
12 | ||
13 | ||
14 | class TransientStreamHandler(logging.StreamHandler): | |
15 | """Standard Python logging Handler for Transient Console Logging | |
16 | ||
17 | Logging transiently means that verbose logging messages like DEBUG | |
18 | will only appear on the last line of your terminal for a short | |
19 | period of time and important messages like WARNING will scroll | |
20 | like normal text. | |
21 | ||
22 | This allows you to log lots of messages without the important | |
23 | stuff getting drowned out. | |
24 | ||
25 | This module integrates with the standard Python logging module. | |
26 | """ | |
27 | ||
28 | def __init__(self, strm=sys.stderr, level=logging.WARNING): | |
29 | logging.StreamHandler.__init__(self, strm) | |
30 | if isinstance(level, int): | |
31 | self.levelno = level | |
32 | else: | |
33 | self.levelno = logging._levelNames[level] | |
34 | self.need_cr = False | |
35 | self.last = "" | |
36 | self.parent = logging.StreamHandler | |
37 | ||
38 | def close(self): | |
39 | if self.need_cr: | |
40 | self.stream.write("\n") | |
41 | self.need_cr = False | |
42 | self.parent.close(self) | |
43 | ||
44 | def write(self, data): | |
45 | if self.need_cr: | |
46 | width = max(min(utils.term.width, len(self.last)), len(data)) | |
47 | fmt = "\r%-" + str(width) + "s\n" + self.last | |
48 | else: | |
49 | fmt = "%s\n" | |
50 | try: | |
51 | self.stream.write(fmt % (data)) | |
52 | except UnicodeError: | |
53 | self.stream.write(fmt % (data.encode("UTF-8"))) | |
54 | ||
55 | def transient_write(self, data): | |
56 | if self.need_cr: | |
57 | self.stream.write('\r') | |
58 | else: | |
59 | self.need_cr = True | |
60 | width = utils.term.width | |
61 | for line in data.rstrip().split('\n'): | |
62 | if line: | |
63 | if len(line) > width: | |
64 | line = line[:width - 3] + '...' | |
65 | line_width = max(min(width, len(self.last)), len(line)) | |
66 | fmt = "%-" + str(line_width) + "s" | |
67 | self.last = line | |
68 | try: | |
69 | self.stream.write(fmt % (line)) | |
70 | except UnicodeError: | |
71 | self.stream.write(fmt % (line.encode("UTF-8"))) | |
72 | else: | |
73 | self.stream.write('\r') | |
74 | self.stream.flush() | |
75 | ||
76 | def emit(self, record): | |
77 | try: | |
78 | msg = self.format(record) | |
79 | if record.levelno >= self.levelno: | |
80 | self.write(msg) | |
81 | else: | |
82 | self.transient_write(msg) | |
83 | except (KeyboardInterrupt, SystemExit): | |
84 | raise | |
85 | except: | |
86 | self.handleError(record) | |
87 | ||
88 | ||
89 | def basicConfig(level=logging.WARNING, transient_level=logging.NOTSET): | |
90 | """Shortcut for setting up transient logging | |
91 | ||
92 | I am a replica of ``logging.basicConfig`` which installs a | |
93 | transient logging handler to stderr. | |
94 | """ | |
95 | fmt = "%(asctime)s [%(levelname)s] [%(name)s:%(lineno)d] %(message)s" | |
96 | logging.root.setLevel(transient_level) # <--- IMPORTANT | |
97 | hand = TransientStreamHandler(level=level) | |
98 | hand.setFormatter(logging.Formatter(fmt)) | |
99 | logging.root.addHandler(hand) |
0 | """ | |
1 | fabulous.rotating_cube | |
2 | ~~~~~~~~~~~~~~~~~~~~~~ | |
3 | ||
4 | Completely pointless terminal renderer of rotating cube | |
5 | ||
6 | Uses a faux 2D rendering technique to create what appears to be a | |
7 | wireframe 3d cube. | |
8 | ||
9 | This doesn't use the curses library, but rather prints entire | |
10 | frames sized to fill the entire terminal display. | |
11 | ||
12 | """ | |
13 | ||
14 | from __future__ import with_statement | |
15 | ||
16 | import sys | |
17 | import time | |
18 | from math import cos, sin, pi | |
19 | ||
20 | from fabulous import color, utils | |
21 | ||
22 | ||
23 | class Frame(object): | |
24 | """Canvas object for drawing a frame to be printed | |
25 | """ | |
26 | ||
27 | def __enter__(self): | |
28 | self.width = utils.term.width | |
29 | self.height = utils.term.height * 2 | |
30 | self.canvas = [[' ' for x in range(self.width)] | |
31 | for y in range(self.height // 2)] | |
32 | return self | |
33 | ||
34 | def __exit__(self, type_, value, traceback): | |
35 | sys.stdout.write(self.render()) | |
36 | sys.stdout.flush() | |
37 | ||
38 | def __setitem__(self, p, c): | |
39 | (x, y) = p | |
40 | self.canvas[int(y // 2)][int(x)] = c | |
41 | ||
42 | def line(self, x0, y0, x1, y1, c='*'): | |
43 | r"""Draws a line | |
44 | ||
45 | Who would have thought this would be so complicated? Thanks | |
46 | again Wikipedia_ <3 | |
47 | ||
48 | .. _Wikipedia: http://en.wikipedia.org/wiki/Bresenham's_line_algorithm | |
49 | """ | |
50 | steep = abs(y1 - y0) > abs(x1 - x0) | |
51 | if steep: | |
52 | (x0, y0) = (y0, x0) | |
53 | (x1, y1) = (y1, x1) | |
54 | if x0 > x1: | |
55 | (x0, x1) = (x1, x0) | |
56 | (y0, y1) = (y1, y0) | |
57 | deltax = x1 - x0 | |
58 | deltay = abs(y1 - y0) | |
59 | error = deltax / 2 | |
60 | y = y0 | |
61 | if y0 < y1: | |
62 | ystep = 1 | |
63 | else: | |
64 | ystep = -1 | |
65 | for x in range(x0, x1 - 1): | |
66 | if steep: | |
67 | self[y, x] = c | |
68 | else: | |
69 | self[x, y] = c | |
70 | error = error - deltay | |
71 | if error < 0: | |
72 | y = y + ystep | |
73 | error = error + deltax | |
74 | ||
75 | def render(self): | |
76 | return "\n".join(["".join(line) for line in self.canvas]) | |
77 | ||
78 | ||
79 | def rotating_cube(degree_change=3, frame_rate=10): | |
80 | """Rotating cube program | |
81 | ||
82 | How it works: | |
83 | ||
84 | 1. Create two imaginary ellipses | |
85 | 2. Sized to fit in the top third and bottom third of screen | |
86 | 3. Create four imaginary points on each ellipse | |
87 | 4. Make those points the top and bottom corners of your cube | |
88 | 5. Connect the lines and render | |
89 | 6. Rotate the points on the ellipses and repeat | |
90 | ||
91 | """ | |
92 | degrees = 0 | |
93 | while True: | |
94 | t1 = time.time() | |
95 | ||
96 | with Frame() as frame: | |
97 | oval_width = frame.width | |
98 | oval_height = frame.height / 3.0 | |
99 | cube_height = oval_height * 2 | |
100 | ||
101 | (p1_x, p1_y) = ellipse_point(degrees, oval_width, oval_height) | |
102 | (p2_x, p2_y) = ellipse_point(degrees + 90, oval_width, oval_height) | |
103 | (p3_x, p3_y) = ellipse_point(degrees + 180, oval_width, oval_height) | |
104 | (p4_x, p4_y) = ellipse_point(degrees + 270, oval_width, oval_height) | |
105 | degrees = (degrees + degree_change) % 360 | |
106 | ||
107 | # connect square thing at top | |
108 | frame.line(p1_x, p1_y, p2_x, p2_y) | |
109 | frame.line(p2_x, p2_y, p3_x, p3_y) | |
110 | frame.line(p3_x, p3_y, p4_x, p4_y) | |
111 | frame.line(p4_x, p4_y, p1_x, p1_y) | |
112 | ||
113 | # connect top to bottom | |
114 | frame.line(p1_x, p1_y, p1_x, p1_y + cube_height) | |
115 | frame.line(p2_x, p2_y, p2_x, p2_y + cube_height) | |
116 | frame.line(p3_x, p3_y, p3_x, p3_y + cube_height) | |
117 | frame.line(p4_x, p4_y, p4_x, p4_y + cube_height) | |
118 | ||
119 | # connect square thing at bottom | |
120 | frame.line(p1_x, p1_y + cube_height, p2_x, p2_y + cube_height) | |
121 | frame.line(p2_x, p2_y + cube_height, p3_x, p3_y + cube_height) | |
122 | frame.line(p3_x, p3_y + cube_height, p4_x, p4_y + cube_height) | |
123 | frame.line(p4_x, p4_y + cube_height, p1_x, p1_y + cube_height) | |
124 | ||
125 | elapsed = (time.time() - t1) | |
126 | time.sleep(abs(1.0 / frame_rate - elapsed)) | |
127 | ||
128 | ||
129 | def ellipse_point(degrees, width, height): | |
130 | """I hate math so much :'( | |
131 | """ | |
132 | width -= 1 | |
133 | height -= 1 | |
134 | radians = degrees * (pi / 180.0) | |
135 | x = width/2.0 * cos(1) * sin(radians) - width/2.0 * sin(1) * cos(radians) | |
136 | y = height/2.0 * sin(1) * sin(radians) + height/2.0 * cos(1) * cos(radians) | |
137 | x = int(x + width/2.0) | |
138 | y = int(y + height/2.0) | |
139 | return (x, y) | |
140 | ||
141 | ||
142 | if __name__ == '__main__': | |
143 | try: | |
144 | rotating_cube() | |
145 | except KeyboardInterrupt: | |
146 | pass |
0 | ||
1 | import logging | |
2 | ||
3 | from fabulous.logs import * | |
4 | from fabulous.gotham import * | |
5 | from fabulous.color import * | |
6 | ||
7 | ||
8 | logger = logging.getLogger('fabulous') | |
9 | ||
10 | ||
11 | def luv(): | |
12 | msg = "hello there how are you? i love you! sincerely compy <3 <3" | |
13 | while True: | |
14 | for n in range(len(msg)) + list(reversed(range(len(msg)))): | |
15 | yield msg[:n+1] | |
16 | ||
17 | ||
18 | def bad_things(): | |
19 | yield red("godzilla attack") | |
20 | yield bold(red("magnetic poles reverse")) | |
21 | yield red("caffeine use criminalized") | |
22 | yield bold(red("more horrible things happening....")) | |
23 | yield highlight_red("THIS INFORMATION IS NOT BEING DROWNED OUT!") | |
24 | yield bold(red("hip hip hooray")) | |
25 | ||
26 | ||
27 | def test_transientlogger(): | |
28 | import random, time | |
29 | happy, nightmare = luv(), bad_things() | |
30 | try: | |
31 | while True: | |
32 | if random.randrange(60) == 0: | |
33 | logger.warning(nightmare.next()) | |
34 | else: | |
35 | logger.debug(happy.next()) | |
36 | time.sleep(0.02) | |
37 | except StopIteration: | |
38 | pass | |
39 | ||
40 | ||
41 | def test_transientlogger2(): | |
42 | import time, random | |
43 | gothic = lorem_gotham() | |
44 | try: | |
45 | while True: | |
46 | if random.randrange(20) == 0: | |
47 | logger.warning(red(gothic.next())) | |
48 | else: | |
49 | logger.debug(gothic.next()) | |
50 | time.sleep(0.1) | |
51 | except StopIteration: | |
52 | pass | |
53 | ||
54 | ||
55 | if __name__ == '__main__': | |
56 | basicConfig(level=logging.WARNING) | |
57 | logging.warning("RUNNING TEST: test_transientlogger()") | |
58 | test_transientlogger() | |
59 | logging.warning("RUNNING TEST: test_transientlogger2()") | |
60 | test_transientlogger2() |
0 | """ | |
1 | fabulous.text | |
2 | ~~~~~~~~~~~~~ | |
3 | ||
4 | I let you print TrueType text to your terminal. The easiest way | |
5 | to get started with me is by running:: | |
6 | ||
7 | jart@compy:~$ python -m fabulous.text --help | |
8 | ||
9 | To make things simple, Fabulous comes with my favorite serif, | |
10 | non-serif, and monospace fonts: | |
11 | ||
12 | - IndUni-H-Bold: Open Source Helvetica Bold clone (sans-serif) | |
13 | ||
14 | This is the real deal and not some cheap ripoff like Verdana. | |
15 | IndUni-H-Bold is the default because not only does it look | |
16 | great, but also renders *perfectly*. and is also used for the | |
17 | Fabulous logo. Commonly found on stret signs. | |
18 | ||
19 | This font is licensed under the GPL. If you're developing | |
20 | proprietary software you might want to ask its author or a | |
21 | lawyer if Fabulous' use of IndUni-H would be considered a "GPL | |
22 | Barrier." | |
23 | ||
24 | - cmr10: Computer Modern (serif) | |
25 | ||
26 | Donald Knuth wrote 23,000 lines for the sole purpose of | |
27 | bestowing this jewel upon the world. This font is commonly seen | |
28 | in scholarly papers. | |
29 | ||
30 | - DejaVuSansMono: DejaVu Sans Mono (formerly Bitstream Vera Sans Mono) | |
31 | ||
32 | At point size 8, this is my favorite programming/terminal font. | |
33 | ||
34 | For other fonts, I'll try my best to figure out where your font | |
35 | files are stored. If I have trouble finding your font, try using | |
36 | an absolute path *with* the extension. You could also try putting | |
37 | the font in your ``~/.fonts`` folder and running ``fc-cache -fv | |
38 | ~/.fonts``. | |
39 | ||
40 | """ | |
41 | ||
42 | import os | |
43 | import sys | |
44 | ||
45 | import grapefruit | |
46 | ||
47 | from fabulous import utils, image | |
48 | ||
49 | ||
50 | class Text(image.Image): | |
51 | """Renders TrueType Text to Terminal | |
52 | ||
53 | I'm a sub-class of :class:`fabulous.image.Image`. My job is | |
54 | limited to simply getting things ready. I do this by: | |
55 | ||
56 | - Turning your text into an RGB-Alpha bitmap image using | |
57 | :mod:`PIL` | |
58 | ||
59 | - Applying way cool effects (if you choose to enable them) | |
60 | ||
61 | For example:: | |
62 | ||
63 | >>> assert Text("Fabulous", shadow=True, skew=5) | |
64 | ||
65 | >>> txt = Text("lorem ipsum", font="IndUni-H-Bold") | |
66 | >>> len(str(txt)) > 0 | |
67 | True | |
68 | >>> txt = Text("lorem ipsum", font="cmr10") | |
69 | >>> len(str(txt)) > 0 | |
70 | True | |
71 | >>> txt = Text("lorem ipsum", font="DejaVuSansMono") | |
72 | >>> len(str(txt)) > 0 | |
73 | True | |
74 | ||
75 | :param text: The text you want to display as a string. | |
76 | ||
77 | :param fsize: The font size in points. This obviously end up | |
78 | looking much larger because in fabulous a single | |
79 | character is treated as one horizontal pixel and two | |
80 | vertical pixels. | |
81 | ||
82 | :param color: The color (specified as you would in HTML/CSS) of | |
83 | your text. For example Red could be specified as | |
84 | follows: ``red``, ``#00F`` or ``#0000FF``. | |
85 | ||
86 | :param shadow: If true, render a simple drop-shadow beneath text. | |
87 | The Fabulous logo uses this feature. | |
88 | ||
89 | :param skew: Skew size in pixels. This applies an affine | |
90 | transform to shift the top-most pixels to the right. | |
91 | The Fabulous logo uses a five pixel skew. | |
92 | ||
93 | :param font: The TrueType font you want. If this is not an | |
94 | absolute path, Fabulous will search for your font by | |
95 | globbing the specified name in various directories. | |
96 | """ | |
97 | ||
98 | def __init__(self, text, fsize=20, color="#0099ff", shadow=False, | |
99 | skew=None, font='IndUni-H-Bold'): | |
100 | utils.pil_check() | |
101 | from PIL import Image, ImageFont, ImageDraw | |
102 | self.text = text | |
103 | self.color = grapefruit.Color.NewFromHtml(color) | |
104 | self.font = ImageFont.truetype(resolve_font(font), fsize) | |
105 | size = tuple([n + 3 for n in self.font.getsize(self.text)]) | |
106 | self.img = Image.new("RGBA", size, (0, 0, 0, 0)) | |
107 | cvs = ImageDraw.Draw(self.img) | |
108 | if shadow: | |
109 | cvs.text((2, 2), self.text, | |
110 | font=self.font, | |
111 | fill=(150, 150, 150, 150)) | |
112 | cvs.text((1, 1), self.text, | |
113 | font=self.font, | |
114 | fill=self.color.html) | |
115 | if skew: | |
116 | self.img = self.img.transform( | |
117 | size, Image.AFFINE, (1.0, 0.1 * skew, -1.0 * skew, | |
118 | 0.0, 1.0, 0.0)) | |
119 | self.resize(None) | |
120 | ||
121 | ||
122 | class FontNotFound(ValueError): | |
123 | """I get raised when the font-searching hueristics fail | |
124 | ||
125 | This class extends the standard :exc:`ValueError` exception so you | |
126 | don't have to import me if you don't want to. | |
127 | """ | |
128 | ||
129 | ||
130 | def resolve_font(name): | |
131 | """Sloppy way to turn font names into absolute filenames | |
132 | ||
133 | This isn't intended to be a proper font lookup tool but rather a | |
134 | dirty tool to not have to specify the absolute filename every | |
135 | time. | |
136 | ||
137 | For example:: | |
138 | ||
139 | >>> path = resolve_font('IndUni-H-Bold') | |
140 | ||
141 | >>> fontdir = os.path.join(os.path.dirname(__file__), 'fonts') | |
142 | >>> indunih_path = os.path.join(fontdir, 'IndUni-H-Bold.ttf') | |
143 | >>> assert path == indunih_path | |
144 | ||
145 | This isn't case-sensitive:: | |
146 | ||
147 | >>> assert resolve_font('induni-h') == indunih_path | |
148 | ||
149 | Raises :exc:`FontNotFound` on failure:: | |
150 | ||
151 | >>> resolve_font('blahahaha') | |
152 | Traceback (most recent call last): | |
153 | ... | |
154 | FontNotFound: Can't find 'blahahaha' :'( Try adding it to ~/.fonts | |
155 | ||
156 | """ | |
157 | for fontdir, fontfiles in get_font_files(): | |
158 | for fontfile in fontfiles: | |
159 | if name.lower() in fontfile.lower(): | |
160 | return os.path.join(fontdir, fontfile) | |
161 | raise FontNotFound("Can't find %r :'( Try adding it to ~/.fonts") | |
162 | ||
163 | ||
164 | @utils.memoize | |
165 | def get_font_files(): | |
166 | """Returns a list of all font files we could find | |
167 | ||
168 | Returned as a list of dir/files tuples:: | |
169 | ||
170 | get_font_files() -> [('/some/dir', ['font1.ttf', ...]), ...] | |
171 | ||
172 | For example:: | |
173 | ||
174 | >>> fabfonts = os.path.join(os.path.dirname(__file__), 'fonts') | |
175 | >>> 'IndUni-H-Bold.ttf' in get_font_files()[fabfontdir] | |
176 | True | |
177 | >>> 'DejaVuSansMono.ttf' in get_font_files()[fabfontdir] | |
178 | True | |
179 | >>> 'cmr10.ttf' in get_font_files()[fabfontdir] | |
180 | True | |
181 | ||
182 | >>> assert len(get_font_files()) > 0 | |
183 | >>> for dirname, filename in get_font_files(): | |
184 | ... assert os.path.exists(os.path.join(dirname, filename)) | |
185 | ... | |
186 | ||
187 | """ | |
188 | dirs = [os.path.join(os.path.dirname(__file__), 'fonts'), | |
189 | os.path.expanduser('~/.fonts')] | |
190 | try: | |
191 | # this is where ubuntu puts fonts | |
192 | dirname = '/usr/share/fonts/truetype' | |
193 | dirs += [os.path.join(dirname, subdir) | |
194 | for subdir in os.listdir(dirname)] | |
195 | except OSError: | |
196 | pass | |
197 | return [(p, os.listdir(p)) for p in dirs if os.path.isdir(p)] | |
198 | ||
199 | ||
200 | def main(args): | |
201 | """I provide a command-line interface for this module | |
202 | """ | |
203 | import optparse | |
204 | parser = optparse.OptionParser() | |
205 | parser.add_option( | |
206 | "-S", "--skew", dest="skew", type="int", default=None, | |
207 | help=("Apply skew effect (measured in pixels) to make it look " | |
208 | "extra cool. For example, Fabulous' logo logo is skewed " | |
209 | "by 5 pixels. Default: %default")) | |
210 | parser.add_option( | |
211 | "-C", "--color", dest="color", default="#0099ff", | |
212 | help=("Color of your text. This can be specified as you would " | |
213 | "using HTML/CSS. Default: %default")) | |
214 | parser.add_option( | |
215 | "-B", "--term-color", dest="term_color", default=None, | |
216 | help=("If you terminal background isn't black, please change " | |
217 | "this value to the proper background so semi-transparent " | |
218 | "pixels will blend properly.")) | |
219 | parser.add_option( | |
220 | "-F", "--font", dest="font", default='IndUni-H-Bold', | |
221 | help=("Path to font file you wish to use. This defaults to a " | |
222 | "free Helvetica-Bold clone which is included with Fabulous. " | |
223 | "Default value: %default")) | |
224 | parser.add_option( | |
225 | "-Z", "--size", dest="fsize", type="int", default=20, | |
226 | help=("Size of font in points. Default: %default")) | |
227 | parser.add_option( | |
228 | "-s", "--shadow", dest="shadow", action="store_true", default=False, | |
229 | help=("Size of font in points. Default: %default")) | |
230 | (options, args) = parser.parse_args(args=args) | |
231 | ||
232 | if options.term_color: | |
233 | utils.term.bgcolor = options.term_color | |
234 | ||
235 | for line in " ".join(args).split("\n"): | |
236 | fab_text = Text(line, skew=options.skew, color=options.color, | |
237 | font=options.font, fsize=options.fsize, | |
238 | shadow=options.shadow) | |
239 | for chunk in fab_text: | |
240 | print chunk | |
241 | ||
242 | ||
243 | if __name__ == '__main__': | |
244 | main(sys.argv[1:]) |
0 | """ | |
1 | fabulous.utils | |
2 | ~~~~~~~~~~~~~~ | |
3 | ||
4 | """ | |
5 | ||
6 | import os | |
7 | import sys | |
8 | import fcntl | |
9 | import struct | |
10 | import termios | |
11 | import textwrap | |
12 | import functools | |
13 | ||
14 | import grapefruit | |
15 | ||
16 | ||
17 | def memoize(function): | |
18 | """A very simple memoize decorator to optimize pure-ish functions | |
19 | ||
20 | Don't use this unless you've examined the code and see the | |
21 | potential risks. | |
22 | """ | |
23 | cache = {} | |
24 | @functools.wraps(function) | |
25 | def _memoize(*args): | |
26 | if args in cache: | |
27 | return cache[args] | |
28 | result = function(*args) | |
29 | cache[args] = result | |
30 | return result | |
31 | return function | |
32 | ||
33 | ||
34 | class TerminalInfo(object): | |
35 | """Quick and easy access to some terminal information | |
36 | ||
37 | I'll tell you the terminal width/height and it's background color. | |
38 | ||
39 | You don't need to use me directly. Just access the global | |
40 | :data:`term` instance:: | |
41 | ||
42 | >>> assert term.width > 0 | |
43 | >>> assert term.height > 0 | |
44 | ||
45 | It's important to know the background color when rendering PNG | |
46 | images with semi-transparency. Because there's no way to detect | |
47 | this, black will be the default:: | |
48 | ||
49 | >>> term.bgcolor | |
50 | (0.0, 0.0, 0.0, 1.0) | |
51 | >>> import grapefruit | |
52 | >>> isinstance(term.bgcolor, grapefruit.Color) | |
53 | True | |
54 | ||
55 | If you use a white terminal, you'll need to manually change this:: | |
56 | ||
57 | >>> term.bgcolor = 'white' | |
58 | >>> term.bgcolor | |
59 | (1.0, 1.0, 1.0, 1.0) | |
60 | >>> term.bgcolor = grapefruit.Color.NewFromRgb(0.0, 0.0, 0.0, 1.0) | |
61 | >>> term.bgcolor | |
62 | (0.0, 0.0, 0.0, 1.0) | |
63 | ||
64 | """ | |
65 | ||
66 | def __init__(self, bgcolor='black'): | |
67 | self.bgcolor = bgcolor | |
68 | ||
69 | @property | |
70 | def termfd(self): | |
71 | """Returns file descriptor number of terminal | |
72 | ||
73 | This will look at all three standard i/o file descriptors and | |
74 | return whichever one is actually a TTY in case you're | |
75 | redirecting i/o through pipes. | |
76 | """ | |
77 | for fd in (2, 1, 0): | |
78 | if os.isatty(fd): | |
79 | return fd | |
80 | raise Exception("No TTY could be found") | |
81 | ||
82 | @property | |
83 | def dimensions(self): | |
84 | """Returns terminal dimensions | |
85 | ||
86 | Don't save this information for long periods of time because | |
87 | the user might resize their terminal. | |
88 | ||
89 | :return: Returns ``(width, height)``. If there's no terminal | |
90 | to be found, we'll just return ``(79, 40)``. | |
91 | """ | |
92 | try: | |
93 | call = fcntl.ioctl(self.termfd, termios.TIOCGWINSZ, "\000" * 8) | |
94 | except: | |
95 | return (79, 40) | |
96 | else: | |
97 | height, width = struct.unpack("hhhh", call)[:2] | |
98 | return (width, height) | |
99 | ||
100 | @property | |
101 | def width(self): | |
102 | """Returns width of terminal in characters | |
103 | """ | |
104 | return self.dimensions[0] | |
105 | ||
106 | @property | |
107 | def height(self): | |
108 | """Returns height of terminal in lines | |
109 | """ | |
110 | return self.dimensions[1] | |
111 | ||
112 | def _get_bgcolor(self): | |
113 | return self._bgcolor | |
114 | ||
115 | def _set_bgcolor(self, color): | |
116 | if isinstance(color, grapefruit.Color): | |
117 | self._bgcolor = color | |
118 | else: | |
119 | self._bgcolor = grapefruit.Color.NewFromHtml(color) | |
120 | ||
121 | bgcolor = property(_get_bgcolor, _set_bgcolor) | |
122 | ||
123 | ||
124 | term = TerminalInfo() | |
125 | ||
126 | ||
127 | def pil_check(): | |
128 | """Check for PIL library, printing friendly error if not found | |
129 | ||
130 | We need PIL for the :mod:`fabulous.text` and :mod:`fabulous.image` | |
131 | modules to work. Because PIL can be very tricky to install, it's | |
132 | not listed in the ``setup.py`` requirements list. | |
133 | ||
134 | Not everyone is going to have PIL installed so it's best that we | |
135 | offer as much help as possible so they don't have to suffer like I | |
136 | have in the past :'( | |
137 | """ | |
138 | try: | |
139 | import PIL | |
140 | except ImportError: | |
141 | raise ImportError(textwrap.dedent(""" | |
142 | Oh no! You don't have the evil PIL library! | |
143 | ||
144 | Here's how you get it: | |
145 | ||
146 | Ubuntu/Debian: | |
147 | ||
148 | sudo apt-get install python-imaging | |
149 | ||
150 | Mac OS X: | |
151 | ||
152 | http://pythonmac.org/packages/py24-fat/index.html | |
153 | http://pythonmac.org/packages/py25-fat/index.html | |
154 | ||
155 | Windows: | |
156 | ||
157 | http://effbot.org/downloads/PIL-1.1.7.win32-py%(pyversion)s.exe | |
158 | ||
159 | Everyone Else: | |
160 | ||
161 | This is like the hardest library in the world to | |
162 | manually install. If your package manager doesn't have | |
163 | it, you can try running ``sudo easy_install pil`` once | |
164 | you get your hands on a C compiler as well as the | |
165 | following libraries (including the development headers) | |
166 | for Python, libz, libjpeg, libgif, libpng, libungif4, | |
167 | libfreetype6, and maybe more >_> | |
168 | ||
169 | """ % {'pyversion': "%s.%s" % sys.version_info[:2]})) |
0 | """ | |
1 | fabulous.xterm256 | |
2 | ~~~~~~~~~~~~~~~~~ | |
3 | ||
4 | Implements Support for the 256 colors supported by xterm as well | |
5 | as quantizing 24-bit RGB color to xterm color ids. | |
6 | ||
7 | Color quantization is very very slow so when this module is | |
8 | loaded, it'll attempt to automatically compile a speedup module | |
9 | using gcc. A :mod:`logging` message will be emitted if it fails | |
10 | and we'll fallback on the Python code. | |
11 | ||
12 | """ | |
13 | ||
14 | import logging | |
15 | ||
16 | ||
17 | CUBE_STEPS = [0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF] | |
18 | BASIC16 = ((0, 0, 0), (205, 0, 0), (0, 205, 0), (205, 205, 0), | |
19 | (0, 0, 238), (205, 0, 205), (0, 205, 205), (229, 229, 229), | |
20 | (127, 127, 127), (255, 0, 0), (0, 255, 0), (255, 255, 0), | |
21 | (92, 92, 255), (255, 0, 255), (0, 255, 255), (255, 255, 255)) | |
22 | ||
23 | ||
24 | def xterm_to_rgb(xcolor): | |
25 | """Convert xterm Color ID to an RGB value | |
26 | ||
27 | All 256 values are precalculated and stored in :data:`COLOR_TABLE` | |
28 | """ | |
29 | assert 0 <= xcolor <= 255 | |
30 | if xcolor < 16: | |
31 | # basic colors | |
32 | return BASIC16[xcolor] | |
33 | elif 16 <= xcolor <= 231: | |
34 | # color cube | |
35 | xcolor -= 16 | |
36 | return (CUBE_STEPS[(xcolor / 36) % 6], | |
37 | CUBE_STEPS[(xcolor / 6) % 6], | |
38 | CUBE_STEPS[xcolor % 6]) | |
39 | elif 232 <= xcolor <= 255: | |
40 | # gray tone | |
41 | c = 8 + (xcolor - 232) * 0x0A | |
42 | return (c, c, c) | |
43 | ||
44 | ||
45 | COLOR_TABLE = [xterm_to_rgb(i) for i in xrange(256)] | |
46 | ||
47 | ||
48 | def rgb_to_xterm(r, g, b): | |
49 | """Quantize RGB values to an xterm 256-color ID | |
50 | ||
51 | This works by envisioning the RGB values for all 256 xterm colors | |
52 | as 3D euclidean space and brute-force searching for the nearest | |
53 | neighbor. | |
54 | ||
55 | This is very slow. If you're very lucky, :func:`compile_speedup` | |
56 | will replace this function automatically with routines in | |
57 | `_xterm256.c`. | |
58 | """ | |
59 | if r < 5 and g < 5 and b < 5: | |
60 | return 16 | |
61 | best_match = 0 | |
62 | smallest_distance = 10000000000 | |
63 | for c in xrange(16, 256): | |
64 | d = (COLOR_TABLE[c][0] - r) ** 2 + \ | |
65 | (COLOR_TABLE[c][1] - g) ** 2 + \ | |
66 | (COLOR_TABLE[c][2] - b) ** 2 | |
67 | if d < smallest_distance: | |
68 | smallest_distance = d | |
69 | best_match = c | |
70 | return best_match | |
71 | ||
72 | ||
73 | def compile_speedup(): | |
74 | """Tries to compile/link the C version of this module | |
75 | ||
76 | Like it really makes a huge difference. With a little bit of luck | |
77 | this should *just work* for you. | |
78 | ||
79 | You need: | |
80 | ||
81 | - Python >= 2.5 for ctypes library | |
82 | - gcc (``sudo apt-get install gcc``) | |
83 | ||
84 | """ | |
85 | import os | |
86 | import ctypes | |
87 | from os.path import join, dirname, getmtime, exists, expanduser | |
88 | # library = join(dirname(__file__), '_xterm256.so') | |
89 | library = expanduser('~/.xterm256.so') | |
90 | sauce = join(dirname(__file__), '_xterm256.c') | |
91 | if not exists(library) or getmtime(sauce) > getmtime(library): | |
92 | build = "gcc -fPIC -shared -o %s %s" % (library, sauce) | |
93 | assert os.system(build + " >/dev/null 2>&1") == 0 | |
94 | xterm256_c = ctypes.cdll.LoadLibrary(library) | |
95 | xterm256_c.init() | |
96 | def xterm_to_rgb(xcolor): | |
97 | res = xterm256_c.xterm_to_rgb_i(xcolor) | |
98 | return ((res >> 16) & 0xFF, (res >> 8) & 0xFF, res & 0xFF) | |
99 | return (xterm256_c.rgb_to_xterm, xterm_to_rgb) | |
100 | ||
101 | ||
102 | try: | |
103 | (rgb_to_xterm, xterm_to_rgb) = compile_speedup() | |
104 | except: | |
105 | logging.debug("fabulous failed to compile xterm256 speedup code") |
0 | Metadata-Version: 1.0 | |
1 | Name: fabulous | |
2 | Version: 0.1.5 | |
3 | Summary: Makes your terminal output totally fabulous | |
4 | Home-page: http://lobstertech.com/fabulous.html | |
5 | Author: J.A. Roberts Tunney | |
6 | Author-email: jtunney@lobstertech.com | |
7 | License: MIT | |
8 | Download-URL: http://lobstertech.com/media/file/fabulous/fabulous-0.1.5.tar.gz | |
9 | Description: .. -*-restructuredtext-*- | |
10 | ||
11 | ========== | |
12 | Fabulous | |
13 | ========== | |
14 | ||
15 | --------------------------------------------- | |
16 | Makes Your Terminal Output Totally Fabulous | |
17 | --------------------------------------------- | |
18 | ||
19 | :Version: 0.1 | |
20 | :Date: 2009-12-07 | |
21 | :Copyright: Copyright (c) 2009 Lobstertech, Inc. | |
22 | :Manual section: 3 | |
23 | :Manual group: Library Calls | |
24 | ||
25 | ||
26 | Getting Started | |
27 | =============== | |
28 | ||
29 | Download and extract the latest version:: | |
30 | ||
31 | sudo apt-get install gcc python-imaging python-setuptools | |
32 | sudo python setup.py install | |
33 | ||
34 | Run the demo to see what's available:: | |
35 | ||
36 | python -m fabulous.demo | |
37 | ||
38 | ||
39 | Basic Examples | |
40 | ============== | |
41 | ||
42 | Colors | |
43 | ------ | |
44 | ||
45 | 4-bit color. These colors and styles are standard and work almost | |
46 | everywhere. They are useful in helping make your program output | |
47 | easier to read:: | |
48 | ||
49 | from fabulous import bold, magenta, highlight_red | |
50 | ||
51 | print bold(magenta('hello kitty')) | |
52 | print highlight_red('DANGER DANGER!') | |
53 | ||
54 | print bold('hello') + ' ' + magenta( kitty') | |
55 | ||
56 | assert len(bold('test')) == 4 | |
57 | ||
58 | 8-bit color. If you want to spice things up a bit, Fabulous supports | |
59 | xterm256 colors:: | |
60 | ||
61 | from fabulous import fg256, bg256 | |
62 | print fg256('#F0F', 'hello kitty') | |
63 | print fg256('magenta', 'hello kitty') | |
64 | ||
65 | ||
66 | Fancy Text | |
67 | ---------- | |
68 | ||
69 | Way cool text. This is something neat you can use when you program | |
70 | starts up to display its name with style:: | |
71 | ||
72 | from fabulous import text | |
73 | print text.Text("Fabulous", color='#0099ff', shadow=True, scew=5) | |
74 | ||
75 | ||
76 | Images | |
77 | ------ | |
78 | ||
79 | Fabulous lets you print images, which is more fun than useful. | |
80 | Fabulous' unique method of printing images really shines when used | |
81 | with semi-transparent PNG files. When blending backgrounds, Fabulous | |
82 | assumes by default that your terminal has a black background. Don't | |
83 | worry if your image is huge, it'll be resized by default to fit your | |
84 | terminal:: | |
85 | ||
86 | from fabulous import utils, image | |
87 | print image.Image("balls.png") | |
88 | ||
89 | # adjust for a white background | |
90 | utils.term.bgcolor = 'white' | |
91 | print image.Image("balls.png") | |
92 | ||
93 | It's scriptable too (like img2txt) :: | |
94 | ||
95 | python -m fabulous.image balls.png >balls.txt | |
96 | cat balls.txt | |
97 | ||
98 | ||
99 | Transient Logging | |
100 | ----------------- | |
101 | ||
102 | This is very useful tool for monitoring what your Python scripts are | |
103 | doing. It allows you to have full verbosity without drowning out | |
104 | important error messages:: | |
105 | ||
106 | import time, logging | |
107 | from fabulous import logs | |
108 | logs.basicConfig(level='WARNING') | |
109 | ||
110 | for n in range(20): | |
111 | logging.debug("verbose stuff you don't care about") | |
112 | time.sleep(0.1) | |
113 | logging.warning("something bad happened!") | |
114 | for n in range(20): | |
115 | logging.debug("verbose stuff you don't care about") | |
116 | time.sleep(0.1) | |
117 | ||
118 | ||
119 | Why Fabulous? | |
120 | ============= | |
121 | ||
122 | Here's how Fabulous compares to other similar libraries: | |
123 | ||
124 | - fabulous_: Licensed MIT. Focuses on delivering useful features in | |
125 | the simplest, most user-friendly way possible (without a repulsive | |
126 | name.) Written in pure-python but will attempt to auto-magically | |
127 | compile/link a speedup library. ~1,000 lines of code. | |
128 | ||
129 | - libcaca_: WTFPL. This is the established and respected standard for | |
130 | doing totally insane things with ascii art (ever wanted to watch a | |
131 | movie on the command line?) Weighing in at ~72k lines of C, this | |
132 | project is a monster. It uses an older, more complex | |
133 | text/dithering-based rendering method. Compared to fabulous, some | |
134 | images look better, some worse. I found the docs somewhat difficult | |
135 | to follow and couldn't find support for transparency or 256-colors. | |
136 | ||
137 | - asciiporn_: GPL. Similar to libcaca but has an interesting feature | |
138 | for drawing math graphs to the terminal... Needs to compile C code, | |
139 | requires numpy/python2.6, and I couldn't get the darn thing to work. | |
140 | Aprox 17k lines of code. | |
141 | ||
142 | - pygments_: BSD. Has *excellent* support for terminal syntax highlighting. | |
143 | ||
144 | - termcolor_: GPL. Only supports 4-bit ANSI colors. | |
145 | ||
146 | .. _fabulous: http://pypi.python.org/pypi/fabulous | |
147 | .. _libcaca: http://caca.zoy.org/ | |
148 | .. _termcolor: http://pypi.python.org/pypi/termcolor | |
149 | .. _pygments: http://pygments.org/ | |
150 | .. _asciiporn: http://pypi.python.org/pypi/asciiporn/2009.05.01 | |
151 | ||
152 | ||
153 | ToDo | |
154 | ==== | |
155 | ||
156 | - <http://www.burgaud.com/bring-colors-to-the-windows-console-with-python/> | |
157 | ||
158 | Platform: UNKNOWN | |
159 | Classifier: Development Status :: 2 - Pre-Alpha | |
160 | Classifier: License :: OSI Approved :: MIT License | |
161 | Classifier: Environment :: Console | |
162 | Classifier: Intended Audience :: Developers | |
163 | Classifier: Programming Language :: C | |
164 | Classifier: Programming Language :: Python | |
165 | Classifier: Topic :: Utilities | |
166 | Classifier: Topic :: Artistic Software | |
167 | Classifier: Topic :: System :: Logging | |
168 | Classifier: Topic :: Multimedia :: Graphics |
0 | .hgignore | |
1 | .hgtags | |
2 | COPYING | |
3 | README | |
4 | ez_setup.py | |
5 | setup.py | |
6 | docs/Makefile | |
7 | docs/conf.py | |
8 | docs/index.rst | |
9 | fabulous/__init__.py | |
10 | fabulous/_xterm256.c | |
11 | fabulous/color.py | |
12 | fabulous/debug.py | |
13 | fabulous/demo.py | |
14 | fabulous/gotham.py | |
15 | fabulous/image.py | |
16 | fabulous/logs.py | |
17 | fabulous/rotating_cube.py | |
18 | fabulous/test_transientlogging.py | |
19 | fabulous/text.py | |
20 | fabulous/utils.py | |
21 | fabulous/xterm256.py | |
22 | fabulous.egg-info/PKG-INFO | |
23 | fabulous.egg-info/SOURCES.txt | |
24 | fabulous.egg-info/dependency_links.txt | |
25 | fabulous.egg-info/not-zip-safe | |
26 | fabulous.egg-info/requires.txt | |
27 | fabulous.egg-info/top_level.txt | |
28 | fabulous/experimental/__init__.py | |
29 | fabulous/experimental/canvas.py | |
30 | fabulous/fonts/DejaVuSansMono.ttf | |
31 | fabulous/fonts/IndUni-H-Bold.otf | |
32 | fabulous/fonts/cmr10.ttf⏎ |
0 | grapefruit⏎ |
0 | fabulous |
0 | ||
1 | # http://packages.python.org/distribute/setuptools.html | |
2 | # http://diveintopython3.org/packaging.html | |
3 | # http://wiki.python.org/moin/CheeseShopTutorial | |
4 | # http://pypi.python.org/pypi?:action=list_classifiers | |
5 | ||
6 | from ez_setup import use_setuptools | |
7 | use_setuptools(version='0.6c11') | |
8 | ||
9 | import os | |
10 | from setuptools import setup, find_packages | |
11 | ||
12 | def read(fname): | |
13 | return open(os.path.join(os.path.dirname(__file__), fname)).read() | |
14 | ||
15 | version = __import__('fabulous').__version__ | |
16 | ||
17 | setup( | |
18 | name = 'fabulous', | |
19 | version = version, | |
20 | url = 'http://lobstertech.com/fabulous.html', | |
21 | author = 'J.A. Roberts Tunney', | |
22 | author_email = 'jtunney@lobstertech.com', | |
23 | description = 'Makes your terminal output totally fabulous', | |
24 | download_url = ('http://lobstertech.com/media/file/fabulous/' | |
25 | 'fabulous-' + version + '.tar.gz'), | |
26 | long_description = read('README'), | |
27 | license = 'MIT', | |
28 | install_requires = ['grapefruit'], | |
29 | packages = find_packages(), | |
30 | zip_safe = False, | |
31 | include_package_data = True, | |
32 | classifiers = [ | |
33 | "Development Status :: 2 - Pre-Alpha", | |
34 | "License :: OSI Approved :: MIT License", | |
35 | "Environment :: Console", | |
36 | "Intended Audience :: Developers", | |
37 | "Programming Language :: C", | |
38 | "Programming Language :: Python", | |
39 | "Topic :: Utilities", | |
40 | "Topic :: Artistic Software", | |
41 | "Topic :: System :: Logging", | |
42 | "Topic :: Multimedia :: Graphics" | |
43 | ], | |
44 | ) |