fabulous-0.1.5/0000755000175000017500000000000011433751355011454 5ustar tagtagfabulous-0.1.5/fabulous.egg-info/0000755000175000017500000000000011433751355014766 5ustar tagtagfabulous-0.1.5/fabulous.egg-info/top_level.txt0000644000175000017500000000001111433751355017510 0ustar tagtagfabulous fabulous-0.1.5/fabulous.egg-info/PKG-INFO0000644000175000017500000001406711433751355016073 0ustar tagtagMetadata-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-safe0000644000175000017500000000000111330026717017205 0ustar tagtag fabulous-0.1.5/fabulous.egg-info/requires.txt0000644000175000017500000000001211433751355017357 0ustar tagtaggrapefruitfabulous-0.1.5/fabulous.egg-info/dependency_links.txt0000644000175000017500000000000111433751355021034 0ustar tagtag fabulous-0.1.5/fabulous.egg-info/SOURCES.txt0000644000175000017500000000130411433751355016650 0ustar tagtag.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.ttffabulous-0.1.5/.hgignore0000644000175000017500000000025711327764514013266 0ustar tagtagsyntax: 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/0000755000175000017500000000000011433751355012404 5ustar tagtagfabulous-0.1.5/docs/index.rst0000644000175000017500000000161011373104534014235 0ustar tagtag.. 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.py0000644000175000017500000001472011373104131013672 0ustar tagtag# -*- 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/Makefile0000644000175000017500000000607011373103664014044 0ustar tagtag# 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/.hgtags0000644000175000017500000000050311433750557012733 0ustar tagtagf0b97fffad309fd8dea60efd7d989fff4ba3058a 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.cfg0000644000175000017500000000007311433751355013275 0ustar tagtag[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 fabulous-0.1.5/PKG-INFO0000644000175000017500000001406711433751355012561 0ustar tagtagMetadata-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/0000755000175000017500000000000012166646411013274 5ustar tagtagfabulous-0.1.5/fabulous/logs.py0000644000175000017500000000612211373065473014615 0ustar tagtag""" 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.py0000644000175000017500000002072211432501503014620 0ustar tagtag""" 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.py0000644000175000017500000001527711433626475014612 0ustar tagtag 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.py0000644000175000017500000001272011433265056014730 0ustar tagtag""" 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.py0000644000175000017500000000304711330006557020260 0ustar tagtag 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.py0000644000175000017500000000222611373104705014730 0ustar tagtag""" 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.py0000644000175000017500000002305311410602266014756 0ustar tagtag# -*- 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.py0000644000175000017500000001055511433563037016477 0ustar tagtag""" 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__.py0000644000175000017500000000006611433751302015377 0ustar tagtagVERSION = (0, 1, 5, 'final', 0) __version__ = '0.1.5' fabulous-0.1.5/fabulous/utils.py0000644000175000017500000001116511433562733015012 0ustar tagtag""" 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/0000755000175000017500000000000011433751355015771 5ustar tagtagfabulous-0.1.5/fabulous/experimental/canvas.py0000644000175000017500000000151311331060366017606 0ustar tagtag 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__.py0000644000175000017500000000000011330317404020055 0ustar tagtagfabulous-0.1.5/fabulous/gotham.py0000644000175000017500000000676011373101271015123 0ustar tagtag""" 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.c0000644000175000017500000000567311433630500015172 0ustar tagtag/** * 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.py0000644000175000017500000000637011433751240015241 0ustar tagtag""" 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.py0000644000175000017500000002414211433624745013671 0ustar tagtag#!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/README0000644000175000017500000001024411433621356012332 0ustar tagtag.. -*-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.py0000644000175000017500000000307011433625663013170 0ustar tagtag # 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/COPYING0000644000175000017500000000204711373023521012500 0ustar tagtagCopyright (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.