#-----------------------------------------------------------------||||||||||||--
# Name: imageTools.py
# Purpose: General image processing and drawing tools using PIL, Tk, and
# vector graphics
#
# Authors: Christopher Ariza
#
# Copyright: (c) 2004-2006 Christopher Ariza
# License: GPL
#-----------------------------------------------------------------||||||||||||--
import os, random, string, copy, time, array
from athenaCL.libATH import drawer
from athenaCL.libATH import interpolate
from athenaCL.libATH import fontLibrary
# dialog import imageTools.py for finding dlg visual formats
from athenaCL.libATH import osTools
_MOD = 'imageTools.py'
#-----------------------------------------------------------------||||||||||||--
# note: this imports must match those tested in drawer.imageFormats
try:
import Image, ImageDraw, ImageFont #, ImageFilter ImageTk
PIL = 1
except ImportError:
PIL = 0
try:
import Tkinter
TK = 1
except ImportError:
TK = 0
#-----------------------------------------------------------------||||||||||||--
FONTPATH = os.path.join(os.path.expanduser('~'), 'Library', 'FontsPIL')
if not os.path.exists(FONTPATH):
FONTPATH = None
# load the better than nothing default
# print _MOD, 'no PIL fonts available'
#-----------------------------------------------------------------||||||||||||--
# these methods borrowed from:
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/266466/index_txt
def RGBToHTMLColor(rgb_tuple):
""" convert an (R, G, B) tuple to #RRGGBB """
if drawer.isStr(rgb_tuple):
if rgb_tuple[0] == '#': # already a string
return rgb_tuple
for x in rgb_tuple:
assert x < 256 and x >= 0
hexcolor = '#%02x%02x%02x' % rgb_tuple
# that's it! '%02x' means zero-padded, 2-digit hex values
hexcolor = hexcolor.upper()
return hexcolor
def HTMLColorToRGB(colorstring):
""" convert #RRGGBB to an (R, G, B) tuple """
colorstring = colorstring.strip()
if colorstring[0] == '#': colorstring = colorstring[1:]
if len(colorstring) != 6:
raise ValueError, "input #%s is not in #RRGGBB format" % colorstring
r, g, b = colorstring[:2], colorstring[2:4], colorstring[4:]
r, g, b = [int(n, 16) for n in (r, g, b)]
return (r, g, b)
def HTMLColorToUnitRGB(colorstring):
""" converts #RRGGBB to postScript style float values
assume that there are 256 values"""
max = 255.0
r, g, b = HTMLColorToRGB(colorstring)
r = r / max
g = g / max
b = b / max
return r, g, b
def HTMLColorToPILColor(colorstring):
""" converts #RRGGBB to PIL-compatible integers"""
colorstring = colorstring.strip()
while colorstring[0] == '#': colorstring = colorstring[1:]
# get bytes in reverse order to deal with PIL quirk
colorstring = colorstring[-2:] + colorstring[2:4] + colorstring[:2]
# finally, make it numeric
color = int(colorstring, 16)
return color
def PILColorToRGB(pil_color):
""" convert a PIL-compatible integer into an (r, g, b) tuple """
hexstr = '%06x' % pil_color
# reverse byte order
r, g, b = hexstr[4:], hexstr[2:4], hexstr[:2]
r, g, b = [int(n, 16) for n in (r, g, b)]
return (r, g, b)
def PILColorToHTMLColor(pil_integer):
return RGBToHTMLColor(PILColorToRGB(pil_integer))
def RGBToPILColor(rgb_tuple):
return HTMLColorToPILColor(RGBToHTMLColor(rgb_tuple))
# added for easy processing
def FloatToRGB(cellValue):
"""translate a cell value to a color or gray shade
positive shift will make that much brighter
negative shift will make darker"""
# must invert cell value, so 1 is black, or 0
cellValue = 1-cellValue
# if not float return after reversing, could be a decimal
return (float(str(cellValue)) * 255, float(str(cellValue)) * 255,
float(str(cellValue)) * 255)
# if not drawer.isInt(cellValue):
# print _MOD, 'converting non int'
#
# cellMax = 255 * max
# if cellMax >= 255: cellMax = 255
# cellMin = 255 * min
# if cellMin <= 0: cellMin = 0
# cellRange = cellMax - cellMin
# shade = int(round(cellMin + (cellRange * cellValue)))
# tk_rgb = (shade,shade,shade) # (shade, shade, shade)
# return tk_rgb
#-----------------------------------------------------------------||||||||||||--
# tools for determining image format types
# vetor here referes to a trademarked name
# dynamic testing of available formats in drawer.imageFormats
imageFormatNames = ['text', 'eps', 'tk', 'png', 'jpg']
def imageFormatParser(usrStr):
"""provide backward compat to older names"""
ref = {
'jpg' : ['pil', 'jpeg', 'jpg', 'j'],
'png' : ['file', 'png', 'p'],
'tk' : ['tk', 'tkinter', 'k'], # t reserved for text
'eps' : ['eps', 'ps', 'e'],
'text': ['text', 't'],
}
usrStr = drawer.selectionParse(usrStr, ref)
return usrStr # may be None
#-----------------------------------------------------------------||||||||||||--
# designed for use with PIL fonts found here
# http://effbot.org/downloads/
fontLibPil = {
'char' : ('charI', 'charR', ),
'courier' : ('courB', 'courO', 'courR'),
'helvetica' : ('helvR', 'helvO', 'helvB'),
'lub' : ('lubB', 'luBIS', 'lubI', 'luBS', 'lubR',
'luIS', 'luRS', 'lutBS', 'lutRS'),
'ncen' : ('ncenB', 'ncenBI', 'ncenI', 'ncenR'),
'symbol' : ('symb', ),
'tech' : ('tech', 'techB',),
'term' : ('termB', 'term',),
'times' : ('timB', 'timBI', 'timI', 'timR'),
}
def getFontPath((face, style, size)): # dont check for errrs
for n in fontLibPil[face]:
if style in n or style == n:
group = n
break
if size <= 9:
sizeStr = '0%s' % size
else:
sizeStr = str(size)
name = '%s%s.pil' % (group, sizeStr)
return os.path.join(FONTPATH, name)
#-----------------------------------------------------------------||||||||||||--
#-----------------------------------------------------------------||||||||||||--
# note: these objects require PIL
class Thumb:
def __init__(self, fileList, size=256, fmt='.jpg'):
"create a thumbnail; if size is none, dont resize"
self.sizeW = size
self.sizeH = size
self.fmt = fmt
# check if file list is a dir, if so, get a listing of image files
if drawer.isList(fileList):
self.fileList = fileList
else:
self.fileList = [fileList, ] # its a single file
def reduce(self):
import Image
procFiles = [] # stores tuples of src, dst file
nameOutFiles = []
for infile in self.fileList:
if self.sizeW == None:
outfile = os.path.splitext(infile)[0] + self.fmt
else:
outfile = os.path.splitext(infile)[0] + ".th%s%s" % (
self.sizeW, self.fmt)
if infile.endswith('.gif'):
print _MOD, 'cannot process gif files: %s' % infile
elif outfile in nameOutFiles:
print _MOD, 'file already generated: %s' % outfile
elif infile != outfile:
try:
im = Image.open(infile)
if self.sizeW != None: # only resize if size is not None
print _MOD, 'reducing size to %s' % self.sizeW
im.thumbnail((self.sizeW, self.sizeH), Image.ANTIALIAS)
#im.save(outfile, "JPEG")
im.save(outfile)
procFiles.append([infile, outfile])
nameOutFiles.append(outfile)
except IOError, msg:
print _MOD, "cannot create thumbnail: %s" % infile, msg
else:
print _MOD, 'name conflict: %s' % infile
return procFiles
#-----------------------------------------------------------------||||||||||||--
class GridGraphic:
"""uess a list of lists as matrix for generating images"""
def __init__(self, gridData=None, dataType='float', transp=None):
"""if dataType == float values are interpreted as gray scale
if dataType == hex, html hex a decimal strings expected
if dataType == rgb, assume rgb triples
rot is rotatioin in degrees
transparency: None is off, value is color that is transparent?
"""
self.dataType = dataType
self.transp = transp
if gridData == None: # use demo
self.gridData = [] # a list of lists
for x in range(20):
self.gridData.append([1,1,1,1,.5,.5,.5,.5,1,1,1,1,
0,0,0,1,1,1,1,0,0,0,0])
else:
self.gridData = gridData
self.noCols = len(self.gridData[0])
self.noRows = len(self.gridData)
self.RGBData = self._convertToRGB()
self.imageObj = Image.new("RGB", (self.noCols, self.noRows))
self.imageObj.putdata(self.RGBData)
# not needed for transparency create mask
#self.imageObj = self.imageObj.convert("RGBA")
#mask = Image.new("1", (self.noCols, self.noRows))
#self.imageObj.putalpha(mask)
# putalpha(band)
# To add an alpha (transparency) band to an "RGBA"-mode image,
# call this method and pass it an
# image of the same size having mode "L" or "1". The pixels of the band
# image replace the alpha
# band of the original image in place
def _convertToRGB(self):
"translate a list of cells to RGB"
inputList = [] # self.gridData
outputList = []
# flatten grid into one long list
for row in self.gridData:
for col in row:
inputList.append(col)
for cell in inputList:
if self.dataType == 'float':
outputList.append(FloatToRGB(cell))
elif self.dataType == 'hex':
outputList.append(HTMLColorToRGB(cell))
elif self.dataType == 'rgb':
outputList.append(cell)
#for each in self.gridData._flatten(self.gridData):
return outputList
def scale(self, xMult, yMult):
self.imageObj = self.imageObj.resize((self.noCols*xMult,
self.noRows*yMult))
def rotate(self, angle):
if angle != 0:
self.imageObj = self.imageObj.rotate(angle)
def write(self, filePath, openMedia=1):
"Saves the Image; .png and .jpg both work; format based on extensions"
self.filePath = filePath
if self.transp != None:
self.imageObj.save(self.filePath, transparency=self.transp)
else: #determine format from name
self.imageObj.save(self.filePath)
def show(self, dir=None, prefDict=None):
"Opens default image viewer and shows picture"
self.imageObj.show(dir, prefDict)
def getData(self):
bundle = []
seq = list(self.imageObj.getdata())
row = []
for pix in seq:
if len(row) == self.noCols:
bundle.append(row)
row = []
row.append(pix)
return bundle
class BarCode(GridGraphic):
def __init__(self, src=None, width=120, foreColor='#000000',
backColor='#aaaaaa', xMult=1, yMult=1, horizontal=1):
if width == None and src == None:
raise ValueError, 'not enough arguments'
#foreColor can be a list of colors that are chosen from at random
if not drawer.isList(foreColor):
foreColor = [foreColor,]
if not drawer.isList(backColor):
backColor = [backColor,]
if backColor[0] == None: # if none, back it transparent
self.transp = 0 # black
backColor[0] = '#000000' # make it black
else:
self.transp = None # none is no transp
if width == None:
width = len(src)
# do not apply xmult yet, but account for it in the lineGraph
lineGraph = []
if src == None: # creates a random distribution
for i in range(0,width):
pixel = random.choice((0,1)) # choose on or off
lineGraph.append(pixel)
else: # wrap src to fill width
for i in range(0,width):
pixel = src[i % len(src)] # wrap
lineGraph.append(pixel)
gridRow = []
for pixel in lineGraph:
#choose random color
if pixel: # if 1
color = random.choice(foreColor)
else:
color = random.choice(backColor)
for x in range(0, xMult):
gridRow.append(color)
gridData = [gridRow]
GridGraphic.__init__(self, gridData, 'hex', self.transp)
self.scale(xMult, yMult)
if not horizontal: # rotate 90 degrees
self.rotate(90)
class GridText(GridGraphic):
def __init__(self, msg='test', foreColor='#000000',
backColor='#aaaaaa', font='micro', kernBit=1,
vertBit=None, xMult=1, yMult=1, horizontal=1,
charWrapWidth=None):
#foreColor can be a list of colors that are chosen from at random
if not drawer.isList(foreColor):
foreColor = [foreColor,]
if not drawer.isList(backColor):
backColor = [backColor,]
if backColor[0] == None: # if none, back it transparent
self.transp = 0 # black
backColor[0] = '#000000' # make it black
else:
self.transp = None # none is no transp
kernEqual = 0 # treat as equla spaced?
vertBit = None # use default
obj = fontLibrary.FontBitMap(msg, font, kernEqual, kernBit, vertBit,
charWrapWidth)
gridData = obj.encode()
for row in range(0, len(gridData)):
rowData = gridData[row]
for col in range(0, len(rowData)):
if gridData[row][col] == 1:
gridData[row][col] = random.choice(foreColor)
else: # its 0
gridData[row][col] = random.choice(backColor)
GridGraphic.__init__(self, gridData, 'hex', self.transp)
self.scale(xMult, yMult)
if not horizontal: # rotate 90 degrees
self.rotate(90)
class GridBlend(GridGraphic):
def __init__(self, colorStart='#000000', colorEnd='#333333', length=20,
direction='x', width=40):
if direction == 'y':
horizontal = 0
else:
horizontal = 1
colorListStart = HTMLColorToRGB(colorStart)
colorListEnd = HTMLColorToRGB(colorEnd)
colorArray = [[], [], []]
for i in range(3): # for r g b
s = colorListStart[i]
e = colorListEnd[i]
interpObj = interpolate.OneDimensionalLinear(s, e)
for val in interpObj.discrete(length, 0):
colorArray[i].append(int(val))
gridData = []
for row in range(length):
rowData = []
code = RGBToHTMLColor((colorArray[0][row],
colorArray[1][row], colorArray[2][row]))
for col in range(width):
rowData.append(code)
gridData.append(rowData)
GridGraphic.__init__(self, gridData, 'hex', None)
if not horizontal: # rotate 90 degrees
self.rotate(90)
#-----------------------------------------------------------------||||||||||||--
class FontText:
"""used for creating text chunk images for web pages"""
def __init__(self, text=None, fontInfo=None, size=None,
bg="#000000", fg="#ffffff", xMult=1, yMult=1,
anti=0):
if text == None:
text = string.ascii_letters
if fontInfo == None: # default font
fontInfo = ('helvetica', 'R', 12)
if FONTPATH == None:
font = ImageFont.load_default() # get basic
else:
font = ImageFont.load(getFontPath(fontInfo))
# should be based on size of text
if size == None: # area of background
width = len(text) * fontInfo[2]
height= fontInfo[2] * 2
else:
width, height = size
if bg == None: # assume transparent
bg = '#000000'
self.transp = 0 # trans color is black?
else:
self.transp = None # no transparency
bg = HTMLColorToRGB(bg)
fg = HTMLColorToRGB(fg)
self.im = Image.new("RGB", (width, height), bg)
draw = ImageDraw.Draw(self.im)
draw.text((0, 0), text, font=font, fill=fg)
#im = im.resize((width, height), Image.ANTIALIAS)
if anti:
self.im = self.im.resize((width*xMult, height*yMult), Image.ANTIALIAS)
else:
self.im = self.im.resize((width*xMult, height*yMult))
#self.im.show()
def write(self, filePath, openMedia=1):
"Saves the Image; .png and .jpg both work; format based on extensions"
self.filePath = filePath
if self.transp != None:
self.im.save(self.filePath, transparency=self.transp)
else: #determine format from name
self.im.save(self.filePath)
#-----------------------------------------------------------------||||||||||||--
class WebText:
"""writes a text image with special styles and mods on a transparent bkg"""
def __init__(self, text, fg, style):
if style in ['miniLargeA', 'macro']:
self.image = GridText(text, fg, None, 'macro', 4, None, 1, 1)
#text = text.upper() # make all caps
#newText = []
#for char in text:
# newText.append('%s ' % char)
#fontInfo = ('helvetica', 'R', 8)
#Text.__init__(self, ''.join(newText), fontInfo, None, None, fg, 2, 1)
elif style in ['miniSmallA', 'micro']:
wrapW = 60 # chars
self.image = GridText(text, fg, None, 'micro', 2, None, 1, 1, 1, wrapW)
#fontInfo = ('helvetica', 'R', 8)
#Text.__init__(self, text, fontInfo, None, None, fg, 1, 1)
elif style in ['poster']:
wrapW = 60 # chars
self.image = GridText(text, fg, None, 'poster', 2, None,
1, 1, 1, wrapW)
elif style in ['strong']:
wrapW = 60 # chars
self.image = GridText(text, fg, None, 'strong', 2, None,
1, 1, 1, wrapW)
elif style in ['strongDouble']:
wrapW = 60 # chars
self.image = GridText(text, fg, None, 'strong', 2, None,
2, 2, 1, wrapW)
elif style in ['capitalSingle', 'capital']:
wrapW = 60 # chars
self.image = GridText(text, fg, None, 'capital', 2, None,
1, 1, 1, wrapW)
elif style in ['capitalDouble']:
wrapW = 60 # chars
self.image = GridText(text, fg, None, 'capital', 2, None,
2, 2, 1, wrapW)
elif style in ['capitalQuad']:
wrapW = 60 # chars
self.image = GridText(text, fg, None, 'capital', 2, None,
4, 4, 1, wrapW)
else:
raise KeyError, 'no such style name %r' % style
def write(self, filePath):
self.image.write(filePath)
#-----------------------------------------------------------------||||||||||||--
#-----------------------------------------------------------------||||||||||||--
# note: these objects use PIL and TK, and only use pil
class _CanvasBase:
"""abstract convas object to allow for drawing in both PIL and tk
all colors are in html style hex"""
def __init__(self, w, h, bg, fmt='png', name='image', transp=None):
self.w = w
self.h = h
self.bg = bg
self.fmt = fmt
self.name = name
self.transp = transp # transparancy only works w/ pil
def _gridCenter(self, point):
"""to find the center of a bitMap dimensions an off by 1 error
occurs in the standard case. a 1 shift must be applied"""
return int(round((point * .5))) - 1
def _widthRound(self, width):
"""vector can hadle floating point widths
here, round; if 0 or None, keep"""
if width == 0 or width == None:
return width
width = int(round(width)) # vector may have fractional widths
if width == 0: # .2, rounded down, for example
width = 1
return width
def gridText(self, x, y, anchor, msg, font='micro', fill='#ffffff',
kernBit=1, vertBit=None, xMult=1, yMult=1,
horizontal=1, charWrapWidth=None):
"""create bit map font; same method for Tk and PIL, as these use
the bitmap method
a postscript version of this actually uses fonts
anchor can be: nw, ne, nc; sw, se, sc
"""
kernEqual = 0 # treat as equal spaced?
vertBit = None # use default
obj = fontLibrary.FontBitMap(msg, font, kernEqual, kernBit, vertBit,
charWrapWidth)
gridData = obj.encode()
xLen = len(gridData[0]) # get first row
yLen = len(gridData)
if anchor == 'nw': # default for most applications: uppr left
xShift = 0
yShift = 0
elif anchor == 'ne': #
xShift = -xLen
yShift = 0
elif anchor == 'nc': #
xShift = -self._gridCenter(xLen)
yShift = 0
# vertical centering
elif anchor == 'cw': #
xShift = 0
yShift = -self._gridCenter(yLen)
elif anchor == 'ce': #
xShift = -xLen
yShift = -self._gridCenter(yLen)
elif anchor == 'cc': #
xShift = -self._gridCenter(xLen)
yShift = -self._gridCenter(yLen)
elif anchor == 'sw':
xShift = 0
yShift = -yLen + 1 # neg shift (reduce nw anchor by vert length)
elif anchor == 'se':
xShift = -xLen
yShift = -yLen + 1
elif anchor == 'sc':
xShift = -self._gridCenter(xLen)
yShift = -yLen + 1
# move bitmap points (they assume a 'nw' origin'
# with shift values calculated based on anchor points
self.bitmap(x+xShift, y+yShift, gridData, fill) # bitmap method in child
def rotate(self, degrees=90):
pass # only PIL
def write(self, filePath=None, openMedia=1):
pass # PIL, PostScript
def destroy(self):
pass # only TK
#-----------------------------------------------------------------||||||||||||--
class TkCanvas(_CanvasBase):
def __init__(self, w, h, bg, name, master):
if not TK:
raise ImportError, 'Tkinter not loaded'
_CanvasBase.__init__(self, w, h, bg, 'tk', name)
if master == None:
self.master = Tkinter.Tk()
else:
self.master = master
# not sure this is needed
#self.top = Tkinter.Toplevel() # create a top level?
self.frame = Tkinter.Frame(self.master, width=self.w, height=self.h, bd=0,
bg=self.bg, relief='flat')
self.frame.pack(fill='both', pady=0, padx=0)
# create base canvas
self.c = Tkinter.Canvas(self.frame, bg=self.bg, bd=0, width=self.w,
height=self.h, highlightthickness=0,
closeenough=1, confine=1)
self.c.pack(fill='both', pady=0, padx=0)
def rectangle(self, x1, y1, x2, y2, fill, outline, width=1):
width = self._widthRound(width) # vector may have fractional widths
# attempt to fix off by one error
# this creates rectangles that better match PIL model
self.c.create_rectangle(x1,y1,x2+1,y2+1, fill=fill, outline=outline,
width=width)
def line(self, x1, y1, x2, y2, fill, width=1):
width = self._widthRound(width) # vector may have fractional widths
self.c.create_line(x1, y1, x2, y2, fill=fill, width=width)
def fontText(self, x, y, anchor, text, font, fill):
"""anchors supirted: 'nw', 'sw', 'ne', 'se'
default is nw (upper left corner)
"""
self.c.create_text(x, y, text, font=font, fill=fill, anchor=anchor)
def oval(self, x1, y1, x2, y2, fill, outline, width=1):
width = self._widthRound(width) # vector may have fractional widths
self.c.create_oval(x1, y1, x2, y2, fill=fill, outline=outline,
width=width)
def bitmap(self, xShift, yShift, gridData, color=None):
"""if color==None, use data value in grid data as color value
note: drawing a pixel image is not uniform b/n tk implementations
on win nothing resuls from equal points: must add a single point"""
# get constant shift values
if os.name == 'mac': xBuf = 0
elif os.name == 'posix': xBuf = 0
else: xBuf = 1 # win tk requires extra pixel
x = 0
y = 0
for row in gridData:
for col in row:
if color != None:
if col == 0: # nothing here, and not a color value
colorUse = None # do not draw
else: # if 1, binary but mapp
colorUse = color
elif color == None: # color not given, use values
colorUse = col
if colorUse != None:
self.c.create_line((xShift+x, yShift+y, xShift+x+xBuf, yShift+y),
fill=colorUse, width=1, capstyle="round")
# capstyle may not be the same on differnt tks, must specify
x = x + 1
x = 0
y = y + 1
def show(self, dir=None, prefDict=None):
"""dir and prefDict do nothing here"""
# may need to pack again?
#frame.pack(expand=1, fill='both', pady=0, padx=0)
self.master.title(self.name)
self.master.iconname(self.name)
geometryStr = "%dx%d%+d%+d" % (self.w, self.h, 100, 100) # upLeft offset
self.master.geometry(geometryStr)
self.master.maxsize(width=self.w, height=self.h)
self.master.minsize(width=self.w, height=self.h)
self.master.resizable(width=0, height=0) # prohibit resizing
# not calling these allows the window to be detached
#self.master.mainloop()
# this should freeze the window
#self.master.wait_window(self.c)
def destroy(self):
# might want to use self.frame instead?
self.master.withdraw()
self.master.destroy()
del self.master
#-----------------------------------------------------------------||||||||||||--
class PilCanvas(_CanvasBase):
def __init__(self, w, h, bg, name, transp, subFmt):
_CanvasBase.__init__(self, w, h, bg, 'png', name, transp)
self.subFmt = subFmt # may be 'jpg' or 'png'
self.imageObj = Image.new("RGB", (self.w, self.h))
self.c = ImageDraw.Draw(self.imageObj)
# add background color if not None
if self.bg == None or transp != None: pass
else:
self.c.rectangle((0,0,self.w, self.h), self.bg, outline=None)
def rectangle(self, x1, y1, x2, y2, fill, outline, width=1):
width = self._widthRound(width) # vector may have fractional widths
self.c.rectangle((x1, y1, x2, y2), fill=fill, outline=outline)
if outline == None:
width = 0
if width > 1: # draw rectangles inside to create width on outline
for i in range(1, width): # one pixel is laready drawin
self.c.rectangle((x1+i, y1+i, x2-i, y2-i),
fill=None, outline=outline)
def line(self, x1, y1, x2, y2, fill, width=1):
"""lines in pil do not have a widht attribute, which forces
drawing multiple lines to simulate width"""
width = self._widthRound(width) # vector may have fractional widths
if width == 1:
self.c.line((x1, y1, x2, y2), fill=fill)
elif width > 1: # even is drawn + 1 (down)
#for i in range(1, width):
for i in range(1, width):
if x1 == x2: # its a vertical line
self.c.line((x1+i, y1, x2+i, y2), fill=fill)
elif y1 == y2: # its a vertical line
self.c.line((x1, y1+i, x2, y2+i), fill=fill)
else: # diagonal: draw a rectangle? this doesnt work well
if i % 2 == 0: # of even
self.c.line((x1+i, y1, x2+i, y2), fill=fill)
else: # odd
self.c.line((x1, y1+i, x2, y2+i), fill=fill)
def fontText(self, x, y, anchor, text, font, fill):
# default anchord position is 'ne' # position is upper right
fontObj = ImageFont.load_default() # gets default font
self.c.text((x, y), text, fill=fill, font=fontObj)
def oval(self, x1, y1, x2, y2, fill, outline, width=1):
width = self._widthRound(width) # vector may have fractional widths
self.c.ellipse((x1, y1, x2, y2), fill=fill, outline=outline)
if outline == None:
width = 0
if width > 1:
for i in range(1, width):
self.c.ellipse((x1+i, y1+i, x2-i, y2-i),
fill=None, outline=outline)
def bitmap(self, xShift, yShift, gridData, color=None):
"""if color==None, use data value in grid data as color value"""
x = 0
y = 0
for row in gridData:
for col in row:
if color != None:
if col == 0: # nothing here, and not a color value
colorUse = None # do not draw
else: # if 1, binary but mapp
colorUse = color
elif color == None: # color not given, use values
colorUse = col
if colorUse != None:
self.c.point((xShift+x, yShift+y), fill=colorUse)
x = x + 1
x = 0
y = y + 1
def rotate(self, angle=90):
self.imageObj = self.imageObj.rotate(angle)
def write(self, filePath=None, openMedia=1, prefDict=None):
"""transparency is stored at init"""
#del self.c # not sure if this is necessary
if filePath == None:
ext = '.%s' % self.subFmt
filePath = osTools.tempFile(ext) # jpg/png
if self.transp != None: # transparency not working
self.imageObj.save(filePath, transparency=self.transp)
else: #determine format from name
self.imageObj.save(filePath)
if openMedia:
osTools.openMedia(filePath, prefDict) #open for user
self.filePath = filePath
def show(self, dir=None, prefDict=None):
"""dir provides a directory to write a file w/ auto-gen name"""
if dir != None: # get path
ext = '.%s' % self.subFmt
filePath = os.path.join(dir, osTools.tempFileName(ext))
else:
ext = '.%s' % self.subFmt
filePath = osTools.tempFile(ext) # jpg/png
self.write(filePath, 1, prefDict)
#if self.subFmt == 'jpg': # gets defaul pil format, which is jpeg
# self.imageObj.show() # used to use show, but sometimes fails
#elif self.subFmt == 'png': # write a file
# self.write()
#-----------------------------------------------------------------||||||||||||--
class EpsCanvas(_CanvasBase):
"""write a postscript vector graphics file"""
def __init__(self, w, h, bg=None, name='untitled'):
# increase size of w/h by to handle ob1 error
_CanvasBase.__init__(self, w, h, bg, 'eps', name)
self.title = name
self.creator = 'athenaCL'
self.forStr = 'athenaCL'
self.date = time.asctime(time.localtime())
self.defIndex = 0
# canvas as a list of strings; append
self.c = []
self.c.append(self._headStr())
# add background color if not None
if self.bg == None: pass
else: # will append to c
self.rectangle(0,0,self.w, self.h, self.bg, None, 0)
def _headStr(self):
"""the head string for an eps/ps file is very sensitive
changing this can result in cropping or other strangness
do not define page size or anything else"""
msg = []
#msg.append('%!PS-Adobe-2.0\n')
msg.append('%!PS-Adobe-3.1 EPSF-3.0\n') # for eps
# % image or page area: lower left x y: upper right x y %
msg.append('%%BoundingBox: ')
# as LLx, LLy, URx, URy
msg.append(" %s %s %s %s " % (0, 0, self.w, self.h))
msg.append("""
%%Title: %s
%%Creator: %s
%%For: %s
%%CreationDate: %s
%%EndComments
/defaults save def
""" % (self.title, self.creator, self.forStr, self.date))
return ''.join(msg)
def _tailStr(self):
"""showpage actual prints ont pages
returns a string: does not append to c"""
msg = []
msg.append("""
%%EndPage
%%Trailer
grestore showpage defaults restore
%%EOF""")
return ''.join(msg)
#------------------------------------------------------------------------||--
# conversion utilities
def _convertCoordSquare(self, x1, y1, x2, y2):
"""convert xy pairs to four points necessary for drawing
assume xy pars are upper left, lower right
but all measurements from ll, ur
NOTE: there is an error in coordinates from lower left to upper right
the sometimes are reversed (rotated 180 degrees)
"""
#assert y1 <= self.h
#assert y2 <= self.h
if y1 >= y2:
yMin = y2
yMax = y1
else:
yMin = y1
yMax = y2
# this was recently added; may cause errors...
if x1 >= x2:
xMin = x2
xMax = x1
else:
xMin = x1
xMax = x2
ll = (xMin, self.h - yMax) # lower left
ul = (xMin, self.h - yMin) # upper left
lr = (xMax, self.h - yMax) # lower right
ur = (xMax, self.h - yMin) # upper right
# return points measured from bottom
return ll, ul, ur, lr
def _convertCoordLine(self, x1, y1, x2, y2):
# using square conversion causes errors when points are specified
# from lower left to upper right; need to just return the appropriate
# sides
ll, ul, ur, lr = self._convertCoordSquare(x1, y1, x2, y2)
if y1 > y2 and x1 < x2: # lower left to upper right
return ll, ur
elif y1 > y2 and x1 > x2: # lower right to upper left
return lr, ul
elif y1 <= y2 and x1 >= x2: # upper right to lower left
return ur, ll
else: # standard case: upper left to lower right
return ul, lr
def _convertCoordArc(self, x1, y1, x2, y2):
# find center point: add smaller size, plus half point
x = x1 + ((x2 - x1) * .5)
y = y1 + ((y2 - y1) * .5)
# readius is simply mid-point: assume this is a circle
radius = (x2 - x1) * .5 # assume its a circle
# all points are the same
ll, ul, ur, lr = self._convertCoordSquare(x, y, x, y)
return ll[0], ll[1], radius
def _getDefName(self):
# get object def name
strDef = 'obj%s' % self.defIndex
self.defIndex = self.defIndex + 1 # increment def index
return strDef
#------------------------------------------------------------------------||--
def rectangle(self, x1, y1, x2, y2, fill=None,
outline=None, width=1):
"""closepath: enclose shape, which can be filled
grey's vary from 0 to 1
set color w/ "setrgbcolor
gsave and grestone necessary to wrap all definitions from
global namespace
rectangles, to correspond to bit-map coordds, will add 1 to x and y
coords...
"""
# if x or y are equal, draw a line instead of a rectangle
# give the line a 1 point width
# if x1 == x2 and y1 == y2:
# self.line(x1, y1, x2, y2, fill, 1)
# return None
# elif x1 == x2 or y1 == y2:
# self.line(x1, y1, x2, y2, fill, 1)
# return None
ll, ul, ur, lr = self._convertCoordSquare(x1, y1, x2+1, y2+1)
# convert coords
if fill == None: strFill = ''
else:
r, g, b = HTMLColorToUnitRGB(fill)
strFill = '%s %s %s setrgbcolor fill' % (r, g, b)
if outline == None: strOutline = ''
else:
r, g, b = HTMLColorToUnitRGB(outline)
strOutline = '%s setlinewidth %s %s %s setrgbcolor stroke' % (width,
r, g, b)
strDefName = self._getDefName() # get a unique def identifier
msg = []
msg.append("""
/%s { newpath %s %s moveto %s %s lineto %s %s lineto %s %s lineto closepath gsave %s grestore gsave %s grestore} def """ % (strDefName,
ll[0], ll[1], ul[0], ul[1], ur[0], ur[1], lr[0], lr[1],
strFill, strOutline))
msg.append("""
gsave %s 1 1 scale grestore
""" % strDefName)
self.c.append(''.join(msg))
def line(self, x1, y1, x2, y2, fill, width=.5):
# there may be an ob1 error as in rectangle here; but cant fix
# as would make straight lines crooked...
pairL, pairR = self._convertCoordLine(x1, y1, x2, y2) # convert coords
if fill == None: fill = ''
else:
r, g, b = HTMLColorToUnitRGB(fill)
# linecap 2 sets projecting caps, extending to width
# this does not yet seem to work
strFill = '%s setlinewidth 2 setlinecap %s %s %s setrgbcolor stroke'%(
width, r, g, b)
strDefName = self._getDefName() # get a unique def identifier
msg = []
msg.append("""
/%s { newpath %s %s moveto %s %s lineto closepath gsave %s grestore} def
""" % (strDefName,
pairL[0], pairL[1], pairR[0], pairR[1],
strFill))
msg.append("""
gsave %s 1 1 scale grestore
""" % strDefName)
self.c.append(''.join(msg))
def _charFilter(self, usrStr):
"""replace characters in a usrStr w/ necssary escape chars"""
msg = []
for char in usrStr:
if char in ['(', ')',]: # not sure if back-slash should be here
msg.append('\%s' % char) # escape
else:
msg.append(char)
return ''.join(msg)
def fontText(self, x, y, anchor='nw', text='test', font=None,
fill="#342396"):
"""vector font text defines 6 acnhors:
nw, ne, nc; sw, se, sc"""
# fontInfo = ('helvetica', 'R', 12)
# for postscript, default anchor is 'sw'; need to translate
# thought these were standard ps fonts: these do not seem to work for all
# times-(roman/bold/italic/bolditalic)
# helvetica-(bold/oblique/boldoblique)
# courier-(bold/oblique/boldoblique)
# symbol
text = self._charFilter(text) # escape necessary characters
ll, ul, ur, lr = self._convertCoordSquare(x, y, x, y) # convert coords
if font == None:
fontFace = 'Times-Roman'
fontSize = '12'
else:
# note: hyphenated font names do not work w/ eps
# it may be that the style was not in the right case
#if font[1] != None:
# fontFace = '%s-%s' % (font[0], font[1])
#else:
fontFace = '%s' % font[0]
fontSize = str(font[2])
# find the fontHeight, not the same as fontSize
# this is found experimentally w/ courier
# want upper left corner of font
fontHeight = round((int(fontSize) * .55), 1)
# this adjustment should be added to vertical positon
# (shifted upward) to account for strange spacing, on
x = ll[0]
y = ll[1]
if fill == None:
strFill = ''
else:
r, g, b = HTMLColorToUnitRGB(fill)
strFill = '%s %s %s setrgbcolor' % (r, g, b)
if anchor == 'sw': # default is lower left
strMove = '%s %s moveto' % (x, y)
elif anchor == 'se': # pop removes y coord
strMove = '%s (%s) stringwidth pop sub %s moveto' % (
x, text, y)
elif anchor == 'sc': # pop removes y coord
strMove = '%s (%s) stringwidth pop 2 div sub %s moveto' % (
x, text, y)
elif anchor == 'cw':
strMove = '%s %s %s 2 div sub moveto' % (
x, y, fontHeight)
elif anchor == 'ce': # pop removes y coord
strMove = '%s (%s) stringwidth pop sub %s %s 2 div sub moveto' % (
x, text, y, fontHeight)
elif anchor == 'cc': # pop removes y coord
strMove = '%s (%s) stringwidth pop 2 div sub %s 2 div moveto' % (
x, text, fontHeight)
elif anchor == 'nw':
strMove = '%s %s %s sub moveto' % (x, y, fontHeight)
# first put x coord on stack, then measure, remove y, then subtract
elif anchor == 'ne': # pop removes y coord
strMove = '%s (%s) stringwidth pop sub %s %s sub moveto' % (
x, text, y, fontHeight)
elif anchor == 'nc':
strMove = '%s (%s) stringwidth pop 2 div sub %s %s sub moveto' % (
x, text, y, fontHeight)
msg = []
msg.append("""
/%s findfont
%s scalefont setfont
newpath
%s
%s
(%s) show
""" % (fontFace, fontSize, strMove, strFill, text))
self.c.append(''.join(msg))
def oval(self, x1, y1, x2, y2, fill, outline, width=1):
xCenter, yCenter, radius = self._convertCoordArc(x1, y1, x2, y2)
if fill == None: strFill = ''
else:
r, g, b = HTMLColorToUnitRGB(fill)
strFill = '%s %s %s setrgbcolor fill' % (r, g, b)
if outline == None: strOutline = ''
else:
r, g, b = HTMLColorToUnitRGB(outline)
strOutline = '%s setlinewidth %s %s %s setrgbcolor stroke' % (width,
r, g, b)
strDefName = self._getDefName() # get a unique def identifier
msg = []
msg.append("""
/%s { newpath %s %s %s %s %s arc closepath gsave %s grestore gsave %s grestore} def """ % (
strDefName,
xCenter, yCenter, radius, 0, 360,
strFill, strOutline))
msg.append("""
gsave %s 1 1 scale grestore
""" % strDefName)
self.c.append(''.join(msg))
#------------------------------------------------------------------------||--
def gridText(self, x, y, anchor='nw', msg='test', font='micro',
fill='#ffffff', kernBit=1,
vertBit=None, xMult=1, yMult=1, horizontal=1, charWrapWidth=None):
"""over-ride this method to provide font support wihtout using
bitmap; get anchor from normal gridText calls"""
# there will be a size that fits similar to a real gridText
if font == 'micro':
# keep case
fontSize = 7 * yMult
fontInfo = ('Courier', 'bold', fontSize)
elif font == 'macro': # make caps
msg = msg.upper()
fontSize = 14 * yMult
fontInfo = ('Courier', 'bold', fontSize)
# will append to self.c
self.fontText(x, y, anchor, msg, fontInfo, fill)
# alternative method; but does not work properly
# def bitmap(self, xShift, yShift, gridData, color=None):
# """if color==None, use data value in grid data as color value
# """
# pairL, pairR = self._convertCoordLine(xShift, yShift, xShift, yShift)
# pixSize = 1
# strDefName = self._getDefName() # get a unique def identifier
# msg = []
# msg.append("""
# /%s %s %s translate %s %s scale { 1 1 1 [1 0 0 1 0 0] {<c936>} image } def
# """ % (strDefName, pairL[0], pairL[1], pixSize, pixSize)
# )
# msg.append("""
# gsave %s 1 1 scale grestore
# """ % strDefName)
# self.c.append(''.join(msg))
def bitmap(self, xShift, yShift, gridData, color=None):
"""this method draws rectangle for every bit; this works but is
very slow"""
x = 0
y = 0
for row in gridData:
for col in row:
if color != None:
if col == 0: # nothing here, and not a color value
colorUse = None # do not draw
else: # if 1, binary but mapp
colorUse = color
elif color == None: # color not given, use values
colorUse = col
if colorUse != None:
self.rectangle(xShift+x, yShift+y, xShift+x, yShift+y,
colorUse, None, 0)
# capstyle may not be the same on differnt tks, must specify
x = x + 1
x = 0
y = y + 1
#------------------------------------------------------------------------||--
def write(self, filePath=None, openMedia=1, prefDict=None):
if filePath == None:
filePath = osTools.tempFile('.eps')
f = open(filePath, 'w')
f.writelines(''.join(self.c) + self._tailStr())
f.close
if openMedia:
osTools.openMedia(filePath, prefDict)
self.filePath = filePath
def show(self, dir=None, prefDict=None):
"""show action is to write file"""
if dir != None: # get path
ext = '.eps'
filePath = os.path.join(dir, osTools.tempFileName(ext))
else:
filePath = osTools.tempFile('.eps') # jpg/png
self.write(filePath, 1, prefDict)
#-----------------------------------------------------------------||||||||||||--
#-----------------------------------------------------------------||||||||||||--
# factory returns an object
def Canvas(fmt, w, h, bg='#000000', name='image', master=None, transp=None):
# this needs to be changed: jpg, png
if fmt in ['jpg','png']:
obj = PilCanvas(w, h, bg, name, transp, fmt)
elif fmt == 'tk':
obj = TkCanvas(w, h, bg, name, master)
elif fmt == 'eps':
obj = EpsCanvas(w, h, bg, name)
else:
raise ValueError, 'bad canvas format type: %s' % fmt
return obj
#-----------------------------------------------------------------||||||||||||--
#-----------------------------------------------------------------||||||||||||--
class ProcessGraphCoordData:
"""process data into a bit map data format
assumes that data is set of numerical coordinants
designed to process multiple streams of data, w/ same rez
finds all real-world values w/n an integer space"""
def __init__(self, dataLib=None, xRez=100, yRez=100,
dataSigDig=(2,2), axisSigDig=(0,0),):
"""dataLib is a dictionary of (x,y) coordinants
key is symbol to be used, value is a list of coords
max and min are calculated based on data
dataLib may contain four coords: (x,y,x,y) for rectangular segments
"""
self.xRez = xRez
self.yRez = yRez
self.dataSigDig = dataSigDig
self.axisSigDig = axisSigDig
if dataLib != None:
self.dataLib = dataLib
else: # generate a random dataLib
self.dataLib = {}
self.dataLib[1] = []
for x in range(1, 101): # fill w/ random values
#self.dataLib[1].append((x, random.random()))
self.dataLib[1].append((x, x, x+3, x))
# check if these are 4-point coords
if len(self.dataLib[1][0]) == 4:
self.dataMode = 'line' # two coords
else:
self.dataMode = 'point' # one coord
def _findRange(self):
"""find min, max x values
check the data for anything that we cannot process
like strings...
"""
self.xRangeLib = {}
self.yRangeLib = {}
for key in self.dataLib.keys():
xMax = self.dataLib[key][0][0] # seed w/ values from data
xMin = self.dataLib[key][0][0]
yMax = self.dataLib[key][0][1]
yMin = self.dataLib[key][0][1]
if self.dataMode == 'point':
allPointList = self.dataLib[key]
elif self.dataMode == 'line':
allPointList = []
for x, y, p, q in self.dataLib[key]:
allPointList.append((x,y)) # stack all pairs of coords
allPointList.append((p,q))
else:
raise ValueError
for x, y in allPointList:
if x >= xMax: xMax = x
if x <= xMin: xMin = x
if y >= yMax: yMax = y
if y <= yMin: yMin = y
# special case for constant values
if xMax == xMin: # if the same
xMax = xMax + 1
xMin = xMin - 1
if yMax == yMin: # if the same
yMax = yMax + 1
yMin = yMin - 1
self.xRangeLib[key] = (xMin, xMax)
self.yRangeLib[key] = (yMin, yMax)
def _genAxis(self, min, max, steps, side):
"""reverses y axis, so returns from largest to smallest
seems to be bug here, as max has to be manually added; is not
reached through increment addition
float axis values must have higher specificity than sigDig"""
axis = []
if side == 'x':
sigDig = self.axisSigDig[0]
elif side == 'y':
sigDig = self.axisSigDig[1]
if sigDig != 0:
if drawer.isInt(min): rMin = min
else: rMin = round(min, sigDig)
if drawer.isInt(max): rMax = max
else: rMax = round(max, sigDig)
else: # make into an int
if not drawer.isInt(min):
rMin = int(round(min, sigDig))
else: rMin = min
if not drawer.isInt(max):
rMax = int(round(max, sigDig))
else: rMax = max
incRaw = float(abs(rMax - rMin)) / (steps)
if sigDig != 0:
inc = round(incRaw, sigDig+4) # add extra resolution
else:
inc = int(round(incRaw, sigDig))
axis.append(rMin) # add first
i = copy.copy(rMin)
for x in range(1, steps-1): # first already added, last to be added
i = i + inc
axis.append(i)
axis.append(rMax) # add first
if side == 'y': # y should go from higher to lower
axis.reverse()
#print _MOD, min, max, rMin, rMax, axis, len(axis)
return axis
def _findAxis(self):
self.xAxisLib = {}
self.yAxisLib = {}
for key in self.dataLib.keys():
self.xAxisLib[key] = self._genAxis(self.xRangeLib[key][0],
self.xRangeLib[key][1], self.xRez, 'x')
self.yAxisLib[key] = self._genAxis(self.yRangeLib[key][0],
self.yRangeLib[key][1], self.yRez, 'y')
def _findIndexInAxis(self, axis, value, side):
"""given an axis, value, and side, determine which point
on the axis the value should be assigned to
axis are already rounded"""
# round value w/ data from sigDig local attribute
# for sig dig, x is 0, y is 1
if side == 'x':
value = round(value, self.dataSigDig[0])
elif side == 'y':
value = round(value, self.dataSigDig[1])
else:
raise ValueError
for i in range(0, len(axis)):
if side == 'x':
if value == axis[i]:
return i
elif i >= len(axis) - 1: # the last point of axis
return i
elif value > axis[i] and value < axis[i+1]:
lower = abs(value - axis[i])
upper = abs(value - axis[i+1])
if lower < upper: # closer to lower:
return i
elif upper < lower: # closer to upper
return i + 1
elif upper == lower: # equal?
return i # round to lower?
else: print _MOD, 'x error'
elif side == 'y': # greater to lesser values
if value == axis[i]:
return i
elif i >= len(axis) - 1: # the last point of axis
return i
elif value < axis[i] and value > axis[i+1]:
lower = abs(value - axis[i])
upper = abs(value - axis[i+1])
if lower < upper: # closer to lower:
return i
elif upper < lower: # closer to upper
return i + 1
elif upper == lower: # equal?
return i # round to lower?
else: print _MOD, 'y error'
def _findPlot(self):
"""create a bit map based on resolution
this used to store a double array of e entire but map
now this converts the floating point values into integer values
within the defined resultion
these values are stored in self.plotLib; provide additional
method to get values out in old format"""
self.plotLib = [] # used to be a dictionary
# each key represents a color/symbol/fill-pattern group
for key in self.dataLib.keys():
for data in self.dataLib[key]:
if self.dataMode == 'point':
x, y = data
# determine which axis position this point is closer to?
xPos = self._findIndexInAxis(self.xAxisLib[key], x, 'x')
yPos = self._findIndexInAxis(self.yAxisLib[key], y, 'y')
#self.plotLib[key][yPos][xPos] = key # key is symbol in this pos
self.plotLib.append((key, xPos, yPos))
#print _MOD, x, y, xSig, ySig, xPos, yPos
elif self.dataMode == 'line':
x1, y1, x2, y2 = data
x1Pos = self._findIndexInAxis(self.xAxisLib[key], x1, 'x')
y1Pos = self._findIndexInAxis(self.yAxisLib[key], y1, 'y')
x2Pos = self._findIndexInAxis(self.xAxisLib[key], x2, 'x')
y2Pos = self._findIndexInAxis(self.yAxisLib[key], y2, 'y')
#self.plotLib[key][yPos][xPos] = key # key is symbol in this pos
self.plotLib.append((key, x1Pos, y1Pos, x2Pos, y2Pos))
else:
raise ValueError
def getBitLib(self):
"""after calling _findPlt aboive, this method will convert self.plotLib
into an old format bit-map, a list of nested lists, w/ each value in
the list being the integer value of the necessary key/fromat/colo
indicator
this may be very inefficient; only use if abs necessary
"""
self.bitLib = {} # this is like the old plotLib format
# build empty double array
for key in self.dataLib.keys():
self.bitLib[key] = []
for y in range(self.yRez): # replace w/ array?
row = array.array('i')
for x in range(self.xRez):
row.append(0) # fill with zeros
self.bitLib[key].append(row)
# fill w/ key, non-zero values
for data in self.plotLib: # plotLib is a list now
if self.dataMode == 'point':
key, x, y = data
# y comes before x because of nested lists
self.bitLib[key][y][x] = key
elif self.dataMode == 'line': # this is likely not used for line graphs
# connect points b/n lines
key, x1, y1, x2, y2 = data
# just store first point
self.bitLib[key][y1][x1] = key
else:
raise ValueError
#print self.bitLib
return self.bitLib
def getPlotLib(self):
"""plotlib is a raw data point value list
leading values are id symbols (ints0
than any number of xy pairs"""
return self.plotLib
def keys(self):
return self.dataLib.keys()
def gridKey(self, key, side='x', lines=3):
"""returns a grid key for this axis
lines are number of lines, not number of spaces
NOT reversed for y axis"""
if side == 'y':
axis = self.yAxisLib[key]
else:
axis = self.xAxisLib[key]
gridKey = [0] * len(axis)
partitions = lines + 1
step = int(round((len(axis) / float(partitions)), 0))
pos = 0
for i in range(0, lines): # always one less
pos = pos + step
#print _MOD, pos, i, lines, len(gridKey)
if pos < len(gridKey):
gridKey[pos] = 1 # indicate grid line w/ 1
else:
pass
#print _MOD, 'grid (%s:%s) line out of range' % (side, lines)
if side == 'y':
gridKey.reverse() # must reverse y axis
return gridKey
def unitKey(self, key, side, gridKey, forceExtreme='min'):
"""uses grid key to get a unit key (label each grid)
fore range adds first and last values
axis values are not rounded; need to be for units"""
if side == 'x':
sigDig = self.axisSigDig[0]
elif side == 'y':
sigDig = self.axisSigDig[1]
if side == 'y':
axis = self.yAxisLib[key] # alrady reversed
else:
axis = self.xAxisLib[key]
unitKey = [None] * len(axis)
for i in range(0, len(axis)):
if gridKey[i] > 0: # if grid active
if sigDig != 0:
unitKey[i] = round(axis[i], sigDig)
else:
unitKey[i] = int(round(axis[i], sigDig))
if side == 'x':
if forceExtreme in ['min', 'range']:
if sigDig != 0:
unitKey[0] = round(axis[0], sigDig)
else:
unitKey[0] = int(round(axis[0], sigDig))
if forceExtreme in ['max','range']:
j = len(axis) - 1 # last value
if sigDig != 0:
unitKey[j] = round(axis[j], sigDig)
else:
unitKey[j] = int(round(axis[j], sigDig))
if side == 'y':
if forceExtreme in ['min', 'range']:
j = len(axis) - 1 # last value
if sigDig != 0:
unitKey[j] = round(axis[j], sigDig)
else:
unitKey[j] = int(round(axis[j], sigDig))
if forceExtreme in ['max','range']:
if sigDig != 0:
unitKey[0] = round(axis[0], sigDig)
else:
unitKey[0] = int(round(axis[0], sigDig))
return unitKey
def __call__(self):
self._findRange()
self._findAxis()
self._findPlot()
#self.display()
#-----------------------------------------------------------------||||||||||||--
class _GraphBase:
def __init__(self, xRez, yRez, plotLib, origin=(0,0)):
"""canvas is only provided through update method
only call update once after setting parameters
xRez and yRez are integer resolution sizes (w/h of a bit map)
plotLib is a list of points, each a list: key, x, y, x, y
"""
# self.bitData
self.plotLib = plotLib
self.xRez = xRez
self.yRez = yRez
self.xOrigin, self.yOrigin = origin
self.master = None
# cells must be an odd number (so that there is a center
self.wIcon = 9
self.hIcon = 9
# widht of horizontal and vertical margins
# applied to one side
self.wIconGutter = 18
self.hIconGutter = 18
self.lMargin = 40 # left
self.rMargin = 2 # right
self.tMargin = 2 # top
self.bMargin = 40 # bottom
#grid binary key for each angle: determines if grid line is drawn
# may want to add other symbols for variation
self.rowGridKey = [0,1,1]
self.colGridKey = [0,1,0,1]
#ticks extend into margin, separate color
# value determines length
# NB: problem w/ 1 pixel horizontal ticks
self.lTickKey = [1,5,2] # left
self.rTickKey = [2,1] # right
self.tTickKey = [2,1,2,0] # top
self.bTickKey = [3,1,1,4] # bottom
# give string for each necessary unit marker
# None translated to no unit
self.colUnitKey = ['a', 'b', 'c', None, 3, 4, 5]
self.rowUnitKey = ['a', 'b', 'c', None, 3, 4, 5]
self.unitGutter = 2 # pixel spacing from tick mark, or edge if none
# labels for x y axis
self.xLabel = 'events'
self.yLabel = 'db'
self.labelGutter = 6 # pixels spaciing from unit gutter +tick
# colors
self.colorBkg = '#000000'
self.colorGrid = '#000033'
self.colorTick = '#CC6600'
self.colorMargin = '#333333'
self.colorUnits = '#333399'
self.colorLabel = '#000000'
self.colorTitle = '#333333'
self.bitColorDict = {1 : '#336633',
2 : '#003366',
3 : '#003366', }
self.transp = None # transparancy default off, passed to canvas
# stores methods for drawing icons
self.bitDict = {}
self.font = 'micro'
self.kernEqual = 0
self.kernBit = 1
self.title = ''
self.titlePos = 'bl' # pick a corner, bottom left here
self.titleGutterVert = 2 # pix from vertical edges
self.titleGutterHorz = 2 # pix from horiz edges
# update values
# self.update(canvas)
def update(self, canvas='png'):
""" canvas can be an object or format string
update all calculated size values
regenerates Canvas, and this bkg color and self.tranp make a dif
must be done manually from out side object after loading all necessary
data formats
if canvas is == None, does not create a canvas, just calc values"""
# size of data
self.graphCol = self.xRez
self.graphRow = self.yRez
# width of graph = icon width, and margins plus 1
self.wGraph = ((self.wIcon * self.graphCol) +
(self.wIconGutter * (self.graphCol + 1)))
self.hGraph = ((self.hIcon * self.graphRow) +
(self.hIconGutter * (self.graphRow + 1)))
#print _MOD, self.wGraph, self.hGraph, self.graphCol, self.graphRow
self.wTotal = self.wGraph + (self.lMargin + self.rMargin)
self.hTotal = self.hGraph + (self.tMargin + self.bMargin)
# create canvas if given a string, othewise use existing canvas
if drawer.isStr(canvas): # its a format string
self.fmt = canvas
self.canvas = Canvas(self.fmt, self.wTotal, self.hTotal,
self.colorBkg, 'graph', self.master, self.transp)
elif canvas != None: # its canvas already
self.fmt = canvas.fmt
self.canvas = canvas
else: # used only to calculate size
self.canvas = None
# upper left corner of where graph starts
self.graphOrigin = (self.xOrigin+self.lMargin, self.yOrigin+self.tMargin)
if self.canvas != None:
self.canvas.rectangle(self.xOrigin, self.yOrigin,
self.xOrigin+self.wTotal-1, self.yOrigin+self.hTotal-1,
self.colorBkg, None)
# create dumy char to measure size
obj = fontLibrary.FontBitMap('a', self.font)
self.wFont = obj.wFont
self.hFont = obj.hFont
def destroy(self):
"""useful for tk windows only"""
self.canvas.destroy()
del self.canvas
#------------------------------------------------------------------------||--
def setIconSize(self, wIcon, hIcon, wIconGutter, hIconGutter):
# reinit w/ the format string, not w/ the canvas
self.wIcon = wIcon
self.hIcon = hIcon
self.wIconGutter = wIconGutter
self.hIconGutter = hIconGutter
def setMargin(self, lMargin, rMargin, tMargin, bMargin):
self.lMargin = lMargin
self.rMargin = rMargin
self.tMargin = tMargin
self.bMargin = bMargin
def setLabel(self, xLabel, yLabel, labelGutter):
self.xLabel = xLabel
self.yLabel = yLabel
self.labelGutter = labelGutter # must include any unit size
def setGrid(self, rowGridKey, colGridKey, rowUnitKey, colUnitKey):
self.rowGridKey = rowGridKey
self.colGridKey = colGridKey
self.rowUnitKey = rowUnitKey
self.colUnitKey = colUnitKey
def setTitle(self, title, titlePos='br',
titleGutterVert=2, titleGutterHorz=None):
self.title = title
self.titlePos = titlePos
self.titleGutterVert = titleGutterVert
if titleGutterHorz != None:
self.titleGutterHorz = titleGutterHorz
else: # make same as vert
self.titleGutterHorz = titleGutterVert
# no update necessary
def setColors(self, bkg, grid, tick, margin, units, label, title,
bitColorDict):
self.colorBkg = bkg
self.colorGrid = grid
self.colorTick = tick
self.colorMargin = margin
self.colorUnits = units
self.colorLabel = label
self.colorTitle = title
self.bitColorDict = bitColorDict
# no update necessary
#------------------------------------------------------------------------||--
def _iconA(self, args):
# x and y do not include margin; are icon's upper left and right corner
# if x1 == x2, this is a width of 1 (in a bit map relm)
x, y = args
if self.hIcon == 1: # draw a line
self.canvas.rectangle(x, y, x+self.wIcon-1, y,
self.bitColorDict[1], None, 0)
#self.canvas.line(x, y, x+self.wIcon, y, color, None, 1)
else:
self.canvas.rectangle(x, y, x+self.wIcon-1, y+self.hIcon-1,
self.bitColorDict[1], None, 0)
def _iconB(self, args):
x, y = args
self.canvas.rectangle(x, y, x+self.wIcon-1, y+self.hIcon-1,
self.bitColorDict[2], None, 1)
def _iconC(self, args):
x, y = args
self.canvas.rectangle(x, y, x+self.wIcon-1, y+self.hIcon-1,
self.bitColorDict[3], None, 0)
def _updateBitDict(self):
self.bitDict = {1 : '_iconA',
2 : '_iconB',
3 : '_iconC',
} # map integers to icon methods
#------------------------------------------------------------------------||--
def _drawMargin(self):
# draw top
if self.tMargin != 0:
self.canvas.rectangle(self.xOrigin, self.yOrigin,
self.wTotal+self.xOrigin-1, self.yOrigin+self.tMargin-1,
self.colorMargin, None, 0)
# draw left
if self.lMargin != 0:
self.canvas.rectangle(self.xOrigin, self.yOrigin,
self.xOrigin+self.lMargin-1, self.yOrigin+self.hTotal-1,
self.colorMargin, None, 0)
# draw right
if self.rMargin != 0:
self.canvas.rectangle((self.xOrigin+self.lMargin+self.wGraph),
self.yOrigin,
self.wTotal+self.xOrigin-1, self.yOrigin+self.hTotal-1,
self.colorMargin, None, 0)
# draw bottom
if self.bMargin != 0:
self.canvas.rectangle(self.xOrigin,
(self.yOrigin+self.tMargin+self.hGraph),
self.wTotal+self.xOrigin-1, self.hTotal+self.yOrigin-1,
self.colorMargin, None, 0)
#------------------------------------------------------------------------||--
def _yGraphPosList(self):
"""gets a list of all necessary graph positions
these are absoult points based on origin values
gets real x/y points as integers?"""
points = []
x = copy.copy(self.graphOrigin[0])
y = copy.copy(self.graphOrigin[1])
i = 0
for row in range(self.yRez):
y = y + self.hIconGutter # upper left of icon
yPos = y + (self.hIcon/2) # center of icon
points.append((x, y, yPos, i)) # add data
# incr data
y = y + self.hIcon
i = i + 1
return points
def _xGraphPosList(self):
"""gets a list of all necessary graph positions
values are in pixels"""
points = []
x = copy.copy(self.graphOrigin[0])
y = copy.copy(self.graphOrigin[1])
j = 0
for col in range(self.xRez): # read col events from first row of data
x = x + self.wIconGutter
xPos = x + (self.wIcon/2)
points.append((x, y, xPos, j)) # add data
# incr data
x = x + self.wIcon
j = j + 1 # j is an index of values
#print _MOD, 'xGraphPosList', points
return points
#------------------------------------------------------------------------||--
def _drawGrid(self):
"""to deal with error, add to length of horizontal grid"""
if self.rowGridKey != []:
for x, y, yPos, i in self._yGraphPosList():
if self.rowGridKey[i % len(self.rowGridKey)] != 0:
# x was reduced by one to account for some error
# this introduced another off by one error in the vector version
# not backgrounded to try to remove problems
self.canvas.line(x, yPos, x+self.wGraph, yPos, self.colorGrid)
#self.canvas.line(x-1, yPos, x+self.wGraph,
# yPos, self.colorGrid)
if self.colGridKey != []:
for x, y, xPos, j in self._xGraphPosList():
if self.colGridKey[j % len(self.colGridKey)] != 0:
# pre vector version
self.canvas.line(xPos, y, xPos, y+self.hGraph, self.colorGrid)
#self.canvas.line(xPos, y-1, xPos, y+self.hGraph, self.colorGrid)
def _drawTick(self):
for x, y, yPos, i in self._yGraphPosList():
# do left
if self.lTickKey != []: # not empty
pix = self.lTickKey[i % len(self.lTickKey)]
if pix != 0:
self.canvas.line(x-pix, yPos,
x-1, yPos, self.colorTick)
# do right
if self.rTickKey != []: # not empty
pix = self.rTickKey[i % len(self.rTickKey)]
if pix != 0:
self.canvas.line(x+self.wGraph, yPos,
x+self.wGraph+pix-1, yPos, self.colorTick)
for x, y, xPos, j in self._xGraphPosList():
# do bottom
if self.bTickKey != []: # not empty
pix = self.bTickKey[j % len(self.bTickKey)]
if pix != 0:
self.canvas.line(xPos, y + self.hGraph,
xPos, y + self.hGraph + pix, self.colorTick)
# do top
if self.tTickKey != []: # not empty
pix = self.tTickKey[j % len(self.tTickKey)]
if pix != 0:
self.canvas.line(xPos, y-pix,
xPos, y, self.colorTick)
def _getUnitGutter(self, side):
if side in ['col', 'b', 'x']:
tick = self.bTickKey
elif side in ['row', 'l', 'y']:
tick = self.lTickKey
max = 0
for pix in tick:
if pix >= max:
max = pix
return max + self.unitGutter
def _drawUnits(self):
"""draw units on grid line spacings
"""
rowGutter = self._getUnitGutter('row')
colGutter = self._getUnitGutter('col')
#self.yUnitMaxShift = 0
self.yUnitOuterIndex = self.yRez #len(self.bitData)
if self.rowUnitKey != []:
for x, y, yPos, i in self._yGraphPosList():
char = self.rowUnitKey[i % len(self.rowUnitKey)]
if char != None: # should be 'ce'
self.canvas.gridText(x-rowGutter, yPos, 'ce', str(char),
self.font, self.colorUnits, self.kernBit)
if i <= self.yUnitOuterIndex:
self.yUnitOuterIndex = i
#self.xUnitMaxShift = 0
self.xUnitOuterIndex = 0
if self.colUnitKey != []:
for x, y, xPos, j in self._xGraphPosList():
char = self.colUnitKey[j % len(self.colUnitKey)]
if char != None:
self.canvas.gridText(xPos, y+self.hGraph+colGutter, 'nc',
str(char), self.font, self.colorUnits, self.kernBit)
if j >= self.xUnitOuterIndex:
self.xUnitOuterIndex = j
def _getLabelGutter(self, side):
if side == 'col' or side == 'b' or side == 'x':
tick = self.bTickKey
#fontShift = self.hFont # need only char height here
elif side == 'row' or side == 'l' or side == 'y':
tick = self.lTickKey
#fontShift = self.yUnitMaxShift # need max word length, not char
max = 0 # get tick size
for pix in tick:
if pix >= max:
max = pix
return max + self.unitGutter + self.labelGutter #+ fontShift
def _drawLabels(self):
# these values may no be incorrect; double check
rowGutter = self._getLabelGutter('row')
colGutter = self._getLabelGutter('col')
if self.yLabel != '':
for x, y, yPos, i in self._yGraphPosList():
if i == self.yUnitOuterIndex: # 0 gets first first
# used to be center of y; now use north anchor
self.canvas.gridText(x-rowGutter, yPos, 'ce', self.yLabel,
self.font, self.colorLabel, self.kernBit)
break
if self.xLabel != '':
for x, y, xPos, j in self._xGraphPosList():
if j == self.xUnitOuterIndex: #len(self.bitData[0]) - 1: #
self.canvas.gridText(xPos, y+self.hGraph+colGutter,
'ne', self.xLabel, self.font, self.colorLabel, self.kernBit)
break
def _drawTitle(self):
if self.title == '': return None # bail
x = copy.copy(self.xOrigin)
y = copy.copy(self.yOrigin)
xPos = x
yPos = y
# can be 'bl' or 'rt' or the like
if 'b' in self.titlePos:
#yPos = y + self.hTotal - hText - self.titleGutterVert
yPos = (y + self.hTotal) - self.titleGutterVert
anchorVertical = 's'
if 'l' in self.titlePos:
xPos = x + self.titleGutterHorz
anchorHorizontal = 'w'
if 'r' in self.titlePos:
#xPos = x + self.wTotal - (wText + self.titleGutterHorz)
xPos = x + self.wTotal - self.titleGutterHorz
anchorHorizontal = 'e'
if 't' in self.titlePos:
#yPos = y + hText + self.titleGutterVert
yPos = y + self.titleGutterVert
anchorVertical = 'n'
anchor = anchorVertical + anchorHorizontal # combine anchor notation
self.canvas.gridText(xPos, yPos, anchor, self.title,
self.font, self.colorTitle, self.kernBit)
#self.canvas.bitmap(xPos, yPos, gridData, self.colorTitle)
#------------------------------------------------------------------------||--
def _xGridToPixel(self, xRaw):
"""returns upper left corner for an integer grid bit map
translated into pixel positions"""
# always a margin (at 0) so add 1
# no width at zero, so leave as is
x = (self.graphOrigin[0] + ((xRaw + 1) * self.wIconGutter) +
(xRaw * self.wIcon))
return x
def _yGridToPixel(self, yRaw):
"""returns upper left corner for an integer grid bit map
translated into pixel positions"""
# always a margin (at 0) so add 1
# no width at zero, so leave as is
y = (self.graphOrigin[1] + ((yRaw + 1) * self.hIconGutter) +
(yRaw * self.hIcon))
return y
# old version did things in order
# def _drawIcon(self):
# """draw each icon
# point based to icon method is the upper left point"""
# x = copy.copy(self.graphOrigin[0])
# y = copy.copy(self.graphOrigin[1])
# for row in self.bitData:
# y = y + self.hIconGutter
# for col in row:
# x = x + self.wIconGutter
# if col != 0:
# meth = getattr(self, self.bitDict[col])
# meth((x, y))
# x = x + self.wIcon
# # reset x
# x = copy.copy(self.graphOrigin[0])
# # add horizontal
# y = y + self.hIcon
def _drawIcon(self):
"""alternative method: just look at data points, calc shift and pos"""
for data in self.plotLib:
if len(data) == 3:
# three element data points are used for point graphs
key, xRaw, yRaw = data # these are raw data points, starting from 0
x = self._xGridToPixel(xRaw)
y = self._yGridToPixel(yRaw)
args = (x,y)
elif len(data) == 5:
# 5 element args used for bar graphs
key, x1Raw, y1Raw, x2Raw, y2Raw = data
x1 = self._xGridToPixel(x1Raw)
y1 = self._yGridToPixel(y1Raw)
x2 = self._xGridToPixel(x2Raw)
y2 = self._yGridToPixel(y2Raw)
# use raw integer x values to get widht, tn convert to pixel
widthStep = x2Raw-x1Raw # number of 'cells' to span
width = ((widthStep * self.wIcon) +
((widthStep+1) * self.wIconGutter)) # one more gutter
args = (x1, y1, x1+width, y1)
meth = getattr(self, self.bitDict[key])
meth(args)
#------------------------------------------------------------------------||--
def draw(self):
self._updateBitDict()
self._drawGrid() # covers horizontal error introduced for vector version
self._drawMargin()
self._drawTick()
self._drawUnits()
self._drawIcon()
self._drawLabels()
self._drawTitle()
def show(self, dir=None, prefDict=None):
"""some canvases use show to write files; a dir can be provided
to use w/ automatically generated files names"""
self.canvas.show(dir, prefDict)
def write(self, filePath=None):
"""Saves the Image; .png and .jpg both work; format based on extensions
None automatically gets a temp file name"""
self.canvas.write(filePath)
self.filePath = self.canvas.filePath # get from lower canvas
#-----------------------------------------------------------------||||||||||||--
class NumericalPointGraph(_GraphBase):
"""this graph is designed for points on a lone
it assumes a fixed icon size
this graph is used for TImap and TPmap displays"""
def __init__(self, dataObj=None, origin=(0,0)):
groupKey = dataObj.keys()[0] # take first key, will be 1
# graph base wants a bit-map of enter data to be written
_GraphBase.__init__(self, dataObj.xRez, dataObj.yRez,
dataObj.getPlotLib(), origin)
# set preferences
# cells must be an odd number (so that there is a center
self.wIcon = 3
self.hIcon = 3
# widht of horizontal and vertical margins
# applied to one sides
self.wIconGutter = 0
self.hIconGutter = 0
self.lMargin = 60 # left
self.rMargin = 1 # right
self.tMargin = 1 # top
self.bMargin = 60 # bottom
#grid binary key for each angle: determines if grid line is drawn
# may want to add other symbols for variation
self.rowGridKey = dataObj.gridKey(groupKey, 'y', 8)
self.colGridKey = dataObj.gridKey(groupKey, 'x', 8)
#ticks extend into margin, separate color
# value determines length
# NB: problem w/ 1 pixel horizontal ticks
self.lTickKey = [] # left
self.rTickKey = [] # right
self.tTickKey = [] # top
self.bTickKey = [] # bottom
# give string for each necessary unit marker
# None translated to no unit
self.rowUnitKey = dataObj.unitKey(groupKey, 'y', self.rowGridKey)
self.colUnitKey = dataObj.unitKey(groupKey, 'x', self.colGridKey)
self.unitGutter = 2 # pixel spacing from tick mark, or edge if none
# labels for x y axis
self.xLabel = 'x'
self.yLabel = 'y'
self.labelGutter = 4 # pixels spaciing from unit+tick
# colors
self.colorBkg = '#000000'
self.colorGrid = '#333333'
self.colorTick = "#CC6600"
self.colorMargin = '#333333'
self.colorUnits = '#333399'
self.colorLabel = '#000000'
self.bitColorDict = {1 : '#aaaaaa',}
# update calculated values
def _iconA(self, args):
x, y = args
# x and y do not include margin; are icon's upper left and right corner
self.canvas.rectangle(x, y, x+self.wIcon-1, y+self.hIcon-1,
self.bitColorDict[1], None, 0)
def _iconB(self, args):
x, y = args
# x and y do not include margin; are icon's upper left and right corner
self.canvas.rectangle(x, y, x+self.wIcon-1, y+self.hIcon-1,
self.bitColorDict[2], None, 0)
def _updateBitDict(self):
self.bitDict = {1 : '_iconA',
2 : '_iconB',
} # map integers to icon methods
#-----------------------------------------------------------------||||||||||||--
class NumericalBarGraph(_GraphBase):
"""this graph is designed for points on a lone
it assumes a fixed icon size
this graph is used for TImap and TPmap displays
dataObj is a raw dictionary of values organized by symbol (all are 1 for
now; may be real-value x,y pairs or x,y,x,y quads"""
def __init__(self, dataObj=None, origin=(0,0)):
groupKey = dataObj.keys()[0] # take first key, will be 1
# will assign self.plotLib
_GraphBase.__init__(self, dataObj.xRez, dataObj.yRez,
dataObj.getPlotLib(), origin)
# set preferences
# cells must be an odd number (so that there is a center
self.wIcon = 3 # used for relative grid size
self.hIcon = 3
# widht of horizontal and vertical margins
# applied to one sides
self.wIconGutter = 0
self.hIconGutter = 0
self.lMargin = 60 # left
self.rMargin = 1 # right
self.tMargin = 1 # top
self.bMargin = 60 # bottom
#grid binary key for each angle: determines if grid line is drawn
# may want to add other symbols for variation
self.rowGridKey = dataObj.gridKey(groupKey, 'y', 8)
self.colGridKey = dataObj.gridKey(groupKey, 'x', 8)
#ticks extend into margin, separate color
# value determines length
# NB: problem w/ 1 pixel horizontal ticks
self.lTickKey = [] # left
self.rTickKey = [] # right
self.tTickKey = [] # top
self.bTickKey = [] # bottom
# give string for each necessary unit marker
# None translated to no unit
self.rowUnitKey = dataObj.unitKey(groupKey, 'y', self.rowGridKey)
self.colUnitKey = dataObj.unitKey(groupKey, 'x', self.colGridKey)
self.unitGutter = 2 # pixel spacing from tick mark, or edge if none
# labels for x y axis
self.xLabel = 'x'
self.yLabel = 'y'
self.labelGutter = 4 # pixels spaciing from unit+tick
# colors
self.colorBkg = '#000000'
self.colorGrid = '#333333'
self.colorTick = "#CC6600"
self.colorMargin = '#333333'
self.colorUnits = '#333399'
self.colorLabel = '#000000'
self.bitColorDict = {1 : '#aaaaaa',
}
# update calculated values
def _iconA(self, args):
x1, y1, x2, y2 = args
# x and y do not include margin; are icon's upper left and right
# corner
# x2+self.wIcon-1
self.canvas.rectangle(x1, y1, x2-1, y2+self.hIcon-1,
self.bitColorDict[1], None, 0)
def _iconB(self, args):
x1, y1, x2, y2 = args
self.canvas.rectangle(x1, y1, x2-1, y2+self.hIcon-1,
self.bitColorDict[2], None, 0)
def _updateBitDict(self):
self.bitDict = {1 : '_iconA',
2 : '_iconB',
} # map integers to icon methods
#-----------------------------------------------------------------||||||||||||--
class TextGraph(_GraphBase):
"""create text in a graph
same interface as GridText object"""
def __init__(self, msg=None, fg='#666633', style='dotMediumA',
backColor=None,
font='micro', kernBit=1, vertBit=None, charWrapWidth=None):
if msg == None:
msg = 'algorithmic.net'
# process data
obj = fontLibrary.FontBitMap(msg, font, 0, kernBit,
vertBit, charWrapWidth)
gridData = obj.encode()
gridData = self._addNoise(gridData)
xRez, yRez, plotLib = self._gridToPlotLib(gridData)
# call base class
canvas ='png'
_GraphBase.__init__(self, xRez, yRez, plotLib)
# set preferences
# cells must be an odd number (so that there is a center
self.wIcon = 2
self.hIcon = 1
# widht of horizontal and vertical margins
# applied to one sides
self.wIconGutter = 1
self.hIconGutter = 1
#grid binary key for each angle: determines if grid line is drawn
# may want to add other symbols for variation
self.rowGridKey = []
self.colGridKey = []
# colors
self.colorBkg = '#000000'
self.colorGrid = '#333333'
self.colorTick = "#CC6600"
self.colorMargin = '#333333'
self.colorUnits = '#333399'
self.colorLabel = '#000000'
self.bitColorDict = {1 : fg,
2 : fg,
}
# other image settings
self.transp = 1
# not used so much in font
self.lTickKey = [] # left
self.rTickKey = [] # right
self.tTickKey = [] # top
self.bTickKey = [] # bottom
self.lMargin = 0 # left
self.rMargin = 0 # right
self.tMargin = 0 # top
self.bMargin = 0 # bottom
# give string for each necessary unit marker
# None translated to no unit
self.rowUnitKey = []
self.colUnitKey = []
self.unitGutter = 2 # pixel spacing from tick mark, or edge if none
# labels for x y axis
self.xLabel = ''
self.yLabel = ''
self.labelGutter = 4 # pixels spaciing from unit+tick
# update calculated values
self.update(canvas)
def _gridToPlotLib(self, gridData):
"""translate a full bit-map grid into a plotLib"""
plotLib = []
for row in range(len(gridData)):
for col in range(len(gridData[0])):
key = gridData[row][col]
if key == 0: pass
plotLib.append((key, col, row))
yRez = len(gridData)
xRez = len(gridData[0])
return xRez, yRez, plotLib
def _addNoise(self, gridData, distribution=[1,1,1,1,1,2]):
for row in range(len(gridData)):
for col in range(len(gridData[0])):
if gridData[row][col] == 0:
pass
else:
gridData[row][col] = random.choice(distribution)
return gridData
def _iconA(self, args):
x, y = args
# x and y do not include margin; are icon's upper left and right
# corner
self.canvas.rectangle(x, y, x+self.wIcon-1, y+self.hIcon-1,
self.bitColorDict[1], None, 1)
def _iconB(self, args):
x, y = args
self.canvas.rectangle(x, y, x+self.wIcon-1, y+self.hIcon-1,
self.bitColorDict[2], None, 1)
#-----------------------------------------------------------------||||||||||||--
#-----------------------------------------------------------------||||||||||||--
class Test:
def __init__(self):
self.testGraph()
self.testGraphText()
self.testCanvas()
self.testProcGraphCoord()
def testBarCode(self):
bg = '#000000'
fg = ['#666633','#333366', '#336633']
a = BarCode(None, 50, fg, bg, 1, 50, 1)
a.show()
def testText(self):
bg = '#000000'
fg = ['#666633','#333366', '#336633']
#a = TextWebTitle('testingTESTNGIN', '#333366', 'algoNetLarge')
#a = TextWebTitle('testingTESTNGIN', '#333366', 'algoNetSmall')
a = GridText('testing a long sample of text!', fg, bg, 'micro', 6, None,
2, 2, 1, 10)
a.show()
def testCanvas(self):
fmtList = ['tk', 'png', 'eps']
import time
for fmt in fmtList:
print _MOD, 'testing format', fmt
a = Canvas(fmt, 200, 200)
a.rectangle(10, 10, 120, 120, '#333333', '#ffffff', 5)
a.rectangle(150, 150, 180, 180, None, '#ffffff', 2)
a.line(10, 130, 200, 130, '#663399', 3)
a.line(10, 180, 10, 200, '#663399', 1)
a.oval(50, 50, 150, 150, '#999933', '#cccccc', 3)
a.oval(75, 75, 125, 125, '#99ff33', None, 0)
a.oval(90, 90, 110, 110, None, '#ff3333', 1)
a.line(0, 100, 200, 100, '#999999', .25)
a.line(100, 0, 100, 200, '#999999', .25)
# problem case
a.line(0, 200, 200, 0, '#999999', .25)
a.gridText(0, 0, 'nw', 'testing some large text', 'micro',
'#33ff99', 2)
a.gridText(20, 180, 'nw', 'a big title', 'macro', '#cccc33', 2)
a.gridText(100, 100, 'ne', 'northEast', 'micro', '#000000')
a.gridText(100, 100, 'nw', 'northWest', 'micro', '#333333')
a.gridText(100, 25, 'nc', 'northCenter', 'micro', '#cccccc')
a.gridText(25, 100, 'ce', 'centerEast', 'micro', '#cccccc')
a.gridText(100, 100, 'se', 'southEast', 'micro', '#666666')
a.gridText(100, 100, 'sw', 'southWest', 'micro', '#999999')
a.gridText(100, 175, 'sc', 'southCenter', 'micro', '#aaaaaa')
a.gridText(175, 100, 'cw', 'centerWest', 'micro', '#aaaaaa')
a.show()
del a
time.sleep(1) # wait a second
# out of date
# def testGraph(self):
# fmtList = ['tk', 'png', 'eps']
# import time
# for fmt in fmtList:
# print _MOD, 'testing format', fmt
# canvas = Canvas(fmt, 400, 600, '#666666')
# a = _GraphBase(None, (30,30))
# a.update(canvas)
# a.draw()
#
# b = _GraphBase(None, (30,30+a.hTotal+2))
# b.setTitle('testTitle')
# b.update(canvas)
# b.draw()
# b.show()
def testGraphText(self):
#d = TextGraph()
#d.draw()
#d.show()
obj = TextGraph('test', '#333366', 'dotMediumA')
obj.draw()
path = '/Volumes/xdisc/_scratch/a.gif'
obj.write(path)
os.system('open %s' % path)
def testProcGraphCoord(self):
a = ProcessGraphCoordData()
a()
if __name__ == '__main__':
Test()
|