1   
   2   
   3   
   4   
   5   
   6   
   7   
   8   
   9  """ 
  10  Embedding matplotlib in wxPython applications is straightforward, but the 
  11  default plotting widget lacks the capabilities necessary for interactive use. 
  12  WxMpl (wxPython+matplotlib) is a library of components that provide these 
  13  missing features in the form of a better matplolib FigureCanvas. 
  14  """ 
  15   
  16   
  17  import wx 
  18  import sys 
  19  import os.path 
  20  import weakref 
  21   
  22  import matplotlib 
  23  matplotlib.use('WXAgg') 
  24  import numpy as NumPy 
  25  from matplotlib.axes import _process_plot_var_args 
  26  from matplotlib.backend_bases import FigureCanvasBase 
  27  from matplotlib.backends.backend_agg import FigureCanvasAgg, RendererAgg 
  28  from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg 
  29  from matplotlib.figure import Figure 
  30  from matplotlib.font_manager import FontProperties 
  31  from matplotlib.projections.polar import PolarAxes 
  32  from matplotlib.transforms import Bbox 
  33   
  34  __version__ = '2.0dev' 
  35   
  36  __all__ = ['PlotPanel', 'PlotFrame', 'PlotApp', 'StripCharter', 'Channel', 
  37      'FigurePrinter', 'PointEvent', 'EVT_POINT', 'SelectionEvent', 
  38      'EVT_SELECTION'] 
  39   
  40   
  41   
  42  POSTSCRIPT_PRINTING_COMMAND = 'lpr' 
  43   
  44   
  45   
  46   
  47   
  48   
  49   
  50   
  51  MATPLOTLIB_0_98_3 = '0.98.3' <= matplotlib.__version__ 
  52   
  53   
  54   
  55   
  56   
  57   
  59      """ 
  60      Returns a coordinate inverted by the specificed C{Transform}. 
  61      """ 
  62      return transform.inverted().transform_point((x, y)) 
   63   
  64   
  66      """ 
  67      Finds the C{Axes} within a matplotlib C{FigureCanvas} contains the canvas 
  68      coordinates C{(x, y)} and returns that axes and the corresponding data 
  69      coordinates C{xdata, ydata} as a 3-tuple. 
  70   
  71      If no axes contains the specified point a 3-tuple of C{None} is returned. 
  72      """ 
  73      evt = matplotlib.backend_bases.MouseEvent('', canvas, x, y) 
  74   
  75      axes = None 
  76      for a in canvas.get_figure().get_axes(): 
  77          if a.in_axes(evt): 
  78              if axes is None: 
  79                  axes = a 
  80              else: 
  81                  return None, None, None 
  82   
  83      if axes is None: 
  84          return None, None, None 
  85   
  86      xdata, ydata = invert_point(x, y, axes.transData) 
  87      return axes, xdata, ydata 
   88   
  89   
  91      """ 
  92      Returns the boundaries of the X and Y intervals of a C{Bbox}. 
  93      """ 
  94      p0 = bbox.min 
  95      p1 = bbox.max 
  96      return (p0[0], p1[0]), (p0[1], p1[1]) 
   97   
  98   
 100      """ 
 101      Finds the C{Axes} within a matplotlib C{FigureCanvas} that overlaps with a 
 102      canvas area from C{(x1, y1)} to C{(x1, y1)}.  That axes and the 
 103      corresponding X and Y axes ranges are returned as a 3-tuple. 
 104   
 105      If no axes overlaps with the specified area, or more than one axes 
 106      overlaps, a 3-tuple of C{None}s is returned. 
 107      """ 
 108      axes = None 
 109      bbox = Bbox.from_extents(x1, y1, x2, y2) 
 110   
 111      for a in canvas.get_figure().get_axes(): 
 112          if bbox.overlaps(a.bbox): 
 113              if axes is None: 
 114                  axes = a 
 115              else: 
 116                  return None, None, None 
 117   
 118      if axes is None: 
 119          return None, None, None 
 120   
 121      x1, y1, x2, y2 = limit_selection(bbox, axes) 
 122      xrange, yrange = get_bbox_lims( 
 123          Bbox.from_extents(x1, y1, x2, y2).inverse_transformed(axes.transData)) 
 124      return axes, xrange, yrange 
  125   
 126   
 128      """ 
 129      Finds the region of a selection C{bbox} which overlaps with the supplied 
 130      C{axes} and returns it as the 4-tuple C{(xmin, ymin, xmax, ymax)}. 
 131      """ 
 132      bxr, byr = get_bbox_lims(bbox) 
 133      axr, ayr = get_bbox_lims(axes.bbox) 
 134   
 135      xmin = max(bxr[0], axr[0]) 
 136      xmax = min(bxr[1], axr[1]) 
 137      ymin = max(byr[0], ayr[0]) 
 138      ymax = min(byr[1], ayr[1]) 
 139      return xmin, ymin, xmax, ymax 
  140   
 141   
 149   
 150   
 152      """ 
 153      Returns the first top-level parent of a wx.Window 
 154      """ 
 155      topwin = window 
 156      while not isinstance(topwin, wx.TopLevelWindow): 
 157          topwin = topwin.GetParent() 
 158      return topwin        
  159   
 160   
 162      """ 
 163      Alters the X and Y limits of C{Axes} objects while maintaining a history of 
 164      the changes. 
 165      """ 
 167          self.autoscaleUnzoom = autoscaleUnzoom 
 168          self.history = weakref.WeakKeyDictionary() 
  169   
 171          """ 
 172          Enable or disable autoscaling the axes as a result of zooming all the 
 173          way back out. 
 174          """ 
 175          self.limits.setAutoscaleUnzoom(state) 
  176   
 177 -    def _get_history(self, axes): 
  178          """ 
 179          Returns the history list of X and Y limits associated with C{axes}. 
 180          """ 
 181          return self.history.setdefault(axes, []) 
  182   
 184          """ 
 185          Returns a boolean indicating whether C{axes} has had its limits 
 186          altered. 
 187          """ 
 188          return not (not self._get_history(axes)) 
  189   
 190 -    def set(self, axes, xrange, yrange): 
  191          """ 
 192          Changes the X and Y limits of C{axes} to C{xrange} and {yrange} 
 193          respectively.  A boolean indicating whether or not the 
 194          axes should be redraw is returned, because polar axes cannot have 
 195          their limits changed sensibly. 
 196          """ 
 197          if not axes.can_zoom(): 
 198              return False 
 199   
 200           
 201           
 202          oldRange = tuple(axes.get_xlim()), tuple(axes.get_ylim()) 
 203   
 204          history = self._get_history(axes) 
 205          history.append(oldRange) 
 206          axes.set_xlim(xrange) 
 207          axes.set_ylim(yrange) 
 208          return True 
  209   
 211          """ 
 212          Changes the X and Y limits of C{axes} to their previous values.  A 
 213          boolean indicating whether or not the axes should be redraw is 
 214          returned. 
 215          """ 
 216          history = self._get_history(axes) 
 217          if not history: 
 218              return False 
 219   
 220          xrange, yrange = history.pop() 
 221          if self.autoscaleUnzoom and not len(history): 
 222              axes.autoscale_view() 
 223          else: 
 224              axes.set_xlim(xrange) 
 225              axes.set_ylim(yrange) 
 226          return True 
   227   
 228   
 229   
 230   
 231   
 232   
 234      """ 
 235      Encapsulates all of the user-interaction logic required by the 
 236      C{PlotPanel}, following the Humble Dialog Box pattern proposed by Michael 
 237      Feathers: 
 238      U{http://www.objectmentor.com/resources/articles/TheHumbleDialogBox.pdf} 
 239      """ 
 240   
 241       
 242       
 243   
 244 -    def __init__(self, view, zoom=True, selection=True, rightClickUnzoom=True, 
 245        autoscaleUnzoom=True): 
  246          """ 
 247          Create a new director for the C{PlotPanel} C{view}.  The keyword 
 248          arguments C{zoom} and C{selection} have the same meanings as for 
 249          C{PlotPanel}. 
 250          """ 
 251          self.view = view 
 252          self.zoomEnabled = zoom 
 253          self.selectionEnabled = selection 
 254          self.rightClickUnzoom = rightClickUnzoom 
 255          self.limits = AxesLimits(autoscaleUnzoom) 
 256          self.leftButtonPoint = None 
  257   
 259          """ 
 260          Enable or disable left-click area selection. 
 261          """ 
 262          self.selectionEnabled = state 
  263   
 265          """ 
 266          Enable or disable zooming as a result of left-click area selection. 
 267          """ 
 268          self.zoomEnabled = state 
  269   
 271          """ 
 272          Enable or disable autoscaling the axes as a result of zooming all the 
 273          way back out. 
 274          """ 
 275          self.limits.setAutoscaleUnzoom(state) 
  276   
 278          """ 
 279          Enable or disable unzooming as a result of right-clicking. 
 280          """ 
 281          self.rightClickUnzoom = state 
  282   
 284          """ 
 285          Indicates if plot may be not redrawn due to the presence of a selection 
 286          box. 
 287          """ 
 288          return self.leftButtonPoint is None 
  289   
 291          """ 
 292          Returns a boolean indicating whether or not the plot has been zoomed in 
 293          as a result of a left-click area selection. 
 294          """ 
 295          return self.limits.zoomed(axes) 
  296   
 298          """ 
 299          Handles wxPython key-press events.  These events are currently skipped. 
 300          """ 
 301          evt.Skip() 
  302   
 304          """ 
 305          Handles wxPython key-release events.  These events are currently 
 306          skipped. 
 307          """ 
 308          evt.Skip() 
  309   
 322   
 366   
 373   
 385   
 387          """ 
 388          Handles wxPython mouse motion events, dispatching them based on whether 
 389          or not a selection is in process and what the cursor is over. 
 390          """ 
 391          view = self.view 
 392          axes, xdata, ydata = find_axes(view, x, y) 
 393   
 394          if self.leftButtonPoint is not None: 
 395              self.selectionMouseMotion(evt, x, y, axes, xdata, ydata) 
 396          else: 
 397              if axes is None: 
 398                  self.canvasMouseMotion(evt, x, y) 
 399              elif not axes.can_zoom(): 
 400                  self.unzoomableAxesMouseMotion(evt, x, y, axes, xdata, ydata) 
 401              else: 
 402                  self.axesMouseMotion(evt, x, y, axes, xdata, ydata) 
  403   
 405          """ 
 406          Handles wxPython mouse motion events that occur during a left-click 
 407          area selection. 
 408          """ 
 409          view = self.view 
 410          x0, y0 = self.leftButtonPoint 
 411          view.rubberband.set(x0, y0, x, y) 
 412          if axes is None: 
 413              view.location.clear() 
 414          else: 
 415              view.location.set(format_coord(axes, xdata, ydata)) 
  416   
 418          """ 
 419          Handles wxPython mouse motion events that occur over the canvas. 
 420          """ 
 421          view = self.view 
 422          view.cursor.setNormal() 
 423          view.crosshairs.clear() 
 424          view.location.clear() 
  425   
 427          """ 
 428          Handles wxPython mouse motion events that occur over an axes. 
 429          """ 
 430          view = self.view 
 431          view.cursor.setCross() 
 432          view.crosshairs.set(x, y) 
 433          view.location.set(format_coord(axes, xdata, ydata)) 
  434   
 436          """ 
 437          Handles wxPython mouse motion events that occur over an axes that does 
 438          not support zooming. 
 439          """ 
 440          view = self.view 
 441          view.cursor.setNormal() 
 442          view.location.set(format_coord(axes, xdata, ydata)) 
   443   
 444   
 445   
 446   
 447   
 448   
 450      """ 
 451      Painters encapsulate the mechanics of drawing some value in a wxPython 
 452      window and erasing it.  Subclasses override template methods to process 
 453      values and draw them. 
 454   
 455      @cvar PEN: C{wx.Pen} to use (defaults to C{wx.BLACK_PEN}) 
 456      @cvar BRUSH: C{wx.Brush} to use (defaults to C{wx.TRANSPARENT_BRUSH}) 
 457      @cvar FUNCTION: Logical function to use (defaults to C{wx.COPY}) 
 458      @cvar FONT: C{wx.Font} to use (defaults to C{wx.NORMAL_FONT}) 
 459      @cvar TEXT_FOREGROUND: C{wx.Colour} to use (defaults to C{wx.BLACK}) 
 460      @cvar TEXT_BACKGROUND: C{wx.Colour} to use (defaults to C{wx.WHITE}) 
 461      """ 
 462   
 463      PEN = wx.BLACK_PEN 
 464      BRUSH = wx.TRANSPARENT_BRUSH 
 465      FUNCTION = wx.COPY 
 466      FONT = wx.NORMAL_FONT 
 467      TEXT_FOREGROUND = wx.BLACK 
 468      TEXT_BACKGROUND = wx.WHITE 
 469   
 470 -    def __init__(self, view, enabled=True): 
  471          """ 
 472          Create a new painter attached to the wxPython window C{view}.  The 
 473          keyword argument C{enabled} has the same meaning as the argument to the 
 474          C{setEnabled()} method. 
 475          """ 
 476          self.view = view 
 477          self.lastValue = None 
 478          self.enabled = enabled 
  479   
 481          """ 
 482          Enable or disable this painter.  Disabled painters do not draw their 
 483          values and calls to C{set()} have no effect on them. 
 484          """ 
 485          oldState, self.enabled = self.enabled, state 
 486          if oldState and not self.enabled: 
 487              self.clear() 
  488   
 489 -    def set(self, *value): 
  490          """ 
 491          Update this painter's value and then draw it.  Values may not be 
 492          C{None}, which is used internally to represent the absence of a current 
 493          value. 
 494          """ 
 495          if self.enabled: 
 496              value = self.formatValue(value) 
 497              self._paint(value, None) 
  498   
 500          """ 
 501          Redraw this painter's current value. 
 502          """ 
 503          value = self.lastValue 
 504          self.lastValue = None 
 505          self._paint(value, dc) 
  506   
 507 -    def clear(self, dc=None): 
  508          """ 
 509          Clear the painter's current value from the screen and the painter 
 510          itself. 
 511          """ 
 512          if self.lastValue is not None: 
 513              self._paint(None, dc) 
  514   
 516          """ 
 517          Draws a previously processed C{value} on this painter's window. 
 518          """ 
 519          if dc is None: 
 520              dc = wx.ClientDC(self.view) 
 521   
 522          dc.SetPen(self.PEN) 
 523          dc.SetBrush(self.BRUSH) 
 524          dc.SetFont(self.FONT) 
 525          dc.SetTextForeground(self.TEXT_FOREGROUND) 
 526          dc.SetTextBackground(self.TEXT_BACKGROUND) 
 527          dc.SetLogicalFunction(self.FUNCTION) 
 528          dc.BeginDrawing() 
 529   
 530          if self.lastValue is not None: 
 531              self.clearValue(dc, self.lastValue) 
 532              self.lastValue = None 
 533   
 534          if value is not None: 
 535              self.drawValue(dc, value) 
 536              self.lastValue = value 
 537   
 538          dc.EndDrawing() 
  539   
 546   
 548          """ 
 549          Template method that draws a previously processed C{value} using the 
 550          wxPython device context C{dc}.  This DC has already been configured, so 
 551          calls to C{BeginDrawing()} and C{EndDrawing()} may not be made. 
 552          """ 
 553          pass 
  554   
 556          """ 
 557          Template method that clears a previously processed C{value} that was 
 558          previously drawn, using the wxPython device context C{dc}.  This DC has 
 559          already been configured, so calls to C{BeginDrawing()} and 
 560          C{EndDrawing()} may not be made. 
 561          """ 
 562          pass 
   563   
 564   
 566      """ 
 567      Draws a text message containing the current position of the mouse in the 
 568      lower left corner of the plot. 
 569      """ 
 570   
 571      PADDING = 2 
 572      PEN = wx.WHITE_PEN 
 573      BRUSH = wx.WHITE_BRUSH 
 574   
 580   
 582          """ 
 583          Returns the upper-left coordinates C{(X, Y)} for the string C{value} 
 584          its width and height C{(W, H)}. 
 585          """ 
 586          height = dc.GetSize()[1] 
 587          w, h = dc.GetTextExtent(value) 
 588          x = self.PADDING 
 589          y = int(height - (h + self.PADDING)) 
 590          return x, y, w, h 
  591   
 593          """ 
 594          Draws the string C{value} in the lower left corner of the plot. 
 595          """ 
 596          x, y, w, h = self.get_XYWH(dc, value) 
 597          dc.DrawText(value, x, y) 
  598   
 600          """ 
 601          Clears the string C{value} from the lower left corner of the plot by 
 602          painting a white rectangle over it. 
 603          """ 
 604          x, y, w, h = self.get_XYWH(dc, value) 
 605          dc.DrawRectangle(x, y, w, h) 
   606   
 607   
 609      """ 
 610      Draws crosshairs through the current position of the mouse. 
 611      """ 
 612   
 613      PEN = wx.WHITE_PEN 
 614      FUNCTION = wx.XOR 
 615   
 622   
 624          """ 
 625          Draws crosshairs through the C{(X, Y)} coordinates. 
 626          """ 
 627          dc.CrossHair(*value) 
  628   
 630          """ 
 631          Clears the crosshairs drawn through the C{(X, Y)} coordinates. 
 632          """ 
 633          dc.CrossHair(*value) 
   634   
 635   
 637      """ 
 638      Draws a selection rubberband from one point to another. 
 639      """ 
 640   
 641      PEN = wx.WHITE_PEN 
 642      FUNCTION = wx.XOR 
 643   
 656   
 658          """ 
 659          Draws the selection rubberband around the rectangle 
 660          C{(x1, y1, x2, y2)}. 
 661          """ 
 662          dc.DrawRectangle(*value) 
  663   
 665          """ 
 666          Clears the selection rubberband around the rectangle 
 667          C{(x1, y1, x2, y2)}. 
 668          """ 
 669          dc.DrawRectangle(*value) 
   670   
 671   
 673      """ 
 674      Manages the current cursor of a wxPython window, allowing it to be switched 
 675      between a normal arrow and a square cross. 
 676      """ 
 677 -    def __init__(self, view, enabled=True): 
  678          """ 
 679          Create a CursorChanger attached to the wxPython window C{view}.  The 
 680          keyword argument C{enabled} has the same meaning as the argument to the 
 681          C{setEnabled()} method. 
 682          """ 
 683          self.view = view 
 684          self.cursor = wx.CURSOR_DEFAULT 
 685          self.enabled = enabled 
  686   
 688          """ 
 689          Enable or disable this cursor changer.  When disabled, the cursor is 
 690          reset to the normal arrow and calls to the C{set()} methods have no 
 691          effect. 
 692          """ 
 693          oldState, self.enabled = self.enabled, state 
 694          if oldState and not self.enabled and self.cursor != wx.CURSOR_DEFAULT: 
 695              self.cursor = wx.CURSOR_DEFAULT 
 696              self.view.SetCursor(wx.STANDARD_CURSOR) 
  697   
 699          """ 
 700          Change the cursor of the associated window to a normal arrow. 
 701          """ 
 702          if self.cursor != wx.CURSOR_DEFAULT and self.enabled: 
 703              self.cursor = wx.CURSOR_DEFAULT 
 704              self.view.SetCursor(wx.STANDARD_CURSOR) 
  705   
 707          """ 
 708          Change the cursor of the associated window to a square cross. 
 709          """ 
 710          if self.cursor != wx.CURSOR_CROSS and self.enabled: 
 711              self.cursor = wx.CURSOR_CROSS 
 712              self.view.SetCursor(wx.CROSS_CURSOR) 
   713   
 714   
 715   
 716   
 717   
 718   
 719   
 720  PS_DPI_HIGH_QUALITY   = 600 
 721  PS_DPI_MEDIUM_QUALITY = 300 
 722  PS_DPI_LOW_QUALITY    = 150 
 723  PS_DPI_DRAFT_QUALITY  = 72 
 724   
 725   
 727      """ 
 728      Sets the default wx.PostScriptDC resolution from a wx.PrintData's quality 
 729      setting. 
 730   
 731      This is a workaround for WX ignoring the quality setting and defaulting to 
 732      72 DPI.  Unfortunately wx.Printout.GetDC() returns a wx.DC object instead 
 733      of the actual class, so it's impossible to set the resolution on the DC 
 734      itself. 
 735   
 736      Even more unforuntately, printing with libgnomeprint appears to always be 
 737      stuck at 72 DPI. 
 738      """ 
 739      if not callable(getattr(wx, 'PostScriptDC_SetResolution', None)): 
 740          return 
 741   
 742      quality = printData.GetQuality() 
 743      if quality > 0: 
 744          dpi = quality 
 745      elif quality == wx.PRINT_QUALITY_HIGH: 
 746          dpi = PS_DPI_HIGH_QUALITY 
 747      elif quality == wx.PRINT_QUALITY_MEDIUM: 
 748          dpi = PS_DPI_MEDIUM_QUALITY 
 749      elif quality == wx.PRINT_QUALITY_LOW: 
 750          dpi = PS_DPI_LOW_QUALITY 
 751      elif quality == wx.PRINT_QUALITY_DRAFT: 
 752          dpi = PS_DPI_DRAFT_QUALITY 
 753      else: 
 754          dpi = PS_DPI_HIGH_QUALITY 
 755    
 756      wx.PostScriptDC_SetResolution(dpi) 
  757   
 758   
 830   
 831   
 994   
 995   
 996   
 997   
 998   
 999   
