Package Vision :: Module matplotlibNodes
[hide private]
[frames] | no frames]

Source Code for Module Vision.matplotlibNodes

   1  ######################################################################### 
   2  # 
   3  # Date: Dec 2004 Authors: Michel Sanner 
   4  # 
   5  #    sanner@scripps.edu 
   6  # 
   7  #       The Scripps Research Institute (TSRI) 
   8  #       Molecular Graphics Lab 
   9  #       La Jolla, CA 92037, USA 
  10  # 
  11  # Copyright: Michel Sanner, and TSRI 
  12  # 
  13  ######################################################################### 
  14   
  15  # latest working version of matplotlib is 0.87.7 using numpy 1.0.3 
  16  # latest working version of matplotlib is 0.87.6 using Numeric 23.8 
  17  # (matplotlib 0.87.7 has a small bug using Numeric 23.8) 
  18  # there are multiple bugs in matplotlib when using Numeric 24.2 
  19   
  20  #TODO: 
  21  # - add and verify controls such as size, labels, legends etc 
  22  # - maybe encapsulate all these common options into a single node 
  23  # - alpha values per axis 
  24  # - use axes rather than subplot 
  25  # 
  26   
  27  from mglutil.util.callback import CallBackFunction 
  28  from NetworkEditor.widgets import TkPortWidget, PortWidget 
  29  import Pmw,math,os 
  30  from matplotlib.colors import cnames 
  31  from matplotlib.lines import Line2D,lineStyles,TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN 
  32  from matplotlib.transforms import Value 
  33  from matplotlib import rcParams 
  34  from mglutil.gui.BasicWidgets.Tk.thumbwheel import ThumbWheel 
  35  from Numeric import array 
  36  """ 
  37  This module implements Vision nodes exposing matplotlib functionatility. 
  38   
  39  MPLBaseNE: 
  40  --------- 
  41  The class provides a base class for all nodes exposing matplotlib functionality 
  42   
  43  its purpose is to to create the attributes described below and implement 
  44  methods shared by all nodes. 
  45   
  46  Attributes: 
  47      self.figure = None:  # node's figure object 
  48      # This attribute always points to the matplotlib Figure object which has 
  49      # a FigureCanvasTkAgg object in its .canvas attribute 
  50   
  51      # self.canvas FigureCanvasTkAgg 
  52   
  53      self.axes = None 
  54      # This attribute points to the matplotlib Axes instance used by this node 
  55   
  56      self.axes.figure # figure in which the axes is currently drawn 
  57   
  58  Methods:         
  59      def createFigure(self, master=None, width=None, height=None, dpi=None, 
  60                       facecolor=None, edgecolor=None, frameon=None, 
  61                       packOpts=None, toolbar=True): 
  62   
  63      # This method is used by all nodes if they need to create a Figure object 
  64      # from the matplotlib library and a FigureCanvasTkAgg object for this 
  65      # figure. 
  66   
  67      def setFigure(self, figure): 
  68      # This method place the node's axes object into the right Figure 
  69              
  70   
  71      def beforeRemovingFromNetwork(self): 
  72      # this method is called when a node is deleted from a network.  Its job is 
  73      # to delete FigureCanvasTkAgg and Axes when appropriate. 
  74       
  75   
  76  MPLFigure: 
  77  ----------- 
  78  The MPLFigure node allows the creation of a plotting area in which one 
  79  or more Axes can be added, where an Axes is a 2D graphical representation 
  80  of a data set (i.e. 2D plot).  A 'master' can be apecified to embed the figure 
  81  in other panels.  This node provides control over parameter that apply to the 
  82  MPLFigure such, width, height, dpi, etc. 
  83   
  84   
  85  Plotting Node: 
  86  ------------- 
  87  Plotting nodes such as Histogram, Plot, Scatter, Pie, etc. take adtasets and 
  88  render them as 2D plots.  They alwas own the axes. 
  89  If the data to be rendered is the only input to these nodes, they will create 
  90  a default Figure, add a default Plot2D to this figure, and draw the data in 
  91  this default 2D plot. 
  92  """ 
  93   
  94  from Vision import UserLibBuild 
  95  from NetworkEditor.items import NetworkNode 
  96   
  97  import Tkinter 
  98  import matplotlib 
  99  import types 
 100  import weakref 
 101   
 102   
 103  # make sure Tk is used as a backend 
 104  matplotlib.use('TkAgg') 
 105   
 106  from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg 
 107  from matplotlib.backends.backend_tkagg import NavigationToolbar2TkAgg 
 108  from matplotlib.figure import Figure as OriginalFigure 
 109  from matplotlib.axes import Axes, Subplot, PolarSubplot, PolarAxes 
 110  from matplotlib.pylab import * 
 111  from Vision.colours import get_colours 
 112  from Vision.posnegFill import posNegFill 
 113  from matplotlib import numerix 
 114  from matplotlib.artist import setp 
 115  ## 
 116  ##  QUESTIONS 
 117  ##  Does this have to be Tk specific or could I use FigureCanvasAgg 
 118  ##  !FigureCanvasAgg is OK 
 119   
 120  ##  Should I use FigureManagerBase rather than FigureCanvasAgg ? 
 121  ##  ! FigureCanvas Agg is OK 
 122   
 123  ##  how to destroy a figure ? 
 124  ##  ! seems to work 
 125   
 126  ##  figure should have a set_dpi method 
 127  ##  ! use figure.dpi.set() for now but might become API later 
 128   
 129  ##  how to remove the tool bar ? 
 130  ##  ! John added a note about this 
 131   
 132  ##  What option in a Figure can beset without destroying the Figure 
 133  ##    and which ones are constructor only options ? 
 134  ##  ! nothing should require rebuilding a Figure 
 135   
 136  ##  Why does add_axes return the axis that already exists if the values for 
 137  ##    building the Axes object are the same ? either this has to change or 
 138  ##    adding an instance of an Axes should be fixed (prefered) 
 139  ##  !John made a note to add an argument to matplotlib 
 140   
 141  ##  Pie seems to have a problem when shadow is on ! 
 142  ##  !Find out the problem, because shadows are on even when check button is off 
 143  ##  !for ce aspect ratio to square 
 144   
 145  ##  Plot lineStyle, color, linewidth etc should be set by a set node that uses 
 146  ##    introspection on the patch? to find otu what can be set ?? 
 147  ## !Use ObjectInspector 
 148   
 149  ## why is figimage not an axes method ? 
 150  ## !use imshow 
 151   
 152  from matplotlib.cbook import iterable, popd 
 153  try: 
 154      from matplotlib.dates import DayLocator, HourLocator, \ 
 155       drange, date2num, timezone 
 156  except: 
 157      pass 
 158  try: 
 159      from pytz import common_timezones 
 160  except: 
 161      common_timezones=[] 
 162  #global variables 
 163  locations={'best' : 0, 
 164            'upper right'  : 1,  
 165            'upper left'   : 2, 
 166            'lower left'   : 3, 
 167            'lower right'  : 4, 
 168            'right'        : 5, 
 169            'center left'  : 6, 
 170            'center right' : 7, 
 171            'lower center' : 8, 
 172            'upper center' : 9, 
 173            'center'       : 10,} 
 174  colors={ 
 175          'blue' : 'b', 
 176          'green' : 'g', 
 177          'red' : 'r', 
 178          'cyan' : 'c', 
 179          'magenta' :'m', 
 180          'yellow' :'y', 
 181          'black': 'k', 
 182          'white' : 'w', 
 183          }           
 184   
 185  markers= { 
 186                  'square'         :  's', 
 187                  'circle'         :  'o', 
 188                  'triangle up'    :  '^', 
 189                  'triangle right' :  '>', 
 190                  'triangle down'  :  'v', 
 191                  'triangle left'  :  '<', 
 192                  'diamond'        :  'd', 
 193                  'pentagram'      :  'p', 
 194                  'hexagon'        :  'h', 
 195                  'octagon'        :  '8', 
 196                  } 
 197    
 198  cmaps=['autumn','bone', 'cool','copper','flag','gray','hot','hsv','jet','pink', 'prism', 'spring', 'summer', 'winter'] 
 199   
