F:/thesis_austausch/dissertation/code_docu_doxygen/Visualiser/Graphs.py

Go to the documentation of this file.
00001 # Module: Graphs.py
00002 #
00003 # This module consists of classes and related methods necessary to create
00004 # graph widgits. From these classes it should be possible to draw histograms.
00005 # line-graphs etc.
00006 # This code is shamelessly taken from an example from the Tkinter book by
00007 # John Grayson.
00008 #
00009 # Adil Hasan,  24/March/2003
00010 # modified for the gui.py module by a.weise December 2005 
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         #print "self.scaled: ", self.scaled
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         #print "arguments: ", x1, y1, x2, y2
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         # Pitfall: shallow copy vs. deep copy. 
00228         # A shallow copy of a list only copies 
00229         # the first-level elements. 
00230         # If a list contains nested lists, 
00231         # a deep copy is necessary to make copies 
00232         # of the nested lists. 
00233         self.labelei = copy.deepcopy(listerus)
00234 
00235         if type == "date":
00236         ##shorten description for d
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         #self.pack(expand=YES,fill=BOTH)
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         #if self.width < 250:
00283         #    self.width = 250
00284         self.height = string.atoi(self.canvas.cget('height'))
00285         #if self.height < 180:
00286         #    self.height = 180
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         #xaxis[0] = 0.0
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]#0.0 
00325             p2 = p2[0], yaxis[1]
00326             yticks = self._ticks(yaxis[0], yaxis[1])#0.0
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             #self._drawAxes(self.canvas, xaxis, yaxis, p1, p2, scale, shift, xticks, yticks)
00348             self._drawAxes(self.canvas, xaxis, yaxis, p1, p2,
00349                 scale, shift, self.labelei, yticks)
00350            # print xticks
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        # print upper, lower
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             #print "range", range
00366             if range == 0.:
00367                # print "war bei 0."
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                 #return lower-0.5, upper+0.5
00373             log = math.log10(range)
00374             power = math.floor(int(log))
00375             fraction = log-power
00376            # print fraction
00377             if fraction <= 0.05:
00378                 power = power-1
00379             # produces an error
00380             # another error happens if lower and upper limit are in the same interval
00381             grid = 10.**power
00382             lower = lower - lower % grid
00383             mod = upper % grid
00384             if mod != 0:
00385                 upper = upper - mod + grid
00386             #print "lower, upper", lower, upper
00387             return 0, upper #changed lower to zero
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                         #print x, label, xticks
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                         #print "p[0]: ", p[0], " p[1]: ", p[1]
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         # description of x-axis
00454         apply(CanvasText,(self.canvas, (width/2), yo), dictat)
00455         
00456         dictat['text'] = self.description
00457         # diagram description
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                         # y - axis change description, only whole numbers are wanted
00476                         if None != re.match('^[0-9]+.[1-9]+', label):
00477                             # negativ approach
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                                 # convert alleged float into integer
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         # description of y - axis
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 # Class containing methods that act on the canvas as a whole
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

Generated on Sun Mar 5 23:04:01 2006 for Virtualiser by  doxygen 1.4.6-NO