1000  EVT_POINT_ID = wx.NewId() 
1001   
1002   
1004      """ 
1005      Register to receive wxPython C{PointEvent}s from a C{PlotPanel} or 
1006      C{PlotFrame}. 
1007      """ 
1008      win.Connect(id, -1, EVT_POINT_ID, func) 
 1009   
1010   
1012      """ 
1013      wxPython event emitted when a left-click-release occurs in a matplotlib 
1014      axes of a window without an area selection. 
1015   
1016      @cvar axes: matplotlib C{Axes} which was left-clicked 
1017      @cvar x: matplotlib X coordinate 
1018      @cvar y: matplotlib Y coordinate 
1019      @cvar xdata: axes X coordinate 
1020      @cvar ydata: axes Y coordinate 
1021      """ 
1023          """ 
1024          Create a new C{PointEvent} for the matplotlib coordinates C{(x, y)} of 
1025          an C{axes}. 
1026          """ 
1027          wx.PyCommandEvent.__init__(self, EVT_POINT_ID, id) 
1028          self.axes = axes 
1029          self.x = x 
1030          self.y = y 
1031          self.xdata, self.ydata = invert_point(x, y, axes.transData) 
 1032   
1034          return PointEvent(self.GetId(), self.axes, self.x, self.y) 
  1035   
