tkNfo - an NFO viewer in Python
An nfo file is an ASCII text file with ASCII art. While these can be opened and read with any text viewer, they lose the funky graphics that make them so cool. They use the extended drawing characters from codepage 437, an IBM standard from the early 80s.
While there are several nice viewers on Win32, there is only Osmo Salomaa's PyGTK script or knfo for Linux. Osmos's script rocks (allows selections, hyperlinks, etc.), but requires PyGTK; both require a specific True Type font to be installed. I wanted a straight up Tkinter version using internal bitmap fonts... nothing needed but Python.
Install
Grab the script, chmod it 755 on linux or add the .pyw extension on Windows. Then just drop it somewhere in your path.
Script notes
- The script uses photoImage objects created from base 64 encoded gif images for the icon and the base font. Here's how they're made:
Now the string is created using the output from the activestate script. It ends up looking like this:
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'''
- The internal font is a bunch of Tkinter bitmapImage objects. They're stored in XBM format, so we can creating a string with the C fragment and call bitmapImage() passing the string. Here's chr(65), and upper case A, in XMB format.
#define im_height 13
static char im_bits[] = {
0x0,0x0,0x3e,0x42,0x42,0x3e,0x42,0x42,0x42,0x3e,0x0,0x0,0x0
};
We'll loop through the range 0 to 255 and generate the strings for each. Since the only thing that changes is the 'im_bits[] = ' string, that's all we need to vary from one character to the next. We can store the as a list of strings. A demo of that is bmpFont_X11.py It weighs in at around 22KB in size, so it takes a bit of space to store all the strings, but it's fast.
We could also create the string on the fly from our source gif image. Here's how:
- We create a photoImage object from our .gif data created above.
- It's a 16x16 grid, so each character is 1/16th of the total width and height. In this case the total image is 128x208, so each character is 8x13.
-
For each character, we need to create a string in the format of the XBM definition.
- The im_width and im_height lines are added first.
-
We need to create the 'static char im_bits[] = ' lines. To calculate the strings we'll look at the pixels in a row, create a hex sting of the pixels and append that to the string.
- For each row we read pixels right to left.
- We look at each pixel. If it's white (255,255,255) we call it a 0, otherwise it's a 1.
- We get a string of 0's and 1's. We convert that to hex and append it to our string.
- Start on the next row...
- When done we'll have a string that looks like the above XBM definition for the character. We call bitmapImage() passing the string we just created as our data.
A demo of that is bmpFont_gif.py At about 6KB, it's a third of the size. It's also a bit slower. A quick benchmark is here. On average, the gif version is some twenty times slower. For this app, the speed isn't an issue as it's only called once and still runs in the tenths of a second.
- One snag I ran into with the script was that the tkColorChooser askcolor function returns a string in Windows, but an object on Linux. Using the following works fine in both.
if fColor != None:
self.fColor = fColor.__str__()
# do stuff here...