200 -def get_styles():
201 styles={} 202 for ls in Line2D._lineStyles.keys(): 203 styles[Line2D._lineStyles[ls][6:]]=ls 204 for ls in Line2D._markers.keys(): 205 styles[Line2D._markers[ls][6:]]=ls 206 #these styles are not recognized 207 if styles.has_key('steps'): 208 del styles['steps'] 209 for s in styles.keys(): 210 if s =="nothing": 211 del styles['nothing'] 212 if s[:4]=='tick': 213 del styles[s] 214 return styles
215
216 -class Figure(OriginalFigure):
217 218 # sub class Figure to override add_axes
219 - def add_axes(self, *args, **kwargs):
220 """hack to circumvent the issue of not adding an axes if the constructor 221 params have already been seen""" 222 223 if kwargs.has_key('force'): 224 force = kwargs['force'] 225 del kwargs['force'] 226 else: 227 force = False 228 229 if iterable(args[0]): 230 key = tuple(args[0]), tuple(kwargs.items()) 231 else: 232 key = args[0], tuple(kwargs.items()) 233 234 if not force and self._seen.has_key(key): 235 ax = self._seen[key] 236 self.sca(ax) 237 return ax 238 239 if not len(args): return 240 if isinstance(args[0], Axes): 241 a = args[0] 242 # this is too early, if done here bbox is 0->1 243 #a.set_figure(self) 244 a.figure = self 245 else: 246 rect = args[0] 247 ispolar = popd(kwargs, 'polar', False) 248 249 if ispolar: 250 a = PolarAxes(self, rect, **kwargs) 251 else: 252 a = Axes(self, rect, **kwargs) 253 254 self.axes.append(a) 255 self._axstack.push(a) 256 self.sca(a) 257 self._seen[key] = a 258 return a
259 260 261
262 -class MPLBaseNE(NetworkNode):
263 """Base class for node wrapping the maptlotlib objects 264 265 """
266 - def __init__(self, name='MPLBase', **kw):
267 kw['name'] = name 268 apply( NetworkNode.__init__, (self,), kw ) 269 270 self.figure = None # matplotlib Figure instance belonging to this node 271 self.axes = None # matplotlib Axes instance 272 273 # this is true for Figures who create the Tk Toplevel 274 # or plotting nodes that have no figure parent node 275 # it is used to decide when the Toplevel should be destroyed 276 self.ownsMaster = False
277 278
279 - def setDrawArea(self, kw):
280 ax = self.axes 281 #ax.clear() 282 if kw.has_key('left'): 283 rect = [kw['left'], kw['bottom'], kw['width'], kw['height']] 284 ax.set_position(rect) 285 if kw.has_key('frameon'): 286 ax.set_frame_on(kw['frameon']) 287 288 if kw.has_key("title"): 289 if type(kw['title'])==types.StringType: 290 ax.set_title(kw['title']) 291 else: 292 print 'Set title as Object' 293 if kw.has_key("xlabel"): 294 ax.set_xlabel(kw['xlabel']) 295 if kw.has_key("ylabel"): 296 ax.set_ylabel(kw['ylabel']) 297 if kw.has_key("xlimit"): 298 if kw['xlimit']!='': 299 ax.set_xlim(eval(kw['xlimit'])) 300 if kw.has_key("ylimit"): 301 if kw['ylimit']!='': 302 ax.set_ylim(eval(kw['ylimit'])) 303 if kw.has_key("xticklabels"): 304 if not kw['xticklabels']: 305 ax.set_xticklabels([]) 306 if kw.has_key("yticklabels"): 307 if not kw['yticklabels']: 308 ax.set_yticklabels([]) 309 if kw.has_key("axison"): 310 if kw['axison']: 311 ax.set_axis_on() 312 else: 313 ax.set_axis_off() 314 if kw.has_key("autoscaleon"): 315 if kw['autoscaleon']: 316 ax.set_autoscale_on(True) 317 else: 318 ax.set_autoscale_on(False) 319 if kw.has_key("adjustable"): 320 ax.set_adjustable(kw['adjustable']) 321 if kw.has_key("aspect"): 322 ax.set_aspect(kw['aspect']) 323 if kw.has_key("anchor"): 324 ax.set_anchor(kw['anchor']) 325 326 if kw.has_key("axisbelow"): 327 if kw['axisbelow']==1: 328 val=True 329 else: 330 val=False 331 rcParams['axes.axisbelow']=val 332 ax.set_axisbelow(val) 333 #grid properties 334 if kw.has_key("gridOn"): 335 if kw['gridOn']==1: 336 ax._gridOn=True 337 val=True 338 if kw.has_key('gridcolor'): 339 gcolor=kw['gridcolor'] 340 else: 341 gcolor=rcParams['grid.color'] 342 if kw.has_key('gridlinestyle'): 343 glinestyle=kw['gridlinestyle'] 344 else: 345 glinestyle=rcParams['grid.linestyle'] 346 if kw.has_key('gridlinewidth'): 347 glinewidth=kw['gridlinewidth'] 348 else: 349 glinewidth=rcParams['grid.linewidth'] 350 if kw.has_key('whichgrid'): 351 whichgrid=kw['whichgrid'] 352 else: 353 whichgrid='major' 354 ax.grid(val,color=gcolor, linestyle=glinestyle, linewidth=glinewidth,which=whichgrid) 355 else: 356 val=False 357 ax.grid(val) 358 359 if kw.has_key("facecolor"): 360 ax.set_axis_bgcolor(kw['facecolor']) 361 if kw.has_key("edgecolor"): 362 ax.axesFrame.set_color(kw['edgecolor']) 363 if kw.has_key('zoomx'): 364 #Zoom in on the x xaxis numsteps (plus for zoom in, minus for zoom out) 365 ax.zoomx(kw['zoomx']) 366 367 if kw.has_key('zoomy'): 368 #Zoom in on the x xaxis numsteps (plus for zoom in, minus for zoom out) 369 ax.zoomy(kw['zoomy']) 370 371 if kw.has_key("xtick.color"): 372 for i in ax.xaxis.get_ticklabels(): 373 i.set_color(kw['xtick.color']) 374 if kw.has_key("ytick.color"): 375 for i in ax.yaxis.get_ticklabels(): 376 i.set_color(kw['ytick.color']) 377 378 if kw.has_key('xtick.labelrotation'): 379 for i in ax.xaxis.get_ticklabels(): 380 i.set_rotation(float(kw['xtick.labelrotation'])) 381 if kw.has_key('ytick.labelrotation'): 382 for i in ax.yaxis.get_ticklabels(): 383 i.set_rotation(float(kw['ytick.labelrotation'])) 384 if kw.has_key("xtick.labelsize"): 385 for i in ax.xaxis.get_ticklabels(): 386 i.set_size(float(kw['xtick.labelsize'])) 387 if kw.has_key("ytick.labelsize"): 388 for i in ax.yaxis.get_ticklabels(): 389 i.set_size(float(kw['ytick.labelsize'])) 390 if kw.has_key("linewidth"): 391 ax.axesFrame.set_linewidth(float(kw['linewidth'])) 392 393 #marker 394 if kw.has_key("markeredgewidth"): 395 for i in ax.get_xticklines(): 396 i.set_markeredgewidth(kw['markeredgewidth']) 397 for i in ax.get_yticklines(): 398 i.set_markeredgewidth(kw['markeredgewidth']) 399 400 if kw.has_key("markeredgecolor"): 401 for i in ax.get_xticklines(): 402 i.set_markeredgecolor(kw['markeredgecolor']) 403 for i in ax.get_yticklines(): 404 i.set_markeredgecolor(kw['markeredgecolor']) 405 406 if kw.has_key("markerfacecolor"): 407 for i in ax.get_xticklines(): 408 i.set_markerfacecolor(kw['markerfacecolor']) 409 for i in ax.get_yticklines(): 410 i.set_markerfacecolor(kw['markerfacecolor']) 411 412 #figure_patch properties 413 if kw.has_key("figpatch_linewidth"): 414 ax.figure.figurePatch.set_linewidth(kw['figpatch_linewidth']) 415 if kw.has_key("figpatch_facecolor"): 416 ax.figure.figurePatch.set_facecolor(kw['figpatch_facecolor']) 417 if kw.has_key("figpatch_edgecolor"): 418 ax.figure.figurePatch.set_edgecolor(kw['figpatch_edgecolor']) 419 if kw.has_key("figpatch_antialiased"): 420 ax.figure.figurePatch.set_antialiased(kw['figpatch_antialiased']) 421 422 #Text properties 423 if kw.has_key('text'): 424 for i in kw['text']: 425 if type(i)==types.DictType: 426 tlab=i['textlabel'] 427 posx=i['posx'] 428 posy=i['posy'] 429 horizontalalignment=i['horizontalalignment'] 430 verticalalignment=i['verticalalignment'] 431 rotation=i['rotation'] 432 ax.text(x=posx,y=posy,s=tlab,horizontalalignment=horizontalalignment,verticalalignment=verticalalignment,rotation=rotation,transform = ax.transAxes) 433 if kw.has_key("text.color"): 434 for t in ax.texts: 435 t.set_color(kw['text.color']) 436 if kw.has_key("text.usetex"): 437 rcParams['text.usetex']=kw['text.usetex'] 438 if kw.has_key("text.dvipnghack"): 439 rcParams['text.dvipnghack']=kw['text.dvipnghack'] 440 if kw.has_key("text.fontstyle"): 441 for t in ax.texts: 442 t.set_fontstyle(kw['text.fontstyle']) 443 if kw.has_key("text.fontangle"): 444 for t in ax.texts: 445 t.set_fontangle(kw['text.fontangle']) 446 if kw.has_key("text.fontvariant"): 447 for t in ax.texts: 448 t.set_fontvariant(kw['text.fontvariant']) 449 if kw.has_key("text.fontweight"): 450 for t in ax.texts: 451 t.set_fontweight(kw['text.fontweight']) 452 if kw.has_key("text.fontsize"): 453 for t in ax.texts: 454 t.set_fontsize(kw['text.fontsize']) 455 456 #Font 457 if kw.has_key("Font.fontfamily"): 458 for t in ax.texts: 459 t.set_family(kw['Font.fontfamily']) 460 if kw.has_key("Font.fontstyle"): 461 for t in ax.texts: 462 t.set_fontstyle(kw['Font.fontstyle']) 463 if kw.has_key("Font.fontangle"): 464 for t in ax.texts: 465 t.set_fontangle(kw['Font.fontangle']) 466 if kw.has_key("Font.fontvariant"): 467 for t in ax.texts: 468 t.set_fontvariant(kw['Font.fontvariant']) 469 if kw.has_key("Font.fontweight"): 470 for t in ax.texts: 471 t.set_fontweight(kw['Font.fontweight']) 472 if kw.has_key("Font.fontsize"): 473 for t in ax.texts: 474 t.set_fontsize(kw['Font.fontsize']) 475 476 #Legend Properties 477 if kw.has_key('legendlabel'): 478 if ',' in kw['legendlabel']: 479 x=kw['legendlabel'].split(",") 480 else: 481 x=(kw['legendlabel'],) 482 if kw.has_key('legend.isaxes'): 483 isaxes=kw['legend.isaxes'] 484 else: 485 isaxes=rcParams['legend.isaxes'] 486 487 if kw.has_key('legend.numpoints'): 488 numpoints=kw['legend.numpoints'] 489 else: 490 numpoints=rcParams['legend.numpoints'] 491 492 if kw.has_key('legend.pad'): 493 pad=kw['legend.pad'] 494 else: 495 pad=rcParams['legend.pad'] 496 497 if kw.has_key('legend.markerscale'): 498 markerscale=kw['legend.markerscale'] 499 else: 500 markerscale=rcParams['legend.markerscale'] 501 if kw.has_key('legend.labelsep'): 502 labelsep=kw['legend.labelsep'] 503 else: 504 labelsep=rcParams['legend.labelsep'] 505 if kw.has_key('legend.handlelen'): 506 handlelen=kw['legend.handlelen'] 507 else: 508 handlelen=rcParams['legend.handlelen'] 509 if kw.has_key('legend.handletextsep'): 510 handletextsep=kw['legend.handletextsep'] 511 else: 512 handletextsep=rcParams['legend.handletextsep'] 513 if kw.has_key('legend.axespad'): 514 axespad=kw['legend.axespad'] 515 else: 516 axespad=rcParams['legend.axespad'] 517 if kw.has_key('legend.shadow'): 518 shadow=kw['legend.shadow'] 519 else: 520 shadow=rcParams['legend.shadow'] 521 leg=self.axes.legend(tuple(x),loc=kw['legendlocation'],isaxes=isaxes,numpoints=numpoints,pad=pad,labelsep=labelsep,handlelen=handlelen,handletextsep=handletextsep,axespad=axespad,shadow=shadow,markerscale=markerscale) 522 if kw.has_key('legend.fontsize'): 523 setp(ax.get_legend().get_texts(),fontsize=kw['legend.fontsize']) 524 525 #Tick Options 526 if kw.has_key('xtick.major.pad'): 527 for i in ax.xaxis.majorTicks: 528 i.set_pad(kw['xtick.major.pad']) 529 if kw.has_key('xtick.minor.pad'): 530 for i in ax.xaxis.minorTicks: 531 i.set_pad(kw['xtick.minor.pad']) 532 if kw.has_key('ytick.major.pad'): 533 for i in ax.yaxis.majorTicks: 534 i.set_pad(kw['ytick.major.pad']) 535 if kw.has_key('ytick.minor.pad'): 536 for i in ax.yaxis.minorTicks: 537 i.set_pad(kw['ytick.minor.pad']) 538 if kw.has_key('xtick.major.size'): 539 rcParams['xtick.major.size']=kw['xtick.major.size'] 540 if kw.has_key('xtick.minor.size'): 541 rcParams['xtick.minor.size']=kw['xtick.minor.size'] 542 if kw.has_key('xtick.direction'): 543 rcParams['xtick.direction']=kw['xtick.direction'] 544 if kw.has_key('ytick.major.size'): 545 rcParams['ytick.major.size']=kw['ytick.major.size'] 546 if kw.has_key('ytick.minor.size'): 547 rcParams['ytick.minor.size']=kw['ytick.minor.size'] 548 if kw.has_key('ytick.direction'): 549 rcParams['ytick.direction']=kw['ytick.direction']
550 551 552
553 - def beforeRemovingFromNetwork(self):
554 #print 'remove' 555 NetworkNode.beforeRemovingFromNetwork(self) 556 # this happens for drawing nodes with no axes specified 557 if self.axes: 558 559 self.axes.figure.delaxes(self.axes) # feel a little strange ! 560 self.canvas._tkcanvas.master.destroy() 561 elif self.canvas: 562 self.canvas._tkcanvas.master.destroy()
563 564
565 - def onlyDataChanged(self, data):
566 """returns true if only he first port (i.e. data) has new data. 567 """ 568 # This can be used to accelerate redraw by only updating the data 569 # rather than redrawing the whole figure 570 # see examples/animation_blit_tk.py 571 ports = self.inputPorts 572 if not ports[0].hasNewData(): 573 return False 574 for p in self.inputPorts: 575 if p.hasNewData(): 576 return False 577 578 return True
579 580 581
582 -class MPLFigureNE(MPLBaseNE):
583 """This node instanciates a Figure object and its FigureCanvasTkAgg object 584 in its .canvas attribute. 585 It also provide control over parameters such as width, height, dpi, etc. 586 587 Input: 588 plots - Matplotlib Axes objects 589 figwidth - width in inches 590 figheigh - height in inches 591 dpi - resolution; defaults to rc figure.dpi 592 facecolor - the background color; defaults to rc figure.facecolor 593 edgecolor - the border color; defaults to rc figure.edgecolor 594 master - Defaults to None, creating a topLevel 595 nbRows - number of rows for subgraph2D 596 nbColumns - number of columns for subgraph2D 597 frameon - boolean 598 hold - boolean 599 toolbar - boolean (init option only) 600 packOpts - string representation of packing options 601 602 Output: 603 canvas: MPLFigure Object 604 605 Todo: 606 legend 607 text 608 image ? 609 """ 610
611 - def afterAddingToNetwork(self):
612 self.figure = Figure() 613 614 master = Tkinter.Toplevel() 615 master.title(self.name) 616 self.canvas = FigureCanvasTkAgg(self.figure, master) 617 self.figure.set_canvas(self.canvas) 618 619 packOptsDict = {'side':'top', 'fill':'both', 'expand':1} 620 self.canvas.get_tk_widget().pack( *(), **packOptsDict ) 621 622 toolbar = NavigationToolbar2TkAgg(self.canvas, master)
623 624
625 - def __init__(self, name='Figure2', **kw):
626 kw['name'] = name 627 apply( MPLBaseNE.__init__, (self,), kw ) 628 629 codeBeforeDisconnect = """def beforeDisconnect(self, c): 630 node1 = c.port1.node 631 node2 = c.port2.node 632 if node2.figure.axes: 633 node2.figure.delaxes(node1.axes) 634 if node1.figure.axes: 635 node1.figure.delaxes(node1.axes) 636 node1.figure.add_axes(node1.axes) 637 """ 638 ip = self.inputPortsDescr 639 ip.append(datatype='MPLAxes', required=False, name='plots', 640 singleConnection=False, 641 beforeDisconnect=codeBeforeDisconnect) 642 ip.append(datatype='float', required=False, name='width') 643 ip.append(datatype='float', required=False, name='height') 644 ip.append(datatype='float', required=False, name='linewidth') 645 ip.append(datatype='int', required=False, name='dpi') 646 ip.append(datatype='colorRGB', required=False, name='facecolor') 647 ip.append(datatype='colorRGB', required=False, name='edgecolor') 648 ip.append(datatype='None', required=False, name='master') 649 ip.append(datatype='int', required=False, name='nbRows') 650 ip.append(datatype='int', required=False, name='nbColumns') 651 ip.append(datatype='boolean', required=False, name='frameon') 652 ip.append(datatype='boolean', required=False, name='hold') 653 ip.append(datatype='boolean', required=False, name='toolbar') 654 ip.append(datatype='None', required=False, name='packOpts') 655 656 op = self.outputPortsDescr 657 op.append(datatype='MPLFigure', name='figure') 658 659 self.widgetDescr['width'] = { 660 'class':'NEThumbWheel','master':'ParamPanel', 661 'width':75, 'height':21, 'oneTurn':2, 'type':'float', 662 'wheelPad':2, 'initialValue':8.125, 663 'labelCfg':{'text':'width in inches'} } 664 665 self.widgetDescr['height'] = { 666 'class':'NEThumbWheel','master':'ParamPanel', 667 'width':75, 'height':21, 'oneTurn':2, 'type':'float', 668 'wheelPad':2, 'initialValue':6.125, 669 'labelCfg':{'text':'height in inches'} } 670 671 self.widgetDescr['linewidth'] = { 672 'class':'NEThumbWheel','master':'ParamPanel', 673 'width':75, 'height':21, 'oneTurn':2, 'type':'int', 674 'wheelPad':2, 'initialValue':1, 675 'labelCfg':{'text':'linewidth'} } 676 677 self.widgetDescr['dpi'] = { 678 'class':'NEThumbWheel','master':'ParamPanel', 679 'width':75, 'height':21, 'oneTurn':10, 'type':'int', 680 'wheelPad':2, 'initialValue':80, 681 'labelCfg':{'text':'DPI'} } 682 683 self.widgetDescr['nbRows'] = { 684 'class':'NEThumbWheel','master':'ParamPanel', 685 'width':75, 'height':21, 'oneTurn':10, 'type':'int', 686 'wheelPad':2, 'initialValue':1, 687 'labelCfg':{'text':'nb. rows'} } 688 689 self.widgetDescr['nbColumns'] = { 690 'class':'NEThumbWheel','master':'ParamPanel', 691 'width':75, 'height':21, 'oneTurn':10, 'type':'int', 692 'wheelPad':2, 'initialValue':1, 693 'labelCfg':{'text':'nb. col'} } 694 695 self.widgetDescr['frameon'] = { 696 'class':'NECheckButton', 'master':'ParamPanel', 697 'initialValue':1, 'labelCfg':{'text':'frame'} } 698 699 self.widgetDescr['hold'] = { 700 'class':'NECheckButton', 'master':'ParamPanel', 701 'initialValue':0, 'labelCfg':{'text':'hold'} } 702 703 self.widgetDescr['toolbar'] = { 704 'class':'NECheckButton', 'master':'ParamPanel', 705 'initialValue':1, 'labelCfg':{'text':'toolbar'} } 706 707 self.widgetDescr['packOpts'] = { 708 'class':'NEEntry', 'master':'ParamPanel', 709 'labelCfg':{'text':'packing Opts.:'}, 710 'initialValue':'{"side":"top", "fill":"both", "expand":1}'} 711 712 code = """def doit(self, plots=None, width=None, height=None, linewidth=1, 713 dpi=None, facecolor=None, edgecolor=None, master=None, nbRows=None, 714 nbColumns=None, frameon=True, hold=False, toolbar=True, packOpts=None): 715 716 self.figure.clear() 717 if plots is not None: 718 for p in plots: 719 self.figure.add_axes(p) 720 721 figure = self.figure 722 # configure size 723 if width is not None or height is not None: 724 defaults = matplotlib.rcParams 725 726 if width is None: 727 width = defaults['figure.figsize'][0] 728 elif height is None: 729 height = defaults['figure.figsize'][1] 730 731 figure.set_figsize_inches(width,height) 732 733 # configure dpi 734 if dpi is not None: 735 figure.set_dpi(dpi) 736 737 # configure facecolor 738 if facecolor is not None: 739 figure.set_facecolor(facecolor) 740 741 # configure edgecolor 742 if edgecolor is not None: 743 figure.set_edgecolor(facecolor) 744 745 # configure frameon 746 if edgecolor is not None: 747 figure.set_edgecolor(facecolor) 748 749 # not sure linewidth is doing anything here 750 figure.figurePatch.set_linewidth(linewidth) 751 figure.hold(hold) 752 753 # FIXME for now we store this here but we might want to add this as 754 # regular attributes to Figure which would be used with subplot 755 #figure.nbRows = nbRows 756 #figure.nbColumns = nbColumns 757 758 self.canvas.draw() 759 760 self.outputData(figure=self.figure) 761 """ 762 self.setFunction(code)
763
764 -class MPLImageNE(MPLBaseNE):
765 """This node creates a PIL image 766 767 Input: 768 plots - Matplotlib Axes objects 769 figwidth - width in inches 770 figheigh - height in inches 771 dpi - resolution; defaults to rc figure.dpi 772 facecolor - the background color; defaults to rc figure.facecolor 773 edgecolor - the border color; defaults to rc figure.edgecolor 774 faceAlpha - alpha value of background 775 edgeAlpha - alpha value of edge 776 frameon - boolean 777 hold - boolean 778 toolbar - boolean (init option only) 779 packOpts - string representation of packing options 780 781 Output: 782 canvas: MPLFigure Object 783 784 Todo: 785 legend 786 text 787 image ? 788 """ 789
790 - def __init__(self, name='imageFigure', **kw):
791 kw['name'] = name 792 apply( MPLBaseNE.__init__, (self,), kw ) 793 794 codeBeforeDisconnect = """def beforeDisconnect(self, c): 795 node1 = c.port1.node 796 node2 = c.port2.node 797 if node1.figure.axes: 798 node1.figure.delaxes(node1.axes) 799 node1.figure.add_axes(node1.axes) 800 """ 801 ip = self.inputPortsDescr 802 ip.append(datatype='MPLAxes', required=False, name='plots', 803 singleConnection=False, 804 beforeDisconnect=codeBeforeDisconnect) 805 ip.append(datatype='float', required=False, name='width') 806 ip.append(datatype='float', required=False, name='height') 807 ip.append(datatype='int', required=False, name='dpi') 808 ip.append(datatype='colorsRGB', required=False, name='facecolor') 809 ip.append(datatype='colorsRGB', required=False, name='edgecolor') 810 ip.append(datatype='float', required=False, name='alphaFace') 811 ip.append(datatype='float', required=False, name='alphaEdge') 812 ip.append(datatype='boolean', required=False, name='frameon') 813 ip.append(datatype='boolean', required=False, name='hold') 814 815 op = self.outputPortsDescr 816 op.append(datatype='image', name='image') 817 818 self.widgetDescr['width'] = { 819 'class':'NEThumbWheel','master':'ParamPanel', 820 'width':75, 'height':21, 'oneTurn':2, 'type':'float', 821 'wheelPad':2, 'initialValue':6.4, 822 'labelCfg':{'text':'width in inches'} } 823 824 self.widgetDescr['height'] = { 825 'class':'NEThumbWheel','master':'ParamPanel', 826 'width':75, 'height':21, 'oneTurn':2, 'type':'float', 827 'wheelPad':2, 'initialValue':4.8, 828 'labelCfg':{'text':'height in inches'} } 829 830 self.widgetDescr['dpi'] = { 831 'class':'NEThumbWheel','master':'ParamPanel', 832 'width':75, 'height':21, 'oneTurn':10, 'type':'int', 833 'wheelPad':2, 'initialValue':80, 834 'labelCfg':{'text':'DPI'} } 835 836 self.widgetDescr['alphaFace'] = { 837 'class':'NEThumbWheel','master':'ParamPanel', 838 'width':75, 'height':21, 'oneTurn':1., 'type':'float', 839 'wheelPad':2, 'initialValue':0.5, 'min':0.0, 'max':1.0, 840 'labelCfg':{'text':'alpha Face'} } 841 842 self.widgetDescr['alphaEdge'] = { 843 'class':'NEThumbWheel','master':'ParamPanel', 844 'width':75, 'height':21, 'oneTurn':1., 'type':'float', 845 'wheelPad':2, 'initialValue':0.5, 'min':0.0, 'max':1.0, 846 'labelCfg':{'text':'alphaEdge'} } 847 848 self.widgetDescr['frameon'] = { 849 'class':'NECheckButton', 'master':'ParamPanel', 850 'initialValue':1, 'labelCfg':{'text':'frame'} } 851 852 self.widgetDescr['hold'] = { 853 'class':'NECheckButton', 'master':'ParamPanel', 854 'initialValue':0, 'labelCfg':{'text':'hold'} } 855 856 code = """def doit(self, plots=None, width=None, height=None, dpi=None, facecolor=None, edgecolor=None, alphaFace=0.5, alphaEdge=0.5, frameon=True, hold=False): 857 figure = self.figure 858 try: 859 self.canvas.renderer.clear() 860 except AttributeError: 861 pass 862 figure.clear() 863 864 # Powers of 2 image to be clean 865 if width>height: 866 htc = float(height)/width 867 w = 512 868 h = int(512*htc) 869 else: 870 wtc = float(width)/height 871 w = int(512*wtc) 872 h = 512 873 874 dpi = figure.get_dpi() 875 876 877 #figure.set_figsize_inches(width,height) 878 figure.set_figsize_inches(w / dpi, h / dpi) 879 880 for p in plots: 881 if hasattr(p,"figure"): 882 p.figure.set_figwidth(w / dpi) 883 p.figure.set_figheight(h / dpi) 884 figure.add_axes(p) 885 p.set_figure(figure) 886 p.axesPatch.set_alpha(alphaFace) 887 888 # configure dpi 889 if dpi is not None: 890 figure.set_dpi(dpi) 891 892 # configure facecolor 893 if facecolor is not None: 894 figure.set_facecolor(tuple(facecolor[0])) 895 896 # configure edgecolor 897 if edgecolor is not None: 898 figure.set_edgecolor(tuple(edgecolor[0])) 899 900 # configure frameon 901 if frameon is not None: 902 figure.set_frameon(frameon) 903 904 figure.hold(hold) 905 906 figure.figurePatch.set_alpha(alphaEdge) 907 self.canvas.draw() # force a draw 908 909 import Image 910 im = self.canvas.buffer_rgba(0,0) 911 ima = Image.frombuffer("RGBA", (w,h), im) 912 ima = ima.transpose(Image.FLIP_TOP_BOTTOM) 913 914 self.outputData(image=ima) 915 """ 916 self.setFunction(code)
917 918
919 - def beforeRemovingFromNetwork(self):
920 #print 'remove' 921 NetworkNode.beforeRemovingFromNetwork(self) 922 # this happens for drawing nodes with no axes specified 923 if self.axes: 924 self.axes.figure.delaxes(self.axes) # feel a little strange !
925
926 - def afterAddingToNetwork(self):
927 self.figure = Figure() 928 from matplotlib.backends.backend_agg import FigureCanvasAgg 929 self.canvas = FigureCanvasAgg(self.figure)
930 931 932
933 -class MPLDrawAreaNE(NetworkNode):
934 """Class for configuring the axes. 935 The following options can be set. 936 left,bottom,width,height ----allows to set the position of the axes. 937 frame on/off --- allows to on or off frame 938 hold on/off --- allows to on or off hold.When hold is True, subsequent plot commands will be added to 939 the current axes. When hold is False, the current axes and figure will be cleared on 940 the next plot command 941 title --- allows to set title of the figure 942 xlabel ---allows to set xlabel of the figure 943 ylabel ---allows to set ylabel of the figure 944 xlimit --- set autoscale off before setting xlimit. 945 y limit --- set autoscale off before setting ylimit. 946 xticklabels on/off --- allows to on or off xticklabels 947 yticklabels on/off --- allows to on or off yticklabels 948 axis on/off --- allows to on or off axis 949 autoscale on/off --- when on sets default axes limits ,when off sets limit from xlimit and ylimit entries. 950 """ 951
952 - def __init__(self, name='Draw Area', **kw):
953 kw['name'] = name 954 apply( NetworkNode.__init__, (self,), kw ) 955 956 ip = self.inputPortsDescr 957 ip.append(datatype='float', required=False, name='left') 958 ip.append(datatype='float', required=False, name='bottom') 959 ip.append(datatype='float', required=False, name='width') 960 ip.append(datatype='float', required=False, name='height') 961 ip.append(datatype='boolean', required=False, name='frameon') 962 ip.append(datatype='boolean', required=False, name='hold') 963 ip.append(datatype='string', required=False, name='title') 964 ip.append(datatype='string', required=False, name='xlabel') 965 ip.append(datatype='string', required=False, name='ylabel') 966 ip.append(datatype='string', required=False, name='xlimit') 967 ip.append(datatype='string', required=False, name='ylimit') 968 ip.append(datatype='boolean', required=False, name='xticklabels') 969 ip.append(datatype='boolean', required=False, name='yticklabels') 970 ip.append(datatype='boolean', required=False, name='axison') 971 ip.append(datatype='boolean', required=False, name='autoscaleon') 972 self.widgetDescr['left'] = { 973 'class':'NEThumbWheel','master':'ParamPanel', 974 'width':75, 'height':21, 'oneTurn':1., 'type':'float', 975 'labelGridCfg':{'sticky':'w'}, 976 'wheelPad':2, 'initialValue':0.1, 977 'labelCfg':{'text':'left (0. to 1.)'} } 978 979 self.widgetDescr['bottom'] = { 980 'class':'NEThumbWheel','master':'ParamPanel', 981 'width':75, 'height':21, 'oneTurn':1., 'type':'float', 982 'labelGridCfg':{'sticky':'w'}, 983 'wheelPad':2, 'initialValue':0.1, 984 'labelCfg':{'text':'bottom (0. to 1.)'} } 985 986 self.widgetDescr['width'] = { 987 'class':'NEThumbWheel','master':'ParamPanel', 988 'width':75, 'height':21, 'oneTurn':1., 'type':'float', 989 'labelGridCfg':{'sticky':'w'}, 990 'wheelPad':2, 'initialValue':0.8, 991 'labelCfg':{'text':'width (0. to 1.)'} } 992 993 self.widgetDescr['height'] = { 994 'class':'NEThumbWheel','master':'ParamPanel', 995 'width':75, 'height':21, 'oneTurn':1., 'type':'float', 996 'labelGridCfg':{'sticky':'w'}, 997 'wheelPad':2, 'initialValue':0.8, 998 'labelCfg':{'text':'height (0. to 1.0)'} } 999 1000 self.widgetDescr['frameon'] = { 1001 'class':'NECheckButton', 'master':'ParamPanel', 1002 'labelGridCfg':{'sticky':'w'}, 1003 'initialValue':1, 'labelCfg':{'text':'frame'} } 1004 1005 self.widgetDescr['hold'] = { 1006 'class':'NECheckButton', 'master':'ParamPanel', 1007 'labelGridCfg':{'sticky':'w'}, 1008 'initialValue':0, 'labelCfg':{'text':'hold'} } 1009 1010 self.widgetDescr['title'] = { 1011 'class':'NEEntry', 'master':'ParamPanel', 1012 'labelCfg':{'text':'title'},'labelGridCfg':{'sticky':'w'}, 1013 'initialValue':'Figure:'} 1014 1015 self.widgetDescr['xlabel'] = { 1016 'class':'NEEntry', 'master':'ParamPanel', 1017 'labelCfg':{'text':'X label'},'labelGridCfg':{'sticky':'w'}, 1018 'initialValue':'X'} 1019 1020 self.widgetDescr['ylabel'] = { 1021 'class':'NEEntry', 'master':'ParamPanel','labelGridCfg':{'sticky':'w'}, 1022 'labelCfg':{'text':'Y label'}, 1023 'initialValue':'Y'} 1024 1025 self.widgetDescr['xlimit'] = { 1026 'class':'NEEntry', 'master':'ParamPanel','labelGridCfg':{'sticky':'w'}, 1027 'labelCfg':{'text':'X limit'}, 1028 'initialValue':''} 1029 1030 self.widgetDescr['ylimit'] = { 1031 'class':'NEEntry', 'master':'ParamPanel','labelGridCfg':{'sticky':'w'}, 1032 'labelCfg':{'text':'Y limit'}, 1033 'initialValue':''} 1034 1035 self.widgetDescr['xticklabels'] = { 1036 'class':'NECheckButton', 'master':'ParamPanel','labelGridCfg':{'sticky':'w'}, 1037 'initialValue':1, 'labelCfg':{'text':'xticklabels'} } 1038 1039 self.widgetDescr['yticklabels'] = { 1040 'class':'NECheckButton', 'master':'ParamPanel','labelGridCfg':{'sticky':'w'},'labelGridCfg':{'sticky':'w'}, 1041 'initialValue':1, 'labelCfg':{'text':'yticklabels'} } 1042 1043 1044 self.widgetDescr['axison'] = { 1045 'class':'NECheckButton', 'master':'ParamPanel', 1046 'initialValue':1, 'labelCfg':{'text':'axis on'} } 1047 self.widgetDescr['autoscaleon'] = { 1048 'class':'NECheckButton', 'master':'ParamPanel','labelGridCfg':{'sticky':'w'}, 1049 'initialValue':1, 'labelCfg':{'text':'autoscale on'} } 1050 op = self.outputPortsDescr 1051 op.append(datatype='MPLDrawArea', name='drawAreaDef') 1052 1053 code = """def doit(self, left=.1, bottom=.1, 1054 width=.8, height=.8, frameon=True, hold=False, title='Figure', xlabel='X', 1055 ylabel='Y', xlimit='',ylimit='',xticklabels=True,yticklabels=True,axison=True,autoscaleon=True): 1056 1057 kw = {'left':left, 'bottom':bottom, 'width':width, 'height':height, 1058 'frameon':frameon, 'hold':hold, 'title':title, 'xlabel':xlabel, 1059 'ylabel':ylabel, 'axison':axison, 'xticklabels': xticklabels,'yticklabels': yticklabels,'xlimit':xlimit,'ylimit':ylimit,'autoscaleon':autoscaleon} 1060 1061 self.outputData(drawAreaDef=kw) 1062 """ 1063 self.setFunction(code)
1064 1065
1066 -class MPLMergeTextNE(NetworkNode):
1067 """Class for writting multiple labels in the axes.Takes input from Text 1068 nodes. 1069 """
1070 - def __init__(self, name='MergeText', **kw):
1071 kw['name'] = name 1072 apply( NetworkNode.__init__, (self,), kw ) 1073 ip = self.inputPortsDescr 1074 ip.append(datatype='MPLDrawArea', required=False,name='textlist',singleConnection=False) 1075 op = self.outputPortsDescr 1076 op.append(datatype='MPLDrawArea', name='drawAreaDef') 1077 code = """def doit(self,textlist): 1078 kw={'text':textlist} 1079 self.outputData(drawAreaDef=kw)""" 1080 self.setFunction(code)
1081
1082 -class MPLPlottingNode(MPLBaseNE):
1083 """Base class for plotting nodes""" 1084
1085 - def afterAddingToNetwork(self):
1086 1087 self.figure = Figure() 1088 self.axes = self.figure.add_subplot( 111 ) 1089 self.axes.node = weakref.ref(self) 1090 1091 master = Tkinter.Toplevel() 1092 master.title(self.name) 1093 self.canvas = FigureCanvasTkAgg(self.figure, master) 1094 self.figure.set_canvas(self.canvas) 1095 1096 packOptsDict = {'side':'top', 'fill':'both', 'expand':1} 1097 self.canvas.get_tk_widget().pack( *(), **packOptsDict ) 1098 self.canvas._master.protocol('WM_DELETE_WINDOW',self.canvas._master.iconify) 1099 toolbar = NavigationToolbar2TkAgg(self.canvas, master)
1100
1101 - def setDrawAreaDef(self,drawAreaDef):
1102 newdrawAreaDef={} 1103 if drawAreaDef: 1104 if len(drawAreaDef)==1 and drawAreaDef[0]!=None: 1105 for d in drawAreaDef[0].keys(): 1106 newdrawAreaDef[d]=drawAreaDef[0][d] 1107 elif len(drawAreaDef)>1: 1108 for dAD in drawAreaDef: 1109 if type(dAD)== types.DictType: 1110 for j in dAD.keys(): 1111 newdrawAreaDef[j]=dAD[j] 1112 self.setDrawArea(newdrawAreaDef)
1113 1114 codeBeforeDisconnect ="""def beforeDisconnect(self,c): 1115 node=c.port2.node 1116 node.axes.clear() 1117 node.canvas.draw() """
1118 1119 ######################################################################## 1120 #### 1121 #### PLOTTING NODES 1122 #### 1123 ######################################################################## 1124
1125 -class FillNE(MPLPlottingNode):
1126 """plots filled polygons. 1127 x - list of x vertices 1128 y - list of y vertices 1129 fillcolor - color 1130 """ 1131
1132 - def __init__(self, name='Fill', **kw):
1133 kw['name'] = name 1134 apply( MPLPlottingNode.