1036   
1037  EVT_SELECTION_ID = wx.NewId() 
1038   
1039   
1041      """ 
1042      Register to receive wxPython C{SelectionEvent}s from a C{PlotPanel} or 
1043      C{PlotFrame}. 
1044      """ 
1045      win.Connect(id, -1, EVT_SELECTION_ID, func) 
 1046   
1047   
1049      """ 
1050      wxPython event emitted when an area selection occurs in a matplotlib axes 
1051      of a window for which zooming has been disabled.  The selection is 
1052      described by a rectangle from C{(x1, y1)} to C{(x2, y2)}, of which only 
1053      one point is required to be inside the axes. 
1054   
1055      @cvar axes: matplotlib C{Axes} which was left-clicked 
1056      @cvar x1: matplotlib x1 coordinate 
1057      @cvar y1: matplotlib y1 coordinate 
1058      @cvar x2: matplotlib x2 coordinate 
1059      @cvar y2: matplotlib y2 coordinate 
1060      @cvar x1data: axes x1 coordinate 
1061      @cvar y1data: axes y1 coordinate 
1062      @cvar x2data: axes x2 coordinate 
1063      @cvar y2data: axes y2 coordinate 
1064      """ 
1065 -    def __init__(self, id, axes, x1, y1, x2, y2): 
 1066          """ 
1067          Create a new C{SelectionEvent} for the area described by the rectangle 
1068          from C{(x1, y1)} to C{(x2, y2)} in an C{axes}. 
1069          """ 
1070          wx.PyCommandEvent.__init__(self, EVT_SELECTION_ID, id) 
1071          self.axes = axes 
1072          self.x1 = x1 
1073          self.y1 = y1 
1074          self.x2 = x2 
1075          self.y2 = y2 
1076          self.x1data, self.y1data = invert_point(x1, y1, axes.transData) 
1077          self.x2data, self.y2data = invert_point(x2, y2, axes.transData) 
 1078   
