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