00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 from Tkinter import *
00013 from Canvas import Line, CanvasText, Rectangle
00014 import string, math
00015 from utils import *
00016 import copy, re
00017
00018 class GraphPoints:
00019 def __init__(self, points, attr):
00020 self.points = points
00021 self.scaled = self.points
00022 self.attributes = {}
00023 for name, value in self._attributes.items():
00024 try:
00025 value = attr[name]
00026 except KeyError: pass
00027 self.attributes[name] = value
00028
00029 def boundingBox(self):
00030 '''
00031 determine min and max value of list
00032 '''
00033 return minBound(self.points), maxBound(self.points)
00034
00035 def fitToScale(self, scale=(1,1), shift=(0,0),lower=0.0):
00036 self.scaled = []
00037 for x,y in self.points:
00038 self.scaled.append(((scale[0]*x)+shift[0],\
00039 (scale[1]*y)+shift[1]))
00040 self.attributes.get('anchor', 0.0)
00041 self.anchor = scale[1]*self.attributes.get('anchor', lower)+\
00042 shift[1]
00043
00044 class GraphLine(GraphPoints):
00045 def __init__(self, points, **attr):
00046 GraphPoints.__init__(self, points, attr)
00047
00048 _attributes = {'color': 'black',
00049 'width': 1,
00050 'smooth': 0,
00051 'splinesteps': 12}
00052
00053 def draw(self, canvas):
00054 color = self.attributes['color']
00055 width = self.attributes['width']
00056 smooth = self.attributes['smooth']
00057 steps = self.attributes['splinesteps']
00058
00059 arguments = (canvas,)
00060 if smooth:
00061 for i in range(len(self.points)):
00062 x1, y1 = self.scaled[i]
00063 arguments = arguments + (x1, y1)
00064 else:
00065 for i in range(len(self.points)-1):
00066 x1, y1 = self.scaled[i]
00067 x2, y2 = self.scaled[i+1]
00068 arguments = arguments + (x1, y1, x2, y2)
00069
00070 apply(Line, arguments, {'fill': color, 'width': width,
00071 'smooth': smooth, 'splinesteps':steps})
00072
00073 class GraphSymbols(GraphPoints):
00074 def __init__(self, points, **attr):
00075 GraphPoints.__init__(self, points, attr)
00076
00077 _attributes = {'color': 'black',
00078 'width': 1,
00079 'fillcolor': 'black',
00080 'size': 2,
00081 'fillstyle': '',
00082 'outline': 'black',
00083 'marker': 'circle'}
00084
00085 def draw(self, canvas):
00086 color = self.attributes['color']
00087 size = self.attributes['size']
00088 fillcolor = self.attributes['fillcolor']
00089 marker = self.attributes['marker']
00090 fillstyle = self.attributes['fillstyle']
00091
00092 self._drawmarkers(canvas, self.scaled, marker, color,
00093 fillstyle, fillcolor, size)
00094
00095 def _drawmarkers(self, c, coords, marker='circle', color='black',
00096 fillstyle='', fillcolor='', size=2):
00097 l = []
00098 f = eval('self._' +marker)
00099 for xc, yc in coords:
00100 id = f(c, xc, yc, outline=color, size=size,
00101 fill=fillcolor, fillstyle=fillstyle)
00102 if type(id) is type(()):
00103 for item in id: l.append(item)
00104 else:
00105 l.append(id)
00106 return l
00107
00108 def _circle(self, c, xc, yc, size=1, fill='', outline='black',
00109 fillstyle=''):
00110 id = c.create_oval(xc-0.5, yc-0.5, xc+0.5, yc+0.5,
00111 fill=fill, outline=outline,
00112 stipple=fillstyle)
00113 c.scale(id, xc, yc, size*5, size*5)
00114 return id
00115
00116 def _dot(self, c, xc, yc, size=1, fill='', outline='black',
00117 fillstyle=''):
00118 id = c.create_oval(xc-0.5, yc-0.5, xc+0.5, yc+0.5,
00119 fill=fill, outline=outline,
00120 stipple=fillstyle)
00121 c.scale(id, xc, yc, size*2.5, size*2.5)
00122 return id
00123
00124 def _square(self, c, xc, yc, size=1, fill='', outline='black',
00125 fillstyle=''):
00126 id = c.create_rectangle(xc-0.5, yc-0.5, xc+0.5, yc+0.5,
00127 fill=fill, outline=outline,
00128 stipple=fillstyle)
00129 c.scale(id, xc, yc, size*5, size*5)
00130 return id
00131
00132 def _triangle(self, c, xc, yc, size=1, fill='', outline='black',
00133 fillstyle=''):
00134 id = c.create_polygon(-0.5, 0.288675134595,
00135 0.5, 0.288675134595,
00136 0.0, -0.577350269189, fill=fill,
00137 outline=outline, stipple=fillstyle)
00138 c.move(id, xc, yc)
00139 c.scale(id, xc, yc, size*5, size*5)
00140 return id
00141
00142 def _triangle_down(self, c, xc, yc, size=1, fill='',
00143 outline='black', fillstyle=''):
00144 id = c.create_polygon(-0.5, -0.288675134595,
00145 0.5, -0.288675134595,
00146 0.0, 0.577350269189, fill=fill,
00147 outline=outline, stipple=fillstyle)
00148 c.move(id, xc, yc)
00149 c.scale(id, xc, yc, size*5, size*5)
00150 return id
00151
00152 def _cross(self, c, xc, yc, size=1, fill='black', outline=None,
00153 fillstyle=''):
00154 if outline: fill=outline
00155 id1 = c.create_line(xc-0.5, yc-0.5, xc+0.5, yc+0.5,
00156 fill=fill)
00157 id2 = c.create_line(xc-0.5, yc+0.5, xc+0.5, yc-0.5,
00158 fill=fill)
00159 c.scale(id1, xc, yc, size*5, size*5)
00160 c.scale(id2, xc, yc, size*5, size*5)
00161 return id1, id2
00162
00163 def _plus(self, c, xc, yc, size=1, fill='black', outline=None,
00164 fillstyle=''):
00165 if outline: fill=outline
00166 id1 = c.create_line(xc-0.5, yc, xc+0.5, yc, fill=fill)
00167 id2 = c.create_line(xc, yc+0.5, xc, yc-0.5, fill=fill)
00168 c.scale(id1, xc, yc, size*5, size*5)
00169 c.scale(id2, xc, yc, size*5, size*5)
00170 return id1, id2
00171
00172 class GraphBars(GraphPoints):
00173 def __init__(self, points, **attr):
00174 GraphPoints.__init__(self, points, attr)
00175
00176 _attributes = {'color': 'black',
00177 'width': 1,
00178 'fillcolor': 'yellow',
00179 'size': 3,
00180 'fillstyle': '',
00181 'outline': 'black'}
00182
00183 def draw(self, canvas):
00184 color = self.attributes['color']
00185 width = self.attributes['width']
00186 fillstyle = self.attributes['fillstyle']
00187 outline = self.attributes['outline']
00188 spread = self.attributes['size']
00189 arguments = (canvas,)
00190 p1, p2 = self.boundingBox()
00191 for i in range(len(self.points)):
00192 x1, y1 = self.scaled[i]
00193 canvas.create_rectangle(x1-spread, y1, x1+spread,
00194 self.anchor, fill=color,
00195 width=width, outline=outline,
00196 stipple=fillstyle)
00197 canvas.create_text(x1,y1,text = self.points[i][1], anchor = 's', fill = 'red' )
00198
00199
00200
00201
00202 class GraphObjects:
00203 def __init__(self, objects):
00204 self.objects = objects
00205
00206 def boundingBox(self):
00207 c1, c2 = self.objects[0].boundingBox()
00208 for object in self.objects[1:]:
00209 c1o, c2o = object.boundingBox()
00210 c1 = minBound([c1, c1o])
00211
00212 c2 = maxBound([c2, c2o])
00213 return c1, c2
00214
00215 def fitToScale(self, scale=(1,1), shift=(0,0),lower = 0.0):
00216 for object in self.objects:
00217 object.fitToScale(scale, shift,lower)
00218
00219 def draw(self, canvas):
00220 for object in self.objects:
00221 object.draw(canvas)
00222
00223 class GraphBase(Frame):
00224 def __init__(self, master, width, height,
00225 background='white', listerus=None, x_label = None, y_label = None, header = None, description = None, label_interval=None, type = None, **kw):
00226
00227
00228
00229
00230
00231
00232
00233 self.labelei = copy.deepcopy(listerus)
00234
00235 if type == "date":
00236
00237 for i in range(len(self.labelei)):
00238 temp = self.labelei[i][1].split("-")
00239 self.labelei[i][1] = "%s-%s" % (temp[1], temp[2])
00240
00241 self.x_label = x_label
00242 self.y_label = y_label
00243 self.header = header
00244 self.description = description
00245 self.label_amount = label_interval
00246 apply(Frame.__init__, (self, master), kw)
00247
00248 self.canvas = Canvas(self, width=width, height=height,
00249 background=background)
00250 self.canvas.pack(fill=BOTH, expand=YES)
00251 border_w = self.canvas.winfo_reqwidth() - \
00252 string.atoi(self.canvas.cget('width'))
00253 border_h = self.canvas.winfo_reqheight() - \
00254 string.atoi(self.canvas.cget('height'))
00255 self.border = (border_w, border_h)
00256 self.canvas.bind('<Configure>', self.configure)
00257 self.plotarea_size = [None, None]
00258 self._setsize()
00259 self.last_drawn = None
00260 self.font = ('Verdana', 10)
00261
00262 def save(self):
00263 self.canvas.save()
00264
00265 def configure(self, event):
00266 new_width = event.width-self.border[0]
00267 new_height = event.height-self.border[1]
00268 width = string.atoi(self.canvas.cget('width'))
00269 height = string.atoi(self.canvas.cget('height'))
00270 if new_width == width and new_height == height:
00271 return
00272 self.canvas.configure(width=new_width, height=new_height)
00273 self._setsize()
00274 self.clear()
00275 self.replot()
00276
00277 def bind(self, *args):
00278 apply(self.canvas.bind, args)
00279
00280 def _setsize(self):
00281 self.width = string.atoi(self.canvas.cget('width'))
00282
00283
00284 self.height = string.atoi(self.canvas.cget('height'))
00285
00286
00287 self.plotarea_size[0] = 0.87 * self.width
00288 self.plotarea_size[1] = 0.80 * -self.height
00289 xo = 0.5*(self.width-self.plotarea_size[0])
00290 yo = self.height-0.5*(self.height+self.plotarea_size[1])
00291 self.plotarea_origin = (xo, yo)
00292
00293 def draw(self, graphics, xaxis = None, yaxis = None):
00294 myvar = None
00295 self.last_drawn = (graphics, xaxis, yaxis)
00296 p1, p2 = graphics.boundingBox()
00297 xaxis = self._axisInterval(xaxis, p1[0], p2[0])
00298 yaxis = self._axisInterval(yaxis, p1[1], p2[1])
00299
00300 text_width = [0., 0.]
00301 text_height = [0., 0.]
00302 if xaxis is not None:
00303 p1 = xaxis[0], p1[1]
00304 p2 = xaxis[1], p2[1]
00305 xticks = self._ticks(xaxis[0], xaxis[1])
00306
00307 bb = self._textBoundingBox(xticks[0][1])
00308 if bb != None:
00309 text_height[1] = bb[3]-bb[1]
00310 text_width[0] = 0.5*(bb[2]-bb[0])
00311
00312 else:
00313 myvar = 0
00314
00315 bb = self._textBoundingBox(xticks[-1][1])
00316 if bb != None:
00317 text_width[1] = 0.5*(bb[2]-bb[0])
00318 else:
00319 myvar = 0
00320
00321 else:
00322 xticks = None
00323 if yaxis is not None:
00324 p1 = p1[0], yaxis[0]
00325 p2 = p2[0], yaxis[1]
00326 yticks = self._ticks(yaxis[0], yaxis[1])
00327 for y in yticks:
00328 bb = self._textBoundingBox(y[1])
00329 w = bb[2]-bb[0]
00330 text_width[0] = max(text_width[0], w)
00331 h = 0.5*(bb[3]-bb[1])
00332 text_height[0] = h
00333 text_height[1] = max(text_height[1], h)
00334 else:
00335 yticks = None
00336 text1 = [text_width[0], -text_height[1]]
00337 text2 = [text_width[1], -text_height[0]]
00338 scale = ((self.plotarea_size[0]-text1[0]-text2[0]) / \
00339 (p2[0]-p1[0]),
00340 (self.plotarea_size[1]-text1[1]-text2[1]) / \
00341 (p2[1]-p1[1]))
00342 shift = ((-p1[0]*scale[0]) + self.plotarea_origin[0] + \
00343 text1[0],
00344 (-p1[1]*scale[1]) + self.plotarea_origin[1] + \
00345 text1[1])
00346 if myvar == None:
00347
00348 self._drawAxes(self.canvas, xaxis, yaxis, p1, p2,
00349 scale, shift, self.labelei, yticks)
00350
00351 graphics.fitToScale(scale, shift,yaxis[0])
00352 graphics.draw(self.canvas)
00353
00354 def _axisInterval(self, spec, lower, upper):
00355 if spec is None:
00356 return None
00357
00358 if spec == 'minimal':
00359 if lower == upper:
00360 return lower-0.5, upper+0.5
00361 else:
00362 return lower, upper
00363 if spec == 'automatic':
00364 range = upper-lower
00365
00366 if range == 0.:
00367
00368 if lower-0.5 < 0.0 and lower-0.5 > -0.99:
00369 return 0.0, 1.0
00370 else:
00371 return lower-0.5, upper+0.5
00372
00373 log = math.log10(range)
00374 power = math.floor(int(log))
00375 fraction = log-power
00376
00377 if fraction <= 0.05:
00378 power = power-1
00379
00380
00381 grid = 10.**power
00382 lower = lower - lower % grid
00383 mod = upper % grid
00384 if mod != 0:
00385 upper = upper - mod + grid
00386
00387 return 0, upper
00388 if type(spec) == type(()):
00389 lower, upper = spec
00390 if lower <= upper:
00391 return lower, upper
00392 else:
00393 return upper, lower
00394 raise ValueError, str(spec) + ': illegal axis specification'
00395
00396 def _drawAxes(self, canvas, xaxis, yaxis,
00397 bb1, bb2, scale, shift, xticks, yticks):
00398 dict = {'anchor': N, 'fill': 'black'}
00399 if self.font is not None:
00400 dict['font'] = self.font
00401 if xaxis is not None:
00402 lower, upper = xaxis
00403 text = 1
00404 for y, d in [(bb1[1], -3), (bb2[1], 3)]:
00405 p1 = (scale[0]*lower)+shift[0], (scale[1]*y)+shift[1]
00406 p2 = (scale[0]*upper)+shift[0], (scale[1]*y)+shift[1]
00407 Line(self.canvas, p1[0], p1[1], p2[0], p2[1],
00408 fill = 'black', width = 1)
00409 if xticks:
00410 tickinterval = float(len(xticks))/float(self.label_amount)
00411 if tickinterval < 1:
00412 tickinterval = 1
00413 tickinterval = round(tickinterval)
00414
00415 for x, label in xticks:
00416
00417 p = (scale[0]*x)+shift[0], \
00418 (scale[1]*y)+shift[1]
00419 Line(self.canvas, p[0], p[1], p[0], p[1]+d,
00420 fill = 'black', width = 1)
00421
00422 m = x%tickinterval
00423 if 0 == m:
00424 if text:
00425 dict['text'] = label
00426 dictat=dict.copy()
00427 dictat['text'] = '|'
00428 dictat['fill'] = 'red'
00429
00430 if x%(2*tickinterval) == 0:
00431 apply(CanvasText, (self.canvas, p[0], p[1]), dictat)
00432 apply(CanvasText, (self.canvas, p[0], p[1]+17), dict)
00433
00434 else:
00435 apply(CanvasText, (self.canvas, p[0], p[1]-7), dictat)
00436 apply(CanvasText, (self.canvas, p[0], p[1]+4), dict)
00437
00438
00439
00440
00441 text = 0
00442
00443 width = string.atoi(self.canvas.cget('width'))
00444 height = string.atoi(self.canvas.cget('height'))
00445 plotarea_size=range(2)
00446 plotarea_size[0] = 0.87 * width
00447 plotarea_size[1] = 0.80 * -height
00448 xo = 0.3*(width-plotarea_size[0])
00449 yo = height-0.3*(height+plotarea_size[1])
00450 dictat['text'] = self.x_label
00451 dictat['fill'] = 'black'
00452
00453
00454 apply(CanvasText,(self.canvas, (width/2), yo), dictat)
00455
00456 dictat['text'] = self.description
00457
00458 apply(CanvasText,(self.canvas, (width/2), 10), dictat)
00459
00460 dict['anchor'] = E
00461 if yaxis is not None:
00462 lower, upper = yaxis
00463 text = 1
00464 for x, d in [(bb1[0], -3), (bb2[0], 3)]:
00465 p1 = (scale[0]*x)+shift[0], (scale[1]*lower)+shift[1]
00466 p2 = (scale[0]*x)+shift[0], (scale[1]*upper)+shift[1]
00467 Line(self.canvas, p1[0], p1[1], p2[0], p2[1],
00468 fill = 'black', width = 1)
00469 if yticks:
00470 for y, label in yticks:
00471 if label == "-0.0" or label == "-0":
00472 label = "0"
00473 p = (scale[0]*x)+shift[0], \
00474 (scale[1]*y)+shift[1]
00475
00476 if None != re.match('^[0-9]+.[1-9]+', label):
00477
00478 pass
00479 else:
00480 Line(self.canvas, p[0], p[1], p[0]-d, p[1],
00481 fill = 'black', width = 1)
00482 if text:
00483
00484 label = float(label)
00485 label = "%d" % int(label)
00486 dict['text'] = label
00487 apply(CanvasText,(self.canvas, p[0]-2,p[1]), dict)
00488 text = 0
00489
00490
00491 dictat['text'] = "\n".join(self.y_label)
00492 apply(CanvasText,(self.canvas, xo, (height/3)), dictat)
00493
00494
00495
00496 def _ticks(self, lower, upper):
00497 ideal = (upper-lower)/7.
00498 log = math.log10(ideal)
00499 power = math.floor(log)
00500 fraction = log-power
00501 factor = 1.
00502 error = fraction
00503 for f, lf in self._multiples:
00504 e = math.fabs(fraction-lf)
00505 if e < error:
00506 error = e
00507 factor = f
00508 grid = factor * 10.**power
00509 if power > 3 or power < -3:
00510 format = '%+7.0e'
00511 elif power >= 0:
00512 digits = max(1, int(power))
00513 format = '%' + `digits`+'.0f'
00514 else:
00515 digits = -int(power)
00516 format = '%'+`digits+2`+'.'+`digits`+'f'
00517 ticks = []
00518 t = -grid*math.floor(-lower/grid)
00519 while t <= upper and len(ticks) < 200:
00520 ticks.append((t, format % (t,)))
00521 t = t + grid
00522 return ticks
00523
00524 _multiples = [(2., math.log10(2.)), (5., math.log10(5.))]
00525
00526 def _textBoundingBox(self, text):
00527 bg = self.canvas.cget('background')
00528 dict = {'anchor': NW, 'text': text, 'fill': bg}
00529 if self.font is not None:
00530 dict['font'] = self.font
00531 item = apply(CanvasText, (self.canvas, 0., 0.), dict)
00532 bb = self.canvas.bbox(item)
00533 self.canvas.delete(item)
00534 return bb
00535
00536 def replot(self):
00537 if self.last_drawn is not None:
00538 try:
00539 apply(self.draw, self.last_drawn)
00540 except:
00541 "canvas closed"
00542
00543 def clear(self):
00544 idList = []
00545 id = 0
00546 idList = self.canvas.find_all()
00547 for id in idList:
00548 self.canvas.delete(id)
00549
00550
00551
00552 class GraphGlobal:
00553 def __init__(self,graphList,maxGraphs):
00554 self.graphList = graphList
00555 self.maxGraphs = maxGraphs
00556 def clearAll(self):
00557 for index in (range(self.maxGraphs)):
00558 self.graphList[index].clear()
00559 return 1