1080          return SelectionEvent(self.GetId(), self.axes, self.x1, self.y1, 
1081              self.x2, self.y2) 
  1082   
1083   
1084   
1085   
1086   
1087   
1089      """ 
1090      A matplotlib canvas suitable for embedding in wxPython applications. 
1091      """ 
1092 -    def __init__(self, parent, id, size=(6.0, 3.70), dpi=96, cursor=True, 
1093       location=True, crosshairs=True, selection=True, zoom=True, 
1094       autoscaleUnzoom=True): 
 1095          """ 
1096          Creates a new PlotPanel window that is the child of the wxPython window 
1097          C{parent} with the wxPython identifier C{id}. 
1098   
1099          The keyword arguments C{size} and {dpi} are used to create the 
1100          matplotlib C{Figure} associated with this canvas.  C{size} is the 
1101          desired width and height of the figure, in inches, as the 2-tuple 
1102          C{(width, height)}.  C{dpi} is the dots-per-inch of the figure. 
1103   
1104          The keyword arguments C{cursor}, C{location}, C{crosshairs}, 
1105          C{selection}, C{zoom}, and C{autoscaleUnzoom} enable or disable various 
1106          user interaction features that are descibed in their associated 
1107          C{set()} methods. 
1108          """ 
1109          FigureCanvasWxAgg.__init__(self, parent, id, Figure(size, dpi)) 
1110   
1111          self.insideOnPaint = False 
1112          self.cursor = CursorChanger(self, cursor) 
1113          self.location = LocationPainter(self, location) 
1114          self.crosshairs = CrosshairPainter(self, crosshairs) 
1115          self.rubberband = RubberbandPainter(self, selection) 
1116          rightClickUnzoom = True  
1117          self.director = PlotPanelDirector(self, zoom, selection, 
1118              rightClickUnzoom, autoscaleUnzoom) 
1119   
1120          self.figure.set_edgecolor('black') 
1121          self.figure.set_facecolor('white') 
1122          self.SetBackgroundColour(wx.WHITE) 
1123   
1124           
1125           
1126          topwin = toplevel_parent_of_window(self) 
1127          topwin.Connect(-1, self.GetId(), wx.wxEVT_ACTIVATE, self.OnActivate) 
1128   
1129          wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground) 
1130          wx.EVT_WINDOW_DESTROY(self, self.OnDestroy) 
 1131   
