Tuesday, November 1, 2011

Better Fancy Formatting and Printing with Python

A while back, I came up with a great way to print fancy boxed and wrapped text to the screen (see the old post) in a way that was easy to use in other scripts, and which allowed the justification of the text. I've reworked the code since then, revisiting my random libraries as I do, and came up with a much better implementation. It more-or-less works the same as the old one, but now returns the string for printing, and some of the arguments are slightly different.

For example, the following code:

print formatText('Wow, this sure is a lot of text.\nQuite a bit of text, actually.\nWhy would anyone have this much text to output?', mode="box", width = 20, justify='center')

...produces this output:


====================
=  Wow, this sure  =
=   is a lot of    =
=  text. Quite a   =
=   bit of text,   =
=  actually. Why   =
=   would anyone   =
=  have this much  =
= text to output?  =
====================


Here's the code from GitHub:

Sunday, July 17, 2011

Fancy Table Printing With Python

Ever want to print out a nice table, maybe have it spaced out nicely? Have you run into a situation where the length of something you want to print differs by more than a tab-length? Stuck in a for loop, with no hope in sight of getting those columns printed nicely? Fear no more!

fancyTable() will accept a multi-dimensional array, and make sure each item in each column is spaced to be as long as the longest element in that column.

For example, should you build up an array of arrays like this:

a = [['Text', '1', 'a'], ['Longer Text', '123', 'abc'], ['Even longer text!', '123456789', 'abcdefghi']]

You'd get this:

Text              1         a         
Longer Text       123       abc       
Even longer text! 123456789 abcdefghi


Amazing, isn't it!

def fancyTable(arrays):

 def areAllEqual(lst):
     return not lst or [lst[0]] * len(lst) == lst

 if not areAllEqual(map(len, arrays)):
  exit('Cannot print a table with unequal array lengths.')

 #lengths = [map(len, a) for a in myar]
 #sizes = map(lambda * x:x, *lengths)
 #maxSize = [max(a) for a in sizes]

 verticalMaxLengths = [max(value) for value in map(lambda * x:x, *[map(len, a) for a in arrays])]

 spacedLines = []

 for array in arrays:
  spacedLine = ''
  for i, field in enumerate(array):
   diff = verticalMaxLengths[i] - len(field)
   spacedLine += field + ' ' * diff + '\t'
  spacedLines.append(spacedLine)

 return '\n'.join(spacedLines)

Let me know what you think! I'm sure there's a better way of doing it.

Monday, May 9, 2011

Fancy Printing with Python

Update: View the new and improved code here!

=================
= Hello, world! =
=================


But, the task of printing these things becomes tedious, especially if you want to say, center the text:

==============================
=       Hello, world!        =
==============================


But what if you wanted to change all of that: not have it in a box, specify arbitrary widths, specify filler characters, specify line characters, margins, padding, etc?

Here's the output of: fancyPrinter('Hello, world!', centered = True, width = 50, char = '*', mode = 'box', filler = 'X', margin = 1, padding = 1):

**************************************************
*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*
*XXXXXXXXXXXXXXXX Hello, world! XXXXXXXXXXXXXXXXX*
*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*
**************************************************


Or what about multiple lines? That are longer than your width?

Try this out: fancyPrinter('Wow, this sure is a lot of text.\nQuite a bit of text, actually.\nWhy would anyone have this much text to output?', centered = True, width = 20)

====================
=  Wow, this sure  =
=   is a lot of    =
=      text.       =
=  Quite a bit of  =
=      text,       =
=    actually.     =
=    Why would     =
=   anyone have    =
=  this much text  =
=    to output?    =
====================


And here's the same thing, not centered, with no width specified:

===================================================
= Wow, this sure is a lot of text.                =
= Quite a bit of text, actually.                  =
= Why would anyone have this much text to output? =
===================================================



Then you would need my fancyPrinter() method:




