[GRASS-SVN] r69075 - in grass/trunk/gui/wxpython: gui_core iclass wxplot

svn_grass at osgeo.org svn_grass at osgeo.org
Thu Aug 4 04:14:52 PDT 2016


Author: annakrat
Date: 2016-08-04 04:14:52 -0700 (Thu, 04 Aug 2016)
New Revision: 69075

Added:
   grass/trunk/gui/wxpython/gui_core/wxlibplot.py
Modified:
   grass/trunk/gui/wxpython/iclass/plots.py
   grass/trunk/gui/wxpython/wxplot/base.py
   grass/trunk/gui/wxpython/wxplot/histogram.py
   grass/trunk/gui/wxpython/wxplot/profile.py
   grass/trunk/gui/wxpython/wxplot/scatter.py
Log:
wxGUI: solve #2558 and #3112 by distributing wx.lib.plot.py with GRASS

Added: grass/trunk/gui/wxpython/gui_core/wxlibplot.py
===================================================================
--- grass/trunk/gui/wxpython/gui_core/wxlibplot.py	                        (rev 0)
+++ grass/trunk/gui/wxpython/gui_core/wxlibplot.py	2016-08-04 11:14:52 UTC (rev 69075)
@@ -0,0 +1,2556 @@
+#-----------------------------------------------------------------------------
+# Name:        wx.lib.plot.py
+# Purpose:     Line, Bar and Scatter Graphs
+#
+# Author:      Gordon Williams
+#
+# Created:     2003/11/03
+# RCS-ID:      $Id$
+# Copyright:   (c) 2002
+# Licence:     Use as you wish.
+#-----------------------------------------------------------------------------
+# Included in GRASS GIS instead of importing it from wxPython
+# to overcome bug present in wxPython 3.0.2.
+# See #2558 and #3112
+#
+# 12/15/2003 - Jeff Grimmett (grimmtooth at softhome.net)
+#
+# o 2.5 compatability update.
+# o Renamed to plot.py in the wx.lib directory.
+# o Reworked test frame to work with wx demo framework. This saves a bit
+#   of tedious cut and paste, and the test app is excellent.
+#
+# 12/18/2003 - Jeff Grimmett (grimmtooth at softhome.net)
+#
+# o wxScrolledMessageDialog -> ScrolledMessageDialog
+#
+# Oct 6, 2004  Gordon Williams (g_will at cyberus.ca)
+#   - Added bar graph demo
+#   - Modified line end shape from round to square.
+#   - Removed FloatDCWrapper for conversion to ints and ints in arguments
+#
+# Oct 15, 2004  Gordon Williams (g_will at cyberus.ca)
+#   - Imported modules given leading underscore to name.
+#   - Added Cursor Line Tracking and User Point Labels.
+#   - Demo for Cursor Line Tracking and Point Labels.
+#   - Size of plot preview frame adjusted to show page better.
+#   - Added helper functions PositionUserToScreen and PositionScreenToUser in PlotCanvas.
+#   - Added functions GetClosestPoints (all curves) and GetClosestPoint (only closest curve)
+#       can be in either user coords or screen coords.
+#
+# Jun 22, 2009  Florian Hoech (florian.hoech at gmx.de)
+#   - Fixed exception when drawing empty plots on Mac OS X
+#   - Fixed exception when trying to draw point labels on Mac OS X (Mac OS X
+#     point label drawing code is still slow and only supports wx.COPY)
+#   - Moved label positions away from axis lines a bit
+#   - Added PolySpline class and modified demo 1 and 2 to use it
+#   - Added center and diagonal lines option (Set/GetEnableCenterLines,
+#     Set/GetEnableDiagonals)
+#   - Added anti-aliasing option with optional high-resolution mode
+#     (Set/GetEnableAntiAliasing, Set/GetEnableHiRes) and demo
+#   - Added option to specify exact number of tick marks to use for each axis
+#     (SetXSpec(<number>, SetYSpec(<number>) -- work like 'min', but with
+#     <number> tick marks)
+#   - Added support for background and foreground colours (enabled via
+#     SetBackgroundColour/SetForegroundColour on a PlotCanvas instance)
+#   - Changed PlotCanvas printing initialization from occuring in __init__ to
+#     occur on access. This will postpone any IPP and / or CUPS warnings
+#     which appear on stderr on some Linux systems until printing functionality
+#     is actually used.
+#
+#
+
+"""
+This is a simple light weight plotting module that can be used with
+Boa or easily integrated into your own wxPython application.  The
+emphasis is on small size and fast plotting for large data sets.  It
+has a reasonable number of features to do line and scatter graphs
+easily as well as simple bar graphs.  It is not as sophisticated or
+as powerful as SciPy Plt or Chaco.  Both of these are great packages
+but consume huge amounts of computer resources for simple plots.
+They can be found at http://scipy.com
+
+This file contains two parts; first the re-usable library stuff, then,
+after a "if __name__=='__main__'" test, a simple frame and a few default
+plots for examples and testing.
+
+Based on wxPlotCanvas
+Written by K.Hinsen, R. Srinivasan;
+Ported to wxPython Harm van der Heijden, feb 1999
+
+Major Additions Gordon Williams Feb. 2003 (g_will at cyberus.ca)
+    -More style options
+    -Zooming using mouse "rubber band"
+    -Scroll left, right
+    -Grid(graticule)
+    -Printing, preview, and page set up (margins)
+    -Axis and title labels
+    -Cursor xy axis values
+    -Doc strings and lots of comments
+    -Optimizations for large number of points
+    -Legends
+
+Did a lot of work here to speed markers up. Only a factor of 4
+improvement though. Lines are much faster than markers, especially
+filled markers.  Stay away from circles and triangles unless you
+only have a few thousand points.
+
+Times for 25,000 points
+Line - 0.078 sec
+Markers
+Square -                   0.22 sec
+dot -                      0.10
+circle -                   0.87
+cross,plus -               0.28
+triangle, triangle_down -  0.90
+
+Thanks to Chris Barker for getting this version working on Linux.
+
+Zooming controls with mouse (when enabled):
+    Left mouse drag - Zoom box.
+    Left mouse double click - reset zoom.
+    Right mouse click - zoom out centred on click location.
+"""
+
+import string as _string
+import time as _time
+import sys
+import wx
+
+# Needs NumPy
+try:
+    import numpy as np
+except:
+    msg = """
+    This module requires the NumPy module, which could not be
+    imported.  It probably is not installed (it's not part of the
+    standard Python distribution). See the Numeric Python site
+    (http://numpy.scipy.org) for information on downloading source or
+    binaries."""
+    raise ImportError("NumPy not found.\n" + msg)
+
+
+#
+# Plotting classes...
+#
+class PolyPoints:
+
+    """Base Class for lines and markers
+        - All methods are private.
+    """
+
+    def __init__(self, points, attr):
+        self._points = np.array(points).astype(np.float64)
+        self._logscale = (False, False)
+        self._pointSize = (1.0, 1.0)
+        self.currentScale = (1, 1)
+        self.currentShift = (0, 0)
+        self.scaled = self.points
+        self.attributes = {}
+        self.attributes.update(self._attributes)
+        for name, value in attr.items():
+            if name not in self._attributes.keys():
+                raise KeyError(
+                    "Style attribute incorrect. Should be one of %s" % self._attributes.keys())
+            self.attributes[name] = value
+
+    def setLogScale(self, logscale):
+        self._logscale = logscale
+
+    def __getattr__(self, name):
+        if name == 'points':
+            if len(self._points) > 0:
+                data = np.array(self._points, copy=True)
+                if self._logscale[0]:
+                    data = self.log10(data, 0)
+                if self._logscale[1]:
+                    data = self.log10(data, 1)
+                return data
+            else:
+                return self._points
+        else:
+            raise AttributeError(name)
+
+    def log10(self, data, ind):
+        data = np.compress(data[:, ind] > 0, data, 0)
+        data[:, ind] = np.log10(data[:, ind])
+        return data
+
+    def boundingBox(self):
+        if len(self.points) == 0:
+            # no curves to draw
+            # defaults to (-1,-1) and (1,1) but axis can be set in Draw
+            minXY = np.array([-1.0, -1.0])
+            maxXY = np.array([1.0, 1.0])
+        else:
+            minXY = np.minimum.reduce(self.points)
+            maxXY = np.maximum.reduce(self.points)
+        return minXY, maxXY
+
+    def scaleAndShift(self, scale=(1, 1), shift=(0, 0)):
+        if len(self.points) == 0:
+            # no curves to draw
+            return
+        if (scale is not self.currentScale) or (shift is not self.currentShift):
+            # update point scaling
+            self.scaled = scale * self.points + shift
+            self.currentScale = scale
+            self.currentShift = shift
+        # else unchanged use the current scaling
+
+    def getLegend(self):
+        return self.attributes['legend']
+
+    def getClosestPoint(self, pntXY, pointScaled=True):
+        """Returns the index of closest point on the curve, pointXY, scaledXY, distance
+            x, y in user coords
+            if pointScaled == True based on screen coords
+            if pointScaled == False based on user coords
+        """
+        if pointScaled == True:
+            # Using screen coords
+            p = self.scaled
+            pxy = self.currentScale * np.array(pntXY) + self.currentShift
+        else:
+            # Using user coords
+            p = self.points
+            pxy = np.array(pntXY)
+        # determine distance for each point
+        d = np.sqrt(np.add.reduce((p - pxy) ** 2, 1))  # sqrt(dx^2+dy^2)
+        pntIndex = np.argmin(d)
+        dist = d[pntIndex]
+        return [pntIndex, self.points[pntIndex], self.scaled[pntIndex] / self._pointSize, dist]
+
+
+class PolyLine(PolyPoints):
+
+    """Class to define line type and style
+        - All methods except __init__ are private.
+    """
+
+    _attributes = {'colour': 'black',
+                   'width': 1,
+                   'style': wx.PENSTYLE_SOLID,
+                   'legend': ''}
+
+    def __init__(self, points, **attr):
+        """
+        Creates PolyLine object
+
+        :param `points`: sequence (array, tuple or list) of (x,y) points making up line
+        :keyword `attr`: keyword attributes, default to:
+
+         ==========================  ================================
+         'colour'= 'black'           wx.Pen Colour any wx.Colour
+         'width'= 1                  Pen width
+         'style'= wx.PENSTYLE_SOLID  wx.Pen style
+         'legend'= ''                Line Legend to display
+         ==========================  ================================
+
+        """
+        PolyPoints.__init__(self, points, attr)
+
+    def draw(self, dc, printerScale, coord=None):
+        colour = self.attributes['colour']
+        width = self.attributes['width'] * printerScale * self._pointSize[0]
+        style = self.attributes['style']
+        if not isinstance(colour, wx.Colour):
+            colour = wx.NamedColour(colour)
+        pen = wx.Pen(colour, width, style)
+        pen.SetCap(wx.CAP_BUTT)
+        dc.SetPen(pen)
+        if coord is None:
+            if len(self.scaled):  # bugfix for Mac OS X
+                dc.DrawLines(self.scaled)
+        else:
+            dc.DrawLines(coord)  # draw legend line
+
+    def getSymExtent(self, printerScale):
+        """Width and Height of Marker"""
+        h = self.attributes['width'] * printerScale * self._pointSize[0]
+        w = 5 * h
+        return (w, h)
+
+
+class PolySpline(PolyLine):
+
+    """Class to define line type and style
+        - All methods except __init__ are private.
+    """
+
+    _attributes = {'colour': 'black',
+                   'width': 1,
+                   'style': wx.PENSTYLE_SOLID,
+                   'legend': ''}
+
+    def __init__(self, points, **attr):
+        """
+        Creates PolyLine object
+
+        :param `points`: sequence (array, tuple or list) of (x,y) points making up spline
+        :keyword `attr`: keyword attributes, default to:
+
+         ==========================  ================================
+         'colour'= 'black'           wx.Pen Colour any wx.Colour
+         'width'= 1                  Pen width
+         'style'= wx.PENSTYLE_SOLID  wx.Pen style
+         'legend'= ''                Line Legend to display
+         ==========================  ================================
+
+        """
+        PolyLine.__init__(self, points, **attr)
+
+    def draw(self, dc, printerScale, coord=None):
+        colour = self.attributes['colour']
+        width = self.attributes['width'] * printerScale * self._pointSize[0]
+        style = self.attributes['style']
+        if not isinstance(colour, wx.Colour):
+            colour = wx.NamedColour(colour)
+        pen = wx.Pen(colour, width, style)
+        pen.SetCap(wx.CAP_ROUND)
+        dc.SetPen(pen)
+        if coord is None:
+            if len(self.scaled):  # bugfix for Mac OS X
+                dc.DrawSpline(self.scaled)
+        else:
+            dc.DrawLines(coord)  # draw legend line
+
+
+class PolyMarker(PolyPoints):
+
+    """Class to define marker type and style
+        - All methods except __init__ are private.
+    """
+
+    _attributes = {'colour': 'black',
+                   'width': 1,
+                   'size': 2,
+                   'fillcolour': None,
+                   'fillstyle': wx.BRUSHSTYLE_SOLID,
+                   'marker': 'circle',
+                   'legend': ''}
+
+    def __init__(self, points, **attr):
+        """
+        Creates PolyMarker object
+
+        :param `points`: sequence (array, tuple or list) of (x,y) points
+        :keyword `attr`: keyword attributes, default to:
+
+         ================================ ================================
+         'colour'= 'black'                wx.Pen Colour any wx.Colour
+         'width'= 1                       Pen width
+         'size'= 2                        Marker size
+         'fillcolour'= same as colour     wx.Brush Colour any wx.Colour
+         'fillstyle'= wx.BRUSHSTYLE_SOLID wx.Brush fill style (use wx.BRUSHSTYLE_TRANSPARENT for no fill)
+         'style'= wx.FONTFAMILY_SOLID     wx.Pen style
+         'marker'= 'circle'               Marker shape
+         'legend'= ''                     Line Legend to display
+         ================================ ================================
+
+         Marker Shapes:
+         - 'circle'
+         - 'dot'
+         - 'square'
+         - 'triangle'
+         - 'triangle_down'
+         - 'cross'
+         - 'plus'
+        """
+
+        PolyPoints.__init__(self, points, attr)
+
+    def draw(self, dc, printerScale, coord=None):
+        colour = self.attributes['colour']
+        width = self.attributes['width'] * printerScale * self._pointSize[0]
+        size = self.attributes['size'] * printerScale * self._pointSize[0]
+        fillcolour = self.attributes['fillcolour']
+        fillstyle = self.attributes['fillstyle']
+        marker = self.attributes['marker']
+
+        if colour and not isinstance(colour, wx.Colour):
+            colour = wx.NamedColour(colour)
+        if fillcolour and not isinstance(fillcolour, wx.Colour):
+            fillcolour = wx.NamedColour(fillcolour)
+
+        dc.SetPen(wx.Pen(colour, width))
+        if fillcolour:
+            dc.SetBrush(wx.Brush(fillcolour, fillstyle))
+        else:
+            dc.SetBrush(wx.Brush(colour, fillstyle))
+        if coord is None:
+            if len(self.scaled):  # bugfix for Mac OS X
+                self._drawmarkers(dc, self.scaled, marker, size)
+        else:
+            self._drawmarkers(dc, coord, marker, size)  # draw legend marker
+
+    def getSymExtent(self, printerScale):
+        """Width and Height of Marker"""
+        s = 5 * self.attributes['size'] * printerScale * self._pointSize[0]
+        return (s, s)
+
+    def _drawmarkers(self, dc, coords, marker, size=1):
+        f = eval('self._' + marker)
+        f(dc, coords, size)
+
+    def _circle(self, dc, coords, size=1):
+        fact = 2.5 * size
+        wh = 5.0 * size
+        rect = np.zeros((len(coords), 4), np.float) + [0.0, 0.0, wh, wh]
+        rect[:, 0:2] = coords - [fact, fact]
+        dc.DrawEllipseList(rect.astype(np.int32))
+
+    def _dot(self, dc, coords, size=1):
+        dc.DrawPointList(coords)
+
+    def _square(self, dc, coords, size=1):
+        fact = 2.5 * size
+        wh = 5.0 * size
+        rect = np.zeros((len(coords), 4), np.float) + [0.0, 0.0, wh, wh]
+        rect[:, 0:2] = coords - [fact, fact]
+        dc.DrawRectangleList(rect.astype(np.int32))
+
+    def _triangle(self, dc, coords, size=1):
+        shape = [(-2.5 * size, 1.44 * size),
+                 (2.5 * size, 1.44 * size), (0.0, -2.88 * size)]
+        poly = np.repeat(coords, 3, 0)
+        poly.shape = (len(coords), 3, 2)
+        poly += shape
+        dc.DrawPolygonList(poly.astype(np.int32))
+
+    def _triangle_down(self, dc, coords, size=1):
+        shape = [(-2.5 * size, -1.44 * size),
+                 (2.5 * size, -1.44 * size), (0.0, 2.88 * size)]
+        poly = np.repeat(coords, 3, 0)
+        poly.shape = (len(coords), 3, 2)
+        poly += shape
+        dc.DrawPolygonList(poly.astype(np.int32))
+
+    def _cross(self, dc, coords, size=1):
+        fact = 2.5 * size
+        for f in [[-fact, -fact, fact, fact], [-fact, fact, fact, -fact]]:
+            lines = np.concatenate((coords, coords), axis=1) + f
+            dc.DrawLineList(lines.astype(np.int32))
+
+    def _plus(self, dc, coords, size=1):
+        fact = 2.5 * size
+        for f in [[-fact, 0, fact, 0], [0, -fact, 0, fact]]:
+            lines = np.concatenate((coords, coords), axis=1) + f
+            dc.DrawLineList(lines.astype(np.int32))
+
+
+class PlotGraphics:
+
+    """Container to hold PolyXXX objects and graph labels
+        - All methods except __init__ are private.
+    """
+
+    def __init__(self, objects, title='', xLabel='', yLabel=''):
+        """Creates PlotGraphics object
+        objects - list of PolyXXX objects to make graph
+        title - title shown at top of graph
+        xLabel - label shown on x-axis
+        yLabel - label shown on y-axis
+        """
+        if type(objects) not in [list, tuple]:
+            raise TypeError("objects argument should be list or tuple")
+        self.objects = objects
+        self.title = title
+        self.xLabel = xLabel
+        self.yLabel = yLabel
+        self._pointSize = (1.0, 1.0)
+
+    def setLogScale(self, logscale):
+        if type(logscale) != tuple:
+            raise TypeError(
+                'logscale must be a tuple of bools, e.g. (False, False)')
+        if len(self.objects) == 0:
+            return
+        for o in self.objects:
+            o.setLogScale(logscale)
+
+    def boundingBox(self):
+        p1, p2 = self.objects[0].boundingBox()
+        for o in self.objects[1:]:
+            p1o, p2o = o.boundingBox()
+            p1 = np.minimum(p1, p1o)
+            p2 = np.maximum(p2, p2o)
+        return p1, p2
+
+    def scaleAndShift(self, scale=(1, 1), shift=(0, 0)):
+        for o in self.objects:
+            o.scaleAndShift(scale, shift)
+
+    def setPrinterScale(self, scale):
+        """Thickens up lines and markers only for printing"""
+        self.printerScale = scale
+
+    def setXLabel(self, xLabel=''):
+        """Set the X axis label on the graph"""
+        self.xLabel = xLabel
+
+    def setYLabel(self, yLabel=''):
+        """Set the Y axis label on the graph"""
+        self.yLabel = yLabel
+
+    def setTitle(self, title=''):
+        """Set the title at the top of graph"""
+        self.title = title
+
+    def getXLabel(self):
+        """Get x axis label string"""
+        return self.xLabel
+
+    def getYLabel(self):
+        """Get y axis label string"""
+        return self.yLabel
+
+    def getTitle(self, title=''):
+        """Get the title at the top of graph"""
+        return self.title
+
+    def draw(self, dc):
+        for o in self.objects:
+            # t=_time.clock()          # profile info
+            o._pointSize = self._pointSize
+            o.draw(dc, self.printerScale)
+            #dt= _time.clock()-t
+            #print(o, "time=", dt)
+
+    def getSymExtent(self, printerScale):
+        """Get max width and height of lines and markers symbols for legend"""
+        self.objects[0]._pointSize = self._pointSize
+        symExt = self.objects[0].getSymExtent(printerScale)
+        for o in self.objects[1:]:
+            o._pointSize = self._pointSize
+            oSymExt = o.getSymExtent(printerScale)
+            symExt = np.maximum(symExt, oSymExt)
+        return symExt
+
+    def getLegendNames(self):
+        """Returns list of legend names"""
+        lst = [None] * len(self)
+        for i in range(len(self)):
+            lst[i] = self.objects[i].getLegend()
+        return lst
+
+    def __len__(self):
+        return len(self.objects)
+
+    def __getitem__(self, item):
+        return self.objects[item]
+
+
+#-------------------------------------------------------------------------
+# Main window that you will want to import into your application.
+
+class PlotCanvas(wx.Panel):
+
+    """
+    Subclass of a wx.Panel which holds two scrollbars and the actual
+    plotting canvas (self.canvas). It allows for simple general plotting
+    of data with zoom, labels, and automatic axis scaling."""
+
+    def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
+                 size=wx.DefaultSize, style=0, name="plotCanvas"):
+        """Constructs a panel, which can be a child of a frame or
+        any other non-control window"""
+
+        wx.Panel.__init__(self, parent, id, pos, size, style, name)
+        self._isWindowCreated = False
+
+        sizer = wx.FlexGridSizer(2, 2, 0, 0)
+        self.canvas = wx.Window(self, -1)
+        self.sb_vert = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL)
+        self.sb_vert.SetScrollbar(0, 1000, 1000, 1000)
+        self.sb_hor = wx.ScrollBar(self, -1, style=wx.SB_HORIZONTAL)
+        self.sb_hor.SetScrollbar(0, 1000, 1000, 1000)
+
+        sizer.Add(self.canvas, 1, wx.EXPAND)
+        sizer.Add(self.sb_vert, 0, wx.EXPAND)
+        sizer.Add(self.sb_hor, 0, wx.EXPAND)
+        sizer.Add((0, 0))
+
+        sizer.AddGrowableRow(0, 1)
+        sizer.AddGrowableCol(0, 1)
+
+        self.sb_vert.Show(False)
+        self.sb_hor.Show(False)
+
+        self.SetSizer(sizer)
+        self.Fit()
+
+        self.border = (1, 1)
+
+        self.SetBackgroundColour("white")
+
+        # Create some mouse events for zooming
+        self.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
+        self.canvas.Bind(wx.EVT_LEFT_UP, self.OnMouseLeftUp)
+        self.canvas.Bind(wx.EVT_MOTION, self.OnMotion)
+        self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.OnMouseDoubleClick)
+        self.canvas.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseRightDown)
+
+        # scrollbar events
+        self.Bind(wx.EVT_SCROLL_THUMBTRACK, self.OnScroll)
+        self.Bind(wx.EVT_SCROLL_PAGEUP, self.OnScroll)
+        self.Bind(wx.EVT_SCROLL_PAGEDOWN, self.OnScroll)
+        self.Bind(wx.EVT_SCROLL_LINEUP, self.OnScroll)
+        self.Bind(wx.EVT_SCROLL_LINEDOWN, self.OnScroll)
+
+        # set curser as cross-hairs
+        self.canvas.SetCursor(wx.CROSS_CURSOR)
+##        self.HandCursor = wx.Cursor(Hand.GetImage())
+        self.HandCursor = wx.CursorFromImage(Hand.GetImage())
+
+        self.GrabHandCursor = wx.CursorFromImage(GrabHand.GetImage())
+
+##        self.GrabHandCursor = wx.Cursor(GrabHand.GetImage())
+##        self.MagCursor = wx.Cursor(MagPlus.GetImage())
+        self.MagCursor = wx.CursorFromImage(MagPlus.GetImage())
+
+
+        # Things for printing
+        self._print_data = None
+        self._pageSetupData = None
+        self.printerScale = 1
+        self.parent = parent
+
+        # scrollbar variables
+        self._sb_ignore = False
+        self._adjustingSB = False
+        self._sb_xfullrange = 0
+        self._sb_yfullrange = 0
+        self._sb_xunit = 0
+        self._sb_yunit = 0
+
+        self._dragEnabled = False
+        self._screenCoordinates = np.array([0.0, 0.0])
+
+        self._logscale = (False, False)
+
+        # Zooming variables
+        self._zoomInFactor = 0.5
+        self._zoomOutFactor = 2
+        self._zoomCorner1 = np.array([0.0, 0.0])  # left mouse down corner
+        self._zoomCorner2 = np.array([0.0, 0.0])   # left mouse up corner
+        self._zoomEnabled = False
+        self._hasDragged = False
+
+        # Drawing Variables
+        self.last_draw = None
+        self._pointScale = 1
+        self._pointShift = 0
+        self._xSpec = 'auto'
+        self._ySpec = 'auto'
+        self._gridEnabled = False
+        self._legendEnabled = False
+        self._titleEnabled = True
+        self._centerLinesEnabled = False
+        self._diagonalsEnabled = False
+
+        # Fonts
+        self._fontCache = {}
+        self._fontSizeAxis = 10
+        self._fontSizeTitle = 15
+        self._fontSizeLegend = 7
+
+        # pointLabels
+        self._pointLabelEnabled = False
+        self.last_PointLabel = None
+        self._pointLabelFunc = None
+        self.canvas.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
+        if sys.platform != "darwin":
+            self._logicalFunction = wx.EQUIV  # (NOT src) XOR dst
+        else:
+            # wx.EQUIV not supported on Mac OS X
+            self._logicalFunction = wx.COPY
+
+        self._useScientificNotation = False
+
+        self._antiAliasingEnabled = False
+        self._hiResEnabled = False
+        self._pointSize = (1.0, 1.0)
+        self._fontScale = 1.0
+
+        self.canvas.Bind(wx.EVT_PAINT, self.OnPaint)
+        self.canvas.Bind(wx.EVT_SIZE, self.OnSize)
+        # OnSize called to make sure the buffer is initialized.
+        # This might result in OnSize getting called twice on some
+        # platforms at initialization, but little harm done.
+        self.OnSize(None)  # sets the initial size based on client size
+
+        self._gridColour = wx.BLACK
+        if '__WXGTK__' in wx.PlatformInfo:
+            self.Bind(wx.EVT_WINDOW_CREATE, self.doSetWindowCreated)
+        else:
+            self.doSetWindowCreated(None)
+
+    def doSetWindowCreated(self, evt):
+        # OnSize called to make sure the buffer is initialized.
+        # This might result in OnSize getting called twice on some
+        # platforms at initialization, but little harm done.
+        self._isWindowCreated = True
+        self.OnSize(None)
+
+    def SetCursor(self, cursor):
+        self.canvas.SetCursor(cursor)
+
+    def GetGridColour(self):
+        return self._gridColour
+
+    def SetGridColour(self, colour):
+        if isinstance(colour, wx.Colour):
+            self._gridColour = colour
+        else:
+            self._gridColour = wx.Colour(colour)
+
+    # SaveFile
+    def SaveFile(self, fileName=''):
+        """Saves the file to the type specified in the extension. If no file
+        name is specified a dialog box is provided.  Returns True if sucessful,
+        otherwise False.
+
+        .bmp  Save a Windows bitmap file.
+        .xbm  Save an X bitmap file.
+        .xpm  Save an XPM bitmap file.
+        .png  Save a Portable Network Graphics file.
+        .jpg  Save a Joint Photographic Experts Group file.
+        """
+        extensions = {
+            "bmp": wx.BITMAP_TYPE_BMP,       # Save a Windows bitmap file.
+            "xbm": wx.BITMAP_TYPE_XBM,       # Save an X bitmap file.
+            "xpm": wx.BITMAP_TYPE_XPM,       # Save an XPM bitmap file.
+            "jpg": wx.BITMAP_TYPE_JPEG,      # Save a JPG file.
+            "png": wx.BITMAP_TYPE_PNG,       # Save a PNG file.
+        }
+
+        fType = _string.lower(fileName[-3:])
+        dlg1 = None
+        while fType not in extensions:
+
+            if dlg1:                   # FileDialog exists: Check for extension
+                dlg2 = wx.MessageDialog(self, 'File name extension\n'
+                                        'must be one of\nbmp, xbm, xpm, png, or jpg',
+                                        'File Name Error', wx.OK | wx.ICON_ERROR)
+                try:
+                    dlg2.ShowModal()
+                finally:
+                    dlg2.Destroy()
+            # FileDialog doesn't exist: just check one
+            else:
+                dlg1 = wx.FileDialog(
+                    self,
+                    "Choose a file with extension bmp, gif, xbm, xpm, png, or jpg", ".", "",
+                    "BMP files (*.bmp)|*.bmp|XBM files (*.xbm)|*.xbm|XPM file (*.xpm)|*.xpm|PNG files (*.png)|*.png|JPG files (*.jpg)|*.jpg",
+                    wx.SAVE | wx.OVERWRITE_PROMPT
+                )
+
+            if dlg1.ShowModal() == wx.ID_OK:
+                fileName = dlg1.GetPath()
+                fType = _string.lower(fileName[-3:])
+            else:                      # exit without saving
+                dlg1.Destroy()
+                return False
+
+        if dlg1:
+            dlg1.Destroy()
+
+        # Save Bitmap
+        res = self._Buffer.SaveFile(fileName, extensions[fType])
+        return res
+
+    @property
+    def print_data(self):
+        if not self._print_data:
+            self._print_data = wx.PrintData()
+            self._print_data.SetPaperId(wx.PAPER_LETTER)
+            self._print_data.SetOrientation(wx.LANDSCAPE)
+        return self._print_data
+
+    @property
+    def pageSetupData(self):
+        if not self._pageSetupData:
+            self._pageSetupData = wx.PageSetupDialogData()
+            self._pageSetupData.SetMarginBottomRight((25, 25))
+            self._pageSetupData.SetMarginTopLeft((25, 25))
+            self._pageSetupData.SetPrintData(self.print_data)
+        return self._pageSetupData
+
+    def PageSetup(self):
+        """Brings up the page setup dialog"""
+        data = self.pageSetupData
+        data.SetPrintData(self.print_data)
+        dlg = wx.PageSetupDialog(self.parent, data)
+        try:
+            if dlg.ShowModal() == wx.ID_OK:
+                data = dlg.GetPageSetupData()  # returns wx.PageSetupDialogData
+                # updates page parameters from dialog
+                self.pageSetupData.SetMarginBottomRight(
+                    data.GetMarginBottomRight())
+                self.pageSetupData.SetMarginTopLeft(data.GetMarginTopLeft())
+                self.pageSetupData.SetPrintData(data.GetPrintData())
+                self._print_data = wx.PrintData(
+                    data.GetPrintData())  # updates print_data
+        finally:
+            dlg.Destroy()
+
+    def Printout(self, paper=None):
+        """Print current plot."""
+        if paper != None:
+            self.print_data.SetPaperId(paper)
+        pdd = wx.PrintDialogData(self.print_data)
+        printer = wx.Printer(pdd)
+        out = PlotPrintout(self)
+        print_ok = printer.Print(self.parent, out)
+        if print_ok:
+            self._print_data = wx.PrintData(
+                printer.GetPrintDialogData().GetPrintData())
+        out.Destroy()
+
+    def PrintPreview(self):
+        """Print-preview current plot."""
+        printout = PlotPrintout(self)
+        printout2 = PlotPrintout(self)
+        self.preview = wx.PrintPreview(printout, printout2, self.print_data)
+        if not self.preview.IsOk():
+            wx.MessageDialog(self, "Print Preview failed.\n"
+                             "Check that default printer is configured\n",
+                             "Print error", wx.OK | wx.CENTRE).ShowModal()
+        self.preview.SetZoom(40)
+        # search up tree to find frame instance
+        frameInst = self
+        while not isinstance(frameInst, wx.Frame):
+            frameInst = frameInst.GetParent()
+        frame = wx.PreviewFrame(self.preview, frameInst, "Preview")
+        frame.Initialize()
+        frame.SetPosition(self.GetPosition())
+        frame.SetSize((600, 550))
+        frame.Centre(wx.BOTH)
+        frame.Show(True)
+
+    def setLogScale(self, logscale):
+        if type(logscale) != tuple:
+            raise TypeError(
+                'logscale must be a tuple of bools, e.g. (False, False)')
+        if self.last_draw is not None:
+            graphics, xAxis, yAxis = self.last_draw
+            graphics.setLogScale(logscale)
+            self.last_draw = (graphics, None, None)
+        self.SetXSpec('min')
+        self.SetYSpec('min')
+        self._logscale = logscale
+
+    def getLogScale(self):
+        return self._logscale
+
+    def SetFontSizeAxis(self, point=10):
+        """Set the tick and axis label font size (default is 10 point)"""
+        self._fontSizeAxis = point
+
+    def GetFontSizeAxis(self):
+        """Get current tick and axis label font size in points"""
+        return self._fontSizeAxis
+
+    def SetFontSizeTitle(self, point=15):
+        """Set Title font size (default is 15 point)"""
+        self._fontSizeTitle = point
+
+    def GetFontSizeTitle(self):
+        """Get current Title font size in points"""
+        return self._fontSizeTitle
+
+    def SetFontSizeLegend(self, point=7):
+        """Set Legend font size (default is 7 point)"""
+        self._fontSizeLegend = point
+
+    def GetFontSizeLegend(self):
+        """Get current Legend font size in points"""
+        return self._fontSizeLegend
+
+    def SetShowScrollbars(self, value):
+        """Set True to show scrollbars"""
+        if value not in [True, False]:
+            raise TypeError("Value should be True or False")
+        if value == self.GetShowScrollbars():
+            return
+        self.sb_vert.Show(value)
+        self.sb_hor.Show(value)
+        wx.CallAfter(self.Layout)
+
+    def GetShowScrollbars(self):
+        """Set True to show scrollbars"""
+        return self.sb_vert.IsShown()
+
+    def SetUseScientificNotation(self, useScientificNotation):
+        self._useScientificNotation = useScientificNotation
+
+    def GetUseScientificNotation(self):
+        return self._useScientificNotation
+
+    def SetEnableAntiAliasing(self, enableAntiAliasing):
+        """Set True to enable anti-aliasing."""
+        self._antiAliasingEnabled = enableAntiAliasing
+        self.Redraw()
+
+    def GetEnableAntiAliasing(self):
+        return self._antiAliasingEnabled
+
+    def SetEnableHiRes(self, enableHiRes):
+        """Set True to enable high-resolution mode when using anti-aliasing."""
+        self._hiResEnabled = enableHiRes
+        self.Redraw()
+
+    def GetEnableHiRes(self):
+        return self._hiResEnabled
+
+    def SetEnableDrag(self, value):
+        """Set True to enable drag."""
+        if value not in [True, False]:
+            raise TypeError("Value should be True or False")
+        if value:
+            if self.GetEnableZoom():
+                self.SetEnableZoom(False)
+            self.SetCursor(self.HandCursor)
+        else:
+            self.SetCursor(wx.CROSS_CURSOR)
+        self._dragEnabled = value
+
+    def GetEnableDrag(self):
+        return self._dragEnabled
+
+    def SetEnableZoom(self, value):
+        """Set True to enable zooming."""
+        if value not in [True, False]:
+            raise TypeError("Value should be True or False")
+        if value:
+            if self.GetEnableDrag():
+                self.SetEnableDrag(False)
+            self.SetCursor(self.MagCursor)
+        else:
+            self.SetCursor(wx.CROSS_CURSOR)
+        self._zoomEnabled = value
+
+    def GetEnableZoom(self):
+        """True if zooming enabled."""
+        return self._zoomEnabled
+
+    def SetEnableGrid(self, value):
+        """Set True, 'Horizontal' or 'Vertical' to enable grid."""
+        if value not in [True, False, 'Horizontal', 'Vertical']:
+            raise TypeError(
+                "Value should be True, False, Horizontal or Vertical")
+        self._gridEnabled = value
+        self.Redraw()
+
+    def GetEnableGrid(self):
+        """True if grid enabled."""
+        return self._gridEnabled
+
+    def SetEnableCenterLines(self, value):
+        """Set True, 'Horizontal' or 'Vertical' to enable center line(s)."""
+        if value not in [True, False, 'Horizontal', 'Vertical']:
+            raise TypeError(
+                "Value should be True, False, Horizontal or Vertical")
+        self._centerLinesEnabled = value
+        self.Redraw()
+
+    def GetEnableCenterLines(self):
+        """True if grid enabled."""
+        return self._centerLinesEnabled
+
+    def SetEnableDiagonals(self, value):
+        """Set True, 'Bottomleft-Topright' or 'Bottomright-Topleft' to enable
+        center line(s)."""
+        if value not in [True, False, 'Bottomleft-Topright', 'Bottomright-Topleft']:
+            raise TypeError(
+                "Value should be True, False, Bottomleft-Topright or Bottomright-Topleft")
+        self._diagonalsEnabled = value
+        self.Redraw()
+
+    def GetEnableDiagonals(self):
+        """True if grid enabled."""
+        return self._diagonalsEnabled
+
+    def SetEnableLegend(self, value):
+        """Set True to enable legend."""
+        if value not in [True, False]:
+            raise TypeError("Value should be True or False")
+        self._legendEnabled = value
+        self.Redraw()
+
+    def GetEnableLegend(self):
+        """True if Legend enabled."""
+        return self._legendEnabled
+
+    def SetEnableTitle(self, value):
+        """Set True to enable title."""
+        if value not in [True, False]:
+            raise TypeError("Value should be True or False")
+        self._titleEnabled = value
+        self.Redraw()
+
+    def GetEnableTitle(self):
+        """True if title enabled."""
+        return self._titleEnabled
+
+    def SetEnablePointLabel(self, value):
+        """Set True to enable pointLabel."""
+        if value not in [True, False]:
+            raise TypeError("Value should be True or False")
+        self._pointLabelEnabled = value
+        self.Redraw()  # will erase existing pointLabel if present
+        self.last_PointLabel = None
+
+    def GetEnablePointLabel(self):
+        """True if pointLabel enabled."""
+        return self._pointLabelEnabled
+
+    def SetPointLabelFunc(self, func):
+        """Sets the function with custom code for pointLabel drawing
+            ******** more info needed ***************
+        """
+        self._pointLabelFunc = func
+
+    def GetPointLabelFunc(self):
+        """Returns pointLabel Drawing Function"""
+        return self._pointLabelFunc
+
+    def Reset(self):
+        """Unzoom the plot."""
+        self.last_PointLabel = None  # reset pointLabel
+        if self.last_draw is not None:
+            self._Draw(self.last_draw[0])
+
+    def ScrollRight(self, units):
+        """Move view right number of axis units."""
+        self.last_PointLabel = None  # reset pointLabel
+        if self.last_draw is not None:
+            graphics, xAxis, yAxis = self.last_draw
+            xAxis = (xAxis[0] + units, xAxis[1] + units)
+            self._Draw(graphics, xAxis, yAxis)
+
+    def ScrollUp(self, units):
+        """Move view up number of axis units."""
+        self.last_PointLabel = None  # reset pointLabel
+        if self.last_draw is not None:
+            graphics, xAxis, yAxis = self.last_draw
+            yAxis = (yAxis[0] + units, yAxis[1] + units)
+            self._Draw(graphics, xAxis, yAxis)
+
+    def GetXY(self, event):
+        """Wrapper around _getXY, which handles log scales"""
+        x, y = self._getXY(event)
+        if self.getLogScale()[0]:
+            x = np.power(10, x)
+        if self.getLogScale()[1]:
+            y = np.power(10, y)
+        return x, y
+
+    def _getXY(self, event):
+        """Takes a mouse event and returns the XY user axis values."""
+        x, y = self.PositionScreenToUser(event.GetPosition())
+        return x, y
+
+    def PositionUserToScreen(self, pntXY):
+        """Converts User position to Screen Coordinates"""
+        userPos = np.array(pntXY)
+        x, y = userPos * self._pointScale + self._pointShift
+        return x, y
+
+    def PositionScreenToUser(self, pntXY):
+        """Converts Screen position to User Coordinates"""
+        screenPos = np.array(pntXY)
+        x, y = (screenPos - self._pointShift) / self._pointScale
+        return x, y
+
+    def SetXSpec(self, type='auto'):
+        """xSpec- defines x axis type. Can be 'none', 'min' or 'auto'
+        where:
+
+        * 'none' - shows no axis or tick mark values
+        * 'min' - shows min bounding box values
+        * 'auto' - rounds axis range to sensible values
+        * <number> - like 'min', but with <number> tick marks
+        """
+        self._xSpec = type
+
+    def SetYSpec(self, type='auto'):
+        """ySpec- defines x axis type. Can be 'none', 'min' or 'auto'
+        where:
+
+        * 'none' - shows no axis or tick mark values
+        * 'min' - shows min bounding box values
+        * 'auto' - rounds axis range to sensible values
+        * <number> - like 'min', but with <number> tick marks
+        """
+        self._ySpec = type
+
+    def GetXSpec(self):
+        """Returns current XSpec for axis"""
+        return self._xSpec
+
+    def GetYSpec(self):
+        """Returns current YSpec for axis"""
+        return self._ySpec
+
+    def GetXMaxRange(self):
+        xAxis = self._getXMaxRange()
+        if self.getLogScale()[0]:
+            xAxis = np.power(10, xAxis)
+        return xAxis
+
+    def _getXMaxRange(self):
+        """Returns (minX, maxX) x-axis range for displayed graph"""
+        graphics = self.last_draw[0]
+        p1, p2 = graphics.boundingBox()     # min, max points of graphics
+        xAxis = self._axisInterval(self._xSpec, p1[0], p2[0])  # in user units
+        return xAxis
+
+    def GetYMaxRange(self):
+        yAxis = self._getYMaxRange()
+        if self.getLogScale()[1]:
+            yAxis = np.power(10, yAxis)
+        return yAxis
+
+    def _getYMaxRange(self):
+        """Returns (minY, maxY) y-axis range for displayed graph"""
+        graphics = self.last_draw[0]
+        p1, p2 = graphics.boundingBox()     # min, max points of graphics
+        yAxis = self._axisInterval(self._ySpec, p1[1], p2[1])
+        return yAxis
+
+    def GetXCurrentRange(self):
+        xAxis = self._getXCurrentRange()
+        if self.getLogScale()[0]:
+            xAxis = np.power(10, xAxis)
+        return xAxis
+
+    def _getXCurrentRange(self):
+        """Returns (minX, maxX) x-axis for currently displayed portion of graph"""
+        return self.last_draw[1]
+
+    def GetYCurrentRange(self):
+        yAxis = self._getYCurrentRange()
+        if self.getLogScale()[1]:
+            yAxis = np.power(10, yAxis)
+        return yAxis
+
+    def _getYCurrentRange(self):
+        """Returns (minY, maxY) y-axis for currently displayed portion of graph"""
+        return self.last_draw[2]
+
+    def Draw(self, graphics, xAxis=None, yAxis=None, dc=None):
+        """Wrapper around _Draw, which handles log axes"""
+
+        graphics.setLogScale(self.getLogScale())
+
+        # check Axis is either tuple or none
+        if type(xAxis) not in [type(None), tuple]:
+            raise TypeError(
+                "xAxis should be None or (minX,maxX)" + str(type(xAxis)))
+        if type(yAxis) not in [type(None), tuple]:
+            raise TypeError(
+                "yAxis should be None or (minY,maxY)" + str(type(xAxis)))
+
+        # check case for axis = (a,b) where a==b caused by improper zooms
+        if xAxis != None:
+            if xAxis[0] == xAxis[1]:
+                return
+            if self.getLogScale()[0]:
+                xAxis = np.log10(xAxis)
+        if yAxis != None:
+            if yAxis[0] == yAxis[1]:
+                return
+            if self.getLogScale()[1]:
+                yAxis = np.log10(yAxis)
+        self._Draw(graphics, xAxis, yAxis, dc)
+
+    def _Draw(self, graphics, xAxis=None, yAxis=None, dc=None):
+        """\
+        Draw objects in graphics with specified x and y axis.
+        graphics- instance of PlotGraphics with list of PolyXXX objects
+        xAxis - tuple with (min, max) axis range to view
+        yAxis - same as xAxis
+        dc - drawing context - doesn't have to be specified.
+        If it's not, the offscreen buffer is used
+        """
+
+        if dc is None:
+            # sets new dc and clears it
+            dc = wx.BufferedDC(wx.ClientDC(self.canvas), self._Buffer)
+            bbr = wx.Brush(self.GetBackgroundColour(), wx.BRUSHSTYLE_SOLID)
+            dc.SetBackground(bbr)
+            dc.SetBackgroundMode(wx.SOLID)
+            dc.Clear()
+        if self._antiAliasingEnabled:
+            if not isinstance(dc, wx.GCDC):
+                try:
+                    dc = wx.GCDC(dc)
+                except Exception:
+                    pass
+                else:
+                    if self._hiResEnabled:
+                        # high precision - each logical unit is 1/20 of a point
+                        dc.SetMapMode(wx.MM_TWIPS)
+                    self._pointSize = tuple(
+                        1.0 / lscale for lscale in dc.GetLogicalScale())
+                    self._setSize()
+        elif self._pointSize != (1.0, 1.0):
+            self._pointSize = (1.0, 1.0)
+            self._setSize()
+        if (sys.platform in ("darwin", "win32") or not isinstance(dc, wx.GCDC) or wx.VERSION >= (2, 9)):
+            self._fontScale = sum(self._pointSize) / 2.0
+        else:
+            # on Linux, we need to correct the font size by a certain factor if wx.GCDC is used,
+            # to make text the same size as if wx.GCDC weren't used
+            screenppi = map(float, wx.ScreenDC().GetPPI())
+            ppi = dc.GetPPI()
+            self._fontScale = (screenppi[
+                               0] / ppi[0] * self._pointSize[0] + screenppi[1] / ppi[1] * self._pointSize[1]) / 2.0
+        graphics._pointSize = self._pointSize
+
+        dc.SetTextForeground(self.GetForegroundColour())
+        dc.SetTextBackground(self.GetBackgroundColour())
+
+        # dc.Clear()
+
+        # set font size for every thing but title and legend
+        dc.SetFont(self._getFont(self._fontSizeAxis))
+
+        # sizes axis to axis type, create lower left and upper right corners of
+        # plot
+        if xAxis is None or yAxis is None:
+            # One or both axis not specified in Draw
+            p1, p2 = graphics.boundingBox()     # min, max points of graphics
+            if xAxis is None:
+                xAxis = self._axisInterval(
+                    self._xSpec, p1[0], p2[0])  # in user units
+            if yAxis is None:
+                yAxis = self._axisInterval(self._ySpec, p1[1], p2[1])
+            # Adjust bounding box for axis spec
+            # lower left corner user scale (xmin,ymin)
+            p1[0], p1[1] = xAxis[0], yAxis[0]
+            # upper right corner user scale (xmax,ymax)
+            p2[0], p2[1] = xAxis[1], yAxis[1]
+        else:
+            # Both axis specified in Draw
+            # lower left corner user scale (xmin,ymin)
+            p1 = np.array([xAxis[0], yAxis[0]])
+            # upper right corner user scale (xmax,ymax)
+            p2 = np.array([xAxis[1], yAxis[1]])
+
+        # saves most recient values
+        self.last_draw = (graphics, np.array(xAxis), np.array(yAxis))
+
+        # Get ticks and textExtents for axis if required
+        if self._xSpec is not 'none':
+            xticks = self._xticks(xAxis[0], xAxis[1])
+        else:
+            xticks = None
+        if xticks:
+            # w h of x axis text last number on axis
+            xTextExtent = dc.GetTextExtent(xticks[-1][1])
+        else:
+            xTextExtent = (0, 0)  # No text for ticks
+        if self._ySpec is not 'none':
+            yticks = self._yticks(yAxis[0], yAxis[1])
+        else:
+            yticks = None
+        if yticks:
+            if self.getLogScale()[1]:
+                yTextExtent = dc.GetTextExtent('-2e-2')
+            else:
+                yTextExtentBottom = dc.GetTextExtent(yticks[0][1])
+                yTextExtentTop = dc.GetTextExtent(yticks[-1][1])
+                yTextExtent = (max(yTextExtentBottom[0], yTextExtentTop[0]),
+                               max(yTextExtentBottom[1], yTextExtentTop[1]))
+        else:
+            yticks = None
+            yTextExtent = (0, 0)  # No text for ticks
+
+        # TextExtents for Title and Axis Labels
+        titleWH, xLabelWH, yLabelWH = self._titleLablesWH(dc, graphics)
+
+        # TextExtents for Legend
+        legendBoxWH, legendSymExt, legendTextExt = self._legendWH(dc, graphics)
+
+        # room around graph area
+        # use larger of number width or legend width
+        rhsW = max(xTextExtent[0], legendBoxWH[0]) + 5 * self._pointSize[0]
+        lhsW = yTextExtent[0] + yLabelWH[1] + 3 * self._pointSize[0]
+        bottomH = max(
+            xTextExtent[1], yTextExtent[1] / 2.) + xLabelWH[1] + 2 * self._pointSize[1]
+        topH = yTextExtent[1] / 2. + titleWH[1]
+        # make plot area smaller by text size
+        textSize_scale = np.array([rhsW + lhsW, bottomH + topH])
+        # shift plot area by this amount
+        textSize_shift = np.array([lhsW, bottomH])
+
+        # draw title if requested
+        if self._titleEnabled:
+            dc.SetFont(self._getFont(self._fontSizeTitle))
+            titlePos = (self.plotbox_origin[0] + lhsW + (self.plotbox_size[0] - lhsW - rhsW) / 2. - titleWH[0] / 2.,
+                        self.plotbox_origin[1] - self.plotbox_size[1])
+            dc.DrawText(graphics.getTitle(), titlePos[0], titlePos[1])
+
+        # draw label text
+        dc.SetFont(self._getFont(self._fontSizeAxis))
+        xLabelPos = (self.plotbox_origin[0] + lhsW + (self.plotbox_size[0] - lhsW - rhsW) / 2. - xLabelWH[0] / 2.,
+                     self.plotbox_origin[1] - xLabelWH[1])
+        dc.DrawText(graphics.getXLabel(), xLabelPos[0], xLabelPos[1])
+        yLabelPos = (self.plotbox_origin[0] - 3 * self._pointSize[0],
+                     self.plotbox_origin[1] - bottomH - (self.plotbox_size[1] - bottomH - topH) / 2. + yLabelWH[0] / 2.)
+        if graphics.getYLabel():  # bug fix for Linux
+            dc.DrawRotatedText(
+                graphics.getYLabel(), yLabelPos[0], yLabelPos[1], 90)
+
+        # drawing legend makers and text
+        if self._legendEnabled:
+            self._drawLegend(
+                dc, graphics, rhsW, topH, legendBoxWH, legendSymExt, legendTextExt)
+
+        # allow for scaling and shifting plotted points
+        scale = (self.plotbox_size - textSize_scale) / \
+            (p2 - p1) * np.array((1, -1))
+        shift = -p1 * scale + self.plotbox_origin + \
+            textSize_shift * np.array((1, -1))
+        # make available for mouse events
+        self._pointScale = scale / self._pointSize
+        self._pointShift = shift / self._pointSize
+        self._drawAxes(dc, p1, p2, scale, shift, xticks, yticks)
+
+        graphics.scaleAndShift(scale, shift)
+        # thicken up lines and markers if printing
+        graphics.setPrinterScale(self.printerScale)
+
+        # set clipping area so drawing does not occur outside axis box
+        ptx, pty, rectWidth, rectHeight = self._point2ClientCoord(p1, p2)
+        # allow graph to overlap axis lines by adding units to width and height
+        dc.SetClippingRegion(ptx * self._pointSize[0], pty * self._pointSize[
+                             1], rectWidth * self._pointSize[0] + 2, rectHeight * self._pointSize[1] + 1)
+        # Draw the lines and markers
+        #start = _time.clock()
+        graphics.draw(dc)
+        # print("entire graphics drawing took: %f second"%(_time.clock() - start))
+        # remove the clipping region
+        dc.DestroyClippingRegion()
+
+        self._adjustScrollbars()
+
+    def Redraw(self, dc=None):
+        """Redraw the existing plot."""
+        if self.last_draw is not None:
+            graphics, xAxis, yAxis = self.last_draw
+            self._Draw(graphics, xAxis, yAxis, dc)
+
+    def Clear(self):
+        """Erase the window."""
+        self.last_PointLabel = None  # reset pointLabel
+        dc = wx.BufferedDC(wx.ClientDC(self.canvas), self._Buffer)
+        bbr = wx.Brush(self.GetBackgroundColour(), wx.SOLID)
+        dc.SetBackground(bbr)
+        dc.SetBackgroundMode(wx.SOLID)
+        dc.Clear()
+        if self._antiAliasingEnabled:
+            try:
+                dc = wx.GCDC(dc)
+            except Exception:
+                pass
+        dc.SetTextForeground(self.GetForegroundColour())
+        dc.SetTextBackground(self.GetBackgroundColour())
+        self.last_draw = None
+
+    def Zoom(self, Center, Ratio):
+        """ Zoom on the plot
+            Centers on the X,Y coords given in Center
+            Zooms by the Ratio = (Xratio, Yratio) given
+        """
+        self.last_PointLabel = None  # reset maker
+        x, y = Center
+        if self.last_draw != None:
+            (graphics, xAxis, yAxis) = self.last_draw
+            w = (xAxis[1] - xAxis[0]) * Ratio[0]
+            h = (yAxis[1] - yAxis[0]) * Ratio[1]
+            xAxis = (x - w / 2, x + w / 2)
+            yAxis = (y - h / 2, y + h / 2)
+            self._Draw(graphics, xAxis, yAxis)
+
+    def GetClosestPoints(self, pntXY, pointScaled=True):
+        """Returns list with
+            [curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
+            list for each curve.
+            Returns [] if no curves are being plotted.
+
+            x, y in user coords
+            if pointScaled == True based on screen coords
+            if pointScaled == False based on user coords
+        """
+        if self.last_draw is None:
+            # no graph available
+            return []
+        graphics, xAxis, yAxis = self.last_draw
+        l = []
+        for curveNum, obj in enumerate(graphics):
+            # check there are points in the curve
+            if len(obj.points) == 0:
+                continue  # go to next obj
+            #[curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
+            cn = [curveNum] + \
+                [obj.getLegend()] + obj.getClosestPoint(pntXY, pointScaled)
+            l.append(cn)
+        return l
+
+    def GetClosestPoint(self, pntXY, pointScaled=True):
+        """Returns list with
+            [curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
+            list for only the closest curve.
+            Returns [] if no curves are being plotted.
+
+            x, y in user coords
+            if pointScaled == True based on screen coords
+            if pointScaled == False based on user coords
+        """
+        # closest points on screen based on screen scaling (pointScaled= True)
+        # list [curveNumber, index, pointXY, scaledXY, distance] for each curve
+        closestPts = self.GetClosestPoints(pntXY, pointScaled)
+        if closestPts == []:
+            return []  # no graph present
+        # find one with least distance
+        dists = [c[-1] for c in closestPts]
+        mdist = min(dists)  # Min dist
+        i = dists.index(mdist)  # index for min dist
+        return closestPts[i]  # this is the closest point on closest curve
+
+    GetClosetPoint = GetClosestPoint
+
+    def UpdatePointLabel(self, mDataDict):
+        """Updates the pointLabel point on screen with data contained in
+            mDataDict.
+
+            mDataDict will be passed to your function set by
+            SetPointLabelFunc.  It can contain anything you
+            want to display on the screen at the scaledXY point
+            you specify.
+
+            This function can be called from parent window with onClick,
+            onMotion events etc.
+        """
+        if self.last_PointLabel != None:
+            # compare pointXY
+            if np.sometrue(mDataDict["pointXY"] != self.last_PointLabel["pointXY"]):
+                # closest changed
+                self._drawPointLabel(self.last_PointLabel)  # erase old
+                self._drawPointLabel(mDataDict)  # plot new
+        else:
+            # just plot new with no erase
+            self._drawPointLabel(mDataDict)  # plot new
+        # save for next erase
+        self.last_PointLabel = mDataDict
+
+    # event handlers **********************************
+    def OnMotion(self, event):
+        if self._zoomEnabled and event.LeftIsDown():
+            if self._hasDragged:
+                self._drawRubberBand(
+                    self._zoomCorner1, self._zoomCorner2)  # remove old
+            else:
+                self._hasDragged = True
+            self._zoomCorner2[0], self._zoomCorner2[1] = self._getXY(event)
+            self._drawRubberBand(
+                self._zoomCorner1, self._zoomCorner2)  # add new
+        elif self._dragEnabled and event.LeftIsDown():
+            coordinates = event.GetPosition()
+            newpos, oldpos = map(np.array, map(
+                self.PositionScreenToUser, [coordinates, self._screenCoordinates]))
+            dist = newpos - oldpos
+            self._screenCoordinates = coordinates
+
+            if self.last_draw is not None:
+                graphics, xAxis, yAxis = self.last_draw
+                yAxis -= dist[1]
+                xAxis -= dist[0]
+                self._Draw(graphics, xAxis, yAxis)
+
+    def OnMouseLeftDown(self, event):
+        self._zoomCorner1[0], self._zoomCorner1[1] = self._getXY(event)
+        self._screenCoordinates = np.array(event.GetPosition())
+        if self._dragEnabled:
+            self.SetCursor(self.GrabHandCursor)
+            self.canvas.CaptureMouse()
+
+    def OnMouseLeftUp(self, event):
+        if self._zoomEnabled:
+            if self._hasDragged == True:
+                self._drawRubberBand(
+                    self._zoomCorner1, self._zoomCorner2)  # remove old
+                self._zoomCorner2[0], self._zoomCorner2[1] = self._getXY(event)
+                self._hasDragged = False  # reset flag
+                minX, minY = np.minimum(self._zoomCorner1, self._zoomCorner2)
+                maxX, maxY = np.maximum(self._zoomCorner1, self._zoomCorner2)
+                self.last_PointLabel = None  # reset pointLabel
+                if self.last_draw != None:
+                    self._Draw(
+                        self.last_draw[0], xAxis=(minX, maxX), yAxis = (minY, maxY), dc = None)
+            # else: # A box has not been drawn, zoom in on a point
+            # this interfered with the double click, so I've disables it.
+            #    X,Y = self._getXY(event)
+            #    self.Zoom( (X,Y), (self._zoomInFactor,self._zoomInFactor) )
+        if self._dragEnabled:
+            self.SetCursor(self.HandCursor)
+            if self.canvas.HasCapture():
+                self.canvas.ReleaseMouse()
+
+    def OnMouseDoubleClick(self, event):
+        if self._zoomEnabled:
+            # Give a little time for the click to be totally finished
+            # before (possibly) removing the scrollbars and trigering
+            # size events, etc.
+            wx.CallLater(200, self.Reset)
+
+    def OnMouseRightDown(self, event):
+        if self._zoomEnabled:
+            X, Y = self._getXY(event)
+            self.Zoom((X, Y), (self._zoomOutFactor, self._zoomOutFactor))
+
+    def OnPaint(self, event):
+        # All that is needed here is to draw the buffer to screen
+        if self.last_PointLabel != None:
+            self._drawPointLabel(self.last_PointLabel)  # erase old
+            self.last_PointLabel = None
+        dc = wx.BufferedPaintDC(self.canvas, self._Buffer)
+        if self._antiAliasingEnabled:
+            try:
+                dc = wx.GCDC(dc)
+            except Exception:
+                pass
+
+    def OnSize(self, event):
+        # The Buffer init is done here, to make sure the buffer is always
+        # the same size as the Window
+        Size = self.canvas.GetClientSize()
+        Size.width = max(1, Size.width)
+        Size.height = max(1, Size.height)
+
+        # Make new offscreen bitmap: this bitmap will always have the
+        # current drawing in it, so it can be used to save the image to
+        # a file, or whatever.
+        self._Buffer = wx.EmptyBitmap(Size.width, Size.height)
+        self._setSize()
+
+        if not self._isWindowCreated:
+            return
+
+        self.last_PointLabel = None  # reset pointLabel
+
+        if self.last_draw is None:
+            self.Clear()
+        else:
+            graphics, xSpec, ySpec = self.last_draw
+            self._Draw(graphics, xSpec, ySpec)
+
+    def OnLeave(self, event):
+        """Used to erase pointLabel when mouse outside window"""
+        if self.last_PointLabel != None:
+            self._drawPointLabel(self.last_PointLabel)  # erase old
+            self.last_PointLabel = None
+
+    def OnScroll(self, evt):
+        if not self._adjustingSB:
+            self._sb_ignore = True
+            sbpos = evt.GetPosition()
+
+            if evt.GetOrientation() == wx.VERTICAL:
+                fullrange, pagesize = self.sb_vert.GetRange(
+                ), self.sb_vert.GetPageSize()
+                sbpos = fullrange - pagesize - sbpos
+                dist = sbpos * self._sb_xunit - \
+                    (self._getXCurrentRange()[0] - self._sb_xfullrange)
+                self.ScrollUp(dist)
+
+            if evt.GetOrientation() == wx.HORIZONTAL:
+                dist = sbpos * self._sb_xunit - \
+                    (self._getXCurrentRange()[0] - self._sb_xfullrange[0])
+                self.ScrollRight(dist)
+
+    # Private Methods **************************************************
+    def _setSize(self, width=None, height=None):
+        """DC width and height."""
+        if width is None:
+            (self.width, self.height) = self.canvas.GetClientSize()
+        else:
+            self.width, self.height = width, height
+        self.width *= self._pointSize[0]  # high precision
+        self.height *= self._pointSize[1]  # high precision
+        self.plotbox_size = 0.97 * np.array([self.width, self.height])
+        xo = 0.5 * (self.width - self.plotbox_size[0])
+        yo = self.height - 0.5 * (self.height - self.plotbox_size[1])
+        self.plotbox_origin = np.array([xo, yo])
+
+    def _setPrinterScale(self, scale):
+        """Used to thicken lines and increase marker size for print out."""
+        # line thickness on printer is very thin at 600 dot/in. Markers small
+        self.printerScale = scale
+
+    def _printDraw(self, printDC):
+        """Used for printing."""
+        if self.last_draw != None:
+            graphics, xSpec, ySpec = self.last_draw
+            self._Draw(graphics, xSpec, ySpec, printDC)
+
+    def _drawPointLabel(self, mDataDict):
+        """Draws and erases pointLabels"""
+        width = self._Buffer.GetWidth()
+        height = self._Buffer.GetHeight()
+        if sys.platform != "darwin":
+            tmp_Buffer = wx.EmptyBitmap(width, height)
+            dcs = wx.MemoryDC()
+            dcs.SelectObject(tmp_Buffer)
+            dcs.Clear()
+        else:
+            tmp_Buffer = self._Buffer.GetSubBitmap((0, 0, width, height))
+            dcs = wx.MemoryDC(self._Buffer)
+        self._pointLabelFunc(dcs, mDataDict)  # custom user pointLabel function
+
+        dc = wx.ClientDC(self.canvas)
+        dc = wx.BufferedDC(dc, self._Buffer)
+        # this will erase if called twice
+        dc.Blit(0, 0, width, height, dcs, 0, 0, self._logicalFunction)
+        if sys.platform == "darwin":
+            self._Buffer = tmp_Buffer
+
+    def _drawLegend(self, dc, graphics, rhsW, topH, legendBoxWH, legendSymExt, legendTextExt):
+        """Draws legend symbols and text"""
+        # top right hand corner of graph box is ref corner
+        trhc = self.plotbox_origin + \
+            (self.plotbox_size - [rhsW, topH]) * [1, -1]
+        # border space between legend sym and graph box
+        legendLHS = .091 * legendBoxWH[0]
+        # 1.1 used as space between lines
+        lineHeight = max(legendSymExt[1], legendTextExt[1]) * 1.1
+        dc.SetFont(self._getFont(self._fontSizeLegend))
+        for i in range(len(graphics)):
+            o = graphics[i]
+            s = i * lineHeight
+            if isinstance(o, PolyMarker):
+                # draw marker with legend
+                pnt = (trhc[0] + legendLHS + legendSymExt[0] / 2.,
+                       trhc[1] + s + lineHeight / 2.)
+                o.draw(dc, self.printerScale, coord=np.array([pnt]))
+            elif isinstance(o, PolyLine):
+                # draw line with legend
+                pnt1 = (trhc[0] + legendLHS, trhc[1] + s + lineHeight / 2.)
+                pnt2 = (trhc[0] + legendLHS + legendSymExt[0],
+                        trhc[1] + s + lineHeight / 2.)
+                o.draw(dc, self.printerScale, coord=np.array([pnt1, pnt2]))
+            else:
+                raise TypeError(
+                    "object is neither PolyMarker or PolyLine instance")
+            # draw legend txt
+            pnt = (trhc[0] + legendLHS + legendSymExt[0] + 5 * self._pointSize[0],
+                   trhc[1] + s + lineHeight / 2. - legendTextExt[1] / 2)
+            dc.DrawText(o.getLegend(), pnt[0], pnt[1])
+        dc.SetFont(self._getFont(self._fontSizeAxis))  # reset
+
+    def _titleLablesWH(self, dc, graphics):
+        """Draws Title and labels and returns width and height for each"""
+        # TextExtents for Title and Axis Labels
+        dc.SetFont(self._getFont(self._fontSizeTitle))
+        if self._titleEnabled:
+            title = graphics.getTitle()
+            titleWH = dc.GetTextExtent(title)
+        else:
+            titleWH = (0, 0)
+        dc.SetFont(self._getFont(self._fontSizeAxis))
+        xLabel, yLabel = graphics.getXLabel(), graphics.getYLabel()
+        xLabelWH = dc.GetTextExtent(xLabel)
+        yLabelWH = dc.GetTextExtent(yLabel)
+        return titleWH, xLabelWH, yLabelWH
+
+    def _legendWH(self, dc, graphics):
+        """Returns the size in screen units for legend box"""
+        if self._legendEnabled != True:
+            legendBoxWH = symExt = txtExt = (0, 0)
+        else:
+            # find max symbol size
+            symExt = graphics.getSymExtent(self.printerScale)
+            # find max legend text extent
+            dc.SetFont(self._getFont(self._fontSizeLegend))
+            txtList = graphics.getLegendNames()
+            txtExt = dc.GetTextExtent(txtList[0])
+            for txt in graphics.getLegendNames()[1:]:
+                txtExt = np.maximum(txtExt, dc.GetTextExtent(txt))
+            maxW = symExt[0] + txtExt[0]
+            maxH = max(symExt[1], txtExt[1])
+            # padding .1 for lhs of legend box and space between lines
+            maxW = maxW * 1.1
+            maxH = maxH * 1.1 * len(txtList)
+            dc.SetFont(self._getFont(self._fontSizeAxis))
+            legendBoxWH = (maxW, maxH)
+        return (legendBoxWH, symExt, txtExt)
+
+    def _drawRubberBand(self, corner1, corner2):
+        """Draws/erases rect box from corner1 to corner2"""
+        ptx, pty, rectWidth, rectHeight = self._point2ClientCoord(
+            corner1, corner2)
+        # draw rectangle
+        dc = wx.ClientDC(self.canvas)
+        dc.SetPen(wx.Pen(wx.BLACK))
+        dc.SetBrush(wx.Brush(wx.WHITE, wx.BRUSHSTYLE_TRANSPARENT))
+        dc.SetLogicalFunction(wx.INVERT)
+        dc.DrawRectangle(ptx, pty, rectWidth, rectHeight)
+        dc.SetLogicalFunction(wx.COPY)
+
+    def _getFont(self, size):
+        """Take font size, adjusts if printing and returns wx.Font"""
+        s = size * self.printerScale * self._fontScale
+        of = self.GetFont()
+        # Linux speed up to get font from cache rather than X font server
+        key = (int(s), of.GetFamily(), of.GetStyle(), of.GetWeight())
+        font = self._fontCache.get(key, None)
+        if font:
+            return font                 # yeah! cache hit
+        else:
+            font = wx.Font(
+                int(s), of.GetFamily(), of.GetStyle(), of.GetWeight())
+            self._fontCache[key] = font
+            return font
+
+    def _point2ClientCoord(self, corner1, corner2):
+        """Converts user point coords to client screen int coords x,y,width,height"""
+        c1 = np.array(corner1)
+        c2 = np.array(corner2)
+        # convert to screen coords
+        pt1 = c1 * self._pointScale + self._pointShift
+        pt2 = c2 * self._pointScale + self._pointShift
+        # make height and width positive
+        pul = np.minimum(pt1, pt2)  # Upper left corner
+        plr = np.maximum(pt1, pt2)  # Lower right corner
+        rectWidth, rectHeight = plr - pul
+        ptx, pty = pul
+        return ptx, pty, rectWidth, rectHeight
+
+    def _axisInterval(self, spec, lower, upper):
+        """Returns sensible axis range for given spec"""
+        if spec == 'none' or spec == 'min' or isinstance(spec, (float, int)):
+            if lower == upper:
+                return lower - 0.5, upper + 0.5
+            else:
+                return lower, upper
+        elif spec == 'auto':
+            range = upper - lower
+            if range == 0.:
+                return lower - 0.5, upper + 0.5
+            log = np.log10(range)
+            power = np.floor(log)
+            fraction = log - power
+            if fraction <= 0.05:
+                power = power - 1
+            grid = 10. ** power
+            lower = lower - lower % grid
+            mod = upper % grid
+            if mod != 0:
+                upper = upper - mod + grid
+            return lower, upper
+        elif type(spec) == type(()):
+            lower, upper = spec
+            if lower <= upper:
+                return lower, upper
+            else:
+                return upper, lower
+        else:
+            raise ValueError(str(spec) + ': illegal axis specification')
+
+    def _drawAxes(self, dc, p1, p2, scale, shift, xticks, yticks):
+
+        # increases thickness for printing only
+        penWidth = self.printerScale * self._pointSize[0]
+        dc.SetPen(wx.Pen(self._gridColour, penWidth))
+
+        # set length of tick marks--long ones make grid
+        if self._gridEnabled:
+            x, y, width, height = self._point2ClientCoord(p1, p2)
+            if self._gridEnabled == 'Horizontal':
+                yTickLength = (width / 2.0 + 1) * self._pointSize[1]
+                xTickLength = 3 * self.printerScale * self._pointSize[0]
+            elif self._gridEnabled == 'Vertical':
+                yTickLength = 3 * self.printerScale * self._pointSize[1]
+                xTickLength = (height / 2.0 + 1) * self._pointSize[0]
+            else:
+                yTickLength = (width / 2.0 + 1) * self._pointSize[1]
+                xTickLength = (height / 2.0 + 1) * self._pointSize[0]
+        else:
+            # lengthens lines for printing
+            yTickLength = 3 * self.printerScale * self._pointSize[1]
+            xTickLength = 3 * self.printerScale * self._pointSize[0]
+
+        if self._xSpec is not 'none':
+            lower, upper = p1[0], p2[0]
+            text = 1
+            # miny, maxy and tick lengths
+            for y, d in [(p1[1], -xTickLength), (p2[1], xTickLength)]:
+                for x, label in xticks:
+                    pt = scale * np.array([x, y]) + shift
+                    # draws tick mark d units
+                    dc.DrawLine(pt[0], pt[1], pt[0], pt[1] + d)
+                    if text:
+                        dc.DrawText(
+                            label, pt[0], pt[1] + 2 * self._pointSize[1])
+                a1 = scale * np.array([lower, y]) + shift
+                a2 = scale * np.array([upper, y]) + shift
+                # draws upper and lower axis line
+                dc.DrawLine(a1[0], a1[1], a2[0], a2[1])
+                text = 0  # axis values not drawn on top side
+
+        if self._ySpec is not 'none':
+            lower, upper = p1[1], p2[1]
+            text = 1
+            h = dc.GetCharHeight()
+            for x, d in [(p1[0], -yTickLength), (p2[0], yTickLength)]:
+                for y, label in yticks:
+                    pt = scale * np.array([x, y]) + shift
+                    dc.DrawLine(pt[0], pt[1], pt[0] - d, pt[1])
+                    if text:
+                        dc.DrawText(label, pt[0] - dc.GetTextExtent(label)[0] - 3 * self._pointSize[0],
+                                    pt[1] - 0.75 * h)
+                a1 = scale * np.array([x, lower]) + shift
+                a2 = scale * np.array([x, upper]) + shift
+                dc.DrawLine(a1[0], a1[1], a2[0], a2[1])
+                text = 0    # axis values not drawn on right side
+
+        if self._centerLinesEnabled:
+            if self._centerLinesEnabled in ('Horizontal', True):
+                y1 = scale[1] * p1[1] + shift[1]
+                y2 = scale[1] * p2[1] + shift[1]
+                y = (y1 - y2) / 2.0 + y2
+                dc.DrawLine(
+                    scale[0] * p1[0] + shift[0], y, scale[0] * p2[0] + shift[0], y)
+            if self._centerLinesEnabled in ('Vertical', True):
+                x1 = scale[0] * p1[0] + shift[0]
+                x2 = scale[0] * p2[0] + shift[0]
+                x = (x1 - x2) / 2.0 + x2
+                dc.DrawLine(
+                    x, scale[1] * p1[1] + shift[1], x, scale[1] * p2[1] + shift[1])
+
+        if self._diagonalsEnabled:
+            if self._diagonalsEnabled in ('Bottomleft-Topright', True):
+                dc.DrawLine(scale[0] * p1[0] + shift[0], scale[1] * p1[1] +
+                            shift[1], scale[0] * p2[0] + shift[0], scale[1] * p2[1] + shift[1])
+            if self._diagonalsEnabled in ('Bottomright-Topleft', True):
+                dc.DrawLine(scale[0] * p1[0] + shift[0], scale[1] * p2[1] +
+                            shift[1], scale[0] * p2[0] + shift[0], scale[1] * p1[1] + shift[1])
+
+    def _xticks(self, *args):
+        if self._logscale[0]:
+            return self._logticks(*args)
+        else:
+            attr = {'numticks': self._xSpec}
+            return self._ticks(*args, **attr)
+
+    def _yticks(self, *args):
+        if self._logscale[1]:
+            return self._logticks(*args)
+        else:
+            attr = {'numticks': self._ySpec}
+            return self._ticks(*args, **attr)
+
+    def _logticks(self, lower, upper):
+        #lower,upper = map(np.log10,[lower,upper])
+        # print('logticks',lower,upper)
+        ticks = []
+        mag = np.power(10, np.floor(lower))
+        if upper - lower > 6:
+            t = np.power(10, np.ceil(lower))
+            base = np.power(10, np.floor((upper - lower) / 6))
+
+            def inc(t):
+                return t * base - t
+        else:
+            t = np.ceil(np.power(10, lower) / mag) * mag
+
+            def inc(t):
+                return 10 ** int(np.floor(np.log10(t) + 1e-16))
+        majortick = int(np.log10(mag))
+        while t <= pow(10, upper):
+            if majortick != int(np.floor(np.log10(t) + 1e-16)):
+                majortick = int(np.floor(np.log10(t) + 1e-16))
+                ticklabel = '1e%d' % majortick
+            else:
+                if upper - lower < 2:
+                    minortick = int(t / pow(10, majortick) + .5)
+                    ticklabel = '%de%d' % (minortick, majortick)
+                else:
+                    ticklabel = ''
+            ticks.append((np.log10(t), ticklabel))
+            t += inc(t)
+        if len(ticks) == 0:
+            ticks = [(0, '')]
+        return ticks
+
+    def _ticks(self, lower, upper, numticks=None):
+        if isinstance(numticks, (float, int)):
+            ideal = (upper - lower) / float(numticks)
+        else:
+            ideal = (upper - lower) / 7.
+        log = np.log10(ideal)
+        power = np.floor(log)
+        if isinstance(numticks, (float, int)):
+            grid = ideal
+        else:
+            fraction = log - power
+            factor = 1.
+            error = fraction
+            for f, lf in self._multiples:
+                e = np.fabs(fraction - lf)
+                if e < error:
+                    error = e
+                    factor = f
+            grid = factor * 10. ** power
+        if self._useScientificNotation and (power > 4 or power < -4):
+            format = '%+7.1e'
+        elif power >= 0:
+            digits = max(1, int(power))
+            format = '%' + repr(digits) + '.0f'
+        else:
+            digits = -int(power)
+            format = '%' + repr(digits + 2) + '.' + repr(digits) + 'f'
+        ticks = []
+        t = -grid * np.floor(-lower / grid)
+        while t <= upper:
+            if t == -0:
+                t = 0
+            ticks.append((t, format % (t,)))
+            t = t + grid
+        return ticks
+
+    _multiples = [(2., np.log10(2.)), (5., np.log10(5.))]
+
+    def _adjustScrollbars(self):
+        if self._sb_ignore:
+            self._sb_ignore = False
+            return
+
+        if not self.GetShowScrollbars():
+            return
+
+        self._adjustingSB = True
+        needScrollbars = False
+
+        # horizontal scrollbar
+        r_current = self._getXCurrentRange()
+        r_max = list(self._getXMaxRange())
+        sbfullrange = float(self.sb_hor.GetRange())
+
+        r_max[0] = min(r_max[0], r_current[0])
+        r_max[1] = max(r_max[1], r_current[1])
+
+        self._sb_xfullrange = r_max
+
+        unit = (r_max[1] - r_max[0]) / float(self.sb_hor.GetRange())
+        pos = int((r_current[0] - r_max[0]) / unit)
+
+        if pos >= 0:
+            pagesize = int((r_current[1] - r_current[0]) / unit)
+
+            self.sb_hor.SetScrollbar(pos, pagesize, sbfullrange, pagesize)
+            self._sb_xunit = unit
+            needScrollbars = needScrollbars or (pagesize != sbfullrange)
+        else:
+            self.sb_hor.SetScrollbar(0, 1000, 1000, 1000)
+
+        # vertical scrollbar
+        r_current = self._getYCurrentRange()
+        r_max = list(self._getYMaxRange())
+        sbfullrange = float(self.sb_vert.GetRange())
+
+        r_max[0] = min(r_max[0], r_current[0])
+        r_max[1] = max(r_max[1], r_current[1])
+
+        self._sb_yfullrange = r_max
+
+        unit = (r_max[1] - r_max[0]) / sbfullrange
+        pos = int((r_current[0] - r_max[0]) / unit)
+
+        if pos >= 0:
+            pagesize = int((r_current[1] - r_current[0]) / unit)
+            pos = (sbfullrange - 1 - pos - pagesize)
+            self.sb_vert.SetScrollbar(pos, pagesize, sbfullrange, pagesize)
+            self._sb_yunit = unit
+            needScrollbars = needScrollbars or (pagesize != sbfullrange)
+        else:
+            self.sb_vert.SetScrollbar(0, 1000, 1000, 1000)
+
+        self.SetShowScrollbars(needScrollbars)
+        self._adjustingSB = False
+
+#-------------------------------------------------------------------------
+# Used to layout the printer page
+
+
+class PlotPrintout(wx.Printout):
+
+    """Controls how the plot is made in printing and previewing"""
+    # Do not change method names in this class,
+    # we have to override wx.Printout methods here!
+
+    def __init__(self, graph):
+        """graph is instance of plotCanvas to be printed or previewed"""
+        wx.Printout.__init__(self)
+        self.graph = graph
+
+    def HasPage(self, page):
+        if page == 1:
+            return True
+        else:
+            return False
+
+    def GetPageInfo(self):
+        return (1, 1, 1, 1)  # disable page numbers
+
+    def OnPrintPage(self, page):
+        dc = self.GetDC()  # allows using floats for certain functions
+##        print("PPI Printer",self.GetPPIPrinter())
+##        print("PPI Screen", self.GetPPIScreen())
+##        print("DC GetSize", dc.GetSize())
+##        print("GetPageSizePixels", self.GetPageSizePixels())
+        # Note PPIScreen does not give the correct number
+        # Calulate everything for printer and then scale for preview
+        PPIPrinter = self.GetPPIPrinter()        # printer dots/inch (w,h)
+        # PPIScreen= self.GetPPIScreen()          # screen dots/inch (w,h)
+        dcSize = dc.GetSize()                    # DC size
+        if self.graph._antiAliasingEnabled and not isinstance(dc, wx.GCDC):
+            try:
+                dc = wx.GCDC(dc)
+            except Exception:
+                pass
+            else:
+                if self.graph._hiResEnabled:
+                    # high precision - each logical unit is 1/20 of a point
+                    dc.SetMapMode(wx.MM_TWIPS)
+        pageSize = self.GetPageSizePixels()  # page size in terms of pixcels
+        clientDcSize = self.graph.GetClientSize()
+
+        # find what the margins are (mm)
+        margLeftSize, margTopSize = self.graph.pageSetupData.GetMarginTopLeft()
+        margRightSize, margBottomSize = self.graph.pageSetupData.GetMarginBottomRight()
+
+        # calculate offset and scale for dc
+        pixLeft = margLeftSize * PPIPrinter[0] / 25.4  # mm*(dots/in)/(mm/in)
+        pixRight = margRightSize * PPIPrinter[0] / 25.4
+        pixTop = margTopSize * PPIPrinter[1] / 25.4
+        pixBottom = margBottomSize * PPIPrinter[1] / 25.4
+
+        plotAreaW = pageSize[0] - (pixLeft + pixRight)
+        plotAreaH = pageSize[1] - (pixTop + pixBottom)
+
+        # ratio offset and scale to screen size if preview
+        if self.IsPreview():
+            ratioW = float(dcSize[0]) / pageSize[0]
+            ratioH = float(dcSize[1]) / pageSize[1]
+            pixLeft *= ratioW
+            pixTop *= ratioH
+            plotAreaW *= ratioW
+            plotAreaH *= ratioH
+
+        # rescale plot to page or preview plot area
+        self.graph._setSize(plotAreaW, plotAreaH)
+
+        # Set offset and scale
+        dc.SetDeviceOrigin(pixLeft, pixTop)
+
+        # Thicken up pens and increase marker size for printing
+        ratioW = float(plotAreaW) / clientDcSize[0]
+        ratioH = float(plotAreaH) / clientDcSize[1]
+        aveScale = (ratioW + ratioH) / 2
+        if self.graph._antiAliasingEnabled and not self.IsPreview():
+            scale = dc.GetUserScale()
+            dc.SetUserScale(
+                scale[0] / self.graph._pointSize[0], scale[1] / self.graph._pointSize[1])
+        self.graph._setPrinterScale(aveScale)  # tickens up pens for printing
+
+        self.graph._printDraw(dc)
+        # rescale back to original
+        self.graph._setSize()
+        self.graph._setPrinterScale(1)
+        self.graph.Redraw()  # to get point label scale and shift correct
+
+        return True
+
+
+#----------------------------------------------------------------------
+from wx.lib.embeddedimage import PyEmbeddedImage
+
+MagPlus = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAOFJ"
+    "REFUeJy1VdEOxCAIo27//8XbuKfuPASGZ0Zisoi2FJABbZM3bY8c13lo5GvbjioBPAUEB0Yc"
+    "VZ0iGRRc56Ee8DcikEgrJD8EFpzRegQASiRtBtzuA0hrdRPYQxaEKyJPG6IHyiK3xnNZvUSS"
+    "NvUuzgYh0il4y14nCFPk5XgmNbRbQbVotGo9msj47G3UXJ7fuz8Q8FAGEu0/PbZh2D3NoshU"
+    "1VUydBGVZKMimlGeErdNGUmf/x7YpjMjcf8HVYvS2adr6aFVlCy/5Ijk9q8SeCR9isJR8SeJ"
+    "8pv7S0Wu2Acr0qdj3w7DRAAAAABJRU5ErkJggg==")
+
+GrabHand = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAARFJ"
+    "REFUeJy1VdESgzAIS2j//4s3s5fRQ6Rad5M7H0oxCZhWSpK1TjwUBCBJAIBItL1fijlfe1yJ"
+    "8noCGC9KgrXO7f0SyZEDAF/H2opsAHv9V/548nplT5Jo7YAFQKQ1RMWzmHUS96suqdBrHkuV"
+    "uxpdJjCS8CfGXWdJ2glzcquKSR5c46QOtCpgNyIHj6oieAXg3282QvMX45hy8a8H0VonJZUO"
+    "clesjOPg/dhBTq64o1Kacz4Ri2x5RKsf8+wcWQaJJL+A+xRcZHeQeBKjK+5EFiVJ4xy4x2Mn"
+    "1Vk4U5/DWmfPieiqbye7a3tV/cCsWKu76K76KUFFchVnhigJ/hmktelm/m3e3b8k+Ec8PqLH"
+    "CT4JRfyK9o1xYwAAAABJRU5ErkJggg==")
+
+Hand = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAARBJ"
+    "REFUeJytluECwiAIhDn1/Z942/UnjCGoq+6XNeWDC1xAqbKr6zyo61Ibds60J8GBT0yS3IEM"
+    "ABuIpJTa4IOLiAAQksuKyixLH1ShHgTgZl8KiALxOsODPoEMkgJ25Su6zoO3ZrjRnI96OLIq"
+    "k7dsqOCboDa4XV/nwQEQVeFtmMnvbSJja+oagKBUaLn9hzd7VipRa9ostIv0O1uhzzaqNJxk"
+    "hViwDVxqg51kksMg9r2rDDIFwHCap130FBhdMzeAfWg//6Ki5WWQrHSv6EIUeVs0g3wT3J7r"
+    "FmWQp/JJDXeRh2TXcJa91zAH2uN2mvXFsrIrsjS8rnftWmWfAiLIStuD9m9h9belvzgS/1fP"
+    "X7075IwDENteAAAAAElFTkSuQmCC")
+
+#---------------------------------------------------------------------------
+# if running standalone...
+#
+#     ...a sample implementation using the above
+#
+
+
+def _draw1Objects():
+    # 100 points sin function, plotted as green circles
+    data1 = 2. * np.pi * np.arange(200) / 200.
+    data1.shape = (100, 2)
+    data1[:, 1] = np.sin(data1[:, 0])
+    markers1 = PolyMarker(
+        data1, legend='Green Markers', colour='green', marker='circle', size=1)
+
+    # 50 points cos function, plotted as red line
+    data1 = 2. * np.pi * np.arange(100) / 100.
+    data1.shape = (50, 2)
+    data1[:, 1] = np.cos(data1[:, 0])
+    lines = PolySpline(data1, legend='Red Line', colour='red')
+
+    # A few more points...
+    pi = np.pi
+    markers2 = PolyMarker([(0., 0.), (pi / 4., 1.), (pi / 2, 0.),
+                           (3. * pi / 4., -1)], legend='Cross Legend', colour='blue',
+                          marker='cross')
+
+    return PlotGraphics([markers1, lines, markers2], "Graph Title", "X Axis", "Y Axis")
+
+
+def _draw2Objects():
+    # 100 points sin function, plotted as green dots
+    data1 = 2. * np.pi * np.arange(200) / 200.
+    data1.shape = (100, 2)
+    data1[:, 1] = np.sin(data1[:, 0])
+    line1 = PolySpline(
+        data1, legend='Green Line', colour='green', width=6, style=wx.PENSTYLE_DOT)
+
+    # 50 points cos function, plotted as red dot-dash
+    data1 = 2. * np.pi * np.arange(100) / 100.
+    data1.shape = (50, 2)
+    data1[:, 1] = np.cos(data1[:, 0])
+    line2 = PolySpline(
+        data1, legend='Red Line', colour='red', width=3, style=wx.PENSTYLE_DOT_DASH)
+
+    # A few more points...
+    pi = np.pi
+    markers1 = PolyMarker([(0., 0.), (pi / 4., 1.), (pi / 2, 0.),
+                           (3. * pi / 4., -1)], legend='Cross Hatch Square', colour='blue', width=3, size=6,
+                          fillcolour='red', fillstyle=wx.CROSSDIAG_HATCH,
+                          marker='square')
+
+    return PlotGraphics([markers1, line1, line2], "Big Markers with Different Line Styles")
+
+
+def _draw3Objects():
+    markerList = ['circle', 'dot', 'square', 'triangle', 'triangle_down',
+                  'cross', 'plus', 'circle']
+    m = []
+    for i in range(len(markerList)):
+        m.append(PolyMarker([(2 * i + .5, i + .5)], legend=markerList[i], colour='blue',
+                            marker=markerList[i]))
+    return PlotGraphics(m, "Selection of Markers", "Minimal Axis", "No Axis")
+
+
+def _draw4Objects():
+    # 25,000 point line
+    data1 = np.arange(5e5, 1e6, 10)
+    data1.shape = (25000, 2)
+    line1 = PolyLine(data1, legend='Wide Line', colour='green', width=5)
+
+    # A few more points...
+    markers2 = PolyMarker(data1, legend='Square', colour='blue',
+                          marker='square')
+    return PlotGraphics([line1, markers2], "25,000 Points", "Value X", "")
+
+
+def _draw5Objects():
+    # Empty graph with axis defined but no points/lines
+    points = []
+    line1 = PolyLine(points, legend='Wide Line', colour='green', width=5)
+    return PlotGraphics([line1], "Empty Plot With Just Axes", "Value X", "Value Y")
+
+
+def _draw6Objects():
+    # Bar graph
+    points1 = [(1, 0), (1, 10)]
+    line1 = PolyLine(points1, colour='green', legend='Feb.', width=10)
+    points1g = [(2, 0), (2, 4)]
+    line1g = PolyLine(points1g, colour='red', legend='Mar.', width=10)
+    points1b = [(3, 0), (3, 6)]
+    line1b = PolyLine(points1b, colour='blue', legend='Apr.', width=10)
+
+    points2 = [(4, 0), (4, 12)]
+    line2 = PolyLine(points2, colour='Yellow', legend='May', width=10)
+    points2g = [(5, 0), (5, 8)]
+    line2g = PolyLine(points2g, colour='orange', legend='June', width=10)
+    points2b = [(6, 0), (6, 4)]
+    line2b = PolyLine(points2b, colour='brown', legend='July', width=10)
+
+    return PlotGraphics([line1, line1g, line1b, line2, line2g, line2b],
+                        "Bar Graph - (Turn on Grid, Legend)", "Months", "Number of Students")
+
+
+def _draw7Objects():
+    # Empty graph with axis defined but no points/lines
+    x = np.arange(1, 1000, 1)
+    y1 = 4.5 * x ** 2
+    y2 = 2.2 * x ** 3
+    points1 = np.transpose([x, y1])
+    points2 = np.transpose([x, y2])
+    line1 = PolyLine(points1, legend='quadratic', colour='blue', width=1)
+    line2 = PolyLine(points2, legend='cubic', colour='red', width=1)
+    return PlotGraphics([line1, line2], "double log plot", "Value X", "Value Y")
+
+
+class TestFrame(wx.Frame):
+
+    def __init__(self, parent, id, title):
+        wx.Frame.__init__(self, parent, id, title,
+                          wx.DefaultPosition, (600, 400))
+
+        # Now Create the menu bar and items
+        self.mainmenu = wx.MenuBar()
+
+        menu = wx.Menu()
+        menu.Append(200, 'Page Setup...', 'Setup the printer page')
+        self.Bind(wx.EVT_MENU, self.OnFilePageSetup, id=200)
+
+        menu.Append(201, 'Print Preview...', 'Show the current plot on page')
+        self.Bind(wx.EVT_MENU, self.OnFilePrintPreview, id=201)
+
+        menu.Append(202, 'Print...', 'Print the current plot')
+        self.Bind(wx.EVT_MENU, self.OnFilePrint, id=202)
+
+        menu.Append(203, 'Save Plot...', 'Save current plot')
+        self.Bind(wx.EVT_MENU, self.OnSaveFile, id=203)
+
+        menu.Append(205, 'E&xit', 'Enough of this already!')
+        self.Bind(wx.EVT_MENU, self.OnFileExit, id=205)
+        self.mainmenu.Append(menu, '&File')
+
+        menu = wx.Menu()
+        menu.Append(206, 'Draw1', 'Draw plots1')
+        self.Bind(wx.EVT_MENU, self.OnPlotDraw1, id=206)
+        menu.Append(207, 'Draw2', 'Draw plots2')
+        self.Bind(wx.EVT_MENU, self.OnPlotDraw2, id=207)
+        menu.Append(208, 'Draw3', 'Draw plots3')
+        self.Bind(wx.EVT_MENU, self.OnPlotDraw3, id=208)
+        menu.Append(209, 'Draw4', 'Draw plots4')
+        self.Bind(wx.EVT_MENU, self.OnPlotDraw4, id=209)
+        menu.Append(210, 'Draw5', 'Draw plots5')
+        self.Bind(wx.EVT_MENU, self.OnPlotDraw5, id=210)
+        menu.Append(260, 'Draw6', 'Draw plots6')
+        self.Bind(wx.EVT_MENU, self.OnPlotDraw6, id=260)
+        menu.Append(261, 'Draw7', 'Draw plots7')
+        self.Bind(wx.EVT_MENU, self.OnPlotDraw7, id=261)
+
+        menu.Append(211, '&Redraw', 'Redraw plots')
+        self.Bind(wx.EVT_MENU, self.OnPlotRedraw, id=211)
+        menu.Append(212, '&Clear', 'Clear canvas')
+        self.Bind(wx.EVT_MENU, self.OnPlotClear, id=212)
+        menu.Append(213, '&Scale', 'Scale canvas')
+        self.Bind(wx.EVT_MENU, self.OnPlotScale, id=213)
+        menu.Append(
+            214, 'Enable &Zoom', 'Enable Mouse Zoom', kind=wx.ITEM_CHECK)
+        self.Bind(wx.EVT_MENU, self.OnEnableZoom, id=214)
+        menu.Append(215, 'Enable &Grid', 'Turn on Grid', kind=wx.ITEM_CHECK)
+        self.Bind(wx.EVT_MENU, self.OnEnableGrid, id=215)
+        menu.Append(
+            217, 'Enable &Drag', 'Activates dragging mode', kind=wx.ITEM_CHECK)
+        self.Bind(wx.EVT_MENU, self.OnEnableDrag, id=217)
+        menu.Append(
+            220, 'Enable &Legend', 'Turn on Legend', kind=wx.ITEM_CHECK)
+        self.Bind(wx.EVT_MENU, self.OnEnableLegend, id=220)
+        menu.Append(
+            222, 'Enable &Point Label', 'Show Closest Point', kind=wx.ITEM_CHECK)
+        self.Bind(wx.EVT_MENU, self.OnEnablePointLabel, id=222)
+
+        menu.Append(
+            223, 'Enable &Anti-Aliasing', 'Smooth output', kind=wx.ITEM_CHECK)
+        self.Bind(wx.EVT_MENU, self.OnEnableAntiAliasing, id=223)
+        menu.Append(224, 'Enable &High-Resolution AA',
+                    'Draw in higher resolution', kind=wx.ITEM_CHECK)
+        self.Bind(wx.EVT_MENU, self.OnEnableHiRes, id=224)
+
+        menu.Append(
+            226, 'Enable Center Lines', 'Draw center lines', kind=wx.ITEM_CHECK)
+        self.Bind(wx.EVT_MENU, self.OnEnableCenterLines, id=226)
+        menu.Append(
+            227, 'Enable Diagonal Lines', 'Draw diagonal lines', kind=wx.ITEM_CHECK)
+        self.Bind(wx.EVT_MENU, self.OnEnableDiagonals, id=227)
+
+        menu.Append(
+            231, 'Set Gray Background', 'Change background colour to gray')
+        self.Bind(wx.EVT_MENU, self.OnBackgroundGray, id=231)
+        menu.Append(
+            232, 'Set &White Background', 'Change background colour to white')
+        self.Bind(wx.EVT_MENU, self.OnBackgroundWhite, id=232)
+        menu.Append(
+            233, 'Set Red Label Text', 'Change label text colour to red')
+        self.Bind(wx.EVT_MENU, self.OnForegroundRed, id=233)
+        menu.Append(
+            234, 'Set &Black Label Text', 'Change label text colour to black')
+        self.Bind(wx.EVT_MENU, self.OnForegroundBlack, id=234)
+
+        menu.Append(225, 'Scroll Up 1', 'Move View Up 1 Unit')
+        self.Bind(wx.EVT_MENU, self.OnScrUp, id=225)
+        menu.Append(230, 'Scroll Rt 2', 'Move View Right 2 Units')
+        self.Bind(wx.EVT_MENU, self.OnScrRt, id=230)
+        menu.Append(235, '&Plot Reset', 'Reset to original plot')
+        self.Bind(wx.EVT_MENU, self.OnReset, id=235)
+
+        self.mainmenu.Append(menu, '&Plot')
+
+        menu = wx.Menu()
+        menu.Append(300, '&About', 'About this thing...')
+        self.Bind(wx.EVT_MENU, self.OnHelpAbout, id=300)
+        self.mainmenu.Append(menu, '&Help')
+
+        self.SetMenuBar(self.mainmenu)
+
+        # A status bar to tell people what's happening
+        self.CreateStatusBar(1)
+
+        self.client = PlotCanvas(self)
+        # define the function for drawing pointLabels
+        self.client.SetPointLabelFunc(self.DrawPointLabel)
+        # Create mouse event for showing cursor coords in status bar
+        self.client.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
+        # Show closest point when enabled
+        self.client.canvas.Bind(wx.EVT_MOTION, self.OnMotion)
+
+        self.Show(True)
+
+    def DrawPointLabel(self, dc, mDataDict):
+        """This is the fuction that defines how the pointLabels are plotted
+            dc - DC that will be passed
+            mDataDict - Dictionary of data that you want to use for the pointLabel
+
+            As an example I have decided I want a box at the curve point
+            with some text information about the curve plotted below.
+            Any wxDC method can be used.
+        """
+        # ----------
+        dc.SetPen(wx.Pen(wx.BLACK))
+        dc.SetBrush(wx.Brush(wx.BLACK, wx.BRUSHSTYLE_SOLID))
+
+        sx, sy = mDataDict["scaledXY"]  # scaled x,y of closest point
+        # 10by10 square centered on point
+        dc.DrawRectangle(sx - 5, sy - 5, 10, 10)
+        px, py = mDataDict["pointXY"]
+        cNum = mDataDict["curveNum"]
+        pntIn = mDataDict["pIndex"]
+        legend = mDataDict["legend"]
+        # make a string to display
+        s = "Crv# %i, '%s', Pt. (%.2f,%.2f), PtInd %i" % (
+            cNum, legend, px, py, pntIn)
+        dc.DrawText(s, sx, sy + 1)
+        # -----------
+
+    def OnMouseLeftDown(self, event):
+        s = "Left Mouse Down at Point: (%.4f, %.4f)" % self.client._getXY(
+            event)
+        self.SetStatusText(s)
+        event.Skip()  # allows plotCanvas OnMouseLeftDown to be called
+
+    def OnMotion(self, event):
+        # show closest point (when enbled)
+        if self.client.GetEnablePointLabel() == True:
+            # make up dict with info for the pointLabel
+            # I've decided to mark the closest point on the closest curve
+            dlst = self.client.GetClosestPoint(
+                self.client._getXY(event), pointScaled=True)
+            if dlst != []:  # returns [] if none
+                curveNum, legend, pIndex, pointXY, scaledXY, distance = dlst
+                # make up dictionary to pass to my user function (see
+                # DrawPointLabel)
+                mDataDict = {"curveNum": curveNum, "legend": legend, "pIndex": pIndex,
+                             "pointXY": pointXY, "scaledXY": scaledXY}
+                # pass dict to update the pointLabel
+                self.client.UpdatePointLabel(mDataDict)
+        event.Skip()  # go to next handler
+
+    def OnFilePageSetup(self, event):
+        self.client.PageSetup()
+
+    def OnFilePrintPreview(self, event):
+        self.client.PrintPreview()
+
+    def OnFilePrint(self, event):
+        self.client.Printout()
+
+    def OnSaveFile(self, event):
+        self.client.SaveFile()
+
+    def OnFileExit(self, event):
+        self.Close()
+
+    def OnPlotDraw1(self, event):
+        self.resetDefaults()
+        self.client.Draw(_draw1Objects())
+
+    def OnPlotDraw2(self, event):
+        self.resetDefaults()
+        self.client.Draw(_draw2Objects())
+
+    def OnPlotDraw3(self, event):
+        self.resetDefaults()
+        self.client.SetFont(
+            wx.Font(10, wx.FONTFAMILY_SCRIPT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
+        self.client.SetFontSizeAxis(20)
+        self.client.SetFontSizeLegend(12)
+        self.client.SetXSpec('min')
+        self.client.SetYSpec('none')
+        self.client.Draw(_draw3Objects())
+
+    def OnPlotDraw4(self, event):
+        self.resetDefaults()
+        drawObj = _draw4Objects()
+        self.client.Draw(drawObj)
+# profile
+##        start = _time.clock()
+# for x in range(10):
+# self.client.Draw(drawObj)
+##        print("10 plots of Draw4 took: %f sec."%(_time.clock() - start))
+# profile end
+
+    def OnPlotDraw5(self, event):
+        # Empty plot with just axes
+        self.resetDefaults()
+        drawObj = _draw5Objects()
+        # make the axis X= (0,5), Y=(0,10)
+        # (default with None is X= (-1,1), Y= (-1,1))
+        self.client.Draw(drawObj, xAxis=(0, 5), yAxis= (0, 10))
+
+    def OnPlotDraw6(self, event):
+        # Bar Graph Example
+        self.resetDefaults()
+        # self.client.SetEnableLegend(True)   #turn on Legend
+        # self.client.SetEnableGrid(True)     #turn on Grid
+        self.client.SetXSpec('none')  # turns off x-axis scale
+        self.client.SetYSpec('auto')
+        self.client.Draw(_draw6Objects(), xAxis=(0, 7))
+
+    def OnPlotDraw7(self, event):
+        # log scale example
+        self.resetDefaults()
+        self.client.setLogScale((True, True))
+        self.client.Draw(_draw7Objects())
+
+    def OnPlotRedraw(self, event):
+        self.client.Redraw()
+
+    def OnPlotClear(self, event):
+        self.client.Clear()
+
+    def OnPlotScale(self, event):
+        if self.client.last_draw != None:
+            graphics, xAxis, yAxis = self.client.last_draw
+            self.client.Draw(graphics, (1, 3.05), (0, 1))
+
+    def OnEnableZoom(self, event):
+        self.client.SetEnableZoom(event.IsChecked())
+        self.mainmenu.Check(217, not event.IsChecked())
+
+    def OnEnableGrid(self, event):
+        self.client.SetEnableGrid(event.IsChecked())
+
+    def OnEnableDrag(self, event):
+        self.client.SetEnableDrag(event.IsChecked())
+        self.mainmenu.Check(214, not event.IsChecked())
+
+    def OnEnableLegend(self, event):
+        self.client.SetEnableLegend(event.IsChecked())
+
+    def OnEnablePointLabel(self, event):
+        self.client.SetEnablePointLabel(event.IsChecked())
+
+    def OnEnableAntiAliasing(self, event):
+        self.client.SetEnableAntiAliasing(event.IsChecked())
+
+    def OnEnableHiRes(self, event):
+        self.client.SetEnableHiRes(event.IsChecked())
+
+    def OnEnableCenterLines(self, event):
+        self.client.SetEnableCenterLines(event.IsChecked())
+
+    def OnEnableDiagonals(self, event):
+        self.client.SetEnableDiagonals(event.IsChecked())
+
+    def OnBackgroundGray(self, event):
+        self.client.SetBackgroundColour("#CCCCCC")
+        self.client.Redraw()
+
+    def OnBackgroundWhite(self, event):
+        self.client.SetBackgroundColour("white")
+        self.client.Redraw()
+
+    def OnForegroundRed(self, event):
+        self.client.SetForegroundColour("red")
+        self.client.Redraw()
+
+    def OnForegroundBlack(self, event):
+        self.client.SetForegroundColour("black")
+        self.client.Redraw()
+
+    def OnScrUp(self, event):
+        self.client.ScrollUp(1)
+
+    def OnScrRt(self, event):
+        self.client.ScrollRight(2)
+
+    def OnReset(self, event):
+        self.client.Reset()
+
+    def OnHelpAbout(self, event):
+        from wx.lib.dialogs import ScrolledMessageDialog
+        about = ScrolledMessageDialog(self, __doc__, "About...")
+        about.ShowModal()
+
+    def resetDefaults(self):
+        """Just to reset the fonts back to the PlotCanvas defaults"""
+        self.client.SetFont(
+            wx.Font(10, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
+        self.client.SetFontSizeAxis(10)
+        self.client.SetFontSizeLegend(7)
+        self.client.setLogScale((False, False))
+        self.client.SetXSpec('auto')
+        self.client.SetYSpec('auto')
+
+
+def __test():
+
+    class MyApp(wx.App):
+
+        def OnInit(self):
+            wx.InitAllImageHandlers()
+            frame = TestFrame(None, -1, "PlotCanvas")
+            # frame.Show(True)
+            self.SetTopWindow(frame)
+            return True
+
+    app = MyApp(0)
+    app.MainLoop()
+
+if __name__ == '__main__':
+    __test()


Property changes on: grass/trunk/gui/wxpython/gui_core/wxlibplot.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/trunk/gui/wxpython/iclass/plots.py
===================================================================
--- grass/trunk/gui/wxpython/iclass/plots.py	2016-08-04 06:48:51 UTC (rev 69074)
+++ grass/trunk/gui/wxpython/iclass/plots.py	2016-08-04 11:14:52 UTC (rev 69075)
@@ -16,7 +16,14 @@
 """
 
 import wx
-import wx.lib.plot as plot
+from core.globalvar import CheckWxVersion
+try:
+    if CheckWxVersion(version=[3, 0, 0]):
+        import gui_core.wxlibplot as plot
+    else:
+        import wx.lib.plot as plot
+except ImportError as e:
+    print >> sys.stderr, e
 import wx.lib.scrolledpanel as scrolled
 from core.utils import _
 from core.gcmd import GError

Modified: grass/trunk/gui/wxpython/wxplot/base.py
===================================================================
--- grass/trunk/gui/wxpython/wxplot/base.py	2016-08-04 06:48:51 UTC (rev 69074)
+++ grass/trunk/gui/wxpython/wxplot/base.py	2016-08-04 11:14:52 UTC (rev 69075)
@@ -19,8 +19,12 @@
 import sys
 
 import wx
+from core.globalvar import CheckWxVersion
 try:
-    import wx.lib.plot as plot
+    if CheckWxVersion(version=[3, 0, 0]):
+        import gui_core.wxlibplot as plot
+    else:
+        import wx.lib.plot as plot
 except ImportError as e:
     print >> sys.stderr, e
 

Modified: grass/trunk/gui/wxpython/wxplot/histogram.py
===================================================================
--- grass/trunk/gui/wxpython/wxplot/histogram.py	2016-08-04 06:48:51 UTC (rev 69074)
+++ grass/trunk/gui/wxpython/wxplot/histogram.py	2016-08-04 11:14:52 UTC (rev 69075)
@@ -18,8 +18,12 @@
 import sys
 
 import wx
+from core.globalvar import CheckWxVersion
 try:
-    import wx.lib.plot as plot
+    if CheckWxVersion(version=[3, 0, 0]):
+        import gui_core.wxlibplot as plot
+    else:
+        import wx.lib.plot as plot
 except ImportError as e:
     print >> sys.stderr, e
 

Modified: grass/trunk/gui/wxpython/wxplot/profile.py
===================================================================
--- grass/trunk/gui/wxpython/wxplot/profile.py	2016-08-04 06:48:51 UTC (rev 69074)
+++ grass/trunk/gui/wxpython/wxplot/profile.py	2016-08-04 11:14:52 UTC (rev 69075)
@@ -20,8 +20,12 @@
 import math
 
 import wx
+from core.globalvar import CheckWxVersion
 try:
-    import wx.lib.plot as plot
+    if CheckWxVersion(version=[3, 0, 0]):
+        import gui_core.wxlibplot as plot
+    else:
+        import wx.lib.plot as plot
 except ImportError as e:
     print >> sys.stderr, e
 

Modified: grass/trunk/gui/wxpython/wxplot/scatter.py
===================================================================
--- grass/trunk/gui/wxpython/wxplot/scatter.py	2016-08-04 06:48:51 UTC (rev 69074)
+++ grass/trunk/gui/wxpython/wxplot/scatter.py	2016-08-04 11:14:52 UTC (rev 69075)
@@ -18,8 +18,12 @@
 import sys
 
 import wx
+from core.globalvar import CheckWxVersion
 try:
-    import wx.lib.plot as plot
+    if CheckWxVersion(version=[3, 0, 0]):
+        import gui_core.wxlibplot as plot
+    else:
+        import wx.lib.plot as plot
 except ImportError as e:
     print >> sys.stderr, e
 



More information about the grass-commit mailing list