1133          """ 
1134          Handles the wxPython window activation event. 
1135          """ 
1136          if not evt.GetActive(): 
1137              self.cursor.setNormal() 
1138              self.location.clear() 
1139              self.crosshairs.clear() 
1140              self.rubberband.clear() 
1141          evt.Skip() 
 1142   
1144          """ 
1145          Overrides the wxPython backround repainting event to reduce flicker. 
1146          """ 
1147          pass 
 1148   
1150          """ 
1151          Handles the wxPython window destruction event. 
1152          """ 
1153          if self.GetId() == evt.GetEventObject().GetId(): 
1154               
1155              topwin = toplevel_parent_of_window(self) 
1156              topwin.Disconnect(-1, self.GetId(), wx.wxEVT_ACTIVATE) 
 1157   
1159          """ 
1160          Overrides the C{FigureCanvasWxAgg} paint event to redraw the 
1161          crosshairs, etc. 
1162          """ 
1163           
1164          if not isinstance(self, FigureCanvasWxAgg): 
1165              return 
1166   
1167          self.insideOnPaint = True 
1168          FigureCanvasWxAgg._onPaint(self, evt) 
1169          self.insideOnPaint = False 
1170   
1171          dc = wx.PaintDC(self) 
1172          self.location.redraw(dc) 
1173          self.crosshairs.redraw(dc) 
1174          self.rubberband.redraw(dc) 
 1175   
1181   
1183          """ 
1184          Enable or disable the changing mouse cursor.  When enabled, the cursor 
1185          changes from the normal arrow to a square cross when the mouse enters a 
1186          matplotlib axes on this canvas. 
1187          """ 
1188          self.cursor.setEnabled(state) 
 1189   
1191          """ 
1192          Enable or disable the display of the matplotlib axes coordinates of the 
1193          mouse in the lower left corner of the canvas. 
1194          """ 
1195          self.location.setEnabled(state) 
 1196   
1198          """ 
1199          Enable or disable drawing crosshairs through the mouse cursor when it 
1200          is inside a matplotlib axes. 
1201          """ 
1202          self.crosshairs.setEnabled(state) 
 1203   
1205          """ 
1206          Enable or disable area selections, where user selects a rectangular 
1207          area of the canvas by left-clicking and dragging the mouse. 
1208          """ 
1209          self.rubberband.setEnabled(state) 
1210          self.director.setSelection(state) 
 1211   
1213          """ 
1214          Enable or disable zooming in when the user makes an area selection and 
1215          zooming out again when the user right-clicks. 
1216          """ 
1217          self.director.setZoomEnabled(state) 
 1218   
1220          """ 
1221          Enable or disable automatic view rescaling when the user zooms out to 
1222          the initial figure. 
1223          """ 
1224          self.director.setAutoscaleUnzoom(state) 
 1225   
1227          """ 
1228          Returns a boolean indicating whether or not the C{axes} is zoomed in. 
1229          """ 
1230          return self.director.zoomed(axes) 
 1231   
1232 -    def draw(self, **kwds): 
 1233          """ 
1234          Draw the associated C{Figure} onto the screen. 
1235          """ 
1236           
1237           
1238          if (not self.director.canDraw() 
1239          or  not isinstance(self, FigureCanvasWxAgg)): 
1240              return 
1241   
1242          if MATPLOTLIB_0_98_3: 
1243              FigureCanvasWxAgg.draw(self, kwds.get('drawDC', None)) 
1244          else: 
1245              FigureCanvasWxAgg.draw(self, kwds.get('repaint', True)) 
1246   
1247           
1248          if not self.insideOnPaint: 
1249              self.location.redraw() 
1250              self.crosshairs.redraw() 
1251              self.rubberband.redraw() 
 1252   
1254          """ 
1255          Called by the associated C{PlotPanelDirector} to emit a C{PointEvent}. 
1256          """ 
1257          wx.PostEvent(self, PointEvent(self.GetId(), axes, x, y)) 
 1258   
1260          """ 
1261          Called by the associated C{PlotPanelDirector} to emit a 
1262          C{SelectionEvent}. 
1263          """ 
1264          wx.PostEvent(self, SelectionEvent(self.GetId(), axes, x1, y1, x2, y2)) 
 1265   
1267          """ 
1268          Returns the X and Y coordinates of a wxPython event object converted to 
1269          matplotlib canavas coordinates. 
1270          """ 
1271          return evt.GetX(), int(self.figure.bbox.height - evt.GetY()) 
 1272   
1274          """ 
1275          Overrides the C{FigureCanvasWxAgg} key-press event handler, dispatching 
1276          the event to the associated C{PlotPanelDirector}. 
1277          """ 
1278          self.director.keyDown(evt) 
 1279   
1281          """ 
1282          Overrides the C{FigureCanvasWxAgg} key-release event handler, 
1283          dispatching the event to the associated C{PlotPanelDirector}. 
1284          """ 
1285          self.director.keyUp(evt) 
 1286    
1294   
1302   
1310   
1318   
1320          """ 
1321          Overrides the C{FigureCanvasWxAgg} mouse motion event handler, 
1322          dispatching the event to the associated C{PlotPanelDirector}. 
1323          """ 
1324          x, y = self._get_canvas_xy(evt) 
1325          self.director.mouseMotion(evt, x, y) 
  1326   