def fancyPrinter(text, filler = ' ', char = None, centered = False, width = None, margin = None, padding = None, mode = 'box', output = sys.stdout):
 
 '''
 Copyright 2011 Sebastian Weigand, released under the GPLv3
 Print text in a fancier fashion.
 print >> output,  text in a fancier fashion.
 
 Accepts multiple lines, and will fit a box (if that is the mode) around it, and truncate
 each line which is over the specified width (maximum of terminal, or specified; minimum
 of the longest word in the text).
 '''
 
 textLength = max(map(len, text.splitlines()))
 textArray = text.split()
 maxWordLength = max(map(len, textArray))
 rows, columns = os.popen('stty size', 'r').read().split()
 termWidth = int(columns)
 modes = ['box', 'overline', 'underline', 'simple']

 if mode not in modes:
  raise TypeError('\'' + mode + '\' is an invalid mode for boxprint >> output, er().')

 if 'line' in mode:
  if not char:
   char = '-'
 elif mode == 'center':
  if not char:
   char = ' '
  centered = True
  if not width:
   width = termWidth
 else:
  if not char:
   char = '='

 if centered and not width:
  width = termWidth

 if width:
  if width < maxWordLength + 4:
   width = maxWordLength + 4
 else:
  width = textLength + 4
 
 # Set maximum width to that of terminal:
 if textLength >= termWidth + 4 or width >= termWidth + 4:
  width = termWidth
 
 lines = []

 # Chop up lines to fit within confines of width, or max width (terminal):
 if width < textLength + 4:
  
  for line in text.splitlines():
   sentence = ''

   for word in line.split():

    word = word + ' '

    newSentence = sentence + word
    
    if len(newSentence) < width - 4:
     sentence = newSentence
    else:
     if len(sentence) > 0:
      lines.append(sentence)
     sentence = word

   lines.append(sentence)
 else:
  lines = (text + ' ').splitlines()

 if margin:
  print >> output,  '\n' * (margin - 1)

 if centered:
  if mode == 'box' or mode == 'overline':
   print >> output,  char * width
  
  if padding:
   for i in xrange(0, padding):
    if 'box' in mode:
     print >> output,  char + filler * (width - 2) + char
    else:
     print >> output,  filler * width
  
  for line in lines:
   # "line" is text + :
   spacing = (width - len(line) - 2)
   
   # Prefer extra right spaces when width is not even:
   leftSpacing = (spacing / 2) + (spacing % 2) - 1
   rightSpacing = (spacing / 2)
   
   if mode == 'box':
    print >> output,  char + (filler * leftSpacing) + ' ' + line + (filler * rightSpacing) + char
   else:
    # Extra padding, as there's no preceeding char:
    print >> output,  (filler * (leftSpacing + 1)) + ' ' + line + (filler * (rightSpacing + 1))
  
  if padding:
   for i in xrange(0, padding):
    if 'box' in mode:
     print >> output,  char + filler * (width - 2) + char
    else:
     print >> output,  filler * width
  
  if mode == 'box' or mode == 'underline':
   print >> output,  char * width
  
 else:
  if mode == 'box' or mode == 'overline':
   print >> output,  char * width
  
  if padding:
   for i in xrange(0, padding):
    if 'box' in mode:
     print >> output,  char + filler * (width - 2) + char
    else:
     print >> output,  filler * width
  
  for line in lines:
   if mode == 'box':
    print >> output,  char + ' ' + line + filler * (width - len(line) - 3) + char
   else:
    print >> output,  ' ' + line
  
  if padding:
   for i in xrange(0, padding):
    if 'box' in mode:
     print >> output,  char + filler * (width - 2) + char
    else:
     print >> output,  filler * width
  
  if mode == 'box' or mode == 'underline':
   # This will produce a line which is slighlty longer on the right,
   #  but it looks nice in underline.
   print >> output,  char * width  

 if margin:
  print >> output,  '\n' * (margin - 1)


I hope you enjoy :)

Thursday, February 3, 2011

Password Generator

I needed a script to generate arbitrary-sized passwords which may or may not include specific characters. Nothing really worked, so I ended up writing one: Available on my Dropbox

Sunday, November 14, 2010

Finalized Layout Design

I've decided to take on something quite difficult: I'm going to *attempt* to create an HTML5+CSS3 (graceful fallback) template which works beautifully on both Blogger and Posterous for my blog, SWDD. I've got a preliminary Webkit-centric layout mocked up already (complete with subtle, yet enjoyable webkit-transitions), and I just need to correlate the Webkit-centric code to Mozilla-centric code, and then futz around with the wonderful hell that is Internet Explorer. Until then, enjoy some of the theme designers best work I've seen on both Blogger and Posterous.

Thursday, June 17, 2010

Profoundly Moving, Transcendental, Haunting or Uplifting Music

Have you ever listened to music? I mean, really listened to music? Have you ever devoted all of your being, focused all your attention on the perception of audio? I frequently do this, to reaffirm my humanity. There's something unique about music which can elevate the essence of being beyond its physical constraints, and help consciousness transcend itself, breaking barriers on emotion, religion, philosophy, art, and reality. It's that feeling I get in the back of my skull, around my ears that wraps itself around my brain, tingling it with every sweet note or powerful beat. It's like your entire mind wants to explode. Wouldn't it be great to experience that? There are so many songs, but which ones are the easiest to make the transition? Luckily, I have an idea which might help.

Below (in alphabetical order by artist), I have assembled a humble list of the songs, which when listened to properly, have helped me achieve transcendental acoustical nirvana. Of over 500 artists, and over 800 albums, these are the songs that gave me that tingly feeling in my head. This list is by no means complete, but it is a start, and I'd thought I'd share this with the rest of the world. I hope you enjoy: Available on my Dropbox.

Wednesday, May 12, 2010

Simple Media Jukebox

I, upon dealing with the myriad frustrations of iTunes, pondered the difficulty of a simple, drop-anywhere jukebox. I just wanted something to scan a directory, organize the files in a meaningful way, and let me choose which one to play (given that I have the ability to decode them). So, I set about throwing together some code.

SWDD: SMJ is my 'simple media jukebox'. To use it, simply execute it on your favorite platform (Linux and Mac OS X supported, though Windows is too, if you give it the right media player binary). It then asks for a media player to use (defaults to mplayer), and a directory in which to search for media files.

Here's where it gets brilliant: If you're using a modern Linux distribution, or OS X, you can alternatively enter 'locate' for the location, and it will query the system indexing service for a list of media files. Then, simply give it a song you wish to play, and voi-la!

Of course, it only bases its limited information on file name, file extension, and location; if you were to give it a mount point of an iPod, for example, it wouldn't be quite so useful. It also does not parse metadata, and the way it handles the printing of matching songs is unordered (because of the way we retrieve them from the disk), but all-in-all, it's a Simple Media Jukebox. Give it a whirl!

Monday, March 8, 2010

ssh-keyscan-aliases.py - A smarter ssh-keyscan

I've been playing around with a wrapper for 'ssh-keyscan' at work, and thought I would share my script with the world.

Motivations

While at work, we have servers which need to have their SSH known-hosts file updated with the various machines we deal with. Additionally (as SSH is very specific), aliases of machines are not added to the known_hosts file unless you specify them. This script seeks to address each of these issues, while providing a more robust command in general (like comparing SSH keys of hosts).

Take a look at the code, available on my GitHub.

If you have any comments, suggestions, or notice a bug: Please feel free to comment, and I'll be sure to fix it.