1327   
1328   
1329   
1330   
1331   
1333      """ 
1334      A matplotlib canvas embedded in a wxPython top-level window. 
1335   
1336      @cvar ABOUT_TITLE: Title of the "About" dialog. 
1337      @cvar ABOUT_MESSAGE: Contents of the "About" dialog. 
1338      """ 
1339   
1340      ABOUT_TITLE = 'About wxmpl.PlotFrame' 
1341      ABOUT_MESSAGE = ('wxmpl.PlotFrame %s\n' %  __version__ 
1342          + 'Written by Ken McIvor <mcivor@iit.edu>\n' 
1343          + 'Copyright 2005-2009 Illinois Institute of Technology') 
1344   
1345 -    def __init__(self, parent, id, title, size=(6.0, 3.7), dpi=96, cursor=True, 
1346       location=True, crosshairs=True, selection=True, zoom=True, 
1347       autoscaleUnzoom=True, **kwds): 
 1348          """ 
1349          Creates a new PlotFrame top-level window that is the child of the 
1350          wxPython window C{parent} with the wxPython identifier C{id} and the 
1351          title of C{title}. 
1352   
1353          All of the named keyword arguments to this constructor have the same 
1354          meaning as those arguments to the constructor of C{PlotPanel}. 
1355   
1356          Any additional keyword arguments are passed to the constructor of 
1357          C{wx.Frame}. 
1358          """ 
1359          wx.Frame.__init__(self, parent, id, title, **kwds) 
1360          self.panel = PlotPanel(self, -1, size, dpi, cursor, location, 
1361              crosshairs, selection, zoom) 
1362   
1363          pData = wx.PrintData() 
1364          pData.SetPaperId(wx.PAPER_LETTER) 
1365          if callable(getattr(pData, 'SetPrinterCommand', None)): 
1366              pData.SetPrinterCommand(POSTSCRIPT_PRINTING_COMMAND) 
1367          self.printer = FigurePrinter(self, pData) 
1368   
1369          self.create_menus() 
1370          sizer = wx.BoxSizer(wx.VERTICAL) 
1371          sizer.Add(self.panel, 1, wx.ALL|wx.EXPAND, 5) 
1372          self.SetSizer(sizer) 
1373          self.Fit() 
 1374   
1376          mainMenu = wx.MenuBar() 
1377          menu = wx.Menu() 
1378   
1379          id = wx.NewId() 
1380          menu.Append(id, '&Save As...\tCtrl+S', 
1381              'Save a copy of the current plot') 
1382          wx.EVT_MENU(self, id, self.OnMenuFileSave) 
1383   
1384          menu.AppendSeparator() 
1385   
1386          if wx.Platform != '__WXMAC__': 
1387              id = wx.NewId() 
1388              menu.Append(id, 'Page Set&up...', 
1389                  'Set the size and margins of the printed figure') 
1390              wx.EVT_MENU(self, id, self.OnMenuFilePageSetup) 
1391   
1392              id = wx.NewId() 
1393              menu.Append(id, 'Print Pre&view...', 
1394                  'Preview the print version of the current plot') 
1395              wx.EVT_MENU(self, id, self.OnMenuFilePrintPreview) 
1396   
1397          id = wx.NewId() 
1398          menu.Append(id, '&Print...\tCtrl+P', 'Print the current plot') 
1399          wx.EVT_MENU(self, id, self.OnMenuFilePrint) 
1400   
1401          menu.AppendSeparator() 
1402   
1403          id = wx.NewId() 
1404          menu.Append(id, '&Close Window\tCtrl+W', 
1405              'Close the current plot window') 
1406          wx.EVT_MENU(self, id, self.OnMenuFileClose) 
1407   
1408          mainMenu.Append(menu, '&File') 
1409          menu = wx.Menu() 
1410   
1411          id = wx.NewId() 
1412          menu.Append(id, '&About...', 'Display version information') 
1413          wx.EVT_MENU(self, id, self.OnMenuHelpAbout) 
1414   
1415          mainMenu.Append(menu, '&Help') 
1416          self.SetMenuBar(mainMenu) 
 1417   
1419          """ 
1420          Handles File->Save menu events. 
1421          """ 
1422          fileName = wx.FileSelector('Save Plot', default_extension='png', 
1423              wildcard=('Portable Network Graphics (*.png)|*.png|' 
1424                  + 'Encapsulated Postscript (*.eps)|*.eps|All files (*.*)|*.*'), 
1425              parent=self, flags=wx.SAVE|wx.OVERWRITE_PROMPT) 
1426   
1427          if not fileName: 
1428              return 
1429   
1430          path, ext = os.path.splitext(fileName) 
1431          ext = ext[1:].lower() 
1432   
1433          if ext != 'png' and ext != 'eps': 
1434              error_message = ( 
1435                  'Only the PNG and EPS image formats are supported.\n' 
1436                  'A file extension of `png\' or `eps\' must be used.') 
1437              wx.MessageBox(error_message, 'Error - plotit', 
1438                  parent=self, style=wx.OK|wx.ICON_ERROR) 
1439              return 
1440   
1441          try: 
1442              self.panel.print_figure(fileName) 
1443          except IOError, e: 
1444              if e.strerror: 
1445                  err = e.strerror 
1446              else: 
1447                  err = e 
1448   
1449              wx.MessageBox('Could not save file: %s' % err, 'Error - plotit', 
1450                  parent=self, style=wx.OK|wx.ICON_ERROR) 
 1451   
1453          """ 
1454          Handles File->Page Setup menu events 
1455          """ 
1456          self.printer.pageSetup() 
 1457   
1463   
1469   
1471          """ 
1472          Handles File->Close menu events. 
1473          """ 
1474          self.Close() 
 1475   
1477          """ 
1478          Handles Help->About menu events. 
1479          """ 
1480          wx.MessageBox(self.ABOUT_MESSAGE, self.ABOUT_TITLE, parent=self, 
1481              style=wx.OK) 
 1482   
1488   
1490          """ 
1491          Enable or disable the changing mouse cursor.  When enabled, the cursor 
1492          changes from the normal arrow to a square cross when the mouse enters a 
1493          matplotlib axes on this canvas. 
1494          """ 
1495          self.panel.set_cursor(state) 
 1496   
1498          """ 
1499          Enable or disable the display of the matplotlib axes coordinates of the 
1500          mouse in the lower left corner of the canvas. 
1501          """ 
1502          self.panel.set_location(state) 
 1503   
1505          """ 
1506          Enable or disable drawing crosshairs through the mouse cursor when it 
1507          is inside a matplotlib axes. 
1508          """ 
1509          self.panel.set_crosshairs(state) 
 1510   
1512          """ 
1513          Enable or disable area selections, where user selects a rectangular 
1514          area of the canvas by left-clicking and dragging the mouse. 
1515          """ 
1516          self.panel.set_selection(state) 
 1517   
1519          """ 
1520          Enable or disable zooming in when the user makes an area selection and 
1521          zooming out again when the user right-clicks. 
1522          """ 
1523          self.panel.set_zoom(state) 
 1524   
1526          """ 
1527          Enable or disable automatic view rescaling when the user zooms out to 
1528          the initial figure. 
1529          """ 
1530          self.panel.set_autoscale_unzoom(state) 
 1531   
1533          """ 
1534          Draw the associated C{Figure} onto the screen. 
1535          """ 
1536          self.panel.draw() 
  1537   
1538   
1539   
1540   
1541   
1542   
1544      """ 
1545      A wxApp that provides a matplotlib canvas embedded in a wxPython top-level 
1546      window, encapsulating wxPython's nuts and bolts. 
1547   
1548      @cvar ABOUT_TITLE: Title of the "About" dialog. 
1549      @cvar ABOUT_MESSAGE: Contents of the "About" dialog. 
1550      """ 
1551   
1552      ABOUT_TITLE = None 
1553      ABOUT_MESSAGE = None 
1554   
1555 -    def __init__(self, title="WxMpl", size=(6.0, 3.7), dpi=96, cursor=True, 
1556       location=True, crosshairs=True, selection=True, zoom=True, **kwds): 
 1557          """ 
1558          Creates a new PlotApp, which creates a PlotFrame top-level window. 
1559   
1560          The keyword argument C{title} specifies the title of this top-level 
1561          window. 
1562   
1563          All of other the named keyword arguments to this constructor have the 
1564          same meaning as those arguments to the constructor of C{PlotPanel}. 
1565   
1566          Any additional keyword arguments are passed to the constructor of 
1567          C{wx.App}. 
1568          """ 
1569          self.title = title 
1570          self.size = size 
1571          self.dpi = dpi 
1572          self.cursor = cursor 
1573          self.location = location 
1574          self.crosshairs = crosshairs 
1575          self.selection = selection 
1576          self.zoom = zoom 
1577          wx.App.__init__(self, **kwds) 
 1578   
1592   
1598   
1600          """ 
1601          Enable or disable the changing mouse cursor.  When enabled, the cursor 
1602          changes from the normal arrow to a square cross when the mouse enters a 
1603          matplotlib axes on this canvas. 
1604          """ 
1605          self.frame.set_cursor(state) 
 1606   
1608          """ 
1609          Enable or disable the display of the matplotlib axes coordinates of the 
1610          mouse in the lower left corner of the canvas. 
1611          """ 
1612          self.frame.set_location(state) 
 1613   
1615          """ 
1616          Enable or disable drawing crosshairs through the mouse cursor when it 
1617          is inside a matplotlib axes. 
1618          """ 
1619          self.frame.set_crosshairs(state) 
 1620   
1622          """ 
1623          Enable or disable area selections, where user selects a rectangular 
1624          area of the canvas by left-clicking and dragging the mouse. 
1625          """ 
1626          self.frame.set_selection(state) 
 1627   
1629          """ 
1630          Enable or disable zooming in when the user makes an area selection and 
1631          zooming out again when the user right-clicks. 
1632          """ 
1633          self.frame.set_zoom(state) 
 1634   
1636          """ 
1637          Draw the associated C{Figure} onto the screen. 
1638          """ 
1639          self.frame.draw() 
  1640   
1641   
1642   
1643   
1644   
1645   
1647      """ 
1648      Manages a Numerical Python vector, automatically growing it as necessary to 
1649      accomodate new entries. 
1650      """ 
1652          self.data = NumPy.zeros((16,), NumPy.Float) 
1653          self.nextRow = 0 
 1654   
1656          """ 
1657          Zero and reset this buffer without releasing the underlying array. 
1658          """ 
1659          self.data[:] = 0.0 
1660          self.nextRow = 0 
 1661   
1663          """ 
1664          Zero and reset this buffer, releasing the underlying array. 
1665          """ 
1666          self.data = NumPy.zeros((16,), NumPy.Float) 
1667          self.nextRow = 0 
 1668   
1670          """ 
1671          Append a new entry to the end of this buffer's vector. 
1672          """ 
1673          nextRow = self.nextRow 
1674          data = self.data 
1675   
1676          resize = False 
1677          if nextRow == data.shape[0]: 
1678              nR = int(NumPy.ceil(self.data.shape[0]*1.5)) 
1679              resize = True 
1680   
1681          if resize: 
1682              self.data = NumPy.zeros((nR,), NumPy.Float) 
1683              self.data[0:data.shape[0]] = data 
1684   
1685          self.data[nextRow] = point 
1686          self.nextRow += 1 
 1687   
1689          """ 
1690          Returns the current vector or C{None} if the buffer contains no data. 
1691          """ 
1692          if self.nextRow == 0: 
1693              return None 
1694          else: 
1695              return self.data[0:self.nextRow] 
  1696   
1697   
1699      """ 
1700      Manages a Numerical Python matrix, automatically growing it as necessary to 
1701      accomodate new rows of entries. 
1702      """ 
1704          self.data = NumPy.zeros((16, 1), NumPy.Float) 
1705          self.nextRow = 0 
 1706   
1708          """ 
1709          Zero and reset this buffer without releasing the underlying array. 
1710          """ 
1711          self.data[:, :] = 0.0 
1712          self.nextRow = 0 
 1713   
1715          """ 
1716          Zero and reset this buffer, releasing the underlying array. 
1717          """ 
1718          self.data = NumPy.zeros((16, 1), NumPy.Float) 
1719          self.nextRow = 0 
 1720   
1722          """ 
1723          Append a new row of entries to the end of this buffer's matrix. 
1724          """ 
1725          row = NumPy.asarray(row, NumPy.Float) 
1726          nextRow = self.nextRow 
1727          data = self.data 
1728          nPts = row.shape[0] 
1729   
1730          if nPts == 0: 
1731              return 
1732   
1733          resize = True 
1734          if nextRow == data.shape[0]: 
1735              nC = data.shape[1] 
1736              nR = int(NumPy.ceil(self.data.shape[0]*1.5)) 
1737              if nC < nPts: 
1738                  nC = nPts 
1739          elif data.shape[1] < nPts: 
1740              nR = data.shape[0] 
1741              nC = nPts 
1742          else: 
1743              resize = False 
1744   
1745          if resize: 
1746              self.data = NumPy.zeros((nR, nC), NumPy.Float) 
1747              rowEnd, colEnd = data.shape 
1748              self.data[0:rowEnd, 0:colEnd] = data 
1749   
1750          self.data[nextRow, 0:nPts] = row 
1751          self.nextRow += 1 
 1752   
1754          """ 
1755          Returns the current matrix or C{None} if the buffer contains no data. 
1756          """ 
1757          if self.nextRow == 0: 
1758              return None 
1759          else: 
1760              return self.data[0:self.nextRow, :] 
  1761   
1762   
1763   
1764   
1765   
1766   
1768      """ 
1769      Returns a C{Bbox} describing the range of difference between two sets of X 
1770      and Y coordinates. 
1771      """ 
1772      return make_bbox(get_delta(X1, X2), get_delta(Y1, Y2)) 
 1773   
1774   
1776      """ 
1777      Returns the vector of contiguous, different points between two vectors. 
1778      """ 
1779      n1 = X1.shape[0] 
1780      n2 = X2.shape[0] 
1781   
1782      if n1 < n2: 
1783          return X2[n1:] 
1784      elif n1 == n2: 
1785           
1786           
1787          return X2 
1788      else: 
1789          return X2 
 1790   
1791   
1793      """ 
1794      Returns a C{Bbox} that contains the supplied sets of X and Y coordinates. 
1795      """ 
1796      if X is None or X.shape[0] == 0: 
1797          x1 = x2 = 0.0 
1798      else: 
1799          x1 = min(X) 
1800          x2 = max(X) 
1801   
1802      if Y is None or Y.shape[0] == 0: 
1803          y1 = y2 = 0.0 
1804      else: 
1805          y1 = min(Y) 
1806          y2 = max(Y) 
1807   
1808      return Bbox.from_extents(x1, y1, x2, y2) 
 1809   
1810   
1811   
1812   
1813   
1814   
1816      """ 
1817      Plots and updates lines on a matplotlib C{Axes}. 
1818      """ 
1820          """ 
1821          Create a new C{StripCharter} associated with a matplotlib C{axes}. 
1822          """ 
1823          self.axes = axes 
1824          self.channels = [] 
1825          self.lines = {} 
 1826   
1828          """ 
1829          Specify the data-providers of the lines to be plotted and updated. 
1830          """ 
1831          self.lines = None 
1832          self.channels = channels[:] 
1833   
1834           
1835          self.axes.legend_ = None 
1836          self.axes.lines = [] 
 1837   
1839          """ 
1840          Redraw the associated axes with updated lines if any of the channels' 
1841          data has changed. 
1842          """ 
1843          axes = self.axes 
1844          figureCanvas = axes.figure.canvas 
1845   
1846          zoomed = figureCanvas.zoomed(axes) 
1847   
1848          redraw = False 
1849          if self.lines is None: 
1850              self._create_plot() 
1851              redraw = True 
1852          else: 
1853              for channel in self.channels: 
1854                  redraw = self._update_channel(channel, zoomed) or redraw 
1855   
1856          if redraw: 
1857              if not zoomed: 
1858                  axes.autoscale_view() 
1859              figureCanvas.draw() 
 1860   
1862          """ 
1863          Initially plot the lines corresponding to the data-providers. 
1864          """ 
1865          self.lines = {} 
1866          axes = self.axes 
1867          styleGen = _process_plot_var_args(axes) 
1868   
1869          for channel in self.channels: 
1870              self._plot_channel(channel, styleGen) 
1871   
1872          if self.channels: 
1873              lines  = [self.lines[x] for x in self.channels] 
1874              labels = [x.get_label() for x in lines] 
1875              self.axes.legend(lines, labels, numpoints=2, 
1876                  prop=FontProperties(size='x-small')) 
 1877   
1879          """ 
1880          Initially plot a line corresponding to one of the data-providers. 
1881          """ 
1882          empty = False 
1883          x = channel.getX() 
1884          y = channel.getY() 
1885          if x is None or y is None: 
1886              x = y = [] 
1887              empty = True 
1888   
1889          line = styleGen(x, y).next() 
1890          line._wxmpl_empty_line = empty 
1891   
1892          if channel.getColor() is not None: 
1893              line.set_color(channel.getColor()) 
1894          if channel.getStyle() is not None: 
1895              line.set_linestyle(channel.getStyle()) 
1896          if channel.getMarker() is not None: 
1897              line.set_marker(channel.getMarker()) 
1898              line.set_markeredgecolor(line.get_color()) 
1899              line.set_markerfacecolor(line.get_color()) 
1900   
1901          line.set_label(channel.getLabel()) 
1902          self.lines[channel] = line 
1903          if not empty: 
1904              self.axes.add_line(line) 
 1905   
1907          """ 
1908          Replot a line corresponding to one of the data-providers if the data 
1909          has changed. 
1910          """ 
1911          if channel.hasChanged(): 
1912              channel.setChanged(False) 
1913          else: 
1914              return False 
1915   
1916          axes = self.axes 
1917          line = self.lines[channel] 
1918          newX = channel.getX() 
1919          newY = channel.getY() 
1920   
1921          if newX is None or newY is None: 
1922              return False 
1923   
1924          oldX = line._x 
1925          oldY = line._y 
1926   
1927          x, y = newX, newY 
1928          line.set_data(x, y) 
1929   
1930          if line._wxmpl_empty_line: 
1931              axes.add_line(line) 
1932              line._wxmpl_empty_line = False 
1933          else: 
1934              if line.get_transform() != axes.transData: 
1935                  xys = axes._get_verts_in_data_coords( 
1936                      line.get_transform(), zip(x, y)) 
1937              else: 
1938                  xys = NumPy.zeros((x.shape[0], 2), NumPy.Float) 
1939                  xys[:,0] = x 
1940                  xys[:,1] = y 
1941              axes.update_datalim(xys) 
1942   
1943          if zoomed: 
1944              return axes.viewLim.overlaps( 
1945                  make_delta_bbox(oldX, oldY, newX, newY)) 
1946          else: 
1947              return True 
  1948   
1949   
1950   
1951   
1952   
1953   
1955      """ 
1956      Provides data for a C{StripCharter} to plot.  Subclasses of C{Channel} 
1957      override the template methods C{getX()} and C{getY()} to provide plot data 
1958      and call C{setChanged(True)} when that data has changed. 
1959      """ 
1960 -    def __init__(self, name, color=None, style=None, marker=None): 
 1961          """ 
1962          Creates a new C{Channel} with the matplotlib label C{name}.  The 
1963          keyword arguments specify the strings for the line color, style, and 
1964          marker to use when the line is plotted. 
1965          """ 
1966          self.name = name 
1967          self.color = color 
1968          self.style = style 
1969          self.marker = marker 
1970          self.changed = False 
 1971   
1973          """ 
1974          Returns the matplotlib label for this channel of data. 
1975          """ 
1976          return self.name 
 1977   
1979          """ 
1980          Returns the line color string to use when the line is plotted, or 
1981          C{None} to use an automatically generated color. 
1982          """ 
1983          return self.color 
 1984   
1986          """ 
1987          Returns the line style string to use when the line is plotted, or 
1988          C{None} to use the default line style. 
1989          """ 
1990          return self.style 
 1991   
1993          """ 
1994          Returns the line marker string to use when the line is plotted, or 
1995          C{None} to use the default line marker. 
1996          """ 
1997          return self.marker 
 1998   
2000          """ 
2001          Returns a boolean indicating if the line data has changed. 
2002          """ 
2003          return self.changed 
 2004   
2006          """ 
2007          Sets the change indicator to the boolean value C{changed}. 
2008   
2009          @note: C{StripCharter} instances call this method after detecting a 
2010          change, so a C{Channel} cannot be shared among multiple charts. 
2011          """ 
2012          self.changed = changed 
 2013   
2015          """ 
2016          Template method that returns the vector of X axis data or C{None} if 
2017          there is no data available. 
2018          """ 
2019          return None 
 2020   
2022          """ 
2023          Template method that returns the vector of Y axis data or C{None} if 
2024          there is no data available. 
2025          """ 
2026          return None 
  2027