Index: setup.py
===================================================================
--- setup.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ setup.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -231,6 +231,6 @@
 
 # Plottools
-package_dir["sas.plottools"] = os.path.join("src", "sas", "plottools")
-packages.append("sas.plottools")
+package_dir["sas.sasgui.plottools"] = os.path.join("src", "sas", "sasgui", "plottools")
+packages.append("sas.sasgui.plottools")
 
 # sas models
Index: src/examples/test_chisq_panel.py
===================================================================
--- src/examples/test_chisq_panel.py	(revision 3a39c2e89f90377b2d15fa13b9ba9cf3b4144070)
+++ src/examples/test_chisq_panel.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -27,6 +27,6 @@
 
 import wx
-from sas.plottools.PlotPanel import PlotPanel
-from sas.plottools.plottables import Plottable, Graph, Data1D, Theory1D
+from sas.sasgui.plottools.PlotPanel import PlotPanel
+from sas.sasgui.plottools.plottables import Plottable, Graph, Data1D, Theory1D
 import  sys
 import numpy
Index: src/examples/test_copy_print.py
===================================================================
--- src/examples/test_copy_print.py	(revision 3a39c2e89f90377b2d15fa13b9ba9cf3b4144070)
+++ src/examples/test_copy_print.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -26,6 +26,6 @@
 
 import wx
-from sas.plottools.PlotPanel import PlotPanel
-from sas.plottools.plottables import Graph, Data1D, Theory1D
+from sas.sasgui.plottools.PlotPanel import PlotPanel
+from sas.sasgui.plottools.plottables import Graph, Data1D, Theory1D
 import  sys
 sys.platform = 'win95'
Index: src/examples/test_panel.py
===================================================================
--- src/examples/test_panel.py	(revision 3a39c2e89f90377b2d15fa13b9ba9cf3b4144070)
+++ src/examples/test_panel.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -37,6 +37,6 @@
 """
 import wx
-from sas.plottools.PlotPanel import PlotPanel
-from sas.plottools.plottables import Data1D
+from sas.sasgui.plottools.PlotPanel import PlotPanel
+from sas.sasgui.plottools.plottables import Data1D
 import  sys
 import numpy
Index: src/examples/test_panel2D.py
===================================================================
--- src/examples/test_panel2D.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/examples/test_panel2D.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -37,6 +37,6 @@
 """
 import wx
-from sas.plottools.PlotPanel import PlotPanel
-from sas.plottools.plottables import Data1D, Theory1D, Data2D
+from sas.sasgui.plottools.PlotPanel import PlotPanel
+from sas.sasgui.plottools.plottables import Data1D, Theory1D, Data2D
 import  sys,os
 import numpy
Index: src/examples/test_text_panel.py
===================================================================
--- src/examples/test_text_panel.py	(revision 3a39c2e89f90377b2d15fa13b9ba9cf3b4144070)
+++ src/examples/test_text_panel.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -27,6 +27,6 @@
 
 import wx
-from sas.plottools.PlotPanel import PlotPanel
-from sas.plottools.plottables import Text, Graph
+from sas.sasgui.plottools.PlotPanel import PlotPanel
+from sas.sasgui.plottools.plottables import Text, Graph
 
 class TestPlotPanel(PlotPanel):
Index: src/sas/guiframe/dataFitting.py
===================================================================
--- src/sas/guiframe/dataFitting.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/guiframe/dataFitting.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -6,7 +6,7 @@
 import math
 from sas.sascalc.data_util.uncertainty import Uncertainty
-from sas.plottools.plottables import Data1D as PlotData1D
-from sas.plottools.plottables import Data2D as PlotData2D
-from sas.plottools.plottables import Theory1D as PlotTheory1D
+from sas.sasgui.plottools.plottables import Data1D as PlotData1D
+from sas.sasgui.plottools.plottables import Data2D as PlotData2D
+from sas.sasgui.plottools.plottables import Theory1D as PlotTheory1D
 
 from sas.sascalc.dataloader.data_info import Data1D as LoadData1D
Index: src/sas/guiframe/data_processor.py
===================================================================
--- src/sas/guiframe/data_processor.py	(revision 44d20afb283734cce1a7aef490219c0398d11978)
+++ src/sas/guiframe/data_processor.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -28,5 +28,5 @@
 from sas.guiframe.events import NewPlotEvent
 from sas.guiframe.events import StatusEvent
-from sas.plottools import plottables
+from sas.sasgui.plottools import plottables
 from sas.guiframe.dataFitting import Data1D
 
Index: src/sas/guiframe/local_perspectives/plotting/Plotter1D.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/Plotter1D.py	(revision 6f16e25ca8391464dfcea6ba0d48d6b3702d148e)
+++ src/sas/guiframe/local_perspectives/plotting/Plotter1D.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -16,5 +16,5 @@
 import numpy
 import logging
-from sas.plottools.PlotPanel import PlotPanel
+from sas.sasgui.plottools.PlotPanel import PlotPanel
 from sas.guiframe.events import StatusEvent
 from sas.guiframe.events import PanelOnFocusEvent
Index: src/sas/guiframe/local_perspectives/plotting/Plotter2D.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/Plotter2D.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/guiframe/local_perspectives/plotting/Plotter2D.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -16,7 +16,7 @@
 import numpy
 import logging
-from sas.plottools.PlotPanel import PlotPanel
-from sas.plottools.plottables import Graph
-from sas.plottools.TextDialog import TextDialog
+from sas.sasgui.plottools.PlotPanel import PlotPanel
+from sas.sasgui.plottools.plottables import Graph
+from sas.sasgui.plottools.TextDialog import TextDialog
 from sas.guiframe.events import StatusEvent
 from sas.guiframe.events import NewPlotEvent
@@ -26,5 +26,5 @@
 from  sas.guiframe.local_perspectives.plotting.binder import BindArtist
 from Plotter1D import ModelPanel1D
-from sas.plottools.toolbar import NavigationToolBar
+from sas.sasgui.plottools.toolbar import NavigationToolBar
 from matplotlib.font_manager import FontProperties
 from graphAppearance import graphAppearance
Index: src/sas/guiframe/local_perspectives/plotting/SimplePlot.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/SimplePlot.py	(revision 098f3d277d54e6ae4bae35ed0188be84b67fb171)
+++ src/sas/guiframe/local_perspectives/plotting/SimplePlot.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -4,6 +4,6 @@
 import wx
 from sas.guiframe.local_perspectives.plotting.Plotter2D import ModelPanel2D as PlotPanel
-from sas.plottools.toolbar import NavigationToolBar
-from sas.plottools.plottables import Graph
+from sas.sasgui.plottools.toolbar import NavigationToolBar
+from sas.sasgui.plottools.plottables import Graph
 from sas.guiframe.utils import PanelMenu
 from sas.guiframe.events import StatusEvent
Index: src/sas/guiframe/local_perspectives/plotting/graphAppearance.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/graphAppearance.py	(revision ac7be54ccf932e813cbe6e41e05b682a1630a03e)
+++ src/sas/guiframe/local_perspectives/plotting/graphAppearance.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -12,5 +12,5 @@
 
 import wx
-from sas.plottools.SimpleFont import SimpleFont
+from sas.sasgui.plottools.SimpleFont import SimpleFont
 
 COLOR = ['black', 'blue', 'green', 'red', 'cyan', 'magenta', 'yellow']
Index: src/sas/guiframe/local_perspectives/plotting/masking.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/masking.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/guiframe/local_perspectives/plotting/masking.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -25,6 +25,6 @@
 import copy
 import numpy
-from sas.plottools.PlotPanel import PlotPanel
-from sas.plottools.plottables import Graph
+from sas.sasgui.plottools.PlotPanel import PlotPanel
+from sas.sasgui.plottools.plottables import Graph
 from binder import BindArtist
 from sas.guiframe.dataFitting import Data1D, Data2D
Index: src/sas/guiframe/local_perspectives/plotting/profile_dialog.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/profile_dialog.py	(revision c039589ad31dec3058311cae229f7aa1dc56faf8)
+++ src/sas/guiframe/local_perspectives/plotting/profile_dialog.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -5,5 +5,5 @@
 import sys
 from copy import deepcopy
-from sas.plottools.plottables import Graph
+from sas.sasgui.plottools.plottables import Graph
 from Plotter1D import ModelPanel1D as PlotPanel
 from sas.guiframe.dataFitting import Data1D
Index: src/sas/perspectives/calculator/data_operator.py
===================================================================
--- src/sas/perspectives/calculator/data_operator.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/perspectives/calculator/data_operator.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -7,7 +7,7 @@
 import numpy
 from sas.sascalc.dataloader.data_info import Data1D
-from sas.plottools.PlotPanel import PlotPanel
-from sas.plottools.plottables import Graph
-from sas.plottools import transform
+from sas.sasgui.plottools.PlotPanel import PlotPanel
+from sas.sasgui.plottools.plottables import Graph
+from sas.sasgui.plottools import transform
 from matplotlib.font_manager import FontProperties
 from sas.guiframe.events import StatusEvent
@@ -888,5 +888,5 @@
             first_item = list.keys()[0]
             if first_item.x != []:
-                from sas.plottools.PropertyDialog import Properties
+                from sas.sasgui.plottools.PropertyDialog import Properties
                 dial = Properties(self, -1, 'Change Scale')
                 # type of view or model used
Index: src/sas/perspectives/calculator/gen_scatter_panel.py
===================================================================
--- src/sas/perspectives/calculator/gen_scatter_panel.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/perspectives/calculator/gen_scatter_panel.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -34,5 +34,5 @@
 from wx.lib.scrolledpanel import ScrolledPanel
 from sas.perspectives.calculator.load_thread import GenReader
-from sas.plottools.arrow3d import Arrow3D
+from sas.sasgui.plottools.arrow3d import Arrow3D
 from sas.perspectives.calculator import calculator_widgets as widget
 from sas.guiframe.events import NewPlotEvent
Index: src/sas/perspectives/fitting/basepage.py
===================================================================
--- src/sas/perspectives/fitting/basepage.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/perspectives/fitting/basepage.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -1832,5 +1832,5 @@
         x, y = self.model.getProfile()
 
-        from sas.plottools import Data1D as pf_data1d
+        from sas.sasgui.plottools import Data1D as pf_data1d
         #from sas.perspectives.theory.profile_dialog import SLDPanel
         from sas.guiframe.local_perspectives.plotting.profile_dialog \
Index: src/sas/perspectives/pr/explore_dialog.py
===================================================================
--- src/sas/perspectives/pr/explore_dialog.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/perspectives/pr/explore_dialog.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -28,8 +28,8 @@
 
 # Import plotting classes
-from sas.plottools.PlotPanel import PlotPanel
-from sas.plottools import Data1D as Model1D
+from sas.sasgui.plottools.PlotPanel import PlotPanel
+from sas.sasgui.plottools import Data1D as Model1D
 from sas.guiframe.gui_style import GUIFRAME_ID
-from sas.plottools.plottables import Graph
+from sas.sasgui.plottools.plottables import Graph
 
 from pr_widgets import PrTextCtrl
Index: c/sas/plottools/BaseInteractor.py
===================================================================
--- src/sas/plottools/BaseInteractor.py	(revision 2df0b74178c150a9f521d5b09abf522c0578e825)
+++ 	(revision )
@@ -1,179 +1,0 @@
-interface_color = 'black'
-disable_color = 'gray'
-active_color = 'red'
-rho_color = 'black'
-mu_color = 'green'
-P_color = 'blue'
-theta_color = 'orange'
-profile_colors = [rho_color, mu_color, P_color, theta_color]
-
-
-class _BaseInteractor(object):
-    """
-    Share some functions between the interface interactor and various layer
-    interactors.
-
-    Individual interactors need the following functions:
-
-        save(ev)  - save the current state for later restore
-        restore() - restore the old state
-        move(x,y,ev) - move the interactor to position x,y
-        moveend(ev) - end the drag event
-        update() - draw the interactors
-
-    The following are provided by the base class:
-
-        connect_markers(markers) - register callbacks for all markers
-        clear_markers() - remove all items in self.markers
-        onHilite(ev) - enter/leave event processing
-        onLeave(ev) - enter/leave event processing
-        onClick(ev) - mouse click: calls save()
-        onRelease(ev) - mouse click ends: calls moveend()
-        onDrag(ev) - mouse move: calls move() or restore()
-        onKey(ev) - keyboard move: calls move() or restore()
-
-    Interactor attributes:
-
-        base  - model we are operating on
-        axes  - axes holding the interactor
-        color - color of the interactor in non-active state
-        markers - list of handles for the interactor
-
-    """
-    def __init__(self, base, axes, color='black'):
-        """
-        """
-        self.base = base
-        self.axes = axes
-        self.color = color
-        self.markers = []
-
-    def clear_markers(self):
-        '''
-        Clear old markers and interfaces.
-        '''
-        for h in self.markers:
-            h.remove()
-        if self.markers:
-            self.base.connect.clear(*self.markers)
-        self.markers = []
-
-    def save(self, ev):
-        """
-        """
-        pass
-
-    def restore(self, ev):
-        """
-        """
-        pass
-
-    def move(self, x, y, ev):
-        """
-        """
-        pass
-
-    def moveend(self, ev):
-        """
-        """
-        pass
-
-    def connect_markers(self, markers):
-        """
-        Connect markers to callbacks
-        """
-        for h in markers:
-            connect = self.base.connect
-            connect('enter', h, self.onHilite)
-            connect('leave', h, self.onLeave)
-            connect('click', h, self.onClick)
-            connect('release', h, self.onRelease)
-            connect('drag', h, self.onDrag)
-            connect('key', h, self.onKey)
-
-    def onHilite(self, ev):
-        """
-        Hilite the artist reporting the event, indicating that it is
-        ready to receive a click.
-        """
-        ev.artist.set_color(active_color)
-        self.base.draw()
-        return True
-
-    def onLeave(self, ev):
-        """
-        Restore the artist to the original colour when the cursor leaves.
-        """
-        ev.artist.set_color(self.color)
-        self.base.draw()
-        return True
-
-    def onClick(self, ev):
-        """
-        Prepare to move the artist.  Calls save() to preserve the state for
-        later restore().
-        """
-        self.clickx, self.clicky = ev.xdata, ev.ydata
-        self.save(ev)
-        return True
-
-    def onRelease(self, ev):
-        """
-        """
-        self.moveend(ev)
-        return True
-
-    def onDrag(self, ev):
-        """
-        Move the artist.  Calls move() to update the state, or restore() if
-        the mouse leaves the window.
-        """
-        inside, _ = self.axes.contains(ev)
-        if inside:
-            self.clickx, self.clicky = ev.xdata, ev.ydata
-            self.move(ev.xdata, ev.ydata, ev)
-        else:
-            self.restore()
-        self.base.update()
-        return True
-
-    def onKey(self, ev):
-        """
-        Respond to keyboard events.  Arrow keys move the widget.  Escape
-        restores it to the position before the last click.
-
-        Calls move() to update the state.  Calls restore() on escape.
-        """
-        if ev.key == 'escape':
-            self.restore()
-        elif ev.key in ['up', 'down', 'right', 'left']:
-            dx, dy = self.dpixel(self.clickx, self.clicky, nudge=ev.control)
-            if ev.key == 'up':
-                self.clicky += dy
-            elif ev.key == 'down':
-                self.clicky -= dy
-            elif ev.key == 'right':
-                self.clickx += dx
-            else:
-                self.clickx -= dx
-            self.move(self.clickx, self.clicky, ev)
-        else:
-            return False
-        self.base.update()
-        return True
-
-    def dpixel(self, x, y, nudge=False):
-        """
-        Return the step size in data coordinates for a small
-        step in screen coordinates.  If nudge is False (default)
-        the step size is one pixel.  If nudge is True, the step
-        size is 0.2 pixels.
-        """
-        ax = self.axes
-        px, py = ax.transData.inverse_xy_tup((x, y))
-        if nudge:
-            nx, ny = ax.transData.xy_tup((px + 0.2, py + 0.2))
-        else:
-            nx, ny = ax.transData.xy_tup((px + 1., py + 1.))
-        dx, dy = nx - x, ny - y
-        return dx, dy
Index: c/sas/plottools/LabelDialog.py
===================================================================
--- src/sas/plottools/LabelDialog.py	(revision 2df0b74178c150a9f521d5b09abf522c0578e825)
+++ 	(revision )
@@ -1,43 +1,0 @@
-import wx
-import sys
-if sys.platform.count("win32") > 0:
-    FONT_VARIANT = 0
-    PNL_WIDTH = 270
-else:
-    FONT_VARIANT = 1
-    PNL_WIDTH = 300
-
-class LabelDialog(wx.Dialog):
-    def __init__(self, parent, id, title, label):
-        wx.Dialog.__init__(self, parent, id, title, size=(PNL_WIDTH, 150))
-
-        # Font
-        self.SetWindowVariant(variant=FONT_VARIANT)
-        mainbox = wx.BoxSizer(wx.VERTICAL)
-        vbox = wx.BoxSizer(wx.VERTICAL)
-        textbox = wx.BoxSizer(wx.HORIZONTAL)
-
-        text1 = "Enter a new title/label:"
-        msg = wx.StaticText(self, -1, text1, (30, 15), style=wx.ALIGN_LEFT)
-        msg.SetLabel(text1)
-        self.label_ctrl = wx.TextCtrl(self, -1, '', (200, 30))
-        self.label_ctrl.SetValue(str(label))
-        textbox.Add(self.label_ctrl, flag=wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE,
-                    border=10, proportion=2)
-        vbox.Add(msg, flag=wx.ALL, border=10, proportion=1)
-        vbox.Add(textbox, flag=wx.EXPAND | wx.TOP | wx.BOTTOM | wx.ADJUST_MINSIZE, border=5)
-        hbox = wx.BoxSizer(wx.HORIZONTAL)
-        ok_button = wx.Button(self, wx.ID_OK, 'OK', size=(70, 25))
-        close_button = wx.Button(self, wx.ID_CANCEL, 'Cancel', size=(70, 25))
-
-        hbox.Add(ok_button, wx.LEFT, 10)
-        hbox.Add((20, 20))
-        hbox.Add(close_button, wx.LEFT, 10)
-
-        mainbox.Add(vbox, flag=wx.LEFT, border=5)
-        mainbox.Add(wx.StaticLine(self), 0, wx.ALL | wx.EXPAND, 5)
-        mainbox.Add(hbox, flag=wx.CENTER, border=20)
-        self.SetSizer(mainbox)
-
-    def getText(self):
-        return self.label_ctrl.GetValue()
Index: c/sas/plottools/LineModel.py
===================================================================
--- src/sas/plottools/LineModel.py	(revision 2df0b74178c150a9f521d5b09abf522c0578e825)
+++ 	(revision )
@@ -1,89 +1,0 @@
-#!/usr/bin/env python
-"""
-Provide Line function (y= A + Bx)
-"""
-
-import math
-
-class LineModel(object):
-    """
-    Class that evaluates a linear model.
-
-    f(x) = A + Bx
-
-    List of default parameters:
-    A = 0.0
-    B = 0.0
-    """
-
-    def __init__(self):
-        """ Initialization """
-        # # Name of the model
-        self.name = "LineModel"
-
-        # # Define parameters
-        self.params = {}
-        self.params['A'] = 1.0
-        self.params['B'] = 1.0
-
-        # # Parameter details [units, min, max]
-        self.details = {}
-        self.details['A'] = ['', None, None]
-        self.details['B'] = ['', None, None]
-
-    def getParam(self, name):
-        """
-            Return parameter value
-        """
-        return self.params[name.upper()]
-
-    def setParam(self, name, value):
-        """
-            Set parameter value
-        """
-        self.params[name.upper()] = value
-
-    def _line(self, x):
-        """
-        Evaluate the function
-
-        :param x: x-value
-
-        :return: function value
-
-        """
-        return  self.params['A'] + (x * self.params['B'])
-
-    def run(self, x=0.0):
-        """
-        Evaluate the model
-
-        :param x: simple value
-
-        :return: (Line value)
-        """
-        if x.__class__.__name__ == 'list':
-            return self._line(x[0] * math.cos(x[1])) * \
-                                self._line(x[0] * math.sin(x[1]))
-        elif x.__class__.__name__ == 'tuple':
-            msg = "Tuples are not allowed as input to BaseComponent models"
-            raise ValueError, msg
-        else:
-            return self._line(x)
-
-    def runXY(self, x=0.0):
-        """
-        Evaluate the model
-
-        :param x: simple value
-
-        :return: Line value
-
-        """
-        if x.__class__.__name__ == 'list':
-            return self._line(x[0]) * self._line(x[1])
-        elif x.__class__.__name__ == 'tuple':
-            msg = "Tuples are not allowed as input to BaseComponent models"
-            raise ValueError, msg
-        else:
-            return self._line(x)
Index: c/sas/plottools/PlotPanel.py
===================================================================
--- src/sas/plottools/PlotPanel.py	(revision d3d67f06423dc337fd087c81b7cbd502dc7e1084)
+++ 	(revision )
@@ -1,2025 +1,0 @@
-"""
-    Plot panel.
-"""
-import logging
-import traceback
-import wx
-# Try a normal import first
-# If it fails, try specifying a version
-import matplotlib
-matplotlib.interactive(False)
-#Use the WxAgg back end. The Wx one takes too long to render
-matplotlib.use('WXAgg')
-from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
-from matplotlib.figure import Figure
-import os
-import transform
-from plottables import Data1D
-#TODO: make the plottables interactive
-from binder import BindArtist
-from matplotlib.font_manager import FontProperties
-DEBUG = False
-
-from plottables import Graph
-from TextDialog import TextDialog
-from LabelDialog import LabelDialog
-import operator
-
-import math
-import pylab
-DEFAULT_CMAP = pylab.cm.jet
-import copy
-import numpy
-
-from ..guiframe.events import StatusEvent
-from .toolbar import NavigationToolBar, PlotPrintout, bind
-
-def show_tree(obj, d=0):
-    """Handy function for displaying a tree of graph objects"""
-    print "%s%s" % ("-"*d, obj.__class__.__name__)
-    if 'get_children' in dir(obj):
-        for a in obj.get_children(): show_tree(a, d + 1)
-
-from convert_units import convert_unit
-
-
-def _rescale(lo, hi, step, pt=None, bal=None, scale='linear'):
-    """
-        Rescale (lo,hi) by step, returning the new (lo,hi)
-        The scaling is centered on pt, with positive values of step
-        driving lo/hi away from pt and negative values pulling them in.
-        If bal is given instead of point, it is already in [0,1] coordinates.
-
-        This is a helper function for step-based zooming.
-
-    """
-    # Convert values into the correct scale for a linear transformation
-    # TODO: use proper scale transformers
-    loprev = lo
-    hiprev = hi
-    if scale == 'log':
-        assert lo > 0
-        if lo > 0:
-            lo = math.log10(lo)
-        if hi > 0:
-            hi = math.log10(hi)
-        if pt is not None:
-            pt = math.log10(pt)
-
-    # Compute delta from axis range * %, or 1-% if persent is negative
-    if step > 0:
-        delta = float(hi - lo) * step / 100
-    else:
-        delta = float(hi - lo) * step / (100 - step)
-
-    # Add scale factor proportionally to the lo and hi values,
-    # preserving the
-    # point under the mouse
-    if bal is None:
-        bal = float(pt - lo) / (hi - lo)
-    lo = lo - (bal * delta)
-    hi = hi + (1 - bal) * delta
-
-    # Convert transformed values back to the original scale
-    if scale == 'log':
-        if (lo <= -250) or (hi >= 250):
-            lo = loprev
-            hi = hiprev
-        else:
-            lo, hi = math.pow(10., lo), math.pow(10., hi)
-    return (lo, hi)
-
-
-class PlotPanel(wx.Panel):
-    """
-    The PlotPanel has a Figure and a Canvas. OnSize events simply set a
-    flag, and the actually redrawing of the
-    figure is triggered by an Idle event.
-    """
-    def __init__(self, parent, id=-1, xtransform=None,
-                 ytransform=None, scale='log_{10}',
-                 color=None, dpi=None, **kwargs):
-        """
-        """
-        wx.Panel.__init__(self, parent, id=id, **kwargs)
-        self.parent = parent
-        if hasattr(parent, "parent"):
-            self.parent = self.parent.parent
-        self.dimension = 1
-        self.gotLegend = 0  # to begin, legend is not picked.
-        self.legend_pos_loc = None
-        self.legend = None
-        self.line_collections_list = []
-        self.figure = Figure(None, dpi, linewidth=2.0)
-        self.color = '#b3b3b3'
-        from canvas import FigureCanvas
-        self.canvas = FigureCanvas(self, -1, self.figure)
-        self.SetColor(color)
-        self._resizeflag = True
-        self._SetSize()
-        self.subplot = self.figure.add_subplot(111)
-        self.figure.subplots_adjust(left=0.2, bottom=.2)
-        self.yscale = 'linear'
-        self.xscale = 'linear'
-        self.sizer = wx.BoxSizer(wx.VERTICAL)
-        self.sizer.Add(self.canvas, 1, wx.EXPAND)
-        #add toolbar
-        self.enable_toolbar = True
-        self.toolbar = None
-        self.add_toolbar()
-        self.SetSizer(self.sizer)
-
-        # Graph object to manage the plottables
-        self.graph = Graph()
-
-        #Boolean value to keep track of whether current legend is
-        #visible or not
-        self.legend_on = True
-        self.grid_on = False
-        #Location of legend, default is 0 or 'best'
-        self.legendLoc = 0
-        self.position = None
-        self._loc_labels = self.get_loc_label()
-
-        self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
-
-        # Define some constants
-        self.colorlist = ['b', 'g', 'r', 'c', 'm', 'y', 'k']
-        self.symbollist = ['o', 'x', '^', 'v', '<', '>', '+',
-                           's', 'd', 'D', 'h', 'H', 'p', '-']
-
-        #List of texts currently on the plot
-        self.textList = []
-        #User scale
-        if xtransform != None:
-            self.xLabel = xtransform
-        else:
-            self.xLabel = "log10(x)"
-        if ytransform != None:
-            self.yLabel = ytransform
-        else:
-            self.yLabel = "log10(y)"
-        self.viewModel = "--"
-        # keep track if the previous transformation of x
-        # and y in Property dialog
-        self.prevXtrans = "log10(x)"
-        self.prevYtrans = "log10(y)"
-        self.scroll_id = self.canvas.mpl_connect('scroll_event', self.onWheel)
-        #taking care of dragging
-        self.motion_id = self.canvas.mpl_connect('motion_notify_event',
-                                                 self.onMouseMotion)
-        self.press_id = self.canvas.mpl_connect('button_press_event',
-                                                self.onLeftDown)
-        self.pick_id = self.canvas.mpl_connect('pick_event', self.onPick)
-        self.release_id = self.canvas.mpl_connect('button_release_event',
-                                                  self.onLeftUp)
-
-        wx.EVT_RIGHT_DOWN(self, self.onLeftDown)
-        # to turn axis off whenn resizing the panel
-        self.resizing = False
-
-        self.leftdown = False
-        self.leftup = False
-        self.mousemotion = False
-        self.axes = [self.subplot]
-        ## Fit dialog
-        self._fit_dialog = None
-        # Interactor
-        self.connect = BindArtist(self.subplot.figure)
-        #self.selected_plottable = None
-
-        # new data for the fit
-        self.fit_result = Data1D(x=[], y=[], dy=None)
-        self.fit_result.symbol = 13
-        #self.fit_result = Data1D(x=[], y=[],dx=None, dy=None)
-        self.fit_result.name = "Fit"
-        # For fit Dialog initial display
-        self.xmin = 0.0
-        self.xmax = 0.0
-        self.xminView = 0.0
-        self.xmaxView = 0.0
-        self._scale_xlo = None
-        self._scale_xhi = None
-        self._scale_ylo = None
-        self._scale_yhi = None
-        self.Avalue = None
-        self.Bvalue = None
-        self.ErrAvalue = None
-        self.ErrBvalue = None
-        self.Chivalue = None
-
-        # for 2D scale
-        if scale != 'linear':
-            scale = 'log_{10}'
-        self.scale = scale
-        self.data = None
-        self.qx_data = None
-        self.qy_data = None
-        self.xmin_2D = None
-        self.xmax_2D = None
-        self.ymin_2D = None
-        self.ymax_2D = None
-        ## store reference to the current plotted vmin and vmax of plotted image
-        ##z range in linear scale
-        self.zmin_2D = None
-        self.zmax_2D = None
-
-        #index array
-        self.index_x = None
-        self.index_y = None
-
-        #number of bins
-        self.x_bins = None
-        self.y_bins = None
-
-        ## default color map
-        self.cmap = DEFAULT_CMAP
-
-        # Dragging info
-        self.begDrag = False
-        self.xInit = None
-        self.yInit = None
-        self.xFinal = None
-        self.yFinal = None
-
-        #axes properties
-        self.xaxis_font = None
-        self.xaxis_label = None
-        self.xaxis_unit = None
-        self.xaxis_color = 'black'
-        self.xaxis_tick = None
-        self.yaxis_font = None
-        self.yaxis_label = None
-        self.yaxis_unit = None
-        self.yaxis_color = 'black'
-        self.yaxis_tick = None
-
-        # check if zoomed.
-        self.is_zoomed = False
-        # Plottables
-        self.plots = {}
-
-        # Default locations
-        self._default_save_location = os.getcwd()
-        # let canvas know about axes
-        self.canvas.set_panel(self)
-        self.ly = None
-        self.q_ctrl = None
-        #Bind focus to change the border color
-        self.canvas.Bind(wx.EVT_SET_FOCUS, self.on_set_focus)
-        self.canvas.Bind(wx.EVT_KILL_FOCUS, self.on_kill_focus)
-
-    def _SetInitialSize(self,):
-        """
-        """
-        pixels = self.parent.GetClientSize()
-        self.canvas.SetSize(pixels)
-        self.figure.set_size_inches(pixels[0] / self.figure.get_dpi(),
-                                    pixels[1] / self.figure.get_dpi(), forward=True)
-
-    def On_Paint(self, event):
-        """
-        """
-        self.canvas.SetBackgroundColour(self.color)
-
-    def on_set_focus(self, event):
-        """
-        Send to the parenet the current panel on focus
-        """
-        # light blue
-        self.color = '#0099f7'
-        self.figure.set_edgecolor(self.color)
-        if self.parent and self.window_caption:
-            self.parent.send_focus_to_datapanel(self.window_caption)
-        self.draw()
-
-    def on_kill_focus(self, event):
-        """
-        Reset the panel color
-        """
-        # light grey
-        self.color = '#b3b3b3'
-        self.figure.set_edgecolor(self.color)
-        self.draw()
-
-    def set_resizing(self, resizing=False):
-        """
-        Set the resizing (True/False)
-        """
-        pass  # Not implemented
-
-    def schedule_full_draw(self, func='append'):
-        """
-        Put self in schedule to full redraw list
-        """
-        pass  # Not implemented
-
-    def add_toolbar(self):
-        """
-        add toolbar
-        """
-        self.enable_toolbar = True
-        self.toolbar = NavigationToolBar(parent=self, canvas=self.canvas)
-        bind(self.toolbar, wx.EVT_TOOL, self.onResetGraph, id=self.toolbar._NTB2_RESET)
-        bind(self.toolbar, wx.EVT_TOOL, self.onContextMenu, id=self.toolbar._NTB2_HOME)
-        self.toolbar.Realize()
-        ## The 'SetToolBar()' is not working on MAC: JHC
-        #if IS_MAC:
-        # Mac platform (OSX 10.3, MacPython) does not seem to cope with
-        # having a toolbar in a sizer. This work-around gets the buttons
-        # back, but at the expense of having the toolbar at the top
-        #self.SetToolBar(self.toolbar)
-        #else:
-        # On Windows platform, default window size is incorrect, so set
-        # toolbar width to figure width.
-        tw, th = self.toolbar.GetSizeTuple()
-        fw, fh = self.canvas.GetSizeTuple()
-        # By adding toolbar in sizer, we are able to put it at the bottom
-        # of the frame - so appearance is closer to GTK version.
-        # As noted above, doesn't work for Mac.
-        self.toolbar.SetSize(wx.Size(fw, th))
-        self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
-
-        # update the axes menu on the toolbar
-        self.toolbar.update()
-
-    def onLeftDown(self, event):
-        """
-        left button down and ready to drag
-        """
-        # Check that the LEFT button was pressed
-        if event.button == 1:
-            self.leftdown = True
-            ax = event.inaxes
-            if ax != None:
-                self.xInit, self.yInit = event.xdata, event.ydata
-                try:
-                    pos_x = float(event.xdata)  # / size_x
-                    pos_y = float(event.ydata)  # / size_y
-                    pos_x = "%8.3g" % pos_x
-                    pos_y = "%8.3g" % pos_y
-                    self.position = str(pos_x), str(pos_y)
-                    wx.PostEvent(self.parent, StatusEvent(status=self.position))
-                except:
-                    self.position = None
-
-    def onLeftUp(self, event):
-        """
-        Dragging is done
-        """
-        # Check that the LEFT button was released
-        if event.button == 1:
-            self.leftdown = False
-            self.mousemotion = False
-            self.leftup = True
-
-        #release the legend
-        if self.gotLegend == 1:
-            self.gotLegend = 0
-            self.set_legend_alpha(1)
-
-    def set_legend_alpha(self, alpha=1):
-        """
-        Set legend alpha
-        """
-        if self.legend != None:
-            self.legend.legendPatch.set_alpha(alpha)
-
-    def onPick(self, event):
-        """
-        On pick legend
-        """
-        legend = self.legend
-        if event.artist == legend:
-            #gets the box of the legend.
-            bbox = self.legend.get_window_extent()
-            #get mouse coordinates at time of pick.
-            self.mouse_x = event.mouseevent.x
-            self.mouse_y = event.mouseevent.y
-            #get legend coordinates at time of pick.
-            self.legend_x = bbox.xmin
-            self.legend_y = bbox.ymin
-            #indicates we picked up the legend.
-            self.gotLegend = 1
-            self.set_legend_alpha(0.5)
-
-    def _on_legend_motion(self, event):
-        """
-        On legend in motion
-        """
-        ax = event.inaxes
-        if ax == None:
-            return
-        # Event occurred inside a plotting area
-        lo_x, hi_x = ax.get_xlim()
-        lo_y, hi_y = ax.get_ylim()
-        # How much the mouse moved.
-        x = mouse_diff_x = self.mouse_x - event.x
-        y = mouse_diff_y = self.mouse_y - event.y
-        # Put back inside
-        if x < lo_x:
-            x = lo_x
-        if x > hi_x:
-            x = hi_x
-        if y < lo_y:
-            y = lo_y
-        if y > hi_y:
-            y = hi_y
-        # Move the legend from its previous location by that same amount
-        loc_in_canvas = self.legend_x - mouse_diff_x, \
-                        self.legend_y - mouse_diff_y
-        # Transform into legend coordinate system
-        trans_axes = self.legend.parent.transAxes.inverted()
-        loc_in_norm_axes = trans_axes.transform_point(loc_in_canvas)
-        self.legend_pos_loc = tuple(loc_in_norm_axes)
-        self.legend._loc = self.legend_pos_loc
-        self.resizing = True
-        self.canvas.set_resizing(self.resizing)
-        self.canvas.draw()
-
-    def onMouseMotion(self, event):
-        """
-        check if the left button is press and the mouse in moving.
-        computer delta for x and y coordinates and then calls draghelper
-        to perform the drag
-        """
-        self.cusor_line(event)
-        if self.gotLegend == 1:
-            self._on_legend_motion(event)
-            return
-        if self.enable_toolbar:
-            #Disable dragging without the toolbar to allow zooming with toolbar
-            return
-        self.mousemotion = True
-        if self.leftdown == True and self.mousemotion == True:
-            ax = event.inaxes
-            if ax != None:  # the dragging is perform inside the figure
-                self.xFinal, self.yFinal = event.xdata, event.ydata
-                # Check whether this is the first point
-                if self.xInit == None:
-                    self.xInit = self.xFinal
-                    self.yInit = self.yFinal
-
-                xdelta = self.xFinal - self.xInit
-                ydelta = self.yFinal - self.yInit
-
-                if self.xscale == 'log':
-                    xdelta = math.log10(self.xFinal) - math.log10(self.xInit)
-                if self.yscale == 'log':
-                    ydelta = math.log10(self.yFinal) - math.log10(self.yInit)
-                self._dragHelper(xdelta, ydelta)
-            else:  # no dragging is perform elsewhere
-                self._dragHelper(0, 0)
-
-    def cusor_line(self, event):
-        """
-        """
-        pass
-
-    def _offset_graph(self):
-        """
-        Zoom and offset the graph to the last known settings
-        """
-        for ax in self.axes:
-            if self._scale_xhi is not None and self._scale_xlo is not None:
-                ax.set_xlim(self._scale_xlo, self._scale_xhi)
-            if self._scale_yhi is not None and self._scale_ylo is not None:
-                ax.set_ylim(self._scale_ylo, self._scale_yhi)
-
-    def _dragHelper(self, xdelta, ydelta):
-        """
-        dragging occurs here
-        """
-        # Event occurred inside a plotting area
-        for ax in self.axes:
-            lo, hi = ax.get_xlim()
-            newlo, newhi = lo - xdelta, hi - xdelta
-            if self.xscale == 'log':
-                if lo > 0:
-                    newlo = math.log10(lo) - xdelta
-                if hi > 0:
-                    newhi = math.log10(hi) - xdelta
-            if self.xscale == 'log':
-                self._scale_xlo = math.pow(10, newlo)
-                self._scale_xhi = math.pow(10, newhi)
-                ax.set_xlim(math.pow(10, newlo), math.pow(10, newhi))
-            else:
-                self._scale_xlo = newlo
-                self._scale_xhi = newhi
-                ax.set_xlim(newlo, newhi)
-
-            lo, hi = ax.get_ylim()
-            newlo, newhi = lo - ydelta, hi - ydelta
-            if self.yscale == 'log':
-                if lo > 0:
-                    newlo = math.log10(lo) - ydelta
-                if hi > 0:
-                    newhi = math.log10(hi) - ydelta
-            if  self.yscale == 'log':
-                self._scale_ylo = math.pow(10, newlo)
-                self._scale_yhi = math.pow(10, newhi)
-                ax.set_ylim(math.pow(10, newlo), math.pow(10, newhi))
-            else:
-                self._scale_ylo = newlo
-                self._scale_yhi = newhi
-                ax.set_ylim(newlo, newhi)
-        self.canvas.draw_idle()
-
-    def resetFitView(self):
-        """
-        For fit Dialog initial display
-        """
-        self.xmin = 0.0
-        self.xmax = 0.0
-        self.xminView = 0.0
-        self.xmaxView = 0.0
-        self._scale_xlo = None
-        self._scale_xhi = None
-        self._scale_ylo = None
-        self._scale_yhi = None
-        self.Avalue = None
-        self.Bvalue = None
-        self.ErrAvalue = None
-        self.ErrBvalue = None
-        self.Chivalue = None
-
-    def onWheel(self, event):
-        """
-        Process mouse wheel as zoom events
-
-        :param event: Wheel event
-
-        """
-        ax = event.inaxes
-        step = event.step
-
-        if ax != None:
-            # Event occurred inside a plotting area
-            lo, hi = ax.get_xlim()
-            lo, hi = _rescale(lo, hi, step,
-                              pt=event.xdata, scale=ax.get_xscale())
-            if not self.xscale == 'log' or lo > 0:
-                self._scale_xlo = lo
-                self._scale_xhi = hi
-                ax.set_xlim((lo, hi))
-
-            lo, hi = ax.get_ylim()
-            lo, hi = _rescale(lo, hi, step, pt=event.ydata,
-                              scale=ax.get_yscale())
-            if not self.yscale == 'log' or lo > 0:
-                self._scale_ylo = lo
-                self._scale_yhi = hi
-                ax.set_ylim((lo, hi))
-        else:
-            # Check if zoom happens in the axes
-            xdata, ydata = None, None
-            x, y = event.x, event.y
-
-            for ax in self.axes:
-                insidex, _ = ax.xaxis.contains(event)
-                if insidex:
-                    xdata, _ = ax.transAxes.inverted().transform_point((x, y))
-                insidey, _ = ax.yaxis.contains(event)
-                if insidey:
-                    _, ydata = ax.transAxes.inverted().transform_point((x, y))
-            if xdata is not None:
-                lo, hi = ax.get_xlim()
-                lo, hi = _rescale(lo, hi, step,
-                                  bal=xdata, scale=ax.get_xscale())
-                if not self.xscale == 'log' or lo > 0:
-                    self._scale_xlo = lo
-                    self._scale_xhi = hi
-                    ax.set_xlim((lo, hi))
-            if ydata is not None:
-                lo, hi = ax.get_ylim()
-                lo, hi = _rescale(lo, hi, step, bal=ydata,
-                                  scale=ax.get_yscale())
-                if not self.yscale == 'log' or lo > 0:
-                    self._scale_ylo = lo
-                    self._scale_yhi = hi
-                    ax.set_ylim((lo, hi))
-        self.canvas.draw_idle()
-
-    def returnTrans(self):
-        """
-        Return values and labels used by Fit Dialog
-        """
-        return self.xLabel, self.yLabel, self.Avalue, self.Bvalue, \
-                self.ErrAvalue, self.ErrBvalue, self.Chivalue
-
-    def setTrans(self, xtrans, ytrans):
-        """
-
-        :param xtrans: set x transformation on Property dialog
-        :param ytrans: set y transformation on Property dialog
-
-        """
-        self.prevXtrans = xtrans
-        self.prevYtrans = ytrans
-
-    def onFitting(self, event):
-        """
-        when clicking on linear Fit on context menu , display Fitting Dialog
-        """
-        plot_dict = {}
-        menu = event.GetEventObject()
-        event_id = event.GetId()
-        self.set_selected_from_menu(menu, event_id)
-        plotlist = self.graph.returnPlottable()
-        if self.graph.selected_plottable is not None:
-            for item in plotlist:
-                if item.id == self.graph.selected_plottable:
-                    plot_dict[item] = plotlist[item]
-        else:
-            plot_dict = plotlist
-        from fitDialog import LinearFit
-
-        if len(plot_dict.keys()) > 0:
-            first_item = plot_dict.keys()[0]
-            dlg = LinearFit(parent=None, plottable=first_item,
-                            push_data=self.onFitDisplay,
-                            transform=self.returnTrans,
-                            title='Linear Fit')
-
-            if (self.xmin != 0.0)and (self.xmax != 0.0)\
-                and(self.xminView != 0.0)and (self.xmaxView != 0.0):
-                dlg.setFitRange(self.xminView, self.xmaxView,
-                                self.xmin, self.xmax)
-            dlg.ShowModal()
-
-    def set_selected_from_menu(self, menu, id):
-        """
-        Set selected_plottable from context menu selection
-
-        :param menu: context menu item
-        :param id: menu item id
-        """
-        if len(self.plots) < 1:
-            return
-        name = menu.GetHelpString(id)
-        for plot in self.plots.values():
-            if plot.name == name:
-                self.graph.selected_plottable = plot.id
-                break
-
-    def linear_plottable_fit(self, plot):
-        """
-            when clicking on linear Fit on context menu, display Fitting Dialog
-
-            :param plot: PlotPanel owning the graph
-
-        """
-        from fitDialog import LinearFit
-        if self._fit_dialog is not None:
-            return
-        self._fit_dialog = LinearFit(None, plot, self.onFitDisplay,
-                                     self.returnTrans, -1, 'Linear Fit')
-        # Set the zoom area
-        if self._scale_xhi is not None and self._scale_xlo is not None:
-            self._fit_dialog.set_fit_region(self._scale_xlo, self._scale_xhi)
-        # Register the close event
-        self._fit_dialog.register_close(self._linear_fit_close)
-        # Show a non-model dialog
-        self._fit_dialog.Show()
-
-    def _linear_fit_close(self):
-        """
-        A fit dialog was closed
-        """
-        self._fit_dialog = None
-
-    def _onProperties(self, event):
-        """
-        when clicking on Properties on context menu ,
-        The Property dialog is displayed
-        The user selects a transformation for x or y value and
-        a new plot is displayed
-        """
-        if self._fit_dialog is not None:
-            self._fit_dialog.Destroy()
-            self._fit_dialog = None
-        plot_list = self.graph.returnPlottable()
-        if len(plot_list.keys()) > 0:
-            first_item = plot_list.keys()[0]
-            if first_item.x != []:
-                from PropertyDialog import Properties
-                dial = Properties(self, -1, 'Properties')
-                dial.setValues(self.prevXtrans, self.prevYtrans, self.viewModel)
-                if dial.ShowModal() == wx.ID_OK:
-                    self.xLabel, self.yLabel, self.viewModel = dial.getValues()
-                    if self.viewModel == "Linear y vs x":
-                        self.xLabel = "x"
-                        self.yLabel = "y"
-                        self.viewModel = "--"
-                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
-                    if self.viewModel == "Guinier lny vs x^(2)":
-                        self.xLabel = "x^(2)"
-                        self.yLabel = "ln(y)"
-                        self.viewModel = "--"
-                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
-                    if self.viewModel == "XS Guinier ln(y*x) vs x^(2)":
-                        self.xLabel = "x^(2)"
-                        self.yLabel = "ln(y*x)"
-                        self.viewModel = "--"
-                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
-                    if self.viewModel == "Porod y*x^(4) vs x^(4)":
-                        self.xLabel = "x^(4)"
-                        self.yLabel = "y*x^(4)"
-                        self.viewModel = "--"
-                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
-                    self._onEVT_FUNC_PROPERTY()
-                dial.Destroy()
-
-    def set_yscale(self, scale='linear'):
-        """
-        Set the scale on Y-axis
-
-        :param scale: the scale of y-axis
-
-        """
-        self.subplot.set_yscale(scale, nonposy='clip')
-        self.yscale = scale
-
-    def get_yscale(self):
-        """
-
-        :return: Y-axis scale
-
-        """
-        return self.yscale
-
-    def set_xscale(self, scale='linear'):
-        """
-        Set the scale on x-axis
-
-        :param scale: the scale of x-axis
-
-        """
-        self.subplot.set_xscale(scale)
-        self.xscale = scale
-
-    def get_xscale(self):
-        """
-
-        :return: x-axis scale
-
-        """
-        return self.xscale
-
-    def SetColor(self, rgbtuple):
-        """
-        Set figure and canvas colours to be the same
-
-        """
-        if not rgbtuple:
-            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
-        col = [c / 255.0 for c in rgbtuple]
-        self.figure.set_facecolor(col)
-        self.figure.set_edgecolor(self.color)
-        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))
-
-    def _onSize(self, event):
-        """
-        """
-        self._resizeflag = True
-
-    def _onIdle(self, evt):
-        """
-        """
-        if self._resizeflag:
-            self._resizeflag = False
-            self._SetSize()
-            self.draw()
-
-    def _SetSize(self, pixels=None):
-        """
-        This method can be called to force the Plot to be a desired size,
-         which defaults to the ClientSize of the panel
-
-        """
-        if not pixels:
-            pixels = tuple(self.GetClientSize())
-        self.canvas.SetSize(pixels)
-        self.figure.set_size_inches(float(pixels[0]) / self.figure.get_dpi(),
-                                    float(pixels[1]) / self.figure.get_dpi())
-
-    def draw(self):
-        """
-        Where the actual drawing happens
-
-        """
-        self.figure.canvas.draw_idle()
-
-    def legend_picker(self, legend, event):
-        """
-            Pick up the legend patch
-        """
-        return self.legend.legendPatch.contains(event)
-
-    def get_loc_label(self):
-        """
-        Associates label to a specific legend location
-        """
-        _labels = {}
-        i = 0
-        _labels['best'] = i
-        i += 1
-        _labels['upper right'] = i
-        i += 1
-        _labels['upper left'] = i
-        i += 1
-        _labels['lower left'] = i
-        i += 1
-        _labels['lower right'] = i
-        i += 1
-        _labels['right'] = i
-        i += 1
-        _labels['center left'] = i
-        i += 1
-        _labels['center right'] = i
-        i += 1
-        _labels['lower center'] = i
-        i += 1
-        _labels['upper center'] = i
-        i += 1
-        _labels['center'] = i
-        return _labels
-
-    def onSaveImage(self, evt):
-        """
-        Implement save image
-        """
-        self.toolbar.save_figure(evt)
-
-    def onContextMenu(self, event):
-        """
-        Default context menu for a plot panel
-
-        """
-        # Slicer plot popup menu
-        wx_id = wx.NewId()
-        slicerpop = wx.Menu()
-        slicerpop.Append(wx_id, '&Save image', 'Save image as PNG')
-        wx.EVT_MENU(self, wx_id, self.onSaveImage)
-
-        wx_id = wx.NewId()
-        slicerpop.Append(wx_id, '&Printer setup', 'Set image size')
-        wx.EVT_MENU(self, wx_id, self.onPrinterSetup)
-
-        wx_id = wx.NewId()
-        slicerpop.Append(wx_id, '&Print image', 'Print image ')
-        wx.EVT_MENU(self, wx_id, self.onPrint)
-
-        wx_id = wx.NewId()
-        slicerpop.Append(wx_id, '&Copy', 'Copy to the clipboard')
-        wx.EVT_MENU(self, wx_id, self.OnCopyFigureMenu)
-
-        wx_id = wx.NewId()
-        slicerpop.AppendSeparator()
-        slicerpop.Append(wx_id, '&Properties')
-        wx.EVT_MENU(self, wx_id, self._onProperties)
-
-        wx_id = wx.NewId()
-        slicerpop.AppendSeparator()
-        slicerpop.Append(wx_id, '&Linear Fit')
-        wx.EVT_MENU(self, wx_id, self.onFitting)
-
-        wx_id = wx.NewId()
-        slicerpop.AppendSeparator()
-        slicerpop.Append(wx_id, '&Toggle Legend On/Off', 'Toggle Legend On/Off')
-        wx.EVT_MENU(self, wx_id, self.onLegend)
-
-        loc_menu = wx.Menu()
-        for label in self._loc_labels:
-            wx_id = wx.NewId()
-            loc_menu.Append(wx_id, str(label), str(label))
-            wx.EVT_MENU(self, wx_id, self.onChangeLegendLoc)
-        wx_id = wx.NewId()
-        slicerpop.AppendMenu(wx_id, '&Modify Legend Location', loc_menu)
-
-        wx_id = wx.NewId()
-        slicerpop.Append(wx_id, '&Modify Y Axis Label')
-        wx.EVT_MENU(self, wx_id, self._on_yaxis_label)
-        wx_id = wx.NewId()
-        slicerpop.Append(wx_id, '&Modify X Axis Label')
-        wx.EVT_MENU(self, wx_id, self._on_xaxis_label)
-
-        try:
-            # mouse event
-            pos_evt = event.GetPosition()
-            pos = self.ScreenToClient(pos_evt)
-        except:
-            # toolbar event
-            pos_x, pos_y = self.toolbar.GetPositionTuple()
-            pos = (pos_x, pos_y + 5)
-
-        self.PopupMenu(slicerpop, pos)
-
-    def onToolContextMenu(self, event):
-        """
-        ContextMenu from toolbar
-
-        :param event: toolbar event
-        """
-        # reset postion
-        self.position = None
-        if self.graph.selected_plottable != None:
-            self.graph.selected_plottable = None
-
-        self.onContextMenu(event)
-
-# modified kieranrcampbell ILL june2012
-    def onLegend(self, legOnOff):
-        """
-        Toggles whether legend is visible/not visible
-        """
-        self.legend_on = legOnOff
-        if not self.legend_on:
-            for ax in self.axes:
-                self.remove_legend(ax)
-        else:
-            # sort them by labels
-            handles, labels = self.subplot.get_legend_handles_labels()
-            hl = sorted(zip(handles, labels),
-                        key=operator.itemgetter(1))
-            handles2, labels2 = zip(*hl)
-            self.line_collections_list = handles2
-            self.legend = self.subplot.legend(handles2, labels2,
-                                              prop=FontProperties(size=10),
-                                              loc=self.legendLoc)
-            if self.legend != None:
-                self.legend.set_picker(self.legend_picker)
-                self.legend.set_axes(self.subplot)
-                self.legend.set_zorder(20)
-
-        self.subplot.figure.canvas.draw_idle()
-
-    def onChangeLegendLoc(self, event):
-        """
-        Changes legend loc based on user input
-        """
-        menu = event.GetEventObject()
-        label = menu.GetLabel(event.GetId())
-        self.ChangeLegendLoc(label)
-
-    def ChangeLegendLoc(self, label):
-        """
-        Changes legend loc based on user input
-        """
-        self.legendLoc = label
-        self.legend_pos_loc = None
-        # sort them by labels
-        handles, labels = self.subplot.get_legend_handles_labels()
-        hl = sorted(zip(handles, labels),
-                    key=operator.itemgetter(1))
-        handles2, labels2 = zip(*hl)
-        self.line_collections_list = handles2
-        self.legend = self.subplot.legend(handles2, labels2,
-                                          prop=FontProperties(size=10),
-                                          loc=self.legendLoc)
-        if self.legend != None:
-            self.legend.set_picker(self.legend_picker)
-            self.legend.set_axes(self.subplot)
-            self.legend.set_zorder(20)
-        self.subplot.figure.canvas.draw_idle()
-
-    def remove_legend(self, ax=None):
-        """
-        Remove legend for ax or the current axes.
-        """
-        from pylab import gca
-        if ax is None:
-            ax = gca()
-        ax.legend_ = None
-
-    def _on_addtext(self, event):
-        """
-        Allows you to add text to the plot
-        """
-        pos_x = 0
-        pos_y = 0
-        if self.position != None:
-            pos_x, pos_y = self.position
-        else:
-            pos_x, pos_y = 0.01, 1.00
-
-        textdial = TextDialog(None, -1, 'Add Custom Text')
-        if textdial.ShowModal() == wx.ID_OK:
-            try:
-                FONT = FontProperties()
-                label = textdial.getText()
-                xpos = pos_x
-                ypos = pos_y
-                font = FONT.copy()
-                font.set_size(textdial.getSize())
-                font.set_family(textdial.getFamily())
-                font.set_style(textdial.getStyle())
-                font.set_weight(textdial.getWeight())
-                colour = textdial.getColor()
-                if len(label) > 0 and xpos > 0 and ypos > 0:
-                    new_text = self.subplot.text(str(xpos), str(ypos), label,
-                                                 fontproperties=font,
-                                                 color=colour)
-                    self.textList.append(new_text)
-                    self.subplot.figure.canvas.draw_idle()
-            except:
-                if self.parent != None:
-                    msg = "Add Text: Error. Check your property values..."
-                    wx.PostEvent(self.parent, StatusEvent(status=msg))
-                else:
-                    raise
-        textdial.Destroy()
-        #Have a pop up box come up for user to type in the
-        #text that they want to add...then create text Plottable
-        #based on this and plot it at user designated coordinates
-
-    def onGridOnOff(self, gridon_off):
-        """
-        Allows ON/OFF Grid
-        """
-        self.grid_on = gridon_off
-
-        self.subplot.figure.canvas.draw_idle()
-
-    def _on_xaxis_label(self, event):
-        """
-        Allows you to add text to the plot
-        """
-        xaxis_label, xaxis_unit, xaxis_font, xaxis_color, \
-                     is_ok, is_tick = self._on_axis_label(axis='x')
-        if not is_ok:
-            return
-
-        self.xaxis_label = xaxis_label
-        self.xaxis_unit = xaxis_unit
-        self.xaxis_font = xaxis_font
-        self.xaxis_color = xaxis_color
-        if is_tick:
-            self.xaxis_tick = xaxis_font
-
-        if self.data != None:
-            # 2D
-            self.xaxis(self.xaxis_label, self.xaxis_unit, \
-                        self.xaxis_font, self.xaxis_color, self.xaxis_tick)
-            self.subplot.figure.canvas.draw_idle()
-        else:
-            # 1D
-            self._check_zoom_plot()
-
-    def _check_zoom_plot(self):
-        """
-        Check the zoom range and plot (1D only)
-        """
-        xlo, xhi = self.subplot.get_xlim()
-        ylo, yhi = self.subplot.get_ylim()
-        ## Set the view scale for all plots
-        self._onEVT_FUNC_PROPERTY(False)
-        if self.is_zoomed:
-            # Recover the x,y limits
-            self.subplot.set_xlim((xlo, xhi))
-            self.subplot.set_ylim((ylo, yhi))
-
-    @property
-    def is_zoomed(self):
-        toolbar_zoomed = self.toolbar.GetToolEnabled(self.toolbar.wx_ids['Back'])
-        return self._is_zoomed or toolbar_zoomed
-
-    @is_zoomed.setter
-    def is_zoomed(self, value):
-        self._is_zoomed = value
-
-    def _on_yaxis_label(self, event):
-        """
-        Allows you to add text to the plot
-        """
-        yaxis_label, yaxis_unit, yaxis_font, yaxis_color, \
-                        is_ok, is_tick = self._on_axis_label(axis='y')
-        if not is_ok:
-            return
-
-        self.yaxis_label = yaxis_label
-        self.yaxis_unit = yaxis_unit
-        self.yaxis_font = yaxis_font
-        self.yaxis_color = yaxis_color
-        if is_tick:
-            self.yaxis_tick = yaxis_font
-
-        if self.data != None:
-            # 2D
-            self.yaxis(self.yaxis_label, self.yaxis_unit, \
-                        self.yaxis_font, self.yaxis_color, self.yaxis_tick)
-            self.subplot.figure.canvas.draw_idle()
-        else:
-            # 1D
-            self._check_zoom_plot()
-
-    def _on_axis_label(self, axis='x'):
-        """
-        Modify axes labels
-
-        :param axis: x or y axis [string]
-        """
-        is_ok = True
-        title = 'Modify %s axis label' % axis
-        font = 'serif'
-        colour = 'black'
-        if axis == 'x':
-            label = self.xaxis_label
-            unit = self.xaxis_unit
-        else:
-            label = self.yaxis_label
-            unit = self.yaxis_unit
-        textdial = TextDialog(None, -1, title, label, unit)
-        if textdial.ShowModal() == wx.ID_OK:
-            try:
-                FONT = FontProperties()
-                font = FONT.copy()
-                font.set_size(textdial.getSize())
-                font.set_family(textdial.getFamily())
-                font.set_style(textdial.getStyle())
-                font.set_weight(textdial.getWeight())
-                unit = textdial.getUnit()
-                colour = textdial.getColor()
-                is_tick = textdial.getTickLabel()
-                label_temp = textdial.getText()
-                if label_temp.count("\%s" % "\\") > 0:
-                    if self.parent != None:
-                        msg = "Add Label: Error. Can not use double '\\' "
-                        msg += "characters..."
-                        wx.PostEvent(self.parent, StatusEvent(status=msg))
-                else:
-                    label = label_temp
-            except:
-                if self.parent != None:
-                    msg = "Add Label: Error. Check your property values..."
-                    wx.PostEvent(self.parent, StatusEvent(status=msg))
-                else:
-                    pass
-        else:
-            is_ok = False
-            is_tick = True
-        textdial.Destroy()
-        return label, unit, font, colour, is_ok, is_tick
-
-    def _on_removetext(self, event):
-        """
-        Removes all text from the plot.
-        Eventually, add option to remove specific text boxes
-        """
-        num_text = len(self.textList)
-        if num_text < 1:
-            if self.parent != None:
-                msg = "Remove Text: Nothing to remove.  "
-                wx.PostEvent(self.parent, StatusEvent(status=msg))
-            else:
-                raise
-            return
-        txt = self.textList[num_text - 1]
-        try:
-            text_remove = txt.get_text()
-            txt.remove()
-            if self.parent != None:
-                msg = "Removed Text: '%s'. " % text_remove
-                wx.PostEvent(self.parent, StatusEvent(status=msg))
-        except:
-            if self.parent != None:
-                msg = "Remove Text: Error occurred. "
-                wx.PostEvent(self.parent, StatusEvent(status=msg))
-            else:
-                raise
-        self.textList.remove(txt)
-
-        self.subplot.figure.canvas.draw_idle()
-
-    def properties(self, prop):
-        """
-        Set some properties of the graph.
-        The set of properties is not yet determined.
-
-        """
-        # The particulars of how they are stored and manipulated (e.g., do
-        # we want an inventory internally) is not settled.  I've used a
-        # property dictionary for now.
-        #
-        # How these properties interact with a user defined style file is
-        # even less clear.
-
-        # Properties defined by plot
-        
-        # Ricardo:
-        # A empty label "$$" will prevent the panel from displaying! 
-        
-        if prop["xlabel"]:
-            self.subplot.set_xlabel(r"$%s$"%prop["xlabel"])
-        if prop["ylabel"]:
-            self.subplot.set_ylabel(r"$%s$"%prop["ylabel"])
-        self.subplot.set_title(prop["title"])
-        
-
-    def clear(self):
-        """Reset the plot"""
-        # TODO: Redraw is brutal.  Render to a backing store and swap in
-        # TODO: rather than redrawing on the fly.
-        self.subplot.clear()
-        self.subplot.hold(True)
-
-    def render(self):
-        """Commit the plot after all objects are drawn"""
-        # TODO: this is when the backing store should be swapped in.
-        if self.legend_on:
-            ax = self.subplot
-            ax.texts = self.textList
-            try:
-                handles, labels = ax.get_legend_handles_labels()
-                # sort them by labels
-                hl = sorted(zip(handles, labels),
-                            key=operator.itemgetter(1))
-                handles2, labels2 = zip(*hl)
-                self.line_collections_list = handles2
-                self.legend = ax.legend(handles2, labels2,
-                                        prop=FontProperties(size=10),
-                                        loc=self.legendLoc)
-                if self.legend != None:
-                    self.legend.set_picker(self.legend_picker)
-                    self.legend.set_axes(self.subplot)
-                    self.legend.set_zorder(20)
-
-            except:
-                self.legend = ax.legend(prop=FontProperties(size=10),
-                                        loc=self.legendLoc)
-
-    def xaxis(self, label, units, font=None, color='black', t_font=None):
-        """xaxis label and units.
-
-        Axis labels know about units.
-
-        We need to do this so that we can detect when axes are not
-        commesurate.  Currently this is ignored other than for formatting
-        purposes.
-
-        """
-
-        self.xcolor = color
-        if units.count("{") > 0 and units.count("$") < 2:
-            units = '$' + units + '$'
-        if label.count("{") > 0 and label.count("$") < 2:
-            label = '$' + label + '$'
-        if units != "":
-            label = label + " (" + units + ")"
-        if font:
-            self.subplot.set_xlabel(label, fontproperties=font, color=color)
-            if t_font != None:
-                for tick in self.subplot.xaxis.get_major_ticks():
-                    tick.label.set_fontproperties(t_font)
-                for line in self.subplot.xaxis.get_ticklines():
-                    size = t_font.get_size()
-                    line.set_markersize(size / 3)
-        else:
-            self.subplot.set_xlabel(label, color=color)
-        pass
-
-    def yaxis(self, label, units, font=None, color='black', t_font=None):
-        """yaxis label and units."""
-        self.ycolor = color
-        if units.count("{") > 0 and units.count("$") < 2:
-            units = '$' + units + '$'
-        if label.count("{") > 0 and label.count("$") < 2:
-            label = '$' + label + '$'
-        if units != "":
-            label = label + " (" + units + ")"
-        if font:
-            self.subplot.set_ylabel(label, fontproperties=font, color=color)
-            if t_font != None:
-                for tick_label in self.subplot.get_yticklabels():
-                    tick_label.set_fontproperties(t_font)
-                for line in self.subplot.yaxis.get_ticklines():
-                    size = t_font.get_size()
-                    line.set_markersize(size / 3)
-        else:
-            self.subplot.set_ylabel(label, color=color)
-        pass
-
-    def _connect_to_xlim(self, callback):
-        """Bind the xlim change notification to the callback"""
-        def process_xlim(axes):
-            lo, hi = subplot.get_xlim()
-            callback(lo, hi)
-        self.subplot.callbacks.connect('xlim_changed', process_xlim)
-
-    def interactive_points(self, x, y, dx=None, dy=None, name='', color=0,
-                           symbol=0, markersize=5, zorder=1, id=None,
-                           label=None, hide_error=False):
-        """Draw markers with error bars"""
-        self.subplot.set_yscale('linear')
-        self.subplot.set_xscale('linear')
-        if id is None:
-            id = name
-        from plottable_interactor import PointInteractor
-        p = PointInteractor(self, self.subplot, zorder=zorder, id=id)
-        if p.markersize != None:
-            markersize = p.markersize
-        p.points(x, y, dx=dx, dy=dy, color=color, symbol=symbol, zorder=zorder,
-                 markersize=markersize, label=label, hide_error=hide_error)
-
-        self.subplot.set_yscale(self.yscale, nonposy='clip')
-        self.subplot.set_xscale(self.xscale)
-
-    def interactive_curve(self, x, y, dy=None, name='', color=0,
-                          symbol=0, zorder=1, id=None, label=None):
-        """Draw markers with error bars"""
-        self.subplot.set_yscale('linear')
-        self.subplot.set_xscale('linear')
-        if id is None:
-            id = name
-        from plottable_interactor import PointInteractor
-        p = PointInteractor(self, self.subplot, zorder=zorder, id=id)
-        p.curve(x, y, dy=dy, color=color, symbol=symbol, zorder=zorder,
-                label=label)
-
-        self.subplot.set_yscale(self.yscale, nonposy='clip')
-        self.subplot.set_xscale(self.xscale)
-
-    def plottable_selected(self, id):
-        """
-        Called to register a plottable as selected
-        """
-        #TODO: check that it really is in the list of plottables
-        self.graph.selected_plottable = id
-
-    def points(self, x, y, dx=None, dy=None,
-               color=0, symbol=0, marker_size=5, label=None,
-               id=None, hide_error=False):
-        """Draw markers with error bars"""
-
-        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
-        if dx != None and type(dx) == type(()):
-            dx = nx.vstack((x - dx[0], dx[1] - x)).transpose()
-        if dy != None and type(dy) == type(()):
-            dy = nx.vstack((y - dy[0], dy[1] - y)).transpose()
-        if dx == None and dy == None:
-            self.subplot.plot(x, y, color=self._color(color),
-                              marker=self._symbol(symbol),
-                              markersize=marker_size,
-                              linestyle='',
-                              label=label)
-        else:
-            col = self._color(color)
-            if hide_error:
-                self.subplot.plot(x, y, color=col,
-                                  marker=self._symbol(symbol),
-                                  markersize=marker_size,
-                                  linestyle='',
-                                  label=label)
-            else:
-                self.subplot.errorbar(x, y, yerr=dy, xerr=None,
-                                      ecolor=col, capsize=2, linestyle='',
-                                      barsabove=False,
-                                      mec=col, mfc=col,
-                                      marker=self._symbol(symbol),
-                                      markersize=marker_size,
-                                      lolims=False, uplims=False,
-                                      xlolims=False, xuplims=False, label=label)
-
-        self.subplot.set_yscale(self.yscale, nonposy='clip')
-        self.subplot.set_xscale(self.xscale)
-
-    def _onToggleScale(self, event):
-        """
-        toggle axis and replot image
-
-        """
-        zmin_2D_temp = self.zmin_2D
-        zmax_2D_temp = self.zmax_2D
-        if self.scale == 'log_{10}':
-            self.scale = 'linear'
-            if not self.zmin_2D is None:
-                zmin_2D_temp = math.pow(10, self.zmin_2D)
-            if not self.zmax_2D is None:
-                zmax_2D_temp = math.pow(10, self.zmax_2D)
-        else:
-            self.scale = 'log_{10}'
-            if not self.zmin_2D is None:
-                # min log value: no log(negative)
-                if self.zmin_2D <= 0:
-                    zmin_2D_temp = -32
-                else:
-                    zmin_2D_temp = math.log10(self.zmin_2D)
-            if not self.zmax_2D is None:
-                zmax_2D_temp = math.log10(self.zmax_2D)
-
-        self.image(data=self.data, qx_data=self.qx_data,
-                   qy_data=self.qy_data, xmin=self.xmin_2D,
-                   xmax=self.xmax_2D,
-                   ymin=self.ymin_2D, ymax=self.ymax_2D,
-                   cmap=self.cmap, zmin=zmin_2D_temp,
-                   zmax=zmax_2D_temp)
-
-    def image(self, data, qx_data, qy_data, xmin, xmax, ymin, ymax,
-              zmin, zmax, color=0, symbol=0, markersize=0,
-              label='data2D', cmap=DEFAULT_CMAP):
-        """
-        Render the current data
-
-        """
-        self.data = data
-        self.qx_data = qx_data
-        self.qy_data = qy_data
-        self.xmin_2D = xmin
-        self.xmax_2D = xmax
-        self.ymin_2D = ymin
-        self.ymax_2D = ymax
-        self.zmin_2D = zmin
-        self.zmax_2D = zmax
-        c = self._color(color)
-        # If we don't have any data, skip.
-        if self.data == None:
-            return
-        if self.data.ndim == 1:
-            output = self._build_matrix()
-        else:
-            output = copy.deepcopy(self.data)
-        # check scale
-        if self.scale == 'log_{10}':
-            try:
-                if  self.zmin_2D <= 0  and len(output[output > 0]) > 0:
-                    zmin_temp = self.zmin_2D
-                    output[output > 0] = numpy.log10(output[output > 0])
-                    #In log scale Negative values are not correct in general
-                    #output[output<=0] = math.log(numpy.min(output[output>0]))
-                elif self.zmin_2D <= 0:
-                    zmin_temp = self.zmin_2D
-                    output[output > 0] = numpy.zeros(len(output))
-                    output[output <= 0] = -32
-                else:
-                    zmin_temp = self.zmin_2D
-                    output[output > 0] = numpy.log10(output[output > 0])
-                    #In log scale Negative values are not correct in general
-                    #output[output<=0] = math.log(numpy.min(output[output>0]))
-            except:
-                #Too many problems in 2D plot with scale
-                pass
-
-        else:
-            zmin_temp = self.zmin_2D
-        self.cmap = cmap
-        if self.dimension != 3:
-            #Re-adjust colorbar
-            self.subplot.figure.subplots_adjust(left=0.2, right=.8, bottom=.2)
-
-            im = self.subplot.imshow(output, interpolation='nearest',
-                                     origin='lower',
-                                     vmin=zmin_temp, vmax=self.zmax_2D,
-                                     cmap=self.cmap,
-                                     extent=(self.xmin_2D, self.xmax_2D,
-                                             self.ymin_2D, self.ymax_2D))
-
-            cbax = self.subplot.figure.add_axes([0.84, 0.2, 0.02, 0.7])
-        else:
-            # clear the previous 2D from memory
-            # mpl is not clf, so we do
-            self.subplot.figure.clear()
-
-            self.subplot.figure.subplots_adjust(left=0.1, right=.8, bottom=.1)
-
-            X = self.x_bins[0:-1]
-            Y = self.y_bins[0:-1]
-            X, Y = numpy.meshgrid(X, Y)
-
-            try:
-                # mpl >= 1.0.0
-                ax = self.subplot.figure.gca(projection='3d')
-                #ax.disable_mouse_rotation()
-                cbax = self.subplot.figure.add_axes([0.84, 0.1, 0.02, 0.8])
-                if len(X) > 60:
-                    ax.disable_mouse_rotation()
-            except:
-                # mpl < 1.0.0
-                try:
-                    from mpl_toolkits.mplot3d import Axes3D
-                except:
-                    logging.error("PlotPanel could not import Axes3D")
-                self.subplot.figure.clear()
-                ax = Axes3D(self.subplot.figure)
-                if len(X) > 60:
-                    ax.cla()
-                cbax = None
-            self.subplot.figure.canvas.resizing = False
-            im = ax.plot_surface(X, Y, output, rstride=1, cstride=1, cmap=cmap,
-                                 linewidth=0, antialiased=False)
-            self.subplot.set_axis_off()
-
-        if cbax == None:
-            ax.set_frame_on(False)
-            cb = self.subplot.figure.colorbar(im, shrink=0.8, aspect=20)
-        else:
-            cb = self.subplot.figure.colorbar(im, cax=cbax)
-        cb.update_bruteforce(im)
-        cb.set_label('$' + self.scale + '$')
-        if self.dimension != 3:
-            self.figure.canvas.draw_idle()
-        else:
-            self.figure.canvas.draw()
-
-    def _build_matrix(self):
-        """
-        Build a matrix for 2d plot from a vector
-        Returns a matrix (image) with ~ square binning
-        Requirement: need 1d array formats of
-        self.data, self.qx_data, and self.qy_data
-        where each one corresponds to z, x, or y axis values
-
-        """
-        # No qx or qy given in a vector format
-        if self.qx_data == None or self.qy_data == None \
-                or self.qx_data.ndim != 1 or self.qy_data.ndim != 1:
-            # do we need deepcopy here?
-            return copy.deepcopy(self.data)
-
-        # maximum # of loops to fillup_pixels
-        # otherwise, loop could never stop depending on data
-        max_loop = 1
-        # get the x and y_bin arrays.
-        self._get_bins()
-        # set zero to None
-
-        #Note: Can not use scipy.interpolate.Rbf:
-        # 'cause too many data points (>10000)<=JHC.
-        # 1d array to use for weighting the data point averaging
-        #when they fall into a same bin.
-        weights_data = numpy.ones([self.data.size])
-        # get histogram of ones w/len(data); this will provide
-        #the weights of data on each bins
-        weights, xedges, yedges = numpy.histogram2d(x=self.qy_data,
-                                                    y=self.qx_data,
-                                                    bins=[self.y_bins, self.x_bins],
-                                                    weights=weights_data)
-        # get histogram of data, all points into a bin in a way of summing
-        image, xedges, yedges = numpy.histogram2d(x=self.qy_data,
-                                                  y=self.qx_data,
-                                                  bins=[self.y_bins, self.x_bins],
-                                                  weights=self.data)
-        # Now, normalize the image by weights only for weights>1: 
-        # If weight == 1, there is only one data point in the bin so
-        # that no normalization is required.
-        image[weights > 1] = image[weights > 1] / weights[weights > 1]
-        # Set image bins w/o a data point (weight==0) as None (was set to zero
-        # by histogram2d.)
-        image[weights == 0] = None
-
-        # Fill empty bins with 8 nearest neighbors only when at least
-        #one None point exists
-        loop = 0
-
-        # do while loop until all vacant bins are filled up up
-        #to loop = max_loop
-        while not(numpy.isfinite(image[weights == 0])).all():
-            if loop >= max_loop:  # this protects never-ending loop
-                break
-            image = self._fillup_pixels(image=image, weights=weights)
-            loop += 1
-
-        return image
-
-    def _get_bins(self):
-        """
-        get bins
-        set x_bins and y_bins into self, 1d arrays of the index with
-        ~ square binning
-        Requirement: need 1d array formats of
-        self.qx_data, and self.qy_data
-        where each one corresponds to  x, or y axis values
-        """
-        # No qx or qy given in a vector format
-        if self.qx_data == None or self.qy_data == None \
-                or self.qx_data.ndim != 1 or self.qy_data.ndim != 1:
-            # do we need deepcopy here?
-            return copy.deepcopy(self.data)
-
-        # find max and min values of qx and qy
-        xmax = self.qx_data.max()
-        xmin = self.qx_data.min()
-        ymax = self.qy_data.max()
-        ymin = self.qy_data.min()
-
-        # calculate the range of qx and qy: this way, it is a little
-        # more independent
-        x_size = xmax - xmin
-        y_size = ymax - ymin
-
-        # estimate the # of pixels on each axes
-        npix_y = int(math.floor(math.sqrt(len(self.qy_data))))
-        npix_x = int(math.floor(len(self.qy_data) / npix_y))
-
-        # bin size: x- & y-directions
-        xstep = x_size / (npix_x - 1)
-        ystep = y_size / (npix_y - 1)
-
-        # max and min taking account of the bin sizes
-        xmax = xmax + xstep / 2.0
-        xmin = xmin - xstep / 2.0
-        ymax = ymax + ystep / 2.0
-        ymin = ymin - ystep / 2.0
-
-        # store x and y bin centers in q space
-        x_bins = numpy.linspace(xmin, xmax, npix_x)
-        y_bins = numpy.linspace(ymin, ymax, npix_y)
-
-        #set x_bins and y_bins
-        self.x_bins = x_bins
-        self.y_bins = y_bins
-
-    def _fillup_pixels(self, image=None, weights=None):
-        """
-        Fill z values of the empty cells of 2d image matrix
-        with the average over up-to next nearest neighbor points
-
-        :param image: (2d matrix with some zi = None)
-
-        :return: image (2d array )
-
-        :TODO: Find better way to do for-loop below
-
-        """
-        # No image matrix given
-        if image == None or numpy.ndim(image) != 2 \
-                or numpy.isfinite(image).all() \
-                or weights == None:
-            return image
-        # Get bin size in y and x directions
-        len_y = len(image)
-        len_x = len(image[1])
-        temp_image = numpy.zeros([len_y, len_x])
-        weit = numpy.zeros([len_y, len_x])
-        # do for-loop for all pixels
-        for n_y in range(len(image)):
-            for n_x in range(len(image[1])):
-                # find only null pixels
-                if weights[n_y][n_x] > 0 or numpy.isfinite(image[n_y][n_x]):
-                    continue
-                else:
-                    # find 4 nearest neighbors
-                    # check where or not it is at the corner
-                    if n_y != 0 and numpy.isfinite(image[n_y - 1][n_x]):
-                        temp_image[n_y][n_x] += image[n_y - 1][n_x]
-                        weit[n_y][n_x] += 1
-                    if n_x != 0 and numpy.isfinite(image[n_y][n_x - 1]):
-                        temp_image[n_y][n_x] += image[n_y][n_x - 1]
-                        weit[n_y][n_x] += 1
-                    if n_y != len_y - 1 and numpy.isfinite(image[n_y + 1][n_x]):
-                        temp_image[n_y][n_x] += image[n_y + 1][n_x]
-                        weit[n_y][n_x] += 1
-                    if n_x != len_x - 1 and numpy.isfinite(image[n_y][n_x + 1]):
-                        temp_image[n_y][n_x] += image[n_y][n_x + 1]
-                        weit[n_y][n_x] += 1
-                    # go 4 next nearest neighbors when no non-zero
-                    # neighbor exists
-                    if n_y != 0 and n_x != 0 and\
-                         numpy.isfinite(image[n_y - 1][n_x - 1]):
-                        temp_image[n_y][n_x] += image[n_y - 1][n_x - 1]
-                        weit[n_y][n_x] += 1
-                    if n_y != len_y - 1 and n_x != 0 and \
-                        numpy.isfinite(image[n_y + 1][n_x - 1]):
-                        temp_image[n_y][n_x] += image[n_y + 1][n_x - 1]
-                        weit[n_y][n_x] += 1
-                    if n_y != len_y and n_x != len_x - 1 and \
-                        numpy.isfinite(image[n_y - 1][n_x + 1]):
-                        temp_image[n_y][n_x] += image[n_y - 1][n_x + 1]
-                        weit[n_y][n_x] += 1
-                    if n_y != len_y - 1 and n_x != len_x - 1 and \
-                        numpy.isfinite(image[n_y + 1][n_x + 1]):
-                        temp_image[n_y][n_x] += image[n_y + 1][n_x + 1]
-                        weit[n_y][n_x] += 1
-
-        # get it normalized
-        ind = (weit > 0)
-        image[ind] = temp_image[ind] / weit[ind]
-
-        return image
-
-    def curve(self, x, y, dy=None, color=0, symbol=0, label=None):
-        """Draw a line on a graph, possibly with confidence intervals."""
-        c = self._color(color)
-        self.subplot.set_yscale('linear')
-        self.subplot.set_xscale('linear')
-
-        self.subplot.plot(x, y, color=c, marker='',
-                          linestyle='-', label=label)
-        self.subplot.set_yscale(self.yscale)
-        self.subplot.set_xscale(self.xscale)
-
-    def _color(self, c):
-        """Return a particular colour"""
-        return self.colorlist[c % len(self.colorlist)]
-
-    def _symbol(self, s):
-        """Return a particular symbol"""
-        return self.symbollist[s % len(self.symbollist)]
-
-    def _replot(self, remove_fit=False):
-        """
-        Rescale the plottables according to the latest
-        user selection and update the plot
-
-        :param remove_fit: Fit line will be removed if True
-
-        """
-        self.graph.reset_scale()
-        self._onEVT_FUNC_PROPERTY(remove_fit=remove_fit)
-        #TODO: Why do we have to have the following line?
-        self.fit_result.reset_view()
-        self.graph.render(self)
-        self.subplot.figure.canvas.draw_idle()
-
-    def _onEVT_FUNC_PROPERTY(self, remove_fit=True, show=True):
-        """
-        Receive the x and y transformation from myDialog,
-        Transforms x and y in View
-        and set the scale
-        """
-        # The logic should be in the right order
-        # Delete first, and then get the whole list...
-        if remove_fit:
-            self.graph.delete(self.fit_result)
-        self.ly = None
-        self.q_ctrl = None
-        list = self.graph.returnPlottable()
-        # Changing the scale might be incompatible with
-        # currently displayed data (for instance, going
-        # from ln to log when all plotted values have
-        # negative natural logs).
-        # Go linear and only change the scale at the end.
-        self.set_xscale("linear")
-        self.set_yscale("linear")
-        _xscale = 'linear'
-        _yscale = 'linear'
-        for item in list:
-            item.setLabel(self.xLabel, self.yLabel)
-
-            # control axis labels from the panel itself
-            yname, yunits = item.get_yaxis()
-            if self.yaxis_label != None:
-                yname = self.yaxis_label
-                yunits = self.yaxis_unit
-            else:
-                self.yaxis_label = yname
-                self.yaxis_unit = yunits
-            xname, xunits = item.get_xaxis()
-            if self.xaxis_label != None:
-                xname = self.xaxis_label
-                xunits = self.xaxis_unit
-            else:
-                self.xaxis_label = xname
-                self.xaxis_unit = xunits
-            # Goes through all possible scales
-            if self.xLabel == "x":
-                item.transformX(transform.toX, transform.errToX)
-                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
-            if self.xLabel == "x^(2)":
-                item.transformX(transform.toX2, transform.errToX2)
-                xunits = convert_unit(2, xunits)
-                self.graph._xaxis_transformed("%s^{2}" % xname, "%s" % xunits)
-            if self.xLabel == "x^(4)":
-                item.transformX(transform.toX4, transform.errToX4)
-                xunits = convert_unit(4, xunits)
-                self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
-            if self.xLabel == "ln(x)":
-                item.transformX(transform.toLogX, transform.errToLogX)
-                self.graph._xaxis_transformed("\ln\\ %s" % xname, "%s" % xunits)
-            if self.xLabel == "log10(x)":
-                item.transformX(transform.toX_pos, transform.errToX_pos)
-                _xscale = 'log'
-                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
-            if self.xLabel == "log10(x^(4))":
-                item.transformX(transform.toX4, transform.errToX4)
-                xunits = convert_unit(4, xunits)
-                self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
-                _xscale = 'log'
-            if self.yLabel == "ln(y)":
-                item.transformY(transform.toLogX, transform.errToLogX)
-                self.graph._yaxis_transformed("\ln\\ %s" % yname, "%s" % yunits)
-            if self.yLabel == "y":
-                item.transformY(transform.toX, transform.errToX)
-                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
-            if self.yLabel == "log10(y)":
-                item.transformY(transform.toX_pos, transform.errToX_pos)
-                _yscale = 'log'
-                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
-            if self.yLabel == "y^(2)":
-                item.transformY(transform.toX2, transform.errToX2)
-                yunits = convert_unit(2, yunits)
-                self.graph._yaxis_transformed("%s^{2}" % yname, "%s" % yunits)
-            if self.yLabel == "1/y":
-                item.transformY(transform.toOneOverX, transform.errOneOverX)
-                yunits = convert_unit(-1, yunits)
-                self.graph._yaxis_transformed("1/%s" % yname, "%s" % yunits)
-            if self.yLabel == "y*x^(4)":
-                item.transformY(transform.toYX4, transform.errToYX4)
-                xunits = convert_unit(4, self.xaxis_unit)
-                self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname, xname),
-                                              "%s%s" % (yunits, xunits))
-            if self.yLabel == "1/sqrt(y)":
-                item.transformY(transform.toOneOverSqrtX,
-                                transform.errOneOverSqrtX)
-                yunits = convert_unit(-0.5, yunits)
-                self.graph._yaxis_transformed("1/\sqrt{%s}" % yname,
-                                              "%s" % yunits)
-            if self.yLabel == "ln(y*x)":
-                item.transformY(transform.toLogXY, transform.errToLogXY)
-                self.graph._yaxis_transformed("\ln (%s \ \ %s)" % (yname, xname),
-                                              "%s%s" % (yunits, self.xaxis_unit))
-            if self.yLabel == "ln(y*x^(2))":
-                item.transformY(transform.toLogYX2, transform.errToLogYX2)
-                xunits = convert_unit(2, self.xaxis_unit)
-                self.graph._yaxis_transformed("\ln (%s \ \ %s^{2})" % (yname, xname),
-                                              "%s%s" % (yunits, xunits))
-            if self.yLabel == "ln(y*x^(4))":
-                item.transformY(transform.toLogYX4, transform.errToLogYX4)
-                xunits = convert_unit(4, self.xaxis_unit)
-                self.graph._yaxis_transformed("\ln (%s \ \ %s^{4})" % (yname, xname),
-                                              "%s%s" % (yunits, xunits))
-            if self.yLabel == "log10(y*x^(4))":
-                item.transformY(transform.toYX4, transform.errToYX4)
-                xunits = convert_unit(4, self.xaxis_unit)
-                _yscale = 'log'
-                self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname, xname),
-                                              "%s%s" % (yunits, xunits))
-            if self.viewModel == "Guinier lny vs x^(2)":
-                item.transformX(transform.toX2, transform.errToX2)
-                xunits = convert_unit(2, xunits)
-                self.graph._xaxis_transformed("%s^{2}" % xname, "%s" % xunits)
-                item.transformY(transform.toLogX, transform.errToLogX)
-                self.graph._yaxis_transformed("\ln\ \ %s" % yname, "%s" % yunits)
-            if self.viewModel == "Porod y*x^(4) vs x^(4)":
-                item.transformX(transform.toX4, transform.errToX4)
-                xunits = convert_unit(4, self.xaxis_unit)
-                self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
-                item.transformY(transform.toYX4, transform.errToYX4)
-                self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname, xname),
-                                              "%s%s" % (yunits, xunits))
-            item.transformView()
-
-        # set new label and units
-        yname = self.graph.prop["ylabel"]
-        yunits = ''
-        xname = self.graph.prop["xlabel"]
-        xunits = ''
-
-        self.resetFitView()
-        self.prevXtrans = self.xLabel
-        self.prevYtrans = self.yLabel
-        self.graph.render(self)
-        self.set_xscale(_xscale)
-        self.set_yscale(_yscale)
-
-        self.xaxis(xname, xunits, self.xaxis_font,
-                   self.xaxis_color, self.xaxis_tick)
-        self.yaxis(yname, yunits, self.yaxis_font,
-                   self.yaxis_color, self.yaxis_tick)
-        self.subplot.texts = self.textList
-        if show:
-            self.subplot.figure.canvas.draw_idle()
-
-    def onFitDisplay(self, tempx, tempy, xminView,
-                     xmaxView, xmin, xmax, func):
-        """
-        Add a new plottable into the graph .In this case this plottable
-        will be used to fit some data
-
-        :param tempx: The x data of fit line
-        :param tempy: The y data of fit line
-        :param xminView: the lower bound of fitting range
-        :param xminView: the upper bound of  fitting range
-        :param xmin: the lowest value of data to fit to the line
-        :param xmax: the highest value of data to fit to the line
-
-        """
-        # Saving value to redisplay in Fit Dialog when it is opened again
-        self.Avalue, self.Bvalue, self.ErrAvalue, \
-                      self.ErrBvalue, self.Chivalue = func
-        self.xminView = xminView
-        self.xmaxView = xmaxView
-        self.xmin = xmin
-        self.xmax = xmax
-        #In case need to change the range of data plotted
-        for item in self.graph.returnPlottable():
-            item.onFitRange(None, None)
-        # Create new data plottable with result
-        self.fit_result.x = []
-        self.fit_result.y = []
-        self.fit_result.x = tempx
-        self.fit_result.y = tempy
-        self.fit_result.dx = None
-        self.fit_result.dy = None
-        #Load the view with the new values
-        self.fit_result.reset_view()
-        # Add the new plottable to the graph
-        self.graph.add(self.fit_result)
-        self.graph.render(self)
-        self._offset_graph()
-        self.subplot.figure.canvas.draw_idle()
-
-    def onChangeCaption(self, event):
-        """
-        """
-        if self.parent == None:
-            return
-        # get current caption
-        old_caption = self.window_caption
-        # Get new caption dialog
-        dial = LabelDialog(None, -1, 'Modify Window Title', old_caption)
-        if dial.ShowModal() == wx.ID_OK:
-            new_caption = dial.getText()
-
-            # send to guiframe to change the panel caption
-            caption = self.parent.on_change_caption(self.window_name,
-                                                    old_caption, new_caption)
-
-            # also set new caption in plot_panels list
-            self.parent.plot_panels[self.uid].window_caption = caption
-            # set new caption
-            self.window_caption = caption
-
-        dial.Destroy()
-
-    def onResetGraph(self, event):
-        """
-        Reset the graph by plotting the full range of data
-        """
-        for item in self.graph.returnPlottable():
-            item.onReset()
-        self.graph.render(self)
-        self._onEVT_FUNC_PROPERTY(False)
-        self.is_zoomed = False
-        self.toolbar.update()
-
-    def onPrint(self, event=None):
-        self.toolbar.print_figure(event)
-
-    def onPrinterSetup(self, event=None):
-        """
-        """
-        self.canvas.Printer_Setup(event=event)
-        self.Update()
-
-    def onPrinterPreview(self, event=None):
-        """
-        Matplotlib camvas can no longer print itself.  Thus need to do
-        everything ourselves: need to create a printpreview frame to to
-        see the preview but needs a parent frame object.  Also needs a
-        printout object (just as any printing task).
-        """
-        try:
-            #check if parent is a frame.  If not keep getting the higher
-            #parent till we find a frame
-            _plot = self
-            while not isinstance(_plot, wx.Frame):
-                _plot = _plot.GetParent()
-                assert _plot is not None
-
-            #now create the printpeview object
-            _preview = wx.PrintPreview(PlotPrintout(self.canvas),
-                                       PlotPrintout(self.canvas))
-            #and tie it to a printpreview frame then show it
-            _frame = wx.PreviewFrame(_preview, _plot, "Print Preview", wx.Point(100, 100), wx.Size(600, 650))
-            _frame.Centre(wx.BOTH)
-            _frame.Initialize()
-            _frame.Show(True)
-        except:
-            traceback.print_exc()
-            pass
-
-    def OnCopyFigureMenu(self, evt):
-        """
-        Copy the current figure to clipboard
-        """
-        try:
-            self.toolbar.copy_figure()
-        except:
-            print "Error in copy Image"
-
-
-#---------------------------------------------------------------
-class NoRepaintCanvas(FigureCanvasWxAgg):
-    """
-    We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
-    the draw method is only called for the first two paint events. After that,
-    the canvas will only be redrawn when it is resized.
-
-    """
-    def __init__(self, *args, **kwargs):
-        """
-        """
-        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
-        self._drawn = 0
-
-    def _onPaint(self, evt):
-        """
-        Called when wxPaintEvt is generated
-
-        """
-        if not self._isRealized:
-            self.realize()
-        if self._drawn < 2:
-            self.draw(repaint=False)
-            self._drawn += 1
-        self.gui_repaint(drawDC=wx.PaintDC(self))
Index: c/sas/plottools/PropertyDialog.py
===================================================================
--- src/sas/plottools/PropertyDialog.py	(revision 3477478de6317fb51d4dd0dbaf9ab2a1236faf28)
+++ 	(revision )
@@ -1,95 +1,0 @@
-"""
-"""
-import wx
-
-class Properties(wx.Dialog):
-    """
-    """
-    def __init__(self, parent, id=-1, title="Select the scale of the graph"):
-        wx.Dialog.__init__(self, parent, id, title)
-        self.parent = parent
-        vbox = wx.BoxSizer(wx.VERTICAL)
-        sizer = wx.GridBagSizer(5, 5)
-
-        x_size = 70
-
-        ix = 1
-        iy = 1
-        sizer.Add(wx.StaticText(self, -1, 'X'), (iy, ix))
-        ix += 2
-        sizer.Add(wx.StaticText(self, -1, 'Y'), (iy, ix))
-        ix += 2
-        sizer.Add(wx.StaticText(self, -1, 'View'), (iy, ix))
-        iy += 1
-        ix = 1
-        self.xvalue = wx.ComboBox(self, -1)
-        x_size += self.xvalue.GetSize()[0]
-        sizer.Add(self.xvalue, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-
-        ix += 2
-        self.yvalue = wx.ComboBox(self, -1)
-        x_size += self.yvalue.GetSize()[0]
-        sizer.Add(self.yvalue, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-
-        ix += 2
-        self.view = wx.ComboBox(self, -1)
-        x_size += self.view.GetSize()[0]
-        self.view.SetMinSize((160, 30))
-        sizer.Add(self.view, (iy, ix), (1, 1),
-                  wx.EXPAND | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
-        self.SetMinSize((x_size, 50))
-        vbox.Add(sizer, 0, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-
-        cancel_button = wx.Button(self, wx.ID_CANCEL, 'Cancel')
-        ok_button = wx.Button(self, wx.ID_OK, "OK")
-        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
-        sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        sizer_button.Add(ok_button, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
-        sizer_button.Add(cancel_button, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
-        vbox.Add(sizer_button, 0,
-                 wx.EXPAND | wx.TOP | wx.BOTTOM | wx.ADJUST_MINSIZE, 10)
-        # scale value for x
-        self.xvalue.SetValue("ln(x)")
-        self.xvalue.Insert("x", 0)
-        self.xvalue.Insert("x^(2)", 1)
-        self.xvalue.Insert("x^(4)", 2)
-        self.xvalue.Insert("ln(x)", 3)
-        self.xvalue.Insert("log10(x)", 4)
-        self.xvalue.Insert("log10(x^(4))", 5)
-
-        # scale value for y
-        self.yvalue.SetValue("ln(y)")
-        self.yvalue.Insert("y", 0)
-        self.yvalue.Insert("1/y", 1)
-        self.yvalue.Insert("ln(y)", 2)
-        self.yvalue.Insert("y^(2)", 3)
-        self.yvalue.Insert("y*x^(4)", 4)
-        self.yvalue.Insert("1/sqrt(y)", 5)
-        self.yvalue.Insert("log10(y)", 6)
-        self.yvalue.Insert("ln(y*x)", 7)
-        self.yvalue.Insert("ln(y*x^(2))", 8)
-        self.yvalue.Insert("ln(y*x^(4))", 9)
-        self.yvalue.Insert("log10(y*x^(4))", 10)
-        # type of view or model used
-        self.view.SetValue("--")
-        self.view.Insert("--", 0)
-        self.view.Insert("Linear y vs x", 1)
-        self.view.Insert("Guinier lny vs x^(2)", 2)
-        self.view.Insert("XS Guinier ln(y*x) vs x^(2)", 3)
-        self.view.Insert("Porod y*x^(4) vs x^(4)", 4)
-        self.view.Insert("Kratky y*x^(2) vs x", 5)
-        self.SetSizer(vbox)
-        self.Fit()
-        self.Centre()
-
-    def setValues(self, x, y, view):
-        """
-        """
-        return  self.xvalue.SetValue(x), self.yvalue.SetValue(y), \
-                    self.view.SetValue(view)
-
-    def getValues(self):
-        """
-        """
-        return self.xvalue.GetValue(), self.yvalue.GetValue(), \
-                            self.view.GetValue()
Index: c/sas/plottools/SimpleFont.py
===================================================================
--- src/sas/plottools/SimpleFont.py	(revision 3477478de6317fb51d4dd0dbaf9ab2a1236faf28)
+++ 	(revision )
@@ -1,114 +1,0 @@
-
-"""
-This software was developed by Institut Laue-Langevin as part of
-Distributed Data Analysis of Neutron Scattering Experiments (DANSE).
-
-Copyright 2012 Institut Laue-Langevin
-"""
-
-# this is a dead simple dialog for getting font family, size,style and weight
-
-
-import wx
-from matplotlib.font_manager import FontProperties
-
-FAMILY = ['serif', 'sas-serif', 'fantasy', 'monospace']
-SIZE = [8, 9, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72]
-STYLE = ['normal', 'italic']
-WEIGHT = ['light', 'normal', 'bold']
-
-
-class SimpleFont(wx.Dialog):
-    def __init__(self, parent, id, title):
-
-        wx.Dialog.__init__(self, parent, id, title, size=(440, 160))
-        self.parent = parent
-#        self.SetWindowVariant(variant=FONT_VARIANT)
-
-        self.family = FAMILY[1]
-        self.size = SIZE[3]
-        self.style = STYLE[0]
-        self.weight = WEIGHT[1]
-        self.tick_label = None
-        self.tick_label_check = None
-        self.InitUI()
-        self.Centre()
-        self.Show()
-
-    def InitUI(self):
-        self.font_family = wx.ComboBox(self, pos=(80, 10), style=wx.CB_READONLY, size=(100, -1))
-        self.font_style = wx.ComboBox(self, pos=(80, 60), style=wx.CB_READONLY, size=(100, -1))
-        self.font_point = wx.ComboBox(self, pos=(300, 10), style=wx.CB_READONLY, size=(100, -1))
-        self.font_weight = wx.ComboBox(self, pos=(300, 60), style=wx.CB_READONLY, size=(100, -1))
-        self.tick_label_check = wx.CheckBox(self, -1, label='', pos=(80, 100), size=(15, -1))
-        self.tick_label_check.SetValue(False)
-        wx.Button(self, wx.ID_OK, 'OK', pos=(215, 100))
-        wx.Button(self, wx.ID_CANCEL, 'Cancel', pos=(315, 100))
-
-        self._set_family_list()
-        self._set_style_list()
-        self._set_weight_list()
-        self._set_point_list()
-
-        wx.StaticText(self, label='Family:', pos=(10, 12))
-        wx.StaticText(self, label='Size:', pos=(220, 12))
-        wx.StaticText(self, label='Style:', pos=(10, 62))
-        wx.StaticText(self, label='Weight:', pos=(220, 62))
-        tick_label_text = wx.StaticText(self, label='Tick label?', pos=(10, 100))
-        tick_label_text.SetToolTipString("Apply to tick label too.")
-
-    def _set_family_list(self):
-        # list of font family
-        for idx in range(len(FAMILY)):
-            self.font_family.Append(FAMILY[idx], idx)
-
-    def _set_style_list(self):
-        # list of styles
-        for idx in range(len(STYLE)):
-            self.font_style.Append(STYLE[idx], idx)
-
-    def _set_weight_list(self):
-        # list of weights
-        for idx in range(len(WEIGHT)):
-            self.font_weight.Append(WEIGHT[idx], idx)
-
-    def _set_point_list(self):
-        # list of point sizes
-        for idx in range(len(SIZE)):
-            self.font_point.Append(str(SIZE[idx]), idx)
-
-    def get_ticklabel_check(self):
-        """
-        Get tick label check value
-        """
-        self.tick_label = self.tick_label_check.GetValue()
-        return self.tick_label
-
-    def set_ticklabel_check(self, check=False):
-        """
-        Set tick label check value
-        """
-        self.tick_label_check.SetValue(check)
-
-    def set_default_font(self, font):
-
-        if not font:
-            self.font_family.SetSelection(1)
-            self.font_weight.SetSelection(1)
-            self.font_point.SetSelection(3)
-            self.font_style.SetSelection(0)
-        else:
-            self.font_weight.SetStringSelection(str(font.get_weight()))
-            self.font_point.SetStringSelection(str(int(font.get_size())))
-            self.font_family.SetStringSelection(str(font.get_family()[0]))
-            self.font_style.SetStringSelection(str(font.get_style()))
-
-    def get_font(self):
-        font_properties = FontProperties()
-        font = font_properties.copy()
-        font.set_size(str(self.font_point.GetValue()))
-        font.set_name(str(self.font_family.GetValue()))
-        font.set_slant(str(self.font_style.GetValue()))
-        font.set_weight(str(self.font_weight.GetValue()))
-
-        return font
Index: c/sas/plottools/SizeDialog.py
===================================================================
--- src/sas/plottools/SizeDialog.py	(revision 3477478de6317fb51d4dd0dbaf9ab2a1236faf28)
+++ 	(revision )
@@ -1,38 +1,0 @@
-import wx
-
-
-class SizeDialog(wx.Dialog):
-    def __init__(self, parent, id, title):
-        wx.Dialog.__init__(self, parent, id, title, size=(300, 175))
-
-        mainbox = wx.BoxSizer(wx.VERTICAL)
-        vbox = wx.BoxSizer(wx.VERTICAL)
-        textbox = wx.BoxSizer(wx.HORIZONTAL)
-        hbox = wx.BoxSizer(wx.HORIZONTAL)
-        text1 = "Enter in a custom size (> 0 accepted)"
-        msg = wx.StaticText(self, -1, text1, (30, 15), style=wx.ALIGN_CENTRE)
-        msg.SetLabel(text1)
-        self.text_ctrl = wx.TextCtrl(self, -1, '', (100, 50))
-
-        textbox.Add(self.text_ctrl, flag=wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, border=10)
-        vbox.Add(msg, flag=wx.ALL, border=10, proportion=1)
-        vbox.Add(textbox, flag=wx.EXPAND | wx.TOP | wx.BOTTOM | wx.ADJUST_MINSIZE, border=10)
-        self.text_ctrl.SetValue(str(5))
-
-        ok_button = wx.Button(self, wx.ID_OK, 'OK', size=(70, 25))
-        close_button = wx.Button(self, wx.ID_CANCEL, 'Cancel', size=(70, 25))
-
-        hbox.Add(ok_button)
-        hbox.Add((20, 20))
-        hbox.Add(close_button)
-
-        mainbox.Add(vbox, flag=wx.ALL, border=10)
-        mainbox.Add(wx.StaticLine(self), 0, wx.ALL | wx.EXPAND, 5)
-        mainbox.Add(hbox, flag=wx.CENTER, border=10)
-        self.SetSizer(mainbox)
-
-    def getText(self):
-        """
-        Get text typed
-        """
-        return self.text_ctrl.GetValue()
Index: c/sas/plottools/TextDialog.py
===================================================================
--- src/sas/plottools/TextDialog.py	(revision 649806d8cadcd2797d6f9c42ca6968c1726e7b34)
+++ 	(revision )
@@ -1,307 +1,0 @@
-import wx
-import sys
-
-if sys.platform.count("win32") > 0:
-    FONT_VARIANT = 0
-    PNL_WIDTH = 460
-else:
-    FONT_VARIANT = 1
-    PNL_WIDTH = 500
-FAMILY = ['serif', 'sans-serif', 'fantasy', 'monospace']
-SIZE = [8, 9, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72]
-STYLE = ['normal', 'italic']
-WEIGHT = ['light', 'normal', 'bold']
-COLOR = ['black', 'blue', 'green', 'red', 'cyan', 'magenta', 'yellow']
-
-
-class TextDialog(wx.Dialog):
-    def __init__(self, parent, id, title, label='', unit=None):
-        """
-        Dialog window pops- up when selecting 'Add Text' on the toolbar
-        """
-        wx.Dialog.__init__(self, parent, id, title, size=(PNL_WIDTH, 300))
-        self.parent = parent
-        # Font
-        self.SetWindowVariant(variant=FONT_VARIANT)
-        # default
-        self.family = FAMILY[1]
-        self.size = SIZE[3]
-        self.style = STYLE[0]
-        self.weight = WEIGHT[1]
-        self.color = COLOR[0]
-        self.tick_label = False
-        # Dialog interface
-        vbox = wx.BoxSizer(wx.VERTICAL)
-        text_box = wx.BoxSizer(wx.HORIZONTAL)
-        sizer = wx.GridBagSizer(1, 3)
-        _BOX_WIDTH = 70
-        font_description = wx.StaticBox(self, -1, 'Font')
-        font_box = wx.StaticBoxSizer(font_description, wx.VERTICAL)
-        family_box = wx.BoxSizer(wx.HORIZONTAL)
-        style_box = wx.BoxSizer(wx.HORIZONTAL)
-        # tcA
-        if unit != None:
-            styles = wx.TAB_TRAVERSAL
-            height = -1
-            unit_text = wx.StaticText(self, -1, 'Unit :')
-            unit_text.SetToolTipString("Unit of the axis.")
-            self.unit_ctrl = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, -1))
-            self.unit_ctrl.SetValue(str(unit))
-            unit_box = wx.BoxSizer(wx.HORIZONTAL)
-            tick_label_text = wx.StaticText(self, -1, 'Tick label')
-            tick_label_text.SetToolTipString("Apply to tick label too.")
-            self.tick_label_check = wx.CheckBox(self, -1, '', (10, 10))
-            self.tick_label_check.SetValue(False)
-            self.tick_label_check.SetToolTipString("Apply to tick label too.")
-            wx.EVT_CHECKBOX(self, self.tick_label_check.GetId(),
-                            self.on_tick_label)
-            enter_text = 'Enter text:'
-        else:
-            styles = wx.TAB_TRAVERSAL | wx.TE_MULTILINE | wx.TE_LINEWRAP | \
-                              wx.TE_PROCESS_ENTER | wx.SUNKEN_BORDER | wx.HSCROLL
-            height = 60
-            unit_text = None
-            self.unit_ctrl = None
-            unit_box = None
-            tick_label_text = None
-            self.tick_label_check = None
-            enter_text = 'Enter text'
-            if len(label) > 0:
-                enter_text += " (this text won't be auto-updated if modified.):"
-            else:
-                enter_text += ":"
-        self.text_string = wx.TextCtrl(self, -1, size=(PNL_WIDTH - 30, height), style=styles)
-        self.text_string.SetValue(str(label))
-        self.text_string.SetToolTipString("The text that will be displayed.")
-        # font family
-        self.font_family = wx.ComboBox(self, -1, style=wx.CB_READONLY)
-        wx.EVT_COMBOBOX(self.font_family, -1, self.on_family)
-        self.font_family.SetMinSize((_BOX_WIDTH, -1))
-        self._set_family_list()
-        self.font_family.SetSelection(1)
-        self.font_family.SetToolTipString("Font family of the text.")
-        # font weight
-        self.font_weight = wx.ComboBox(self, -1, style=wx.CB_READONLY)
-        wx.EVT_COMBOBOX(self.font_weight, -1, self.on_weight)
-        self.font_weight.SetMinSize((_BOX_WIDTH, -1))
-        self._set_weight_list()
-        self.font_weight.SetSelection(1)
-        self.font_weight.SetToolTipString("Font weight of the text.")
-        # font family
-        self.font_size = wx.ComboBox(self, -1, style=wx.CB_READONLY)
-        wx.EVT_COMBOBOX(self.font_size, -1, self.on_size)
-        self.font_size.SetMinSize((_BOX_WIDTH, -1))
-        self._set_size_list()
-        self.font_size.SetSelection(5)
-        self.font_size.SetToolTipString("Font size of the text.")
-        # font style
-        self.font_style = wx.ComboBox(self, -1, style=wx.CB_READONLY)
-        wx.EVT_COMBOBOX(self.font_style, -1, self.on_style)
-        self.font_style.SetMinSize((_BOX_WIDTH, -1))
-        self._set_style_list()
-        self.font_style.SetSelection(0)
-        self.font_style.SetToolTipString("Font style of the text.")
-        # font color
-        self.font_color = wx.ComboBox(self, -1, style = wx.CB_READONLY)
-        wx.EVT_COMBOBOX(self.font_color, -1, self.on_color)
-        self.font_color.SetMinSize((_BOX_WIDTH, -1))
-        self._set_color_list()
-        self.font_color.SetSelection(0)
-        self.font_color.SetToolTipString("Font color of the text.")
-        # Buttons on the bottom
-        self.static_line_1 = wx.StaticLine(self, -1)
-        self.ok_button = wx.Button(self, wx.ID_OK, 'OK',
-                                   size = (_BOX_WIDTH, 25))
-        self.close_button = wx.Button(self, wx.ID_CANCEL, 'Cancel',
-                                      size = (_BOX_WIDTH, 25))
-
-        # Intro
-        explanation = "Select font properties :"
-        vbox.Add(sizer)
-        ix = 0
-        iy = 1
-        sizer.Add(wx.StaticText(self, -1, explanation), (iy, ix),
-                  (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
-        family_box.Add(wx.StaticText(self, -1, 'Family :', size = (50, -1)),
-                       0, wx.TOP, 5)
-        family_box.Add(self.font_family, 0, 0)
-        family_box.Add((_BOX_WIDTH / 2, -1))
-        family_box.Add(wx.StaticText(self, -1, 'Size :', size = (50, -1)),
-                       0, wx.TOP, 5)
-        family_box.Add(self.font_size, 0, 0)
-        if unit_box != None:
-            family_box.Add((_BOX_WIDTH / 2, -1))
-            family_box.Add(tick_label_text, 0, 0)
-            family_box.Add(self.tick_label_check, 0, 0)
-        style_box.Add(wx.StaticText(self, -1, 'Style :', size = (50, -1)),
-                      0, wx.TOP, 5)
-        style_box.Add(self.font_style, 0, 0)
-        style_box.Add((_BOX_WIDTH / 2, -1))
-        style_box.Add(wx.StaticText(self, -1, 'Weight :', size = (50, -1)),
-                      0, wx.TOP, 5)
-        style_box.Add(self.font_weight, 0, 0)
-        style_box.Add((_BOX_WIDTH / 2, -1))
-        style_box.Add(wx.StaticText(self, -1, 'Color :', size = (45, -1)),
-                      0, wx.TOP, 5)
-        style_box.Add(self.font_color, 0, 0)
-        font_box.Add(family_box, 0, 10)
-        font_box.Add((0,5))
-        font_box.Add(style_box, 0, 10)
-        iy += 1
-        ix = 0
-        sizer.Add(font_box, (iy, ix),
-                  (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
-        iy += 2
-        ix = 0
-        sizer.Add(wx.StaticText(self, -1, enter_text), (iy, ix),
-                  (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
-        text_box.Add((15, 10))
-        text_box.Add(self.text_string)
-        vbox.Add(text_box, 0, wx.EXPAND, 15)
-        if unit_box != None:
-            unit_box.Add(unit_text, 0, 0)
-            unit_box.Add(self.unit_ctrl, 0, 0)
-            vbox.Add((5, 5))
-            vbox.Add(unit_box, 0, wx.LEFT, 15)
-
-        vbox.Add((10, 10))
-        vbox.Add(self.static_line_1, 0, wx.EXPAND, 10)
-        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
-        sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        sizer_button.Add(self.ok_button, 0,
-                         wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
-        sizer_button.Add(self.close_button, 0,
-                         wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
-        vbox.Add(sizer_button, 0, wx.EXPAND | wx.BOTTOM | wx.TOP, 10)
-        self.SetSizer(vbox)
-        self.Centre()
-
-    def _set_family_list(self):
-        """
-        Set the list of font family
-        """
-        # list of family choices
-        for idx in range(len(FAMILY)):
-            self.font_family.Append(FAMILY[idx], idx)
-
-    def _set_size_list(self):
-        """
-        Set the list of font size
-        """
-        # list of size choices
-        for idx in range(len(SIZE)):
-            self.font_size.Append(str(SIZE[idx]), idx)
-
-    def _set_weight_list(self):
-        """
-        Set the list of font weight
-        """
-        # list of weight choices
-        for idx in range(len(WEIGHT)):
-            self.font_weight.Append(WEIGHT[idx], idx)
-
-    def _set_style_list(self):
-        """
-        Set the list of font style
-        """
-        # list of style choices
-        for idx in range(len(STYLE)):
-            self.font_style.Append(STYLE[idx], idx)
-
-    def _set_color_list(self):
-        """
-        Set the list of font color
-        """
-        # list of tyle choices
-        for idx in range(len(COLOR)):
-            self.font_color.Append(COLOR[idx], idx)
-
-    def on_tick_label(self, event):
-        """
-        Set the font for tick label
-        """
-        event.Skip()
-        self.tick_label = self.tick_label_check.GetValue()
-
-    def on_family(self, event):
-        """
-        Set the family
-        """
-        event.Skip()
-        self.family = self.font_family.GetValue()
-
-    def on_style(self, event):
-        """
-        Set the style
-        """
-        event.Skip()
-        self.style = self.font_style.GetValue()
-
-    def on_weight(self, event):
-        """
-        Set the weight
-        """
-        event.Skip()
-        self.weight = self.font_weight.GetValue()
-
-    def on_size(self, event):
-        """
-        Set the size
-        """
-        event.Skip()
-        self.size = self.font_size.GetValue()
-
-    def on_color(self, event):
-        """
-        Set the color
-        """
-        event.Skip()
-        self.color = self.font_color.GetValue()
-
-    def getText(self):
-        """
-        Returns text string as input by user.
-        """
-        return self.text_string.GetValue()
-
-    def getUnit(self):
-        """
-        Returns unit string as input by user.
-        """
-        return self.unit_ctrl.GetValue()
-
-    def getFamily(self):
-        """
-        Returns font family for the text box
-        """
-        return str(self.family)
-
-    def getStyle(self):
-        """
-        Returns font tyle for the text box
-        """
-        return str(self.style)
-
-    def getWeight(self):
-        """
-        Returns font weight for the text box
-        """
-        return str(self.weight)
-
-    def getSize(self):
-        """
-        Returns font size for the text box
-        """
-        return int(self.size)
-
-    def getColor(self):
-        """
-        Returns font size for the text box
-        """
-        return str(self.color)
-
-    def getTickLabel(self):
-        """
-        Bool for use on tick label
-        """
-        return self.tick_label
Index: c/sas/plottools/__init__.py
===================================================================
--- src/sas/plottools/__init__.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,3 +1,0 @@
-import config
-from PlotPanel import PlotPanel
-from plottables import Data1D, Theory1D
Index: c/sas/plottools/arrow3d.py
===================================================================
--- src/sas/plottools/arrow3d.py	(revision 2df0b74178c150a9f521d5b09abf522c0578e825)
+++ 	(revision )
@@ -1,67 +1,0 @@
-"""
-Module that draws multiple arrows in 3D coordinates
-"""
-
-from matplotlib.patches import FancyArrowPatch
-from mpl_toolkits.mplot3d import proj3d
-import time
-# from matplotlib.artist import allow_rasterization
-class Arrow3D(FancyArrowPatch):
-    """
-    Draw 3D arrow
-    """
-
-    def __init__(self, base, xs, ys, zs, colors, *args, **kwargs):
-        """
-        Init
-
-        :Params xs: [[x0, x0+dx0], [x1, x1+dx1], ...]
-        :Params ys: [[y0, y0+dy0], [y1, y1+dy1], ...]
-        :Params zs: [[z0, z0+dz0], [z1, z1+dz1], ...]
-        :Params colors: [[R0, G0, B0], [R1, G1, B1], ...]
-        where R, G, B ranges (0,1)
-        """
-        FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)
-        self.leftdown = False
-        self.t_click = 0
-        self._verts3d = xs, ys, zs
-        self.colors = colors
-        self.base = base
-
-        if base != None:
-            # To turn the updating off during dragging
-            base.canvas.mpl_connect('button_press_event', self.on_left_down)
-            base.canvas.mpl_connect('button_release_event', self.on_left_up)
-
-    def on_left_down(self, event):
-        """
-            Mouse left-down event
-        """
-        self.leftdown = True
-        self.t_click = time.time()
-
-    def on_left_up(self, event):
-        """
-            Mouse left up event
-        """
-        t_up = time.time() - self.t_click
-        # Avoid just clicking
-        if t_up > 0.1:
-            self.leftdown = False
-            self.base.canvas.draw()
-
-    def draw(self, renderer, rasterized=True):
-        """
-        Drawing actually happens here
-        """
-        # Draws only when the dragging finished
-        if self.leftdown:
-            return
-        xs3d, ys3d, zs3d = self._verts3d
-        for i in xrange(len(xs3d)):
-            xs, ys, _ = proj3d.proj_transform(xs3d[i], ys3d[i], zs3d[i], renderer.M)
-            self.set_positions((xs[0], ys[0]), (xs[1], ys[1]))
-            self.set_color(self.colors[i])
-            FancyArrowPatch.draw(self, renderer)
-
-        self.leftdown = False
Index: c/sas/plottools/binder.py
===================================================================
--- src/sas/plottools/binder.py	(revision 2df0b74178c150a9f521d5b09abf522c0578e825)
+++ 	(revision )
@@ -1,396 +1,0 @@
-"""
-Extension to MPL to support the binding of artists to key/mouse events.
-"""
-import sys
-import logging
-
-class Selection(object):
-    """
-    Store and compare selections.
-    """
-    # TODO: We need some way to check in prop matches, preferably
-    # TODO: without imposing structure on prop.
-
-    artist = None
-    prop = {}
-
-    def __init__(self, artist=None, prop={}):
-        self.artist, self.prop = artist, self.prop
-
-    def __eq__(self, other):
-        return self.artist is other.artist
-
-    def __ne__(self, other):
-        return self.artist is not other.artist
-
-    def __nonzero__(self):
-        return self.artist is not None
-
-
-class BindArtist(object):
-    """
-    """
-    # Track keyboard modifiers for events.
-    # TODO: Move keyboard modifier support into the backend.  We cannot
-    # TODO: properly support it from outside the windowing system since there
-    # TODO: is no way to recognized whether shift is held down when the mouse
-    # TODO: first clicks on the the application window.
-    control, shift, alt, meta = False, False, False, False
-
-    # Track doubleclick
-    dclick_threshhold = 0.25
-    _last_button, _last_time = None, 0
-
-    # Mouse/keyboard events we can bind to
-    events = ['enter', 'leave', 'motion', 'click', 'dclick', 'drag', 'release',
-              'scroll', 'key', 'keyup']
-    # TODO: Need our own event structure
-
-    def __init__(self, figure):
-        canvas = figure.canvas
-
-        # Link to keyboard/mouse
-        try:
-            self._connections = [
-                canvas.mpl_connect('motion_notify_event', self._onMotion),
-                canvas.mpl_connect('button_press_event', self._onClick),
-                canvas.mpl_connect('button_release_event', self._onRelease),
-                canvas.mpl_connect('key_press_event', self._onKey),
-                canvas.mpl_connect('key_release_event', self._onKeyRelease),
-                canvas.mpl_connect('scroll_event', self._onScroll)
-            ]
-        except:
-            print "bypassing scroll_event: wrong matplotlib version"
-            self._connections = [
-                canvas.mpl_connect('motion_notify_event', self._onMotion),
-                canvas.mpl_connect('button_press_event', self._onClick),
-                canvas.mpl_connect('button_release_event', self._onRelease),
-                canvas.mpl_connect('key_press_event', self._onKey),
-                canvas.mpl_connect('key_release_event', self._onKeyRelease),
-            ]
-
-        self._current = None
-        self._actions = {}
-        self.canvas = canvas
-        self.figure = figure
-        self.clearall()
-
-    def clear(self, *artists):
-        """
-        self.clear(h1,h2,...)
-            Remove connections for artists h1, h2, ...
-
-        Use clearall() to reset all connections.
-        """
-
-        for h in artists:
-            for a in self.events:
-                if h in self._actions[a]:
-                    del self._actions[a][h]
-            if h in self._artists:
-                self._artists.remove(h)
-        if self._current.artist in artists:
-            self._current = Selection()
-        if self._hasclick.artist in artists:
-            self._hasclick = Selection()
-        if self._haskey.artist in artists:
-            self._haskey = Selection()
-
-    def clearall(self):
-        """
-        Clear connections to all artists.
-
-        Use clear(h1,h2,...) to reset specific artists.
-        """
-        # Don't monitor any actions
-        self._actions = {}
-        for action in self.events:
-            self._actions[action] = {}
-
-        # Need activity state
-        self._artists = []
-        self._current = Selection()
-        self._hasclick = Selection()
-        self._haskey = Selection()
-
-    def disconnect(self):
-        """
-        In case we need to disconnect from the canvas...
-        """
-        try:
-            for cid in self._connections: self.canvas.mpl_disconnect(cid)
-        except:
-            logging.error("Error disconnection canvas: %s" % sys.exc_value)
-        self._connections = []
-
-    def __del__(self):
-        self.disconnect()
-
-    def __call__(self, trigger, artist, action):
-        """Register a callback for an artist to a particular trigger event.
-
-        usage:
-            self.connect(eventname,artist,action)
-
-        where:
-            eventname is a string
-            artist is the particular graph object to respond to the event
-            action(event,**kw) is called when the event is triggered
-
-        The action callback is associated with particular artists.
-        Different artists will have different kwargs.  See documentation
-        on the contains() method for each artist.  One common properties
-        are ind for the index of the item under the cursor, which is
-        returned by Line2D and by collections.
-
-        The following events are supported:
-            enter: mouse cursor moves into the artist or to a new index
-            leave: mouse cursor leaves the artist
-            click: mouse button pressed on the artist
-            drag: mouse button pressed on the artist and cursor moves
-            release: mouse button released for the artist
-            key: key pressed when mouse is on the artist
-            keyrelease: key released for the artist
-
-        The event received by action has a number of attributes:
-            name is the event name which was triggered
-            artist is the object which triggered the event
-            x,y are the screen coordinates of the mouse
-            xdata,ydata are the graph coordinates of the mouse
-            button is the mouse button being pressed/released
-            key is the key being pressed/released
-            shift,control,alt,meta are flags which are true if the
-                corresponding key is pressed at the time of the event.
-            details is a dictionary of artist specific details, such as the
-                id(s) of the point that were clicked.
-
-        When receiving an event, first check the modifier state to be
-        sure it applies.  E.g., the callback for 'press' might be:
-            if event.button == 1 and event.shift: process Shift-click
-
-        TODO: Only receive events with the correct modifiers (e.g., S-click,
-        TODO:   or *-click for any modifiers).
-        TODO: Only receive button events for the correct button (e.g., click1
-        TODO:   release3, or dclick* for any button)
-        TODO: Support virtual artist, so that and artist can be flagged as
-        TODO:   having a tag list and receive the correct events
-        TODO: Support virtual events for binding to button-3 vs shift button-1
-        TODO:   without changing callback code
-        TODO: Attach multiple callbacks to the same event?
-        TODO: Clean up interaction with toolbar modes
-        TODO: push/pushclear/pop context so that binding changes for
-             the duration
-        TODO:   e.g., to support ? context sensitive help
-        """
-        # Check that the trigger is valid
-        if trigger not in self._actions:
-            raise ValueError, "%s invalid --- valid triggers are %s"\
-                 % (trigger, ", ".join(self.events))
-
-        # Register the trigger callback
-        self._actions[trigger][artist] = action
-
-        # Maintain a list of all artists
-        if artist not in self._artists:
-            self._artists.append(artist)
-
-    def trigger(self, actor, action, ev):
-        """
-        Trigger a particular event for the artist.  Fallback to axes,
-        to figure, and to 'all' if the event is not processed.
-        """
-        if action not in self.events:
-            raise ValueError, "Trigger expects " + ", ".join(self.events)
-
-        # Tag the event with modifiers
-        for mod in ('alt', 'control', 'shift', 'meta'):
-            setattr(ev, mod, getattr(self, mod))
-        setattr(ev, 'artist', None)
-        setattr(ev, 'action', action)
-        setattr(ev, 'prop', {})
-
-        # Fallback scheme.  If the event does not return false, pass to parent.
-        processed = False
-        artist, prop = actor.artist, actor.prop
-        if artist in self._actions[action]:
-            ev.artist, ev.prop = artist, prop
-            processed = self._actions[action][artist](ev)
-        if not processed and ev.inaxes in self._actions[action]:
-            ev.artist, ev.prop = ev.inaxes, {}
-            processed = self._actions[action][ev.inaxes](ev)
-        if not processed and self.figure in self._actions[action]:
-            ev.artist, ev.prop = self.figure, {}
-            processed = self._actions[action][self.figure](ev)
-        if not processed and 'all' in self._actions[action]:
-            ev.artist, ev.prop = None, {}
-            processed = self._actions[action]['all'](ev)
-        return processed
-
-    def _find_current(self, event):
-        """
-        Find the artist who will receive the event.  Only search
-        registered artists.  All others are invisible to the mouse.
-        """
-        # TODO: sort by zorder of axes then by zorder within axes
-        self._artists.sort(cmp=lambda x, y: cmp(y.zorder, x.zorder))
-        found = Selection()
-        for artist in self._artists:
-            # TODO: should contains() return false if invisible?
-            if not artist.get_visible():
-                continue
-            # TODO: optimization - exclude artists not inaxes
-            try:
-                inside, prop = artist.contains(event)
-            except:
-                # Probably an old version of matplotlib
-                inside = False
-            if inside:
-                found.artist, found.prop = artist, prop
-                break
-
-        # TODO: how to check if prop is equal?
-        if found != self._current:
-            self.trigger(self._current, 'leave', event)
-            self.trigger(found, 'enter', event)
-        self._current = found
-
-        return found
-
-    def _onMotion(self, event):
-        """
-        Track enter/leave/motion through registered artists; all
-        other artists are invisible.
-        """
-        # # Can't kill double-click on motion since Windows produces
-        # # spurious motion events.
-        # self._last_button = None
-
-        # Dibs on the motion event for the clicked artist
-        if self._hasclick:
-            # Make sure the x,y data use the coordinate system of the
-            # artist rather than the default axes coordinates.
-
-            transform = self._hasclick.artist.get_transform()
-            # x,y = event.xdata,event.ydata
-            x, y = event.x, event.y
-            try:
-                if transform.__class__.__name__ == "IdentityTransform":
-                    x, y = transform.inverted().transform((x, y))
-                else:
-                    # # For interactive plottable apply transform is not working
-                    # # don't know why maybe marker definition
-                    # #transform ="CompositeGenericTransform" crash
-                    pass
-            except:
-                # # CRUFT matplotlib-0.91 support
-                # # exception for transform ="CompositeGenericTransform"
-                # # crashes also here
-                x, y = transform.inverse_xy_tup((x, y))
-
-            # event.xdata, event.ydata = x, y
-            self.trigger(self._hasclick, 'drag', event)
-        else:
-            found = self._find_current(event)
-            self.trigger(found, 'motion', event)
-
-    def _onClick(self, event):
-        """
-        Process button click
-        """
-        import time
-
-        # Check for double-click
-        event_time = time.time()
-        if (event.button != self._last_button) or \
-                (event_time > self._last_time + self.dclick_threshhold):
-            action = 'click'
-        else:
-            action = 'dclick'
-        self._last_button = event.button
-        self._last_time = event_time
-
-        # If an artist is already dragging, feed any additional button
-        # presses to that artist.
-        # TODO: do we want to force a single button model on the user?
-        # TODO: that is, once a button is pressed, no other buttons
-        # TODO: can come through?  I think this belongs in canvas, not here.
-        if self._hasclick:
-            found = self._hasclick
-        else:
-            found = self._find_current(event)
-        # print "button %d pressed"%event.button
-        # Note: it seems like if "click" returns False then hasclick should
-        # not be set.  The problem is that there are two reasons it can
-        # return false: because there is no click action for this artist
-        # or because the click action returned false.  A related problem
-        # is that click actions will go to the canvas if there is no click
-        # action for the artist, even if the artist has a drag. I'll leave
-        # it to future maintainers to sort out this problem.  For now the
-        # recommendation is that users should define click if they have
-        # drag or release on the artist.
-        self.trigger(found, action, event)
-        self._hasclick = found
-
-    def _onDClick(self, event):
-        """
-        Process button double click
-        """
-        # If an artist is already dragging, feed any additional button
-        # presses to that artist.
-        # TODO: do we want to force a single button model on the user?
-        # TODO: that is, once a button is pressed, no other buttons
-        # TODO: can come through?  I think this belongs in canvas, not here.
-        if self._hasclick:
-            found = self._hasclick
-        else:
-            found = self._find_current(event)
-        self.trigger(found, 'dclick', event)
-        self._hasclick = found
-
-    def _onRelease(self, event):
-        """
-        Process release release
-        """
-        self.trigger(self._hasclick, 'release', event)
-        self._hasclick = Selection()
-
-    def _onKey(self, event):
-        """
-        Process key click
-        """
-        # TODO: Do we really want keyboard focus separate from mouse focus?
-        # TODO: Do we need an explicit focus command for keyboard?
-        # TODO: Can we tab between items?
-        # TODO: How do unhandled events get propogated to axes, figure and
-        # TODO: finally to application?  Do we need to implement a full tags
-        # TODO: architecture a la Tk?
-        # TODO: Do modifiers cause a grab?  Does the artist see the modifiers?
-        if event.key in ('alt', 'meta', 'control', 'shift'):
-            setattr(self, event.key, True)
-            return
-
-        if self._haskey:
-            found = self._haskey
-        else:
-            found = self._find_current(event)
-        self.trigger(found, 'key', event)
-        self._haskey = found
-
-    def _onKeyRelease(self, event):
-        """
-        Process key release
-        """
-        if event.key in ('alt', 'meta', 'control', 'shift'):
-            setattr(self, event.key, False)
-            return
-
-        if self._haskey:
-            self.trigger(self._haskey, 'keyup', event)
-        self._haskey = Selection()
-
-    def _onScroll(self, event):
-        """
-        Process scroll event
-        """
-        found = self._find_current(event)
-        self.trigger(found, 'scroll', event)
Index: c/sas/plottools/canvas.py
===================================================================
--- src/sas/plottools/canvas.py	(revision 2df0b74178c150a9f521d5b09abf522c0578e825)
+++ 	(revision )
@@ -1,269 +1,0 @@
-"""
-This module implements a faster canvas for plotting.
-it ovewrites some matplolib methods to allow printing on sys.platform=='win32'
-"""
-import wx
-import sys
-import logging
-from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
-from matplotlib.backend_bases import MouseEvent, RendererBase
-from matplotlib.backends.backend_wx import GraphicsContextWx, PrintoutWx
-from matplotlib.backends.backend_wx import RendererWx
-
-
-def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None):
-    """
-    Draw the image instance into the current axes;
-
-    :param x: is the distance in pixels from the left hand side of the canvas.
-    :param y: the distance from the origin.  That is, if origin is
-        upper, y is the distance from top.  If origin is lower, y
-        is the distance from bottom
-    :param im: the class`matplotlib._image.Image` instance
-    :param bbox: a class `matplotlib.transforms.Bbox` instance for clipping, or
-        None
-
-    """
-    pass
-
-
-def select(self):
-    """
-    """
-    pass
-
-
-def unselect(self):
-    """
-    """
-    pass
-
-
-def OnPrintPage(self, page):
-    """
-    override printPage of matplotlib
-    """
-    self.canvas.draw()
-    dc = self.GetDC()
-    try:
-        (ppw, pph) = self.GetPPIPrinter()  # printer's pixels per in
-    except:
-        ppw = 1
-        pph = 1
-    (pgw, _) = self.GetPageSizePixels()  # page size in pixels
-    (dcw, _) = dc.GetSize()
-    (grw, _) = self.canvas.GetSizeTuple()
-
-    # save current figure dpi resolution and bg color,
-    # so that we can temporarily set them to the dpi of
-    # the printer, and the bg color to white
-    bgcolor = self.canvas.figure.get_facecolor()
-    fig_dpi = self.canvas.figure.dpi
-
-    # draw the bitmap, scaled appropriately
-    vscale = float(ppw) / fig_dpi
-
-    # set figure resolution,bg color for printer
-    self.canvas.figure.dpi = ppw
-    self.canvas.figure.set_facecolor('#FFFFFF')
-
-    renderer = RendererWx(self.canvas.bitmap, self.canvas.figure.dpi)
-    self.canvas.figure.draw(renderer)
-    self.canvas.bitmap.SetWidth(int(self.canvas.bitmap.GetWidth() * vscale))
-    self.canvas.bitmap.SetHeight(int(self.canvas.bitmap.GetHeight() * vscale))
-    self.canvas.draw()
-
-    # page may need additional scaling on preview
-    page_scale = 1.0
-    if self.IsPreview():
-        page_scale = float(dcw) / pgw
-
-    # get margin in pixels = (margin in in) * (pixels/in)
-    top_margin = int(self.margin * pph * page_scale)
-    left_margin = int(self.margin * ppw * page_scale)
-
-    # set scale so that width of output is self.width inches
-    # (assuming grw is size of graph in inches....)
-    user_scale = (self.width * fig_dpi * page_scale) / float(grw)
-    dc.SetDeviceOrigin(left_margin, top_margin)
-    dc.SetUserScale(user_scale, user_scale)
-
-    # this cute little number avoid API inconsistencies in wx
-    try:
-        dc.DrawBitmap(self.canvas.bitmap, 0, 0)
-    except:
-        try:
-            dc.DrawBitmap(self.canvas.bitmap, (0, 0))
-        except:
-            logging.error(sys.exc_value)
-
-    # restore original figure  resolution
-    self.canvas.figure.set_facecolor(bgcolor)
-    # # used to be self.canvas.figure.dpi.set( fig_dpi)
-    self.canvas.figure.dpi = fig_dpi
-    self.canvas.draw()
-    return True
-
-GraphicsContextWx.select = select
-GraphicsContextWx.unselect = unselect
-PrintoutWx.OnPrintPage = OnPrintPage
-RendererBase.draw_image = draw_image
-
-
-class FigureCanvas(FigureCanvasWxAgg):
-    """
-    Add features to the wx agg canvas for better support of AUI and
-    faster plotting.
-    """
-
-    def __init__(self, *args, **kw):
-        super(FigureCanvas, self).__init__(*args, **kw)
-        self._isRendered = False
-
-        # Create an timer for handling draw_idle requests
-        # If there are events pending when the timer is
-        # complete, reset the timer and continue.  The
-        # alternative approach, binding to wx.EVT_IDLE,
-        # doesn't behave as nicely.
-        self.idletimer = wx.CallLater(1, self._onDrawIdle)
-        # panel information
-        self.panel = None
-        self.resizing = False
-        self.xaxis = None
-        self.yaxis = None
-        self.ndraw = 0
-        # Support for mouse wheel
-        self.Bind(wx.EVT_MOUSEWHEEL, self._onMouseWheel)
-
-    def set_panel(self, panel):
-        """
-        Set axes
-        """
-        # set panel
-        self.panel = panel
-        # set axes
-        self.xaxis = panel.subplot.xaxis
-        self.yaxis = panel.subplot.yaxis
-
-    def draw_idle(self, *args, **kwargs):
-        """
-        Render after a delay if no other render requests have been made.
-        """
-        self.panel.subplot.grid(self.panel.grid_on)
-        if self.panel.legend != None and self.panel.legend_pos_loc:
-            self.panel.legend._loc = self.panel.legend_pos_loc
-        self.idletimer.Restart(5, *args, **kwargs)  # Delay by 5 ms
-
-    def _onDrawIdle(self, *args, **kwargs):
-        """
-        """
-        if False and wx.GetApp().Pending():
-            self.idletimer.Restart(5, *args, **kwargs)
-        else:
-            # Draw plot, changes resizing too
-            self.draw(*args, **kwargs)
-            self.resizing = False
-
-    def _get_axes_switch(self):
-        """
-        """
-        # Check resize whether or not True
-        if self.panel.dimension == 3:
-            return
-
-        # This is for fast response when plot is being resized
-        if not self.resizing:
-            self.xaxis.set_visible(True)
-            self.yaxis.set_visible(True)
-            self.panel.schedule_full_draw('del')
-        else:
-            self.xaxis.set_visible(False)
-            self.yaxis.set_visible(False)
-            self.panel.schedule_full_draw('append')
-        # set the resizing back to default= False
-        self.set_resizing(False)
-
-    def set_resizing(self, resizing=False):
-        """
-        Setting the resizing
-        """
-        self.resizing = resizing
-        self.panel.set_resizing(False)
-
-    def draw(self, drawDC=None):
-        """
-        Render the figure using agg.
-        """
-        # Only draw if window is shown, otherwise graph will bleed through
-        # on the notebook style AUI widgets.
-        #    raise
-        fig = FigureCanvasWxAgg
-        if self.IsShownOnScreen() and self.ndraw != 1:
-            self._isRendered = True
-            self._get_axes_switch()
-            # import time
-            # st = time.time()
-            try:
-                fig.draw(self)
-            except ValueError:
-                logging.error(sys.exc_value)
-        else:
-            self._isRendered = False
-        if self.ndraw <= 1:
-            self.ndraw += 1
-
-    def _onMouseWheel(self, evt):
-        """Translate mouse wheel events into matplotlib events"""
-        # Determine mouse location
-        _, h = self.figure.canvas.get_width_height()
-        x = evt.GetX()
-        y = h - evt.GetY()
-
-        # Convert delta/rotation/rate into a floating point step size
-        delta = evt.GetWheelDelta()
-        rotation = evt.GetWheelRotation()
-        rate = evt.GetLinesPerAction()
-        # print "delta,rotation,rate",delta,rotation,rate
-        step = rate * float(rotation) / delta
-
-        # Convert to mpl event
-        evt.Skip()
-        self.scroll_event(x, y, step, guiEvent=evt)
-
-    def scroll_event(self, x, y, step=1, guiEvent=None):
-        """
-        Backend derived classes should call this function on any
-        scroll wheel event.  x,y are the canvas coords: 0,0 is lower,
-        left.  button and key are as defined in MouseEvent
-        """
-        button = 'up' if step >= 0 else 'down'
-        self._button = button
-        s = 'scroll_event'
-        event = MouseEvent(s, self, x, y, button, self._key, guiEvent=guiEvent)
-        setattr(event, 'step', step)
-        self.callbacks.process(s, event)
-        if step != 0:
-            self.panel.is_zoomed = True
-
-    def _onRightButtonDown(self, evt):
-        """
-        Overload the right button down call back to avoid a problem
-        with the context menu over matplotlib plots on linux.
-
-        :TODO: Investigate what the root cause of the problem is.
-
-        """
-        if sys.platform == 'linux2' or self.panel.dimension == 3:
-            evt.Skip()
-        else:
-            FigureCanvasWxAgg._onRightButtonDown(self, evt)
-            # This solves the focusing on rightclick.
-            # Todo: better design
-            self.panel.parent.set_plot_unfocus()
-            self.panel.on_set_focus(None)
-        return
-
-    # CRUFT: wx 3.0.0.0 on OS X doesn't release the mouse on leaving window
-    def _onLeave(self, evt):
-        if self.HasCapture(): self.ReleaseMouse()
-        super(FigureCanvas, self)._onLeave(evt)
Index: c/sas/plottools/config.py
===================================================================
--- src/sas/plottools/config.py	(revision d3d67f06423dc337fd087c81b7cbd502dc7e1084)
+++ 	(revision )
@@ -1,67 +1,0 @@
-# This program is public domain
-"""
-Configure plotter for plottools.
-
-This must be imported first in __init__.py for plottools.
-
-If your application uses matplotlib outside plottools, then
-please do the following at the start of your application:
-
-    # Select matplotlib version and backend
-    import sas.plottools.config
-
-Note that plottools requires particular versions of matplotlib
-and a particular backend.  As of this writing it is the WXAgg
-backend for matplotlib>=0.98.
-
-The plottools package uses pkg_resources if available to select
-the correct version of matplotlib.  If you need multiple matplotlib
-versions in your path, be sure to use "easy_install -m" for all
-of them.  If a version is installed without "-m" that does not
-meet the requirements, then pkg_resources.require() will fail,
-even if you have installed a suitable version with "-m".  In this
-case you will need to fix up your site-packages directory,
-probably by removing site-packages/matplotlib and the associated
-egg file for that version, and reinstalling with "-m".  You may
-also need to edit site-packages/easy-install.pth.
-"""
-import sys
-
-__all__ = []
-
-plot_version = "0.98"
-plot_backend = "WXAgg"
-
-# Sort out matplotlib version
-import matplotlib
-try:
-    import pkg_resources
-    pkg_resources.require("matplotlib>=" + plot_version)
-except:
-    from distutils.version import LooseVersion as Version
-    if Version(matplotlib.__version__) < Version(plot_version):
-        msg = "Matplotlib version must be %s or newer" % (plot_version, )
-        raise ImportError(msg)
-
-# Sort out matplotlib backend
-#import matplotlib
-if 'matplotlib.backends' not in sys.modules:
-    # if no backend yet, be sure to use the correct one
-    matplotlib.use(plot_backend)
-elif matplotlib.get_backend() != plot_backend:
-    # if a backend has already been selected, make sure it is the correct one.
-    raise ImportError("Matplotlib not using backend " + plot_backend)
-
-# set global plot style
-param = 'legend.handletextpad'
-if param not in matplotlib.rcParams: param = 'legend.handletextsep'
-matplotlib.rcParams[param] = 0.05
-matplotlib.rcParams['legend.numpoints'] = 1
-
-# this should happen after initial matplotlib configuration
-from .toolbar import NavigationToolBar
-from matplotlib.backends import backend_wxagg
-backend_wxagg.NavigationToolbar2WxAgg = NavigationToolBar
-
-# CRUFT: bumps 0.7.5.6 and older uses wrong toolbar
-backend_wxagg.NavigationToolbar2Wx = NavigationToolBar
Index: c/sas/plottools/convert_units.py
===================================================================
--- src/sas/plottools/convert_units.py	(revision 3477478de6317fb51d4dd0dbaf9ab2a1236faf28)
+++ 	(revision )
@@ -1,78 +1,0 @@
-"""
-    Convert units to strings that can be displayed
-    This is a cleaned up version of unitConverter.py
-"""
-import re
-import string
-
-def convert_unit(power, unit):
-    """
-        Convert units to strings that can be displayed
-    """
-    if power != 0:
-        if string.find(unit, "^") != -1:  # if the unit contains a powerer ^
-            toks = re.split("\^", unit)
-            if string.find(toks[0], "/") != -1 or \
-                string.find(toks[0], "-") != -1:
-                if power == 1:
-                    unit = unit
-                else:
-                    unit = "(" + unit + ")" + "^{" + str(power) + "}"
-            else:
-                if string.find(toks[1], "{") != -1:  # if found a {
-                    find_power_toks = re.split("{", toks[1])
-                    if string.find(find_power_toks[1], "}") != -1:  # found }
-                        unit_toks = re.split("}", find_power_toks[1])
-                        if string.find(unit_toks[0], ".") != -1:
-                            powerer = float(unit_toks[0]) * power
-                        elif string.find(unit_toks[0], "/") != -1:
-                            power_toks = re.split("/", unit_toks[0])
-                            powerer = power * int(power_toks[0])\
-                                            / int(power_toks[1])
-                        else:
-                            powerer = int(unit_toks[0]) * power
-
-                        if powerer == 1.0:
-                            unit = toks[0]
-                        elif powerer == 0.5:
-                            unit = toks[0] + "^{1/2}"
-                        elif powerer == -0.5:
-                            unit = toks[0] + "^{-1/2}"
-                        else:
-                            unit = toks[0] + "^{" + str(powerer) + "}"
-                else:
-                    raise ValueError, "missing } in unit expression"
-        else:  # no powerer
-            if  power != 1:
-                unit = "(" + unit + ")" + "^{" + str(power) + "}"
-    else:
-        raise ValueError, "empty unit ,enter a powerer different from zero"
-    return unit
-
-
-if __name__ == "__main__":
-    # pylint: disable=invalid-name
-    # Input   ->  new scale  ->  Output
-    unit1 = "A^{-1} "  #             x                    A^{-1}
-    unit2 = "A"  #                   x                     A
-    unit3 = "A"  #                   x^2                  A^{2}
-    unit4 = "A "  #                  1/x                  A^{-1}
-    unit5 = "A^{0.5} "  #        x^2                      A
-    unit9 = "m^{1/2}"  #         x^2               m
-
-    # If you don't recognize the pattern, give up
-    # and just put some parentheses around the unit and write the transoformation:
-
-    unit6 = "m/s"  #                x^2               (m/s)^{2}
-    unit7 = "m/s^{2}"  #         1/x                 (m/s^{2})^{-1}
-    unit8 = "m/s^{4}"  #         x^2               (m/s^{4})^{2}
-
-    print "this unit1 %s ,its powerer %s , and value %s" % (unit1, 1, convert_unit(1, unit1))
-    print "this unit2 %s ,its powerer %s , and value %s" % (unit2, 1, convert_unit(1, unit2))
-    print "this unit3 %s ,its powerer %s , and value %s" % (unit3, 2, convert_unit(2, unit3))
-    print "this unit4 %s ,its powerer %s , and value %s" % (unit4, -1, convert_unit(-1, unit4))
-    print "this unit5 %s ,its powerer %s , and value %s" % (unit5, 2, convert_unit(2, unit5))
-    print "this unit6 %s ,its powerer %s , and value %s" % (unit6, 2, convert_unit(2, unit6))
-    print "this unit7 %s ,its powerer %s , and value %s" % (unit7, -1, convert_unit(-1, unit7))
-    print "this unit8 %s ,its powerer %s , and value %s" % (unit8, 2, convert_unit(2, unit8))
-    print "this unit9 %s ,its powerer %s , and value %s" % (unit9, 2, convert_unit(2, unit9))
Index: c/sas/plottools/fitDialog.py
===================================================================
--- src/sas/plottools/fitDialog.py	(revision 2df0b74178c150a9f521d5b09abf522c0578e825)
+++ 	(revision )
@@ -1,697 +1,0 @@
-import wx
-from plottables import Theory1D
-import math
-import numpy
-import fittings
-import transform
-import sys
-
-# Linear fit panel size
-if sys.platform.count("win32") > 0:
-    FONT_VARIANT = 0
-    PNL_WIDTH = 450
-    PNL_HEIGHT = 500
-else:
-    FONT_VARIANT = 1
-    PNL_WIDTH = 500
-    PNL_HEIGHT = 500
-RG_ON = True
-
-def format_number(value, high=False):
-    """
-    Return a float in a standardized, human-readable formatted string
-    """
-    try:
-        value = float(value)
-    except:
-        output = "NaN"
-        return output.lstrip().rstrip()
-
-    if high:
-        output = "%-6.4g" % value
-
-    else:
-        output = "%-5.3g" % value
-    return output.lstrip().rstrip()
-
-
-class LinearFit(wx.Dialog):
-    def __init__(self, parent, plottable, push_data, transform, title):
-        """
-        Dialog window pops- up when select Linear fit on Context menu
-        Displays fitting parameters
-        """
-        wx.Dialog.__init__(self, parent, title=title,
-                           size=(PNL_WIDTH, 350))
-        self.parent = parent
-        self.transform = transform
-        # Font
-        self.SetWindowVariant(variant=FONT_VARIANT)
-        # Registered owner for close event
-        self._registered_close = None
-
-        # dialog panel self call function to plot the fitting function
-        self.push_data = push_data
-        # dialog self plottable
-        self.plottable = plottable
-        self.rg_on = False
-        # Receive transformations of x and y
-        self.xLabel, self.yLabel, self.Avalue, self.Bvalue, \
-               self.ErrAvalue, self.ErrBvalue, self.Chivalue = self.transform()
-
-        # Dialog interface
-        vbox = wx.BoxSizer(wx.VERTICAL)
-        sizer = wx.GridBagSizer(5, 5)
-        _BOX_WIDTH = 100
-
-        self.tcA = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
-        self.tcA.SetToolTipString("Fit value for the slope parameter.")
-        self.tcErrA = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
-        self.tcErrA.SetToolTipString("Error on the slope parameter.")
-        self.tcB = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
-        self.tcA.SetToolTipString("Fit value for the constant parameter.")
-        self.tcErrB = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
-        self.tcErrB.SetToolTipString("Error on the constant parameter.")
-        self.tcChi = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
-        self.tcChi.SetToolTipString("Chi^2 over degrees of freedom.")
-        self.xminFit = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
-        msg = "Enter the minimum value on "
-        msg += "the x-axis to be included in the fit."
-        self.xminFit.SetToolTipString(msg)
-        self.xmaxFit = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
-        msg = "Enter the maximum value on "
-        msg += " the x-axis to be included in the fit."
-        self.xmaxFit.SetToolTipString(msg)
-        self.initXmin = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
-        msg = "Minimum value on the x-axis for the plotted data."
-        self.initXmin.SetToolTipString(msg)
-        self.initXmax = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
-        msg = "Maximum value on the x-axis for the plotted data."
-        self.initXmax.SetToolTipString(msg)
-
-        # Make the info box not editable
-        # _BACKGROUND_COLOR = '#ffdf85'
-        _BACKGROUND_COLOR = self.GetBackgroundColour()
-        self.initXmin.SetEditable(False)
-        self.initXmin.SetBackgroundColour(_BACKGROUND_COLOR)
-        self.initXmax.SetEditable(False)
-        self.initXmax.SetBackgroundColour(_BACKGROUND_COLOR)
-
-        # Buttons on the bottom
-        self.bg_on = False
-        self.static_line_1 = wx.StaticLine(self, -1)
-        self.btFit = wx.Button(self, -1, 'Fit')
-        self.btFit.Bind(wx.EVT_BUTTON, self._onFit)
-        self.btFit.SetToolTipString("Perform fit.")
-        self.btClose = wx.Button(self, wx.ID_CANCEL, 'Close')
-        self.btClose.Bind(wx.EVT_BUTTON, self._on_close)
-        if RG_ON:
-            if (self.yLabel == "ln(y)" or self.yLabel == "ln(y*x)") and \
-                    (self.xLabel == "x^(2)"):
-                self.rg_on = True
-            if (self.xLabel == "x^(4)") and (self.yLabel == "y*x^(4)"):
-                self.bg_on = True
-        # Intro
-        explanation = "Perform fit for y(x) = ax + b"
-        if self.bg_on:
-            param_a = 'Background (= Parameter a)'
-        else:
-            param_a = 'Parameter a'
-        vbox.Add(sizer)
-        ix = 0
-        iy = 1
-        sizer.Add(wx.StaticText(self, -1, explanation), (iy, ix),
-                  (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
-        iy += 2
-        sizer.Add(wx.StaticText(self, -1, param_a), (iy, ix),
-                  (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
-        ix += 1
-        sizer.Add(self.tcA, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        ix += 1
-        sizer.Add(wx.StaticText(self, -1, '+/-'),
-                  (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        ix += 1
-        sizer.Add(self.tcErrA, (iy, ix), (1, 1),
-                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        iy += 1
-        ix = 0
-        sizer.Add(wx.StaticText(self, -1, 'Parameter b'), (iy, ix), (1, 1),
-                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
-        ix += 1
-        sizer.Add(self.tcB, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        ix += 1
-        sizer.Add(wx.StaticText(self, -1, '+/-'), (iy, ix),
-                  (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        ix += 1
-        sizer.Add(self.tcErrB, (iy, ix), (1, 1),
-                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        iy += 1
-        ix = 0
-        sizer.Add(wx.StaticText(self, -1, 'Chi2/dof'), (iy, ix), (1, 1),
-                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
-        ix += 1
-        sizer.Add(self.tcChi, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        iy += 2
-        ix = 1
-        sizer.Add(wx.StaticText(self, -1, 'Min'), (iy, ix), (1, 1),
-                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        ix += 2
-        sizer.Add(wx.StaticText(self, -1, 'Max'), (iy, ix),
-                  (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-
-        iy += 1
-        ix = 0
-        sizer.Add(wx.StaticText(self, -1, 'Maximum range (linear scale)'),
-                  (iy, ix), (1, 1),
-                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
-        ix += 1
-        sizer.Add(self.initXmin, (iy, ix), (1, 1),
-                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        ix += 2
-        sizer.Add(self.initXmax, (iy, ix), (1, 1),
-                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-
-        iy += 1
-        ix = 0
-        sizer.Add(wx.StaticText(self, -1, 'Fit range of ' + self.xLabel),
-                  (iy, ix), (1, 1),
-                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
-        ix += 1
-        sizer.Add(self.xminFit, (iy, ix), (1, 1),
-                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        ix += 2
-        sizer.Add(self.xmaxFit, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        if self.rg_on:
-            self.SetSize((PNL_WIDTH, PNL_HEIGHT))
-            I0_stxt = wx.StaticText(self, -1, 'I(q=0)')
-            self.I0_tctr = wx.TextCtrl(self, -1, '')
-            self.I0_tctr.SetEditable(False)
-            self.I0_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
-            self.I0err_tctr = wx.TextCtrl(self, -1, '')
-            self.I0err_tctr.SetEditable(False)
-            self.I0err_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
-            Rg_stxt = wx.StaticText(self, -1, 'Rg [A]')
-            Rg_stxt.Show(self.yLabel == "ln(y)")
-            self.Rg_tctr = wx.TextCtrl(self, -1, '')
-            self.Rg_tctr.SetEditable(False)
-            self.Rg_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
-            self.Rg_tctr.Show(self.yLabel == "ln(y)")
-            self.Rgerr_tctr = wx.TextCtrl(self, -1, '')
-            self.Rgerr_tctr.SetEditable(False)
-            self.Rgerr_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
-            self.Rgerr_tctr.Show(self.yLabel == "ln(y)")
-            self.Rgerr_pm = wx.StaticText(self, -1, '+/-')
-            self.Rgerr_pm.Show(self.yLabel == "ln(y)")
-            Diameter_stxt = wx.StaticText(self, -1, 'Rod Diameter [A]')
-            Diameter_stxt.Show(self.yLabel == "ln(y*x)")
-            self.Diameter_tctr = wx.TextCtrl(self, -1, '')
-            self.Diameter_tctr.SetEditable(False)
-            self.Diameter_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
-            self.Diameter_tctr.Show(self.yLabel == "ln(y*x)")
-            self.Diameter_pm = wx.StaticText(self, -1, '+/-')
-            self.Diameter_pm.Show(self.yLabel == "ln(y*x)")
-            self.Diametererr_tctr = wx.TextCtrl(self, -1, '')
-            self.Diametererr_tctr.SetEditable(False)
-            self.Diametererr_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
-            self.Diametererr_tctr.Show(self.yLabel == "ln(y*x)")
-            RgQmin_stxt = wx.StaticText(self, -1, 'Rg*Qmin')
-            self.RgQmin_tctr = wx.TextCtrl(self, -1, '')
-            self.RgQmin_tctr.SetEditable(False)
-            self.RgQmin_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
-            RgQmax_stxt = wx.StaticText(self, -1, 'Rg*Qmax')
-            self.RgQmax_tctr = wx.TextCtrl(self, -1, '')
-            self.RgQmax_tctr.SetEditable(False)
-            self.RgQmax_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
-
-            iy += 2
-            ix = 0
-            sizer.Add(I0_stxt, (iy, ix), (1, 1),
-                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
-            ix += 1
-            sizer.Add(self.I0_tctr, (iy, ix), (1, 1),
-                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-            ix += 1
-            sizer.Add(wx.StaticText(self, -1, '+/-'), (iy, ix),
-                      (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-            ix += 1
-            sizer.Add(self.I0err_tctr, (iy, ix), (1, 1),
-                      wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-
-            iy += 1
-            ix = 0
-            sizer.Add(Rg_stxt, (iy, ix), (1, 1),
-                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
-            ix += 1
-            sizer.Add(self.Rg_tctr, (iy, ix), (1, 1),
-                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-
-            ix += 1
-            sizer.Add(self.Rgerr_pm, (iy, ix),
-                      (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-            ix += 1
-            sizer.Add(self.Rgerr_tctr, (iy, ix), (1, 1),
-                      wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-            iy += 1
-            ix = 0
-            sizer.Add(Diameter_stxt, (iy, ix), (1, 1),
-                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
-            ix += 1
-            sizer.Add(self.Diameter_tctr, (iy, ix), (1, 1),
-                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-
-            ix += 1
-            sizer.Add(self.Diameter_pm, (iy, ix),
-                      (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-            ix += 1
-            sizer.Add(self.Diametererr_tctr, (iy, ix), (1, 1),
-                      wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-            iy += 1
-            ix = 0
-            sizer.Add(RgQmin_stxt, (iy, ix), (1, 1),
-                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
-            ix += 1
-            sizer.Add(self.RgQmin_tctr, (iy, ix), (1, 1),
-                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-            iy += 1
-            ix = 0
-            sizer.Add(RgQmax_stxt, (iy, ix), (1, 1),
-                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
-            ix += 1
-            sizer.Add(self.RgQmax_tctr, (iy, ix), (1, 1),
-                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-
-        iy += 1
-        ix = 1
-
-        vbox.Add(self.static_line_1, 0, wx.EXPAND, 0)
-        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
-        sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        sizer_button.Add(self.btFit, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
-        sizer_button.Add(self.btClose, 0,
-                         wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
-        vbox.Add(sizer_button, 0, wx.EXPAND | wx.BOTTOM | wx.TOP, 10)
-
-        sizer.Add(self.btFit, (iy, ix), (1, 1), wx.LEFT | wx.ADJUST_MINSIZE, 0)
-        # panel.SetSizer(sizer)
-        self.SetSizer(vbox)
-        self.Centre()
-        # Receives the type of model for the fitting
-        from LineModel import LineModel
-        self.model = LineModel()
-        # Display the fittings values
-        self.default_A = self.model.getParam('A')
-        self.default_B = self.model.getParam('B')
-        self.cstA = fittings.Parameter(self.model, 'A', self.default_A)
-        self.cstB = fittings.Parameter(self.model, 'B', self.default_B)
-
-        # Set default value of parameter in fit dialog
-        if self.Avalue == None:
-            self.tcA.SetValue(format_number(self.default_A))
-        else:
-            self.tcA.SetLabel(format_number(self.Avalue))
-        if self.Bvalue == None:
-            self.tcB.SetValue(format_number(self.default_B))
-        else:
-            self.tcB.SetLabel(format_number(self.Bvalue))
-        if self.ErrAvalue == None:
-            self.tcErrA.SetLabel(format_number(0.0))
-        else:
-            self.tcErrA.SetLabel(format_number(self.ErrAvalue))
-        if self.ErrBvalue == None:
-            self.tcErrB.SetLabel(format_number(0.0))
-        else:
-            self.tcErrB.SetLabel(format_number(self.ErrBvalue))
-        if self.Chivalue == None:
-            self.tcChi.SetLabel(format_number(0.0))
-        else:
-            self.tcChi.SetLabel(format_number(self.Chivalue))
-        if self.plottable.x != []:
-            # store the values of View in self.x,self.y,self.dx,self.dy
-            self.x, self.y, self.dx, \
-                     self.dy = self.plottable.returnValuesOfView()
-            try:
-                self.mini = self.floatForwardTransform(min(self.x))
-            except:
-                self.mini = "Invalid"
-            try:
-                self.maxi = self.floatForwardTransform(max(self.x))
-            except:
-                self.maxi = "Invalid"
-
-            self.initXmin.SetValue(format_number(min(self.plottable.x)))
-            self.initXmax.SetValue(format_number(max(self.plottable.x)))
-            self.mini = min(self.x)
-            self.maxi = max(self.x)
-            self.xminFit.SetValue(format_number(self.mini))
-            self.xmaxFit.SetValue(format_number(self.maxi))
-
-    def register_close(self, owner):
-        """
-        Method to register the close event to a parent
-        window that needs notification when the dialog
-        is closed
-
-        :param owner: parent window
-
-        """
-        self._registered_close = owner
-
-    def _on_close(self, event):
-        """
-        Close event.
-        Notify registered owner if available.
-        """
-        event.Skip()
-        if self._registered_close is not None:
-            self._registered_close()
-
-    def _onFit(self, event):
-        """
-        Performs the fit. Receive an event when clicking on
-        the button Fit.Computes chisqr ,
-        A and B parameters of the best linear fit y=Ax +B
-        Push a plottable to
-        """
-        tempx = []
-        tempy = []
-        tempdy = []
-
-        # Check if View contains a x array .we online fit when x exits
-        # makes transformation for y as a line to fit
-        if self.x != []:
-            if self.checkFitValues(self.xminFit) == True:
-                # Check if the field of Fit Dialog contain values
-                # and use the x max and min of the user
-                if not self._checkVal(self.xminFit, self.xmaxFit):
-                    return
-                xminView = float(self.xminFit.GetValue())
-                xmaxView = float(self.xmaxFit.GetValue())
-                xmin = xminView
-                xmax = xmaxView
-                # Store the transformed values of view x, y,dy
-                # in variables  before the fit
-                if self.yLabel.lower() == "log10(y)":
-                    if self.xLabel.lower() == "log10(x)":
-                        for i in range(len(self.x)):
-                            if self.x[i] >= math.log10(xmin):
-                                tempy.append(math.log10(self.y[i]))
-                                tempdy.append(transform.errToLogX(self.y[i], 0, self.dy[i], 0))
-                    else:
-                        for i in range(len(self.y)):
-                            tempy.append(math.log10(self.y[i]))
-                            tempdy.append(transform.errToLogX(self.y[i], 0, self.dy[i], 0))
-                else:
-                    tempy = self.y
-                    tempdy = self.dy
-
-                if self.xLabel.lower() == "log10(x)":
-                    for x_i in self.x:
-                        if x_i >= math.log10(xmin):
-                            tempx.append(math.log10(x_i))
-                else:
-                    tempx = self.x
-
-                # Find the fitting parameters
-                # Always use the same defaults, so that fit history
-                # doesn't play a role!
-                self.cstA = fittings.Parameter(self.model, 'A', self.default_A)
-                self.cstB = fittings.Parameter(self.model, 'B', self.default_B)
-
-                if self.xLabel.lower() == "log10(x)":
-                    tempdy = numpy.asarray(tempdy)
-                    tempdy[tempdy == 0] = 1
-                    chisqr, out, cov = fittings.sasfit(self.model,
-                                                       [self.cstA, self.cstB],
-                                                       tempx, tempy,
-                                                       tempdy,
-                                                       math.log10(xmin),
-                                                       math.log10(xmax))
-                else:
-                    tempdy = numpy.asarray(tempdy)
-                    tempdy[tempdy == 0] = 1
-                    chisqr, out, cov = fittings.sasfit(self.model,
-                                                       [self.cstA, self.cstB],
-                                                       tempx, tempy, tempdy,
-                                                       xminView, xmaxView)
-                # Use chi2/dof
-                if len(tempx) > 0:
-                    chisqr = chisqr / len(tempx)
-
-                # Check that cov and out are iterable before displaying them
-                if cov == None:
-                    errA = 0.0
-                    errB = 0.0
-                else:
-                    errA = math.sqrt(cov[0][0])
-                    errB = math.sqrt(cov[1][1])
-                if out == None:
-                    cstA = 0.0
-                    cstB = 0.0
-                else:
-                    cstA = out[0]
-                    cstB = out[1]
-                # Reset model with the right values of A and B
-                self.model.setParam('A', float(cstA))
-                self.model.setParam('B', float(cstB))
-
-                tempx = []
-                tempy = []
-                y_model = 0.0
-                # load tempy with the minimum transformation
-
-                if self.xLabel == "log10(x)":
-                    y_model = self.model.run(math.log10(xmin))
-                    tempx.append(xmin)
-                else:
-                    y_model = self.model.run(xminView)
-                    tempx.append(xminView)
-
-                if self.yLabel == "log10(y)":
-                    tempy.append(math.pow(10, y_model))
-                else:
-                    tempy.append(y_model)
-
-                # load tempy with the maximum transformation
-                if self.xLabel == "log10(x)":
-                    y_model = self.model.run(math.log10(xmax))
-                    tempx.append(xmax)
-                else:
-                    y_model = self.model.run(xmaxView)
-                    tempx.append(xmaxView)
-
-                if self.yLabel == "log10(y)":
-                    tempy.append(math.pow(10, y_model))
-                else:
-                    tempy.append(y_model)
-                # Set the fit parameter display when  FitDialog is opened again
-                self.Avalue = cstB
-                self.Bvalue = cstA
-                self.ErrAvalue = errA
-                self.ErrBvalue = errB
-                self.Chivalue = chisqr
-                self.push_data(tempx, tempy, xminView, xmaxView,
-                               xmin, xmax, self._ongetValues())
-
-                # Display the fitting value on the Fit Dialog
-                self._onsetValues(cstB, cstA, errA, errB, chisqr)
-
-    def _onsetValues(self, cstA, cstB, errA, errB, Chi):
-        """
-        Display  the value on fit Dialog
-        """
-        rg = None
-        self.tcA.SetValue(format_number(cstA))
-        self.tcB.SetValue(format_number(cstB))
-        self.tcErrA.SetValue(format_number(errA))
-        self.tcErrB.SetValue(format_number(errB))
-        self.tcChi.SetValue(format_number(Chi))
-        if self.rg_on:
-            if self.Rg_tctr.IsShown():
-                rg = numpy.sqrt(-3 * float(cstA))
-                value = format_number(rg)
-                self.Rg_tctr.SetValue(value)
-                if self.I0_tctr.IsShown():
-                    val = numpy.exp(cstB)
-                    self.I0_tctr.SetValue(format_number(val))
-            if self.Rgerr_tctr.IsShown():
-                if rg != None and rg != 0:
-                    value = format_number(3 * float(cstA) / (2 * rg))
-                else:
-                    value = ''
-                self.Rgerr_tctr.SetValue(value)
-                if self.I0err_tctr.IsShown():
-                    val = numpy.abs(numpy.exp(cstB) - numpy.exp(cstB + errB))
-                    self.I0err_tctr.SetValue(format_number(val))
-            if self.Diameter_tctr.IsShown():
-                rg = 4 * numpy.sqrt(-float(cstA))
-                value = format_number(rg)
-                self.Diameter_tctr.SetValue(value)
-            if self.Diametererr_tctr.IsShown():
-                if rg != None and rg != 0:
-                    value = format_number(8 * float(cstA) / rg)
-                else:
-                    value = ''
-                self.Diametererr_tctr.SetValue(value)
-            if self.RgQmin_tctr.IsShown():
-                value = format_number(rg * self.mini)
-                self.RgQmin_tctr.SetValue(value)
-            if self.RgQmax_tctr.IsShown():
-                value = format_number(rg * self.maxi)
-                self.RgQmax_tctr.SetValue(value)
-
-    def _ongetValues(self):
-        """
-        Display  the value on fit Dialog
-        """
-        return self.Avalue, self.Bvalue, self.ErrAvalue, \
-                            self.ErrBvalue, self.Chivalue
-
-    def _checkVal(self, usermin, usermax):
-        """
-        Ensure that fields parameter contains a min and a max value
-        within x min and x max range
-        """
-        self.mini = float(self.xminFit.GetValue())
-        self.maxi = float(self.xmaxFit.GetValue())
-        flag = True
-        try:
-            mini = float(usermin.GetValue())
-            maxi = float(usermax.GetValue())
-            if mini < maxi:
-                usermin.SetBackgroundColour(wx.WHITE)
-                usermin.Refresh()
-            else:
-                flag = False
-                usermin.SetBackgroundColour("pink")
-                usermin.Refresh()
-        except:
-            # Check for possible values entered
-            flag = False
-            usermin.SetBackgroundColour("pink")
-            usermin.Refresh()
-
-        return flag
-
-    def floatForwardTransform(self, x):
-        """
-        transform a float.
-        """
-        # TODO: refactor this with proper object-oriented design
-        # This code stinks.
-        if self.xLabel == "x":
-            return transform.toX(x)
-        if self.xLabel == "x^(2)":
-            return transform.toX2(x)
-        if self.xLabel == "ln(x)":
-            return transform.toLogX(x)
-        if self.xLabel == "log10(x)":
-            return math.log10(x)
-
-    def floatTransform(self, x):
-        """
-        transform a float.It is use to determine the x.
-        View min and x.View max for values
-        not in x
-        """
-        # TODO: refactor this with proper object-oriented design
-        # This code stinks.
-        if self.xLabel == "x":
-            return transform.toX(x)
-        if self.xLabel == "x^(2)":
-            return transform.toX2(x)
-        if self.xLabel == "ln(x)":
-            return transform.toLogX(x)
-        if self.xLabel == "log10(x)":
-            if x > 0:
-                return x
-            else:
-                raise ValueError, "cannot compute log of a negative number"
-
-    def floatInvTransform(self, x):
-        """
-        transform a float.It is use to determine the x.View min and x.View
-        max for values not in x
-
-        """
-        # TODO: refactor this. This is just a hack to make the
-        # functionality work without rewritting the whole code
-        # with good design (which really should be done...).
-        if self.xLabel == "x^(2)":
-            return math.sqrt(x)
-        elif self.xLabel == "log10(x)":
-            return math.pow(10, x)
-        elif self.xLabel == "ln(x)":
-            return math.exp(x)
-        return x
-
-    def checkFitValues(self, item):
-        """
-            Check the validity of input values
-        """
-        flag = True
-        value = item.GetValue()
-        # Check for possible values entered
-        if self.xLabel == "log10(x)":
-            if float(value) > 0:
-                item.SetBackgroundColour(wx.WHITE)
-                item.Refresh()
-            else:
-                flag = False
-                item.SetBackgroundColour("pink")
-                item.Refresh()
-        return flag
-
-    def setFitRange(self, xmin, xmax, xminTrans, xmaxTrans):
-        """
-        Set fit parameters
-        """
-        self.xminFit.SetValue(format_number(xmin))
-        self.xmaxFit.SetValue(format_number(xmax))
-
-    def set_fit_region(self, xmin, xmax):
-        """
-        Set the fit region
-        :param xmin: minimum x-value to be included in fit
-        :param xmax: maximum x-value to be included in fit
-        """
-        # Check values
-        try:
-            float(xmin)
-            float(xmax)
-        except:
-            msg = "LinearFit.set_fit_region: fit range must be floats"
-            raise ValueError, msg
-        self.xminFit.SetValue(format_number(xmin))
-        self.xmaxFit.SetValue(format_number(xmax))
-
-
-class MyApp(wx.App):
-    """
-        Test application
-    """
-    def OnInit(self):
-        """
-            Test application initialization
-        """
-        wx.InitAllImageHandlers()
-        plot = Theory1D([], [])
-        dialog = LinearFit(parent=None, plottable=plot,
-                           push_data=self.onFitDisplay,
-                           transform=self.returnTrans,
-                           title='Linear Fit')
-        if dialog.ShowModal() == wx.ID_OK:
-            pass
-        dialog.Destroy()
-        return 1
-
-    def onFitDisplay(self, tempx, tempy, xminView, xmaxView, xmin, xmax, func):
-        """
-            Test application dummy method
-        """
-        pass
-
-    def returnTrans(self):
-        """
-            Test application dummy method
-        """
-        return '', '', 0, 0, 0, 0, 0
Index: c/sas/plottools/fittings.py
===================================================================
--- src/sas/plottools/fittings.py	(revision 2df0b74178c150a9f521d5b09abf522c0578e825)
+++ 	(revision )
@@ -1,95 +1,0 @@
-"""
-"""
-from scipy import optimize
-
-
-class Parameter(object):
-    """
-    Class to handle model parameters
-    """
-    def __init__(self, model, name, value=None):
-        self.model = model
-        self.name = name
-        if not value == None:
-            self.model.setParam(self.name, value)
-
-    def set(self, value):
-        """
-            Set the value of the parameter
-        """
-        self.model.setParam(self.name, value)
-
-    def __call__(self):
-        """
-            Return the current value of the parameter
-        """
-        return self.model.getParam(self.name)
-
-
-def sasfit(model, pars, x, y, err_y, qmin=None, qmax=None):
-    """
-    Fit function
-
-    :param model: sas model object
-    :param pars: list of parameters
-    :param x: vector of x data
-    :param y: vector of y data
-    :param err_y: vector of y errors
-    """
-    def f(params):
-        """
-        Calculates the vector of residuals for each point
-        in y for a given set of input parameters.
-
-        :param params: list of parameter values
-        :return: vector of residuals
-        """
-        i = 0
-        for p in pars:
-            p.set(params[i])
-            i += 1
-
-        residuals = []
-        for j in range(len(x)):
-            if x[j] >= qmin and x[j] <= qmax:
-                residuals.append((y[j] - model.runXY(x[j])) / err_y[j])
-        return residuals
-
-    def chi2(params):
-        """
-        Calculates chi^2
-
-        :param params: list of parameter values
-
-        :return: chi^2
-
-        """
-        sum = 0
-        res = f(params)
-        for item in res:
-            sum += item * item
-        return sum
-
-    p = [param() for param in pars]
-    out, cov_x, info, mesg, success = optimize.leastsq(f, p, full_output=1)
-    # Calculate chi squared
-    if len(pars) > 1:
-        chisqr = chi2(out)
-    elif len(pars) == 1:
-        chisqr = chi2([out])
-
-    return chisqr, out, cov_x
-
-
-def calcCommandline(event):
-    # Testing implementation
-    # Fit a Line model
-    from LineModel import LineModel
-    line = LineModel()
-    cstA = Parameter(line, 'A', event.cstA)
-    cstB = Parameter(line, 'B', event.cstB)
-    y = line.run()
-    chisqr, out, cov = sasfit(line, [cstA, cstB], event.x, y, 0)
-    # print "Output parameters:", out
-    print "The right answer is [70.0, 1.0]"
-    print chisqr, out, cov
Index: c/sas/plottools/plottable_interactor.py
===================================================================
--- src/sas/plottools/plottable_interactor.py	(revision 3477478de6317fb51d4dd0dbaf9ab2a1236faf28)
+++ 	(revision )
@@ -1,231 +1,0 @@
-"""
-    This module allows more interaction with the plot
-"""
-from BaseInteractor import _BaseInteractor
-
-class PointInteractor(_BaseInteractor):
-    """
-    """
-    def __init__(self, base, axes, color='black', zorder=3, id=''):
-        """
-        """
-        _BaseInteractor.__init__(self, base, axes, color=color)
-        self.zorder = zorder
-        self.id = id
-        self.color = color
-        self.colorlist = ['b', 'g', 'r', 'c', 'm', 'y', 'k']
-        self.symbollist = ['o', 'x', '^', 'v', '<', '>',
-                           '+', 's', 'd', 'D', 'h', 'H', 'p', '-', '--',
-                           'vline', 'step']
-        self.markersize = None
-        self.marker = None
-        self.marker2 = None
-        self._button_down = False
-        self._context_menu = False
-        self._dragged = False
-        self.connect_markers([self.axes])
-
-    def _color(self, c):
-        """Return a particular colour"""
-        return self.colorlist[c % len(self.colorlist)]
-
-    def _symbol(self, s):
-        """Return a particular symbol"""
-        return self.symbollist[s % len(self.symbollist)]
-
-    def points(self, x, y, dx=None, dy=None, color=0, symbol=0, zorder=1,
-               markersize=5, label=None, hide_error=False):
-        """
-        """
-        # Draw curve
-        if self._symbol(symbol) == '-' or self._symbol(symbol) == '--':
-            l_width = markersize * 0.4
-            return self.curve(x=x, y=y, color=color, symbol=symbol,
-                              label=label, width=l_width)
-            # return
-        if self._symbol(symbol) == 'vline':
-            l_width = markersize * 0.4
-            return self.vline(x=x, y=y, color=color, label=label, width=l_width)
-        if self._symbol(symbol) == 'step':
-            l_width = markersize * 0.4
-            return self.step(x=x, y=y, color=color, label=label, width=l_width)
-        if not self.marker == None:
-            self.base.connect.clear([self.marker])
-        self.color = self._color(color)
-        if self.markersize != None:
-            markersize = self.markersize
-        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
-        if dx != None and type(dx) == type(()):
-            dx = nx.vstack((x - dx[0], dx[1] - x)).transpose()
-        if dy != None and type(dy) == type(()):
-            dy = nx.vstack((y - dy[0], dy[1] - y)).transpose()
-
-        if dx == None and dy == None:
-            # zorder = 1
-            self.marker = self.axes.plot(x, y, color=self.color,
-                                         marker=self._symbol(symbol),
-                                         markersize=markersize,
-                                         linestyle='', label=label,
-                                         zorder=zorder)[0]
-        else:
-
-            if hide_error:
-                # zorder = 1
-                self.marker = self.axes.plot(x, y, color=self.color,
-                                             marker=self._symbol(symbol),
-                                             markersize=markersize,
-                                             linestyle='', label=label,
-                                             zorder=1)[0]
-            else:
-                # zorder = 2
-                self.marker = self.axes.errorbar(x, y, yerr=dy,
-                                                 xerr=None,
-                                                 ecolor=self.color,
-                                                 color=self.color,
-                                                 capsize=2,
-                                                 linestyle='',
-                                                 barsabove=False,
-                                                 marker=self._symbol(symbol),
-                                                 markersize=markersize,
-                                                 lolims=False, uplims=False,
-                                                 xlolims=False, xuplims=False,
-                                                 label=label,
-                                                 zorder=1)[0]
-
-        self.connect_markers([self.marker])
-        self.update()
-
-    def curve(self, x, y, dy=None, color=0, symbol=0, zorder=10,
-              label=None, width=2.0):
-        """
-        """
-        if not self.marker == None:
-            self.base.connect.clear([self.marker])
-        self.color = self._color(color)
-        self.marker = self.axes.plot(x, y, color=self.color, lw=width,
-                                     marker='', linestyle=self._symbol(symbol),
-                                     label=label, zorder=zorder)[0]
-
-        self.connect_markers([self.marker])
-        self.update()
-
-
-    def vline(self, x, y, dy=None, color=0, symbol=0, zorder=1,
-              label=None, width=2.0):
-        """
-        """
-        if not self.marker == None:
-            self.base.connect.clear([self.marker])
-        self.color = self._color(color)
-        if min(y) < 0:
-            y_min = 0.0
-        else:
-            y_min = min(y) * 9 / 10
-        self.marker = self.axes.vlines(x=x, ymin=y_min, ymax=y,
-                                       color=self.color,
-                                       linestyle='-', label=label,
-                                       lw=width, zorder=zorder)
-        self.connect_markers([self.marker])
-        self.update()
-
-    def step(self, x, y, dy=None, color=0, symbol=0, zorder=1,
-             label=None, width=2.0):
-        """
-        """
-        if not self.marker == None:
-            self.base.connect.clear([self.marker])
-        self.color = self._color(color)
-        self.marker = self.axes.step(x, y, color=self.color,
-                                     marker='',
-                                     linestyle='-', label=label,
-                                     lw=width, zorder=zorder)[0]
-        self.connect_markers([self.marker])
-        self.update()
-
-    def connect_markers(self, markers):
-        """
-        Connect markers to callbacks
-        """
-        for h in markers:
-            connect = self.base.connect
-            connect('enter', h, self._on_enter)
-            connect('leave', h, self._on_leave)
-            connect('click', h, self._on_click)
-            connect('release', h, self._on_release)
-            connect('key', h, self.onKey)
-
-    def clear(self):
-        print "plottable_interactor.clear()"
-
-    def _on_click(self, evt):
-        """
-        Called when a mouse button is clicked
-        from within the boundaries of an artist.
-        """
-        if self._context_menu == True:
-            self._context_menu = False
-            evt.artist = self.marker
-            self._on_leave(evt)
-
-    def _on_release(self, evt):
-        """
-        Called when a mouse button is released
-        within the boundaries of an artist
-        """
-        # Check to see whether we are about to pop
-        # the context menu up
-        if evt.button == 3:
-            self._context_menu = True
-
-    def _on_enter(self, evt):
-        """
-        Called when we are entering the boundaries
-        of an artist.
-        """
-        if not evt.artist.__class__.__name__ == "AxesSubplot":
-            self.base.plottable_selected(self.id)
-
-            if evt.artist.get_color() == 'y':
-                try:
-                    evt.artist.set_color('b')
-                except:
-                    evt.artist.set_color_cycle('b')
-                if hasattr(evt.artist, "set_facecolor"):
-                    evt.artist.set_facecolor('b')
-                if hasattr(evt.artist, "set_edgecolor"):
-                    evt.artist.set_edgecolor('b')
-            else:
-                try:
-                    evt.artist.set_color('y')
-                except:
-                    evt.artist.set_color_cycle('y')
-                if hasattr(evt.artist, "set_facecolor"):
-                    evt.artist.set_facecolor('y')
-                if hasattr(evt.artist, "set_edgecolor"):
-                    evt.artist.set_edgecolor('y')
-
-            self.axes.figure.canvas.draw_idle()
-
-    def _on_leave(self, evt):
-        """
-        Called when we are leaving the boundaries
-        of an artist.
-        """
-        if not evt.artist.__class__.__name__ == "AxesSubplot":
-            if self._context_menu == False:
-                self.base.plottable_selected(None)
-                try:
-                    evt.artist.set_color(self.color)
-                except:
-                    evt.artist.set_color_cycle(self.color)
-                if hasattr(evt.artist, "set_facecolor"):
-                    evt.artist.set_facecolor(self.color)
-                if hasattr(evt.artist, "set_edgecolor"):
-                    evt.artist.set_edgecolor(self.color)
-                self.axes.figure.canvas.draw_idle()
-
-    def update(self):
-        """
-        Update
-        """
-        pass
Index: c/sas/plottools/plottables.py
===================================================================
--- src/sas/plottools/plottables.py	(revision 3477478de6317fb51d4dd0dbaf9ab2a1236faf28)
+++ 	(revision )
@@ -1,1284 +1,0 @@
-"""
-Prototype plottable object support.
-
-The main point of this prototype is to provide a clean separation between
-the style (plotter details: color, grids, widgets, etc.) and substance
-(application details: which information to plot).  Programmers should not be
-dictating line colours and plotting symbols.
-
-Unlike the problem of style in CSS or Word, where most paragraphs look
-the same, each line on a graph has to be distinguishable from its neighbours.
-Our solution is to provide parametric styles, in which a number of
-different classes of object (e.g., reflectometry data, reflectometry
-theory) representing multiple graph primitives cycle through a colour
-palette provided by the underlying plotter.
-
-A full treatment would provide perceptual dimensions of prominence and
-distinctiveness rather than a simple colour number.
-
-"""
-
-# Design question: who owns the color?
-# Is it a property of the plottable?
-# Or of the plottable as it exists on the graph?
-# Or if the graph?
-# If a plottable can appear on multiple graphs, in some case the
-# color should be the same on each graph in which it appears, and
-# in other cases (where multiple plottables from different graphs
-# coexist), the color should be assigned by the graph.  In any case
-# once a plottable is placed on the graph its color should not
-# depend on the other plottables on the graph.  Furthermore, if
-# a plottable is added and removed from a graph and added again,
-# it may be nice, but not necessary, to have the color persist.
-#
-# The safest approach seems to be to give ownership of color
-# to the graph, which will allocate the colors along with the
-# plottable.  The plottable will need to return the number of
-# colors that are needed.
-#
-# The situation is less clear for symbols.  It is less clear
-# how much the application requires that symbols be unique across
-# all plots on the graph.
-
-# Support for ancient python versions
-import copy
-import numpy
-import sys
-import logging
-
-if 'any' not in dir(__builtins__):
-    def any(L):
-        for cond in L:
-            if cond:
-                return True
-        return False
-
-    def all(L):
-        for cond in L:
-            if not cond:
-                return False
-        return True
-
-
-class Graph(object):
-    """
-    Generic plottables graph structure.
-
-    Plot styles are based on color/symbol lists.  The user gets to select
-    the list of colors/symbols/sizes to choose from, not the application
-    developer.  The programmer only gets to add/remove lines from the
-    plot and move to the next symbol/color.
-
-    Another dimension is prominence, which refers to line sizes/point sizes.
-
-    Axis transformations allow the user to select the coordinate view
-    which provides clarity to the data.  There is no way we can provide
-    every possible transformation for every application generically, so
-    the plottable objects themselves will need to provide the transformations.
-    Here are some examples from reflectometry: ::
-
-       independent: x -> f(x)
-          monitor scaling: y -> M*y
-          log:  y -> log(y if y > min else min)
-          cos:  y -> cos(y*pi/180)
-       dependent:   x -> f(x,y)
-          Q4:      y -> y*x^4
-          fresnel: y -> y*fresnel(x)
-       coordinated: x,y = f(x,y)
-          Q:    x -> 2*pi/L (cos(x*pi/180) - cos(y*pi/180))
-                y -> 2*pi/L (sin(x*pi/180) + sin(y*pi/180))
-       reducing: x,y = f(x1,x2,y1,y2)
-          spin asymmetry: x -> x1, y -> (y1 - y2)/(y1 + y2)
-          vector net: x -> x1, y -> y1*cos(y2*pi/180)
-
-    Multiple transformations are possible, such as Q4 spin asymmetry
-
-    Axes have further complications in that the units of what are being
-    plotted should correspond to the units on the axes.  Plotting multiple
-    types on the same graph should be handled gracefully, e.g., by creating
-    a separate tab for each available axis type, breaking into subplots,
-    showing multiple axes on the same plot, or generating inset plots.
-    Ultimately the decision should be left to the user.
-
-    Graph properties such as grids/crosshairs should be under user control,
-    as should the sizes of items such as axis fonts, etc.  No direct
-    access will be provided to the application.
-
-    Axis limits are mostly under user control.  If the user has zoomed or
-    panned then those limits are preserved even if new data is plotted.
-    The exception is when, e.g., scanning through a set of related lines
-    in which the user may want to fix the limits so that user can compare
-    the values directly.  Another exception is when creating multiple
-    graphs sharing the same limits, though this case may be important
-    enough that it is handled by the graph widget itself.  Axis limits
-    will of course have to understand the effects of axis transformations.
-
-    High level plottable objects may be composed of low level primitives.
-    Operations such as legend/hide/show copy/paste, etc. need to operate
-    on these primitives as a group.  E.g., allowing the user to have a
-    working canvas where they can drag lines they want to save and annotate
-    them.
-
-    Graphs need to be printable.  A page layout program for entire plots
-    would be nice.
-
-    """
-    def _xaxis_transformed(self, name, units):
-        """
-        Change the property of the x axis
-        according to an axis transformation
-        (as opposed to changing the basic properties)
-        """
-        if units != "":
-            name = "%s (%s)" % (name, units)
-        self.prop["xlabel"] = name
-        self.prop["xunit"] = units
-
-    def _yaxis_transformed(self, name, units):
-        """
-        Change the property of the y axis
-        according to an axis transformation
-        (as opposed to changing the basic properties)
-        """
-        if units != "":
-            name = "%s (%s)" % (name, units)
-        self.prop["ylabel"] = name
-        self.prop["yunit"] = units
-
-    def xaxis(self, name, units):
-        """
-        Properties of the x axis.
-        """
-        if units != "":
-            name = "%s (%s)" % (name, units)
-        self.prop["xlabel"] = name
-        self.prop["xunit"] = units
-        self.prop["xlabel_base"] = name
-        self.prop["xunit_base"] = units
-
-    def yaxis(self, name, units):
-        """
-        Properties of the y axis.
-        """
-        if units != "":
-            name = "%s (%s)" % (name, units)
-        self.prop["ylabel"] = name
-        self.prop["yunit"] = units
-        self.prop["ylabel_base"] = name
-        self.prop["yunit_base"] = units
-
-    def title(self, name):
-        """
-        Graph title
-        """
-        self.prop["title"] = name
-
-    def get(self, key):
-        """
-        Get the graph properties
-        """
-        if key == "color":
-            return self.color
-        elif key == "symbol":
-            return self.symbol
-        else:
-            return self.prop[key]
-
-    def set(self, **kw):
-        """
-        Set the graph properties
-        """
-        for key in kw:
-            if key == "color":
-                self.color = kw[key] % len(self.colorlist)
-            elif key == "symbol":
-                self.symbol = kw[key] % len(self.symbollist)
-            else:
-                self.prop[key] = kw[key]
-
-    def isPlotted(self, plottable):
-        """Return True is the plottable is already on the graph"""
-        if plottable in self.plottables:
-            return True
-        return False
-
-    def add(self, plottable, color=None):
-        """Add a new plottable to the graph"""
-        # record the colour associated with the plottable
-        if not plottable in self.plottables:
-            if color is not None:
-                self.plottables[plottable] = color
-            else:
-                self.color += plottable.colors()
-                self.plottables[plottable] = self.color
-
-    def changed(self):
-        """Detect if any graphed plottables have changed"""
-        return any([p.changed() for p in self.plottables])
-
-    def get_range(self):
-        """
-        Return the range of all displayed plottables
-        """
-        min_value = None
-        max_value = None
-        for p in self.plottables:
-            if p.hidden == True:
-                continue
-            if not p.x == None:
-                for x_i in p.x:
-                    if min_value == None or x_i < min_value:
-                        min_value = x_i
-                    if max_value == None or x_i > max_value:
-                        max_value = x_i
-        return min_value, max_value
-
-    def replace(self, plottable):
-        """Replace an existing plottable from the graph"""
-        selected_color = None
-        selected_plottable = None
-        for p in self.plottables.keys():
-            if plottable.id == p.id:
-                selected_plottable = p
-                selected_color = self.plottables[p]
-                break
-        if  selected_plottable is not None and selected_color is not None:
-            del self.plottables[selected_plottable]
-            self.plottables[plottable] = selected_color
-
-    def delete(self, plottable):
-        """Remove an existing plottable from the graph"""
-        if plottable in self.plottables:
-            del self.plottables[plottable]
-            self.color = len(self.plottables)
-
-    def reset_scale(self):
-        """
-        Resets the scale transformation data to the underlying data
-        """
-        for p in self.plottables:
-            p.reset_view()
-
-    def reset(self):
-        """Reset the graph."""
-        self.color = -1
-        self.symbol = 0
-        self.prop = {"xlabel": "", "xunit": None,
-                     "ylabel": "", "yunit": None,
-                     "title": ""}
-        self.plottables = {}
-
-    def _make_labels(self):
-        """
-        """
-        # Find groups of related plottables
-        sets = {}
-        for p in self.plottables:
-            if p.__class__ in sets:
-                sets[p.__class__].append(p)
-            else:
-                sets[p.__class__] = [p]
-        # Ask each plottable class for a set of unique labels
-        labels = {}
-        for c in sets:
-            labels.update(c.labels(sets[c]))
-        return labels
-
-    def get_plottable(self, name):
-        """
-        Return the plottable with the given
-        name if it exists. Otherwise return None
-        """
-        for item in self.plottables:
-            if item.name == name:
-                return item
-        return None
-
-    def returnPlottable(self):
-        """
-        This method returns a dictionary of plottables contained in graph
-        It is just by Plotpanel to interact with the complete list of plottables
-        inside the graph.
-        """
-        return self.plottables
-
-    def render(self, plot):
-        """Redraw the graph"""
-        plot.connect.clearall()
-        plot.clear()
-        plot.properties(self.prop)
-        labels = self._make_labels()
-        for p in self.plottables:
-            if p.custom_color is not None:
-                p.render(plot, color=p.custom_color, symbol=0,
-                         markersize=p.markersize, label=labels[p])
-            else:
-                p.render(plot, color=self.plottables[p], symbol=0,
-                         markersize=p.markersize, label=labels[p])
-        plot.render()
-
-    def __init__(self, **kw):
-        self.reset()
-        self.set(**kw)
-        # Name of selected plottable, if any
-        self.selected_plottable = None
-
-
-# Transform interface definition
-# No need to inherit from this class, just need to provide
-# the same methods.
-class Transform(object):
-    """
-    Define a transform plugin to the plottable architecture.
-
-    Transforms operate on axes.  The plottable defines the
-    set of transforms available for it, and the axes on which
-    they operate.  These transforms can operate on the x axis
-    only, the y axis only or on the x and y axes together.
-
-    This infrastructure is not able to support transformations
-    such as log and polar plots as these require full control
-    over the drawing of axes and grids.
-
-    A transform has a number of attributes.
-
-    name
-      user visible name for the transform.  This will
-      appear in the context menu for the axis and the transform
-      menu for the graph.
-
-    type
-      operational axis.  This determines whether the
-      transform should appear on x,y or z axis context
-      menus, or if it should appear in the context menu for
-      the graph.
-
-    inventory
-      (not implemented)
-      a dictionary of user settable parameter names and
-      their associated types.  These should appear as keyword
-      arguments to the transform call.  For example, Fresnel
-      reflectivity requires the substrate density:
-      ``{ 'rho': type.Value(10e-6/units.angstrom**2) }``
-      Supply reasonable defaults in the callback so that
-      limited plotting clients work even though they cannot
-      set the inventory.
-
-    """
-    def __call__(self, plottable, **kwargs):
-        """
-        Transform the data.  Whenever a plottable is added
-        to the axes, the infrastructure will apply all required
-        transforms.  When the user selects a different representation
-        for the axes (via menu, script, or context menu), all
-        plottables on the axes will be transformed.  The
-        plottable should store the underlying data but set
-        the standard x,dx,y,dy,z,dz attributes appropriately.
-
-        If the call raises a NotImplemented error the dataline
-        will not be plotted.  The associated string will usually
-        be 'Not a valid transform', though other strings are possible.
-        The application may or may not display the message to the
-        user, along with an indication of which plottable was at fault.
-
-        """
-        raise NotImplemented, "Not a valid transform"
-
-    # Related issues
-    # ==============
-    #
-    # log scale:
-    #    All axes have implicit log/linear scaling options.
-    #
-    # normalization:
-    #    Want to display raw counts vs detector efficiency correction
-    #    Want to normalize by time/monitor/proton current/intensity.
-    #    Want to display by eg. counts per 3 sec or counts per 10000 monitor.
-    #    Want to divide by footprint (ab initio, fitted or measured).
-    #    Want to scale by attenuator values.
-    #
-    # compare/contrast:
-    #    Want to average all visible lines with the same tag, and
-    #    display difference from one particular line.  Not a transform
-    #    issue?
-    #
-    # multiline graph:
-    #    How do we show/hide data parts.  E.g., data or theory, or
-    #    different polarization cross sections?  One way is with
-    #    tags: each plottable has a set of tags and the tags are
-    #    listed as check boxes above the plotting area.  Click a
-    #    tag and all plottables with that tag are hidden on the
-    #    plot and on the legend.
-    #
-    # nonconformant y-axes:
-    #    What do we do with temperature vs. Q and reflectivity vs. Q
-    #    on the same graph?
-    #
-    # 2D -> 1D:
-    #    Want various slices through the data.  Do transforms apply
-    #    to the sliced data as well?
-
-
-class Plottable(object):
-    """
-    """
-    # Short ascii name to refer to the plottable in a menu
-    short_name = None
-    # Fancy name
-    name = None
-    # Data
-    x = None
-    y = None
-    dx = None
-    dy = None
-    # Parameter to allow a plot to be part of the list without being displayed
-    hidden = False
-    # Flag to set whether a plottable has an interactor or not
-    interactive = True
-    custom_color = None
-    markersize = 5  # default marker size is 'size 5'
-
-    def __init__(self):
-        self.view = View()
-        self._xaxis = ""
-        self._xunit = ""
-        self._yaxis = ""
-        self._yunit = ""
-
-    def __setattr__(self, name, value):
-        """
-        Take care of changes in View when data is changed.
-        This method is provided for backward compatibility.
-        """
-        object.__setattr__(self, name, value)
-        if name in ['x', 'y', 'dx', 'dy']:
-            self.reset_view()
-            # print "self.%s has been called" % name
-
-    def set_data(self, x, y, dx=None, dy=None):
-        """
-        """
-        self.x = x
-        self.y = y
-        self.dy = dy
-        self.dx = dx
-        self.transformView()
-
-    def xaxis(self, name, units):
-        """
-        Set the name and unit of x_axis
-
-        :param name: the name of x-axis
-        :param units: the units of x_axis
-
-        """
-        self._xaxis = name
-        self._xunit = units
-
-    def yaxis(self, name, units):
-        """
-        Set the name and unit of y_axis
-
-        :param name: the name of y-axis
-        :param units: the units of y_axis
-
-        """
-        self._yaxis = name
-        self._yunit = units
-
-    def get_xaxis(self):
-        """Return the units and name of x-axis"""
-        return self._xaxis, self._xunit
-
-    def get_yaxis(self):
-        """ Return the units and name of y- axis"""
-        return self._yaxis, self._yunit
-
-    @classmethod
-    def labels(cls, collection):
-        """
-        Construct a set of unique labels for a collection of plottables of
-        the same type.
-
-        Returns a map from plottable to name.
-
-        """
-        n = len(collection)
-        label_dict = {}
-        if n > 0:
-            basename = str(cls).split('.')[-1]
-            if n == 1:
-                label_dict[collection[0]] = basename
-            else:
-                for i in xrange(len(collection)):
-                    label_dict[collection[i]] = "%s %d" % (basename, i)
-        return label_dict
-
-    # #Use the following if @classmethod doesn't work
-    # labels = classmethod(labels)
-    def setLabel(self, labelx, labely):
-        """
-        It takes a label of the x and y transformation and set View parameters
-
-        :param transx: The label of x transformation is sent by Properties Dialog
-        :param transy: The label of y transformation is sent Properties Dialog
-
-        """
-        self.view.xLabel = labelx
-        self.view.yLabel = labely
-
-    def set_View(self, x, y):
-        """Load View"""
-        self.x = x
-        self.y = y
-        self.reset_view()
-
-    def reset_view(self):
-        """Reload view with new value to plot"""
-        self.view = View(self.x, self.y, self.dx, self.dy)
-        self.view.Xreel = self.view.x
-        self.view.Yreel = self.view.y
-        self.view.DXreel = self.view.dx
-        self.view.DYreel = self.view.dy
-
-    def render(self, plot):
-        """
-        The base class makes sure the correct units are being used for
-        subsequent plottable.
-
-        For now it is assumed that the graphs are commensurate, and if you
-        put a Qx object on a Temperature graph then you had better hope
-        that it makes sense.
-
-        """
-        plot.xaxis(self._xaxis, self._xunit)
-        plot.yaxis(self._yaxis, self._yunit)
-
-    def is_empty(self):
-        """
-        Returns True if there is no data stored in the plottable
-        """
-        if not self.x == None and len(self.x) == 0 \
-            and not self.y == None and len(self.y) == 0:
-            return True
-        return False
-
-    def colors(self):
-        """Return the number of colors need to render the object"""
-        return 1
-
-    def transformView(self):
-        """
-        It transforms x, y before displaying
-        """
-        self.view.transform(self.x, self.y, self.dx, self.dy)
-
-    def returnValuesOfView(self):
-        """
-        Return View parameters and it is used by Fit Dialog
-        """
-        return self.view.returnXview()
-
-    def check_data_PlottableX(self):
-        """
-        Since no transformation is made for log10(x), check that
-        no negative values is plot in log scale
-        """
-        self.view.check_data_logX()
-
-    def check_data_PlottableY(self):
-        """
-        Since no transformation is made for log10(y), check that
-        no negative values is plot in log scale
-        """
-        self.view.check_data_logY()
-
-    def transformX(self, transx, transdx):
-        """
-        Receive pointers to function that transform x and dx
-        and set corresponding View pointers
-
-        :param transx: pointer to function that transforms x
-        :param transdx: pointer to function that transforms dx
-
-        """
-        self.view.setTransformX(transx, transdx)
-
-    def transformY(self, transy, transdy):
-        """
-        Receive pointers to function that transform y and dy
-        and set corresponding View pointers
-
-        :param transy: pointer to function that transforms y
-        :param transdy: pointer to function that transforms dy
-
-        """
-        self.view.setTransformY(transy, transdy)
-
-    def onReset(self):
-        """
-        Reset x, y, dx, dy view with its parameters
-        """
-        self.view.onResetView()
-
-    def onFitRange(self, xmin=None, xmax=None):
-        """
-        It limits View data range to plot from min to max
-
-        :param xmin: the minimum value of x to plot.
-        :param xmax: the maximum value of x to plot
-
-        """
-        self.view.onFitRangeView(xmin, xmax)
-
-
-class View(object):
-    """
-    Representation of the data that might include a transformation
-    """
-    x = None
-    y = None
-    dx = None
-    dy = None
-
-    def __init__(self, x=None, y=None, dx=None, dy=None):
-        """
-        """
-        self.x = x
-        self.y = y
-        self.dx = dx
-        self.dy = dy
-        # To change x range to the reel range
-        self.Xreel = self.x
-        self.Yreel = self.y
-        self.DXreel = self.dx
-        self.DYreel = self.dy
-        # Labels of x and y received from Properties Dialog
-        self.xLabel = ""
-        self.yLabel = ""
-        # Function to transform x, y, dx and dy
-        self.funcx = None
-        self.funcy = None
-        self.funcdx = None
-        self.funcdy = None
-
-    def transform(self, x=None, y=None, dx=None, dy=None):
-        """
-        Transforms the x,y,dx and dy vectors and stores
-         the output in View parameters
-
-        :param x: array of x values
-        :param y: array of y values
-        :param dx: array of  errors values on x
-        :param dy: array of error values on y
-
-        """
-        # Sanity check
-        # Do the transofrmation only when x and y are empty
-        has_err_x = not (dx == None or len(dx) == 0)
-        has_err_y = not (dy == None or len(dy) == 0)
-
-        if(x != None) and (y != None):
-            if not dx == None and not len(dx) == 0 and not len(x) == len(dx):
-                msg = "Plottable.View: Given x and dx are not"
-                msg += " of the same length"
-                raise ValueError, msg
-            # Check length of y array
-            if not len(y) == len(x):
-                msg = "Plottable.View: Given y "
-                msg += "and x are not of the same length"
-                raise ValueError, msg
-
-            if not dy == None and not len(dy) == 0 and not len(y) == len(dy):
-                msg = "Plottable.View: Given y and dy are not of the same "
-                msg += "length: len(y)=%s, len(dy)=%s" % (len(y), len(dy))
-                raise ValueError, msg
-            self.x = []
-            self.y = []
-            if has_err_x:
-                self.dx = []
-            else:
-                self.dx = None
-            if has_err_y:
-                self.dy = []
-            else:
-                self.dy = None
-            tempx = []
-            tempy = []
-            if not has_err_x:
-                dx = numpy.zeros(len(x))
-            if not has_err_y:
-                dy = numpy.zeros(len(y))
-            for i in range(len(x)):
-                try:
-                    tempx = self.funcx(x[i], y[i])
-                    tempy = self.funcy(y[i], x[i])
-                    if has_err_x:
-                        tempdx = self.funcdx(x[i], y[i], dx[i], dy[i])
-                    if has_err_y:
-                        tempdy = self.funcdy(y[i], x[i], dy[i], dx[i])
-                    self.x.append(tempx)
-                    self.y.append(tempy)
-                    if has_err_x:
-                        self.dx.append(tempdx)
-                    if has_err_y:
-                        self.dy.append(tempdy)
-                except:
-                    tempx = x[i]
-                    tempy = y[i]
-                    tempdy = dy[i]
-            # Sanity check
-            if not len(self.x) == len(self.y):
-                msg = "Plottable.View: transformed x "
-                msg += "and y are not of the same length"
-                raise ValueError, msg
-            if has_err_x and not (len(self.x) and len(self.dx)):
-                msg = "Plottable.View: transformed x and dx"
-                msg += " are not of the same length"
-                raise ValueError, msg
-            if has_err_y and not (len(self.y) and len(self.dy)):
-                msg = "Plottable.View: transformed y"
-                msg += " and dy are not of the same length"
-                raise ValueError, msg
-            # Check that negative values are not plot on x and y axis for
-            # log10 transformation
-            self.check_data_logX()
-            self.check_data_logY()
-            # Store x ,y dx,and dy in their full range for reset
-            self.Xreel = self.x
-            self.Yreel = self.y
-            self.DXreel = self.dx
-            self.DYreel = self.dy
-
-    def onResetView(self):
-        """
-        Reset x,y,dx and y in their full range  and in the initial scale
-        in case their previous range has changed
-        """
-        self.x = self.Xreel
-        self.y = self.Yreel
-        self.dx = self.DXreel
-        self.dy = self.DYreel
-
-    def setTransformX(self, funcx, funcdx):
-        """
-        Receive pointers to function that transform x and dx
-        and set corresponding View pointers
-
-        :param transx: pointer to function that transforms x
-        :param transdx: pointer to function that transforms dx
-        """
-        self.funcx = funcx
-        self.funcdx = funcdx
-
-    def setTransformY(self, funcy, funcdy):
-        """
-        Receive pointers to function that transform y and dy
-        and set corresponding View pointers
-
-        :param transx: pointer to function that transforms y
-        :param transdx: pointer to function that transforms dy
-        """
-        self.funcy = funcy
-        self.funcdy = funcdy
-
-    def returnXview(self):
-        """
-        Return View  x,y,dx,dy
-        """
-        return self.x, self.y, self.dx, self.dy
-
-    def check_data_logX(self):
-        """
-        Remove negative value in x vector to avoid plotting negative
-        value of Log10
-        """
-        tempx = []
-        tempdx = []
-        tempy = []
-        tempdy = []
-        if self.dx == None:
-            self.dx = numpy.zeros(len(self.x))
-        if self.dy == None:
-            self.dy = numpy.zeros(len(self.y))
-        if self.xLabel == "log10(x)":
-            for i in range(len(self.x)):
-                try:
-                    if self.x[i] > 0:
-                        tempx.append(self.x[i])
-                        tempdx.append(self.dx[i])
-                        tempy.append(self.y[i])
-                        tempdy.append(self.dy[i])
-                except:
-                    logging.error("check_data_logX: skipping point x %g", self.x[i])
-                    logging.error(sys.exc_value)
-            self.x = tempx
-            self.y = tempy
-            self.dx = tempdx
-            self.dy = tempdy
-
-    def check_data_logY(self):
-        """
-        Remove negative value in y vector
-        to avoid plotting negative value of Log10
-
-        """
-        tempx = []
-        tempdx = []
-        tempy = []
-        tempdy = []
-        if self.dx == None:
-            self.dx = numpy.zeros(len(self.x))
-        if self.dy == None:
-            self.dy = numpy.zeros(len(self.y))
-        if self.yLabel == "log10(y)":
-            for i in range(len(self.x)):
-                try:
-                    if self.y[i] > 0:
-                        tempx.append(self.x[i])
-                        tempdx.append(self.dx[i])
-                        tempy.append(self.y[i])
-                        tempdy.append(self.dy[i])
-                except:
-                    logging.error("check_data_logY: skipping point %g", self.y[i])
-                    logging.error(sys.exc_value)
-
-            self.x = tempx
-            self.y = tempy
-            self.dx = tempdx
-            self.dy = tempdy
-
-    def onFitRangeView(self, xmin=None, xmax=None):
-        """
-        It limits View data range to plot from min to max
-
-        :param xmin: the minimum value of x to plot.
-        :param xmax: the maximum value of x to plot
-
-        """
-        tempx = []
-        tempdx = []
-        tempy = []
-        tempdy = []
-        if self.dx == None:
-            self.dx = numpy.zeros(len(self.x))
-        if self.dy == None:
-            self.dy = numpy.zeros(len(self.y))
-        if xmin != None and xmax != None:
-            for i in range(len(self.x)):
-                if self.x[i] >= xmin and self.x[i] <= xmax:
-                    tempx.append(self.x[i])
-                    tempdx.append(self.dx[i])
-                    tempy.append(self.y[i])
-                    tempdy.append(self.dy[i])
-            self.x = tempx
-            self.y = tempy
-            self.dx = tempdx
-            self.dy = tempdy
-
-
-class Data2D(Plottable):
-    """
-    2D data class for image plotting
-    """
-    def __init__(self, image=None, qx_data=None, qy_data=None,
-                 err_image=None, xmin=None, xmax=None, ymin=None,
-                 ymax=None, zmin=None, zmax=None):
-        """
-        Draw image
-        """
-        Plottable.__init__(self)
-        self.name = "Data2D"
-        self.label = None
-        self.data = image
-        self.qx_data = qx_data
-        self.qy_data = qx_data
-        self.err_data = err_image
-        self.source = None
-        self.detector = []
-
-        # # Units for Q-values
-        self.xy_unit = 'A^{-1}'
-        # # Units for I(Q) values
-        self.z_unit = 'cm^{-1}'
-        self._zaxis = ''
-        # x-axis unit and label
-        self._xaxis = '\\rm{Q_{x}}'
-        self._xunit = 'A^{-1}'
-        # y-axis unit and label
-        self._yaxis = '\\rm{Q_{y}}'
-        self._yunit = 'A^{-1}'
-
-        # ## might remove that later
-        # # Vector of Q-values at the center of each bin in x
-        self.x_bins = []
-        # # Vector of Q-values at the center of each bin in y
-        self.y_bins = []
-
-        # x and y boundaries
-        self.xmin = xmin
-        self.xmax = xmax
-        self.ymin = ymin
-        self.ymax = ymax
-
-        self.zmin = zmin
-        self.zmax = zmax
-        self.id = None
-
-    def xaxis(self, label, unit):
-        """
-        set x-axis
-
-        :param label: x-axis label
-        :param unit: x-axis unit
-
-        """
-        self._xaxis = label
-        self._xunit = unit
-
-    def yaxis(self, label, unit):
-        """
-        set y-axis
-
-        :param label: y-axis label
-        :param unit: y-axis unit
-
-        """
-        self._yaxis = label
-        self._yunit = unit
-
-    def zaxis(self, label, unit):
-        """
-        set z-axis
-
-        :param label: z-axis label
-        :param unit: z-axis unit
-
-        """
-        self._zaxis = label
-        self._zunit = unit
-
-    def setValues(self, datainfo=None):
-        """
-        Use datainfo object to initialize data2D
-
-        :param datainfo: object
-
-        """
-        self.image = copy.deepcopy(datainfo.data)
-        self.qx_data = copy.deepcopy(datainfo.qx_data)
-        self.qy_data = copy.deepcopy(datainfo.qy_data)
-        self.err_image = copy.deepcopy(datainfo.err_data)
-
-        self.xy_unit = datainfo.Q_unit
-        self.z_unit = datainfo.I_unit
-        self._zaxis = datainfo._zaxis
-
-        self.xaxis(datainfo._xunit, datainfo._xaxis)
-        self.yaxis(datainfo._yunit, datainfo._yaxis)
-        # x and y boundaries
-        self.xmin = datainfo.xmin
-        self.xmax = datainfo.xmax
-        self.ymin = datainfo.ymin
-        self.ymax = datainfo.ymax
-        # # Vector of Q-values at the center of each bin in x
-        self.x_bins = datainfo.x_bins
-        # # Vector of Q-values at the center of each bin in y
-        self.y_bins = datainfo.y_bins
-
-    def set_zrange(self, zmin=None, zmax=None):
-        """
-        """
-        if zmin < zmax:
-            self.zmin = zmin
-            self.zmax = zmax
-        else:
-            raise "zmin is greater or equal to zmax "
-
-    def render(self, plot, **kw):
-        """
-        Renders the plottable on the graph
-
-        """
-        plot.image(self.data, self.qx_data, self.qy_data,
-                   self.xmin, self.xmax, self.ymin,
-                   self.ymax, self.zmin, self.zmax, **kw)
-
-    def changed(self):
-        """
-        """
-        return False
-
-    @classmethod
-    def labels(cls, collection):
-        """Build a label mostly unique within a collection"""
-        label_dict = {}
-        for item in collection:
-            if item.label == "Data2D":
-                item.label = item.name
-            label_dict[item] = item.label
-        return label_dict
-
-
-class Data1D(Plottable):
-    """
-    Data plottable: scatter plot of x,y with errors in x and y.
-    """
-
-    def __init__(self, x, y, dx=None, dy=None):
-        """
-        Draw points specified by x[i],y[i] in the current color/symbol.
-        Uncertainty in x is given by dx[i], or by (xlo[i],xhi[i]) if the
-        uncertainty is asymmetric.  Similarly for y uncertainty.
-
-        The title appears on the legend.
-        The label, if it is different, appears on the status bar.
-        """
-        Plottable.__init__(self)
-        self.name = "data"
-        self.label = "data"
-        self.x = x
-        self.y = y
-        self.dx = dx
-        self.dy = dy
-        self.source = None
-        self.detector = None
-        self.xaxis('', '')
-        self.yaxis('', '')
-        self.view = View(self.x, self.y, self.dx, self.dy)
-        self.symbol = 0
-        self.custom_color = None
-        self.markersize = 5
-        self.id = None
-        self.zorder = 1
-        self.hide_error = False
-
-    def render(self, plot, **kw):
-        """
-        Renders the plottable on the graph
-        """
-        if self.interactive == True:
-            kw['symbol'] = self.symbol
-            kw['id'] = self.id
-            kw['hide_error'] = self.hide_error
-            kw['markersize'] = self.markersize
-            plot.interactive_points(self.view.x, self.view.y,
-                                    dx=self.view.dx, dy=self.view.dy,
-                                    name=self.name, zorder=self.zorder, **kw)
-        else:
-            kw['id'] = self.id
-            kw['hide_error'] = self.hide_error
-            kw['symbol'] = self.symbol
-            kw['color'] = self.custom_color
-            kw['markersize'] = self.markersize
-            plot.points(self.view.x, self.view.y, dx=self.view.dx,
-                        dy=self.view.dy, zorder=self.zorder,
-                        marker=self.symbollist[self.symbol], **kw)
-
-    def changed(self):
-        return False
-
-    @classmethod
-    def labels(cls, collection):
-        """Build a label mostly unique within a collection"""
-        label_dict = {}
-        for item in collection:
-            if item.label == "data":
-                item.label = item.name
-            label_dict[item] = item.label
-        return label_dict
-
-
-class Theory1D(Plottable):
-    """
-    Theory plottable: line plot of x,y with confidence interval y.
-    """
-    def __init__(self, x, y, dy=None):
-        """
-        Draw lines specified in x[i],y[i] in the current color/symbol.
-        Confidence intervals in x are given by dx[i] or by (xlo[i],xhi[i])
-        if the limits are asymmetric.
-
-        The title is the name that will show up on the legend.
-        """
-        Plottable.__init__(self)
-        msg = "Theory1D is no longer supported, please use Data1D and change symbol.\n"
-        raise DeprecationWarning, msg
-
-class Fit1D(Plottable):
-    """
-    Fit plottable: composed of a data line plus a theory line.  This
-    is treated like a single object from the perspective of the graph,
-    except that it will have two legend entries, one for the data and
-    one for the theory.
-
-    The color of the data and theory will be shared.
-
-    """
-    def __init__(self, data=None, theory=None):
-        """
-        """
-        Plottable.__init__(self)
-        self.data = data
-        self.theory = theory
-
-    def render(self, plot, **kw):
-        """
-        """
-        self.data.render(plot, **kw)
-        self.theory.render(plot, **kw)
-
-    def changed(self):
-        """
-        """
-        return self.data.changed() or self.theory.changed()
-
-
-# ---------------------------------------------------------------
-class Text(Plottable):
-    """
-    """
-    def __init__(self, text=None, xpos=0.5, ypos=0.9, name='text'):
-        """
-        Draw the user-defined text in plotter
-        We can specify the position of text
-        """
-        Plottable.__init__(self)
-        self.name = name
-        self.text = text
-        self.xpos = xpos
-        self.ypos = ypos
-
-    def render(self, plot, **kw):
-        """
-        """
-        from matplotlib import transforms
-
-        xcoords = transforms.blended_transform_factory(plot.subplot.transAxes,
-                                                       plot.subplot.transAxes)
-        plot.subplot.text(self.xpos,
-                          self.ypos,
-                          self.text,
-                          label=self.name,
-                          transform=xcoords)
-
-    def setText(self, text):
-        """Set the text string."""
-        self.text = text
-
-    def getText(self, text):
-        """Get the text string."""
-        return self.text
-
-    def set_x(self, x):
-        """
-        Set the x position of the text
-        ACCEPTS: float
-        """
-        self.xpos = x
-
-    def set_y(self, y):
-        """
-        Set the y position of the text
-        ACCEPTS: float
-        """
-        self.ypos = y
-
-
-# ---------------------------------------------------------------
-class Chisq(Plottable):
-    """
-    Chisq plottable plots the chisq
-    """
-    def __init__(self, chisq=None):
-        """
-        Draw the chisq in plotter
-        We can specify the position of chisq
-        """
-        Plottable.__init__(self)
-        self.name = "chisq"
-        self._chisq = chisq
-        self.xpos = 0.5
-        self.ypos = 0.9
-
-    def render(self, plot, **kw):
-        """
-        """
-        if  self._chisq == None:
-            chisqTxt = r'$\chi^2=$'
-        else:
-            chisqTxt = r'$\chi^2=%g$' % (float(self._chisq))
-
-        from matplotlib import transforms
-
-        xcoords = transforms.blended_transform_factory(plot.subplot.transAxes,
-                                                      plot.subplot.transAxes)
-        plot.subplot.text(self.xpos,
-                          self.ypos,
-                          chisqTxt, label='chisq',
-                          transform=xcoords)
-
-    def setChisq(self, chisq):
-        """
-        Set the chisq value.
-        """
-        self._chisq = chisq
-
-
-######################################################
-
-def sample_graph():
-    import numpy as nx
-
-    # Construct a simple graph
-    if False:
-        x = nx.array([1, 2, 3, 4, 5, 6], 'd')
-        y = nx.array([4, 5, 6, 5, 4, 5], 'd')
-        dy = nx.array([0.2, 0.3, 0.1, 0.2, 0.9, 0.3])
-    else:
-        x = nx.linspace(0, 1., 10000)
-        y = nx.sin(2 * nx.pi * x * 2.8)
-        dy = nx.sqrt(100 * nx.abs(y)) / 100
-    data = Data1D(x, y, dy=dy)
-    data.xaxis('distance', 'm')
-    data.yaxis('time', 's')
-    graph = Graph()
-    graph.title('Walking Results')
-    graph.add(data)
-    graph.add(Theory1D(x, y, dy=dy))
-    return graph
-
-
-def demo_plotter(graph):
-    import wx
-    from pylab_plottables import Plotter
-    # from mplplotter import Plotter
-
-    # Make a frame to show it
-    app = wx.PySimpleApp()
-    frame = wx.Frame(None, -1, 'Plottables')
-    plotter = Plotter(frame)
-    frame.Show()
-
-    # render the graph to the pylab plotter
-    graph.render(plotter)
-
-    class GraphUpdate(object):
-        callnum = 0
-
-        def __init__(self, graph, plotter):
-            self.graph, self.plotter = graph, plotter
-
-        def __call__(self):
-            if self.graph.changed():
-                self.graph.render(self.plotter)
-                return True
-            return False
-
-        def onIdle(self, event):
-            self.callnum = self.callnum + 1
-            if self.__call__():
-                pass  # event.RequestMore()
-    update = GraphUpdate(graph, plotter)
-    frame.Bind(wx.EVT_IDLE, update.onIdle)
-    app.MainLoop()
Index: c/sas/plottools/toolbar.py
===================================================================
--- src/sas/plottools/toolbar.py	(revision 2d88fc4a6cc278c2ea7ac05c75e83d324432f287)
+++ 	(revision )
@@ -1,172 +1,0 @@
-"""
-    This module overwrites matplotlib toolbar
-"""
-import wx
-from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg
-from matplotlib.backends.backend_wx import _load_bitmap
-import logging
-
-# Event binding code changed after version 2.5
-if wx.VERSION_STRING >= '2.5':
-    def bind(actor, event, action, **kw):
-        actor.Bind(event, action, **kw)
-else:
-    def bind(actor, event, action, id=None):
-        if id is not None:
-            event(actor, id, action)
-        else:
-            event(actor, action)
-
-class NavigationToolBar(NavigationToolbar2WxAgg):
-    _NTB2_HOME = wx.NewId()
-    _NTB2_BACK = wx.NewId()
-    _NTB2_FORWARD = wx.NewId()
-    _NTB2_PAN = wx.NewId()
-    _NTB2_ZOOM = wx.NewId()
-    _NTB2_SAVE = wx.NewId()
-    _NTB2_PRINT = wx.NewId()
-    _NTB2_RESET = wx.NewId()
-    _NTB2_COPY = wx.NewId()
-    """
-    Overwrite matplotlib toolbar
-    """
-    def __init__(self, canvas, parent=None):
-        NavigationToolbar2WxAgg.__init__(self, canvas)
-
-    # CRUFT: mpl 1.1 uses save rather than save_figure
-    try: save_figure = NavigationToolbar2WxAgg.save
-    except AttributeError: pass
-    
-    def _init_toolbar(self):
-        self._parent = self.canvas.GetParent()
-
-        # for mpl 1.2+ compatibility
-        self.wx_ids = {}
-        self.wx_ids['Back'] = self._NTB2_BACK
-        self.wx_ids['Forward'] = self._NTB2_FORWARD
-        self.wx_ids['Pan'] = self._NTB2_PAN
-        self.wx_ids['Zoom'] = self._NTB2_ZOOM
-
-        self.SetToolBitmapSize(wx.Size(24, 24))
-
-        context_tip = 'Graph Menu: \n'
-        context_tip += '    For more menu options, \n'
-        context_tip += '    right-click the data symbols.'
-        context = wx.ArtProvider.GetBitmap(wx.ART_LIST_VIEW, wx.ART_TOOLBAR)
-        self.AddSimpleTool(self._NTB2_HOME, context, context_tip, context_tip)
-
-        self.InsertSeparator(1)
-
-        self.AddSimpleTool(self._NTB2_BACK, _load_bitmap('back.png'),
-                           'Back', 'Back navigation view')
-        self.AddSimpleTool(self._NTB2_FORWARD, _load_bitmap('forward.png'),
-                           'Forward', 'Forward navigation view')
-        # todo: get new bitmap
-        self.AddCheckTool(self._NTB2_PAN, _load_bitmap('move.png'),
-                          shortHelp='Pan',
-                          longHelp='Pan with left, zoom with right')
-        self.AddCheckTool(self._NTB2_ZOOM, _load_bitmap('zoom_to_rect.png'),
-                          shortHelp='Zoom', longHelp='Zoom to rectangle')
-
-        self.AddSeparator()
-        self.AddSimpleTool(self._NTB2_SAVE, _load_bitmap('filesave.png'),
-                           'Save', 'Save plot contents to file')
-
-        print_bmp = wx.ArtProvider.GetBitmap(wx.ART_PRINT, wx.ART_TOOLBAR)
-        self.AddSimpleTool(self._NTB2_PRINT, print_bmp, 'Print', 'Print plot')
-
-        reset_bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_HOME, wx.ART_TOOLBAR)
-        self.AddSimpleTool(self._NTB2_RESET, reset_bmp, 'Reset', 'Reset graph range')
-
-        bind(self, wx.EVT_TOOL, self.context_menu, id=self._NTB2_HOME)
-        bind(self, wx.EVT_TOOL, self.forward, id=self._NTB2_FORWARD)
-        bind(self, wx.EVT_TOOL, self.back, id=self._NTB2_BACK)
-        bind(self, wx.EVT_TOOL, self.zoom, id=self._NTB2_ZOOM)
-        bind(self, wx.EVT_TOOL, self.pan, id=self._NTB2_PAN)
-        bind(self, wx.EVT_TOOL, self.save_figure, id=self._NTB2_SAVE)
-        bind(self, wx.EVT_TOOL, self.print_figure, id=self._NTB2_PRINT)
-        bind(self, wx.EVT_TOOL, self.home, id=self._NTB2_RESET)
-
-        self.Realize()
-
-    def on_menu(self, event):
-        try:
-            self._parent.onToolContextMenu(event=event)
-        except:
-            logging.error("Plot toolbar could not show menu")
-
-    def context_menu(self, event):
-        """
-        Default context menu for a plot panel
-
-        """
-        # Slicer plot popup menu
-        popup = wx.Menu()
-        popup.Append(self._NTB2_SAVE, '&Save image', 'Save image as PNG')
-        wx.EVT_MENU(self, self._NTB2_SAVE, self.save_figure)
-
-        popup.Append(self._NTB2_PRINT, '&Print image', 'Print image ')
-        wx.EVT_MENU(self, self._NTB2_PRINT, self.print_figure)
-
-        popup.Append(self._NTB2_COPY, '&Copy to Clipboard', 'Copy image to the clipboard')
-        wx.EVT_MENU(self, self._NTB2_COPY, self.copy_figure)
-
-        # Show the popup menu relative to the location of the toolbar
-        self.PopupMenu(popup, (0,0))
-
-
-    def print_figure(self, event):
-        try:
-            _printer = wx.Printer()
-            _printer.Print(self.canvas, PlotPrintout(self.canvas), True)
-        except:
-            import traceback
-            logging.error(traceback.format_exc())
-
-    def copy_figure(self, event):
-        copy_image_to_clipboard(self.canvas)
-
-class PlotPrintout(wx.Printout):
-    """
-    Create the wx.Printout object for matplotlib figure from the PlotPanel.
-    Provides the required OnPrintPage and HasPage overrides.  Other methods
-    may be added/overriden in the future.
-    :TODO: this needs LOTS of TLC .. but fixes immediate problem
-    """
-    def __init__(self, canvas):
-        """
-        Initialize wx.Printout and get passed figure object
-        """
-        wx.Printout.__init__(self)
-        self.canvas = canvas
-
-    def OnPrintPage(self, page):
-        """
-        Most rudimentry OnPrintPage overide.  instatiates a dc object, gets
-        its size, gets the size of the figure object, scales it to the dc
-        canvas size keeping the aspect ratio intact, then prints as bitmap
-        """
-        _dc = self.GetDC()
-        (_dcX, _dcY) = _dc.GetSizeTuple()
-        (_bmpX,_bmpY) = self.canvas.GetSize()
-        _scale = min(_dcX/_bmpX, _dcY/_bmpY)
-        _dc.SetUserScale(_scale, _scale)
-        _dc.DrawBitmap(self.canvas.bitmap, 0, 0, False,)
-        return True
-
-    def GetPageInfo(self):
-        """
-        just sets the page to 1 - no flexibility for now
-        """
-        return (1, 1, 1, 1)
-
-
-def copy_image_to_clipboard(canvas):
-    bmp = wx.BitmapDataObject()
-    bmp.SetBitmap(canvas.bitmap)
-
-    wx.TheClipboard.Open()
-    wx.TheClipboard.SetData(bmp)
-    wx.TheClipboard.Close()
-
-
Index: c/sas/plottools/transform.py
===================================================================
--- src/sas/plottools/transform.py	(revision 3477478de6317fb51d4dd0dbaf9ab2a1236faf28)
+++ 	(revision )
@@ -1,413 +1,0 @@
-import math
-
-
-def toX(x, y=None):
-    """
-    This function is used to load value on Plottable.View
-
-    :param x: Float value
-
-    :return: x
-
-    """
-    return x
-
-
-def toX_pos(x, y=None):
-    """
-    This function is used to load value on Plottable.View
-
-    :param x: Float value
-
-    :return: x
-
-    """
-    if not x > 0:
-        raise ValueError, "Transformation only accepts positive values."
-    else:
-        return x
-
-
-def toX2(x, y=None):
-    """
-    This function is used to load value on Plottable.View
-
-    Calculate x^(2)
-
-    :param x: float value
-
-    """
-    return x * x
-
-
-def fromX2(x, y=None):
-    """
-    This function is used to load value on Plottable.View
-    Calculate square root of x
-
-    :param x: float value
-
-    """
-    if not x >= 0:
-        raise ValueError, "square root of a negative value "
-    else:
-        return math.sqrt(x)
-
-
-def toX4(x, y=None):
-    """
-    This function is used to load value on Plottable.View
-
-    Calculate x^(4)
-
-    :param x: float value
-
-    """
-    return x * x * x * x
-
-
-def fromX4(x, y=None):
-    """
-    This function is used to load value on Plottable.View
-    Calculate square root of x
-
-    :param x: float value
-
-    """
-    if not x >= 0:
-        raise ValueError, "double square root of a negative value "
-    else:
-        return math.sqrt(math.sqrt(x))
-
-
-def toLogX(x, y=None):
-    """
-    This function is used to load value on Plottable.View
-    calculate log x
-
-    :param x: float value
-
-    """
-    if not x > 0:
-        raise ValueError, "Log(x)of a negative value "
-    else:
-        return math.log(x)
-
-def toOneOverX(x, y=None):
-    """
-    """
-    if x != 0:
-        return 1 / x
-    else:
-        raise ValueError, "cannot divide by zero"
-
-
-def toOneOverSqrtX(y, x=None):
-    """
-    """
-    if y > 0:
-        return 1 / math.sqrt(y)
-    else:
-        raise ValueError, "transform.toOneOverSqrtX: cannot be computed"
-
-
-def toLogYX2(y, x):
-    """
-    """
-    if (y * (x ** 2)) > 0:
-        return math.log(y * (x ** 2))
-    else:
-        raise ValueError, "transform.toLogYX2: cannot be computed"
-
-
-def toLogYX4(y, x):
-    """
-    """
-    if (math.pow(x, 4) * y) > 0:
-        return math.log(math.pow(x, 4) * y)
-    else:
-        raise ValueError, "transform.toLogYX4: input error"
-
-
-def toYX4(y, x):
-    """
-    """
-    return math.pow(x, 4) * y
-
-def toYX2(y, x):
-    """
-    """
-    return math.pow(x, 2) * y
-
-def toLogXY(y, x):
-    """
-    This function is used to load value on Plottable.View
-    calculate log x
-
-    :param x: float value
-
-    """
-    if not (x * y) > 0:
-        raise ValueError, "Log(X*Y)of a negative value "
-    else:
-        return math.log(x * y)
-
-
-def errToX(x, y=None, dx=None, dy=None):
-    """
-    calculate error of x**2
-
-    :param x: float value
-    :param dx: float value
-
-    """
-    if dx == None:
-        dx = 0
-    return dx
-
-
-def errToX_pos(x, y=None, dx=None, dy=None):
-    """
-    calculate error of x**2
-
-    :param x: float value
-    :param dx: float value
-
-    """
-    if dx == None:
-        dx = 0
-    return dx
-
-
-def errToX2(x, y=None, dx=None, dy=None):
-    """
-    calculate error of x**2
-
-    :param x: float value
-    :param dx: float value
-
-    """
-    if  dx != None:
-        err = 2 * x * dx
-        return math.fabs(err)
-    else:
-        return 0.0
-
-
-def errFromX2(x, y=None, dx=None, dy=None):
-    """
-    calculate error of sqrt(x)
-
-    :param x: float value
-    :param dx: float value
-
-    """
-    if x > 0:
-        if dx != None:
-            err = dx / (2 * math.sqrt(x))
-        else:
-            err = 0
-        return math.fabs(err)
-    else:
-        msg = "transform.errFromX2: can't compute error of negative x"
-        raise ValueError, msg
-
-
-def errToX4(x, y=None, dx=None, dy=None):
-    """
-    calculate error of x**4
-
-    :param x: float value
-    :param dx: float value
-
-    """
-    if dx != None:
-        err = 4 * math.pow(x, 3) * dx
-        return math.fabs(err)
-    else:
-        return 0.0
-
-
-def errFromX4(x, y=None, dx=None, dy=None):
-    """
-    calculate error of x^1/4
-
-    :param x: float value
-    :param dx: float value
-
-    """
-    if x > 0:
-        if dx != None:
-            err = dx / (4 * math.pow(x, 3 / 4))
-        else:
-            err = 0
-        return math.fabs(err)
-    else:
-        msg = "transform.errFromX4: can't compute error of negative x"
-        raise ValueError, msg
-
-
-def errToLog10X(x, y=None, dx=None, dy=None):
-    """
-    calculate error of Log(x)
-
-    :param x: float value
-    :param dx: float value
-
-    """
-    if dx == None:
-        dx = 0
-
-    # Check that the point on the graph is positive
-    # within errors
-    if not (x - dx) > 0:
-        msg = "Transformation does not accept"
-        msg += " point that are consistent with zero."
-        raise ValueError, msg
-    if x != 0:
-        dx = dx / (x * math.log(10))
-    else:
-        raise ValueError, "errToLogX: divide by zero"
-    return dx
-
-
-def errToLogX(x, y=None, dx=None, dy=None):
-    """
-    calculate error of Log(x)
-
-    :param x: float value
-    :param dx: float value
-
-    """
-    if dx == None:
-        dx = 0
-
-    # Check that the x point on the graph is zero
-    if x != 0:
-        dx = dx / x
-    else:
-        raise ValueError, "errToLogX: divide by zero"
-    return dx
-
-
-def errToYX2(x, y, dx=None, dy=None):
-    """
-    """
-    if dx == None:
-        dx = 0
-    if dy == None:
-        dy = 0
-    err = math.sqrt((2 * x * y * dx) ** 2 + ((x ** 2) * dy) ** 2)
-    return err
-
-
-def errToLogXY(x, y, dx=None, dy=None):
-    """
-    calculate error of Log(xy)
-
-    """
-    # Check that the point on the graph is positive
-    # within errors
-    if not (x - dx) > 0 or not (y - dy) > 0:
-        msg = "Transformation does not accept point "
-        msg += " that are consistent with zero."
-        raise ValueError, msg
-    if x != 0 and y != 0:
-        if dx == None:
-            dx = 0
-        if dy == None:
-            dy = 0
-        err = (dx / x) ** 2 + (dy / y) ** 2
-    else:
-        raise ValueError, "cannot compute this error"
-
-    return math.sqrt(math.fabs(err))
-
-
-def errToLogYX2(x, y, dx=None, dy=None):
-    """
-    calculate error of Log(yx**2)
-
-    """
-    # Check that the point on the graph is positive
-    # within errors
-    if not (x - dx) > 0 or not (y - dy) > 0:
-        msg = "Transformation does not accept point"
-        msg += " that are consistent with zero."
-        raise ValueError, msg
-    if x > 0 and y > 0:
-        if dx == None:
-            dx = 0
-        if dy == None:
-            dy = 0
-        err = (2.0 * dx / x) ** 2 + (dy / y) ** 2
-    else:
-        raise ValueError, "cannot compute this error"
-    return math.sqrt(math.fabs(err))
-
-
-def errOneOverX(x, y=None, dx=None, dy=None):
-    """
-    calculate error on 1/x
-
-    """
-    if x != 0:
-        if dx == None:
-            dx = 0
-        err = dx / x ** 2
-    else:
-        raise ValueError, "Cannot compute this error"
-    return math.fabs(err)
-
-
-def errOneOverSqrtX(x, y=None, dx=None, dy=None):
-    """
-    Calculate error on 1/sqrt(x)
-
-    """
-    if x > 0:
-        if dx == None:
-            dx = 0
-        err = -1 / 2 * math.pow(x, -3.0 / 2.0) * dx
-    else:
-        raise ValueError, "Cannot compute this error"
-    return math.fabs(err)
-
-
-def errToLogYX4(x, y=None, dx=None, dy=None):
-    """
-    error for ln(y*x^(4))
-
-    :param x: float value
-
-    """
-    # Check that the point on the graph is positive
-    # within errors
-    if (not (x - dx) > 0) or (not (y - dy) > 0):
-        msg = "Transformation does not accept point "
-        msg += " that are consistent with zero."
-        raise ValueError, msg
-    if dx == None:
-        dx = 0
-    if dy == None:
-        dy = 0
-    err = math.sqrt((4.0 * dx / x) ** 2 + (dy / y) ** 2)
-    return err
-
-
-def errToYX4(x, y=None, dx=None, dy=None):
-    """
-    error for (y*x^(4))
-
-    :param x: float value
-
-    """
-    # Check that the point on the graph is positive
-    # within errors
-
-    if dx == None:
-        dx = 0
-    if dy == None:
-        dy = 0
-    err = math.sqrt((dy * pow(x, 4)) ** 2 + (4 * y * dx * math.pow(x, 3)) ** 2)
-    return err
Index: src/sas/sascalc/calculator/sas_gen.py
===================================================================
--- src/sas/sascalc/calculator/sas_gen.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/calculator/sas_gen.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -1040,5 +1040,5 @@
         Test code
     """
-    from sas.plottools.arrow3d import Arrow3D
+    from sas.sasgui.plottools.arrow3d import Arrow3D
     current_dir = os.path.abspath(os.path.curdir)
     print current_dir
Index: src/sas/sasgui/plottools/BaseInteractor.py
===================================================================
--- src/sas/sasgui/plottools/BaseInteractor.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/BaseInteractor.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,179 @@
+interface_color = 'black'
+disable_color = 'gray'
+active_color = 'red'
+rho_color = 'black'
+mu_color = 'green'
+P_color = 'blue'
+theta_color = 'orange'
+profile_colors = [rho_color, mu_color, P_color, theta_color]
+
+
+class _BaseInteractor(object):
+    """
+    Share some functions between the interface interactor and various layer
+    interactors.
+
+    Individual interactors need the following functions:
+
+        save(ev)  - save the current state for later restore
+        restore() - restore the old state
+        move(x,y,ev) - move the interactor to position x,y
+        moveend(ev) - end the drag event
+        update() - draw the interactors
+
+    The following are provided by the base class:
+
+        connect_markers(markers) - register callbacks for all markers
+        clear_markers() - remove all items in self.markers
+        onHilite(ev) - enter/leave event processing
+        onLeave(ev) - enter/leave event processing
+        onClick(ev) - mouse click: calls save()
+        onRelease(ev) - mouse click ends: calls moveend()
+        onDrag(ev) - mouse move: calls move() or restore()
+        onKey(ev) - keyboard move: calls move() or restore()
+
+    Interactor attributes:
+
+        base  - model we are operating on
+        axes  - axes holding the interactor
+        color - color of the interactor in non-active state
+        markers - list of handles for the interactor
+
+    """
+    def __init__(self, base, axes, color='black'):
+        """
+        """
+        self.base = base
+        self.axes = axes
+        self.color = color
+        self.markers = []
+
+    def clear_markers(self):
+        '''
+        Clear old markers and interfaces.
+        '''
+        for h in self.markers:
+            h.remove()
+        if self.markers:
+            self.base.connect.clear(*self.markers)
+        self.markers = []
+
+    def save(self, ev):
+        """
+        """
+        pass
+
+    def restore(self, ev):
+        """
+        """
+        pass
+
+    def move(self, x, y, ev):
+        """
+        """
+        pass
+
+    def moveend(self, ev):
+        """
+        """
+        pass
+
+    def connect_markers(self, markers):
+        """
+        Connect markers to callbacks
+        """
+        for h in markers:
+            connect = self.base.connect
+            connect('enter', h, self.onHilite)
+            connect('leave', h, self.onLeave)
+            connect('click', h, self.onClick)
+            connect('release', h, self.onRelease)
+            connect('drag', h, self.onDrag)
+            connect('key', h, self.onKey)
+
+    def onHilite(self, ev):
+        """
+        Hilite the artist reporting the event, indicating that it is
+        ready to receive a click.
+        """
+        ev.artist.set_color(active_color)
+        self.base.draw()
+        return True
+
+    def onLeave(self, ev):
+        """
+        Restore the artist to the original colour when the cursor leaves.
+        """
+        ev.artist.set_color(self.color)
+        self.base.draw()
+        return True
+
+    def onClick(self, ev):
+        """
+        Prepare to move the artist.  Calls save() to preserve the state for
+        later restore().
+        """
+        self.clickx, self.clicky = ev.xdata, ev.ydata
+        self.save(ev)
+        return True
+
+    def onRelease(self, ev):
+        """
+        """
+        self.moveend(ev)
+        return True
+
+    def onDrag(self, ev):
+        """
+        Move the artist.  Calls move() to update the state, or restore() if
+        the mouse leaves the window.
+        """
+        inside, _ = self.axes.contains(ev)
+        if inside:
+            self.clickx, self.clicky = ev.xdata, ev.ydata
+            self.move(ev.xdata, ev.ydata, ev)
+        else:
+            self.restore()
+        self.base.update()
+        return True
+
+    def onKey(self, ev):
+        """
+        Respond to keyboard events.  Arrow keys move the widget.  Escape
+        restores it to the position before the last click.
+
+        Calls move() to update the state.  Calls restore() on escape.
+        """
+        if ev.key == 'escape':
+            self.restore()
+        elif ev.key in ['up', 'down', 'right', 'left']:
+            dx, dy = self.dpixel(self.clickx, self.clicky, nudge=ev.control)
+            if ev.key == 'up':
+                self.clicky += dy
+            elif ev.key == 'down':
+                self.clicky -= dy
+            elif ev.key == 'right':
+                self.clickx += dx
+            else:
+                self.clickx -= dx
+            self.move(self.clickx, self.clicky, ev)
+        else:
+            return False
+        self.base.update()
+        return True
+
+    def dpixel(self, x, y, nudge=False):
+        """
+        Return the step size in data coordinates for a small
+        step in screen coordinates.  If nudge is False (default)
+        the step size is one pixel.  If nudge is True, the step
+        size is 0.2 pixels.
+        """
+        ax = self.axes
+        px, py = ax.transData.inverse_xy_tup((x, y))
+        if nudge:
+            nx, ny = ax.transData.xy_tup((px + 0.2, py + 0.2))
+        else:
+            nx, ny = ax.transData.xy_tup((px + 1., py + 1.))
+        dx, dy = nx - x, ny - y
+        return dx, dy
Index: src/sas/sasgui/plottools/LabelDialog.py
===================================================================
--- src/sas/sasgui/plottools/LabelDialog.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/LabelDialog.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,43 @@
+import wx
+import sys
+if sys.platform.count("win32") > 0:
+    FONT_VARIANT = 0
+    PNL_WIDTH = 270
+else:
+    FONT_VARIANT = 1
+    PNL_WIDTH = 300
+
+class LabelDialog(wx.Dialog):
+    def __init__(self, parent, id, title, label):
+        wx.Dialog.__init__(self, parent, id, title, size=(PNL_WIDTH, 150))
+
+        # Font
+        self.SetWindowVariant(variant=FONT_VARIANT)
+        mainbox = wx.BoxSizer(wx.VERTICAL)
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        textbox = wx.BoxSizer(wx.HORIZONTAL)
+
+        text1 = "Enter a new title/label:"
+        msg = wx.StaticText(self, -1, text1, (30, 15), style=wx.ALIGN_LEFT)
+        msg.SetLabel(text1)
+        self.label_ctrl = wx.TextCtrl(self, -1, '', (200, 30))
+        self.label_ctrl.SetValue(str(label))
+        textbox.Add(self.label_ctrl, flag=wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE,
+                    border=10, proportion=2)
+        vbox.Add(msg, flag=wx.ALL, border=10, proportion=1)
+        vbox.Add(textbox, flag=wx.EXPAND | wx.TOP | wx.BOTTOM | wx.ADJUST_MINSIZE, border=5)
+        hbox = wx.BoxSizer(wx.HORIZONTAL)
+        ok_button = wx.Button(self, wx.ID_OK, 'OK', size=(70, 25))
+        close_button = wx.Button(self, wx.ID_CANCEL, 'Cancel', size=(70, 25))
+
+        hbox.Add(ok_button, wx.LEFT, 10)
+        hbox.Add((20, 20))
+        hbox.Add(close_button, wx.LEFT, 10)
+
+        mainbox.Add(vbox, flag=wx.LEFT, border=5)
+        mainbox.Add(wx.StaticLine(self), 0, wx.ALL | wx.EXPAND, 5)
+        mainbox.Add(hbox, flag=wx.CENTER, border=20)
+        self.SetSizer(mainbox)
+
+    def getText(self):
+        return self.label_ctrl.GetValue()
Index: src/sas/sasgui/plottools/LineModel.py
===================================================================
--- src/sas/sasgui/plottools/LineModel.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/LineModel.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+"""
+Provide Line function (y= A + Bx)
+"""
+
+import math
+
+class LineModel(object):
+    """
+    Class that evaluates a linear model.
+
+    f(x) = A + Bx
+
+    List of default parameters:
+    A = 0.0
+    B = 0.0
+    """
+
+    def __init__(self):
+        """ Initialization """
+        # # Name of the model
+        self.name = "LineModel"
+
+        # # Define parameters
+        self.params = {}
+        self.params['A'] = 1.0
+        self.params['B'] = 1.0
+
+        # # Parameter details [units, min, max]
+        self.details = {}
+        self.details['A'] = ['', None, None]
+        self.details['B'] = ['', None, None]
+
+    def getParam(self, name):
+        """
+            Return parameter value
+        """
+        return self.params[name.upper()]
+
+    def setParam(self, name, value):
+        """
+            Set parameter value
+        """
+        self.params[name.upper()] = value
+
+    def _line(self, x):
+        """
+        Evaluate the function
+
+        :param x: x-value
+
+        :return: function value
+
+        """
+        return  self.params['A'] + (x * self.params['B'])
+
+    def run(self, x=0.0):
+        """
+        Evaluate the model
+
+        :param x: simple value
+
+        :return: (Line value)
+        """
+        if x.__class__.__name__ == 'list':
+            return self._line(x[0] * math.cos(x[1])) * \
+                                self._line(x[0] * math.sin(x[1]))
+        elif x.__class__.__name__ == 'tuple':
+            msg = "Tuples are not allowed as input to BaseComponent models"
+            raise ValueError, msg
+        else:
+            return self._line(x)
+
+    def runXY(self, x=0.0):
+        """
+        Evaluate the model
+
+        :param x: simple value
+
+        :return: Line value
+
+        """
+        if x.__class__.__name__ == 'list':
+            return self._line(x[0]) * self._line(x[1])
+        elif x.__class__.__name__ == 'tuple':
+            msg = "Tuples are not allowed as input to BaseComponent models"
+            raise ValueError, msg
+        else:
+            return self._line(x)
Index: src/sas/sasgui/plottools/PlotPanel.py
===================================================================
--- src/sas/sasgui/plottools/PlotPanel.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/PlotPanel.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,2025 @@
+"""
+    Plot panel.
+"""
+import logging
+import traceback
+import wx
+# Try a normal import first
+# If it fails, try specifying a version
+import matplotlib
+matplotlib.interactive(False)
+#Use the WxAgg back end. The Wx one takes too long to render
+matplotlib.use('WXAgg')
+from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
+from matplotlib.figure import Figure
+import os
+import transform
+from plottables import Data1D
+#TODO: make the plottables interactive
+from binder import BindArtist
+from matplotlib.font_manager import FontProperties
+DEBUG = False
+
+from plottables import Graph
+from TextDialog import TextDialog
+from LabelDialog import LabelDialog
+import operator
+
+import math
+import pylab
+DEFAULT_CMAP = pylab.cm.jet
+import copy
+import numpy
+
+from sas.guiframe.events import StatusEvent
+from .toolbar import NavigationToolBar, PlotPrintout, bind
+
+def show_tree(obj, d=0):
+    """Handy function for displaying a tree of graph objects"""
+    print "%s%s" % ("-"*d, obj.__class__.__name__)
+    if 'get_children' in dir(obj):
+        for a in obj.get_children(): show_tree(a, d + 1)
+
+from convert_units import convert_unit
+
+
+def _rescale(lo, hi, step, pt=None, bal=None, scale='linear'):
+    """
+        Rescale (lo,hi) by step, returning the new (lo,hi)
+        The scaling is centered on pt, with positive values of step
+        driving lo/hi away from pt and negative values pulling them in.
+        If bal is given instead of point, it is already in [0,1] coordinates.
+
+        This is a helper function for step-based zooming.
+
+    """
+    # Convert values into the correct scale for a linear transformation
+    # TODO: use proper scale transformers
+    loprev = lo
+    hiprev = hi
+    if scale == 'log':
+        assert lo > 0
+        if lo > 0:
+            lo = math.log10(lo)
+        if hi > 0:
+            hi = math.log10(hi)
+        if pt is not None:
+            pt = math.log10(pt)
+
+    # Compute delta from axis range * %, or 1-% if persent is negative
+    if step > 0:
+        delta = float(hi - lo) * step / 100
+    else:
+        delta = float(hi - lo) * step / (100 - step)
+
+    # Add scale factor proportionally to the lo and hi values,
+    # preserving the
+    # point under the mouse
+    if bal is None:
+        bal = float(pt - lo) / (hi - lo)
+    lo = lo - (bal * delta)
+    hi = hi + (1 - bal) * delta
+
+    # Convert transformed values back to the original scale
+    if scale == 'log':
+        if (lo <= -250) or (hi >= 250):
+            lo = loprev
+            hi = hiprev
+        else:
+            lo, hi = math.pow(10., lo), math.pow(10., hi)
+    return (lo, hi)
+
+
+class PlotPanel(wx.Panel):
+    """
+    The PlotPanel has a Figure and a Canvas. OnSize events simply set a
+    flag, and the actually redrawing of the
+    figure is triggered by an Idle event.
+    """
+    def __init__(self, parent, id=-1, xtransform=None,
+                 ytransform=None, scale='log_{10}',
+                 color=None, dpi=None, **kwargs):
+        """
+        """
+        wx.Panel.__init__(self, parent, id=id, **kwargs)
+        self.parent = parent
+        if hasattr(parent, "parent"):
+            self.parent = self.parent.parent
+        self.dimension = 1
+        self.gotLegend = 0  # to begin, legend is not picked.
+        self.legend_pos_loc = None
+        self.legend = None
+        self.line_collections_list = []
+        self.figure = Figure(None, dpi, linewidth=2.0)
+        self.color = '#b3b3b3'
+        from canvas import FigureCanvas
+        self.canvas = FigureCanvas(self, -1, self.figure)
+        self.SetColor(color)
+        self._resizeflag = True
+        self._SetSize()
+        self.subplot = self.figure.add_subplot(111)
+        self.figure.subplots_adjust(left=0.2, bottom=.2)
+        self.yscale = 'linear'
+        self.xscale = 'linear'
+        self.sizer = wx.BoxSizer(wx.VERTICAL)
+        self.sizer.Add(self.canvas, 1, wx.EXPAND)
+        #add toolbar
+        self.enable_toolbar = True
+        self.toolbar = None
+        self.add_toolbar()
+        self.SetSizer(self.sizer)
+
+        # Graph object to manage the plottables
+        self.graph = Graph()
+
+        #Boolean value to keep track of whether current legend is
+        #visible or not
+        self.legend_on = True
+        self.grid_on = False
+        #Location of legend, default is 0 or 'best'
+        self.legendLoc = 0
+        self.position = None
+        self._loc_labels = self.get_loc_label()
+
+        self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
+
+        # Define some constants
+        self.colorlist = ['b', 'g', 'r', 'c', 'm', 'y', 'k']
+        self.symbollist = ['o', 'x', '^', 'v', '<', '>', '+',
+                           's', 'd', 'D', 'h', 'H', 'p', '-']
+
+        #List of texts currently on the plot
+        self.textList = []
+        #User scale
+        if xtransform != None:
+            self.xLabel = xtransform
+        else:
+            self.xLabel = "log10(x)"
+        if ytransform != None:
+            self.yLabel = ytransform
+        else:
+            self.yLabel = "log10(y)"
+        self.viewModel = "--"
+        # keep track if the previous transformation of x
+        # and y in Property dialog
+        self.prevXtrans = "log10(x)"
+        self.prevYtrans = "log10(y)"
+        self.scroll_id = self.canvas.mpl_connect('scroll_event', self.onWheel)
+        #taking care of dragging
+        self.motion_id = self.canvas.mpl_connect('motion_notify_event',
+                                                 self.onMouseMotion)
+        self.press_id = self.canvas.mpl_connect('button_press_event',
+                                                self.onLeftDown)
+        self.pick_id = self.canvas.mpl_connect('pick_event', self.onPick)
+        self.release_id = self.canvas.mpl_connect('button_release_event',
+                                                  self.onLeftUp)
+
+        wx.EVT_RIGHT_DOWN(self, self.onLeftDown)
+        # to turn axis off whenn resizing the panel
+        self.resizing = False
+
+        self.leftdown = False
+        self.leftup = False
+        self.mousemotion = False
+        self.axes = [self.subplot]
+        ## Fit dialog
+        self._fit_dialog = None
+        # Interactor
+        self.connect = BindArtist(self.subplot.figure)
+        #self.selected_plottable = None
+
+        # new data for the fit
+        self.fit_result = Data1D(x=[], y=[], dy=None)
+        self.fit_result.symbol = 13
+        #self.fit_result = Data1D(x=[], y=[],dx=None, dy=None)
+        self.fit_result.name = "Fit"
+        # For fit Dialog initial display
+        self.xmin = 0.0
+        self.xmax = 0.0
+        self.xminView = 0.0
+        self.xmaxView = 0.0
+        self._scale_xlo = None
+        self._scale_xhi = None
+        self._scale_ylo = None
+        self._scale_yhi = None
+        self.Avalue = None
+        self.Bvalue = None
+        self.ErrAvalue = None
+        self.ErrBvalue = None
+        self.Chivalue = None
+
+        # for 2D scale
+        if scale != 'linear':
+            scale = 'log_{10}'
+        self.scale = scale
+        self.data = None
+        self.qx_data = None
+        self.qy_data = None
+        self.xmin_2D = None
+        self.xmax_2D = None
+        self.ymin_2D = None
+        self.ymax_2D = None
+        ## store reference to the current plotted vmin and vmax of plotted image
+        ##z range in linear scale
+        self.zmin_2D = None
+        self.zmax_2D = None
+
+        #index array
+        self.index_x = None
+        self.index_y = None
+
+        #number of bins
+        self.x_bins = None
+        self.y_bins = None
+
+        ## default color map
+        self.cmap = DEFAULT_CMAP
+
+        # Dragging info
+        self.begDrag = False
+        self.xInit = None
+        self.yInit = None
+        self.xFinal = None
+        self.yFinal = None
+
+        #axes properties
+        self.xaxis_font = None
+        self.xaxis_label = None
+        self.xaxis_unit = None
+        self.xaxis_color = 'black'
+        self.xaxis_tick = None
+        self.yaxis_font = None
+        self.yaxis_label = None
+        self.yaxis_unit = None
+        self.yaxis_color = 'black'
+        self.yaxis_tick = None
+
+        # check if zoomed.
+        self.is_zoomed = False
+        # Plottables
+        self.plots = {}
+
+        # Default locations
+        self._default_save_location = os.getcwd()
+        # let canvas know about axes
+        self.canvas.set_panel(self)
+        self.ly = None
+        self.q_ctrl = None
+        #Bind focus to change the border color
+        self.canvas.Bind(wx.EVT_SET_FOCUS, self.on_set_focus)
+        self.canvas.Bind(wx.EVT_KILL_FOCUS, self.on_kill_focus)
+
+    def _SetInitialSize(self,):
+        """
+        """
+        pixels = self.parent.GetClientSize()
+        self.canvas.SetSize(pixels)
+        self.figure.set_size_inches(pixels[0] / self.figure.get_dpi(),
+                                    pixels[1] / self.figure.get_dpi(), forward=True)
+
+    def On_Paint(self, event):
+        """
+        """
+        self.canvas.SetBackgroundColour(self.color)
+
+    def on_set_focus(self, event):
+        """
+        Send to the parenet the current panel on focus
+        """
+        # light blue
+        self.color = '#0099f7'
+        self.figure.set_edgecolor(self.color)
+        if self.parent and self.window_caption:
+            self.parent.send_focus_to_datapanel(self.window_caption)
+        self.draw()
+
+    def on_kill_focus(self, event):
+        """
+        Reset the panel color
+        """
+        # light grey
+        self.color = '#b3b3b3'
+        self.figure.set_edgecolor(self.color)
+        self.draw()
+
+    def set_resizing(self, resizing=False):
+        """
+        Set the resizing (True/False)
+        """
+        pass  # Not implemented
+
+    def schedule_full_draw(self, func='append'):
+        """
+        Put self in schedule to full redraw list
+        """
+        pass  # Not implemented
+
+    def add_toolbar(self):
+        """
+        add toolbar
+        """
+        self.enable_toolbar = True
+        self.toolbar = NavigationToolBar(parent=self, canvas=self.canvas)
+        bind(self.toolbar, wx.EVT_TOOL, self.onResetGraph, id=self.toolbar._NTB2_RESET)
+        bind(self.toolbar, wx.EVT_TOOL, self.onContextMenu, id=self.toolbar._NTB2_HOME)
+        self.toolbar.Realize()
+        ## The 'SetToolBar()' is not working on MAC: JHC
+        #if IS_MAC:
+        # Mac platform (OSX 10.3, MacPython) does not seem to cope with
+        # having a toolbar in a sizer. This work-around gets the buttons
+        # back, but at the expense of having the toolbar at the top
+        #self.SetToolBar(self.toolbar)
+        #else:
+        # On Windows platform, default window size is incorrect, so set
+        # toolbar width to figure width.
+        tw, th = self.toolbar.GetSizeTuple()
+        fw, fh = self.canvas.GetSizeTuple()
+        # By adding toolbar in sizer, we are able to put it at the bottom
+        # of the frame - so appearance is closer to GTK version.
+        # As noted above, doesn't work for Mac.
+        self.toolbar.SetSize(wx.Size(fw, th))
+        self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
+
+        # update the axes menu on the toolbar
+        self.toolbar.update()
+
+    def onLeftDown(self, event):
+        """
+        left button down and ready to drag
+        """
+        # Check that the LEFT button was pressed
+        if event.button == 1:
+            self.leftdown = True
+            ax = event.inaxes
+            if ax != None:
+                self.xInit, self.yInit = event.xdata, event.ydata
+                try:
+                    pos_x = float(event.xdata)  # / size_x
+                    pos_y = float(event.ydata)  # / size_y
+                    pos_x = "%8.3g" % pos_x
+                    pos_y = "%8.3g" % pos_y
+                    self.position = str(pos_x), str(pos_y)
+                    wx.PostEvent(self.parent, StatusEvent(status=self.position))
+                except:
+                    self.position = None
+
+    def onLeftUp(self, event):
+        """
+        Dragging is done
+        """
+        # Check that the LEFT button was released
+        if event.button == 1:
+            self.leftdown = False
+            self.mousemotion = False
+            self.leftup = True
+
+        #release the legend
+        if self.gotLegend == 1:
+            self.gotLegend = 0
+            self.set_legend_alpha(1)
+
+    def set_legend_alpha(self, alpha=1):
+        """
+        Set legend alpha
+        """
+        if self.legend != None:
+            self.legend.legendPatch.set_alpha(alpha)
+
+    def onPick(self, event):
+        """
+        On pick legend
+        """
+        legend = self.legend
+        if event.artist == legend:
+            #gets the box of the legend.
+            bbox = self.legend.get_window_extent()
+            #get mouse coordinates at time of pick.
+            self.mouse_x = event.mouseevent.x
+            self.mouse_y = event.mouseevent.y
+            #get legend coordinates at time of pick.
+            self.legend_x = bbox.xmin
+            self.legend_y = bbox.ymin
+            #indicates we picked up the legend.
+            self.gotLegend = 1
+            self.set_legend_alpha(0.5)
+
+    def _on_legend_motion(self, event):
+        """
+        On legend in motion
+        """
+        ax = event.inaxes
+        if ax == None:
+            return
+        # Event occurred inside a plotting area
+        lo_x, hi_x = ax.get_xlim()
+        lo_y, hi_y = ax.get_ylim()
+        # How much the mouse moved.
+        x = mouse_diff_x = self.mouse_x - event.x
+        y = mouse_diff_y = self.mouse_y - event.y
+        # Put back inside
+        if x < lo_x:
+            x = lo_x
+        if x > hi_x:
+            x = hi_x
+        if y < lo_y:
+            y = lo_y
+        if y > hi_y:
+            y = hi_y
+        # Move the legend from its previous location by that same amount
+        loc_in_canvas = self.legend_x - mouse_diff_x, \
+                        self.legend_y - mouse_diff_y
+        # Transform into legend coordinate system
+        trans_axes = self.legend.parent.transAxes.inverted()
+        loc_in_norm_axes = trans_axes.transform_point(loc_in_canvas)
+        self.legend_pos_loc = tuple(loc_in_norm_axes)
+        self.legend._loc = self.legend_pos_loc
+        self.resizing = True
+        self.canvas.set_resizing(self.resizing)
+        self.canvas.draw()
+
+    def onMouseMotion(self, event):
+        """
+        check if the left button is press and the mouse in moving.
+        computer delta for x and y coordinates and then calls draghelper
+        to perform the drag
+        """
+        self.cusor_line(event)
+        if self.gotLegend == 1:
+            self._on_legend_motion(event)
+            return
+        if self.enable_toolbar:
+            #Disable dragging without the toolbar to allow zooming with toolbar
+            return
+        self.mousemotion = True
+        if self.leftdown == True and self.mousemotion == True:
+            ax = event.inaxes
+            if ax != None:  # the dragging is perform inside the figure
+                self.xFinal, self.yFinal = event.xdata, event.ydata
+                # Check whether this is the first point
+                if self.xInit == None:
+                    self.xInit = self.xFinal
+                    self.yInit = self.yFinal
+
+                xdelta = self.xFinal - self.xInit
+                ydelta = self.yFinal - self.yInit
+
+                if self.xscale == 'log':
+                    xdelta = math.log10(self.xFinal) - math.log10(self.xInit)
+                if self.yscale == 'log':
+                    ydelta = math.log10(self.yFinal) - math.log10(self.yInit)
+                self._dragHelper(xdelta, ydelta)
+            else:  # no dragging is perform elsewhere
+                self._dragHelper(0, 0)
+
+    def cusor_line(self, event):
+        """
+        """
+        pass
+
+    def _offset_graph(self):
+        """
+        Zoom and offset the graph to the last known settings
+        """
+        for ax in self.axes:
+            if self._scale_xhi is not None and self._scale_xlo is not None:
+                ax.set_xlim(self._scale_xlo, self._scale_xhi)
+            if self._scale_yhi is not None and self._scale_ylo is not None:
+                ax.set_ylim(self._scale_ylo, self._scale_yhi)
+
+    def _dragHelper(self, xdelta, ydelta):
+        """
+        dragging occurs here
+        """
+        # Event occurred inside a plotting area
+        for ax in self.axes:
+            lo, hi = ax.get_xlim()
+            newlo, newhi = lo - xdelta, hi - xdelta
+            if self.xscale == 'log':
+                if lo > 0:
+                    newlo = math.log10(lo) - xdelta
+                if hi > 0:
+                    newhi = math.log10(hi) - xdelta
+            if self.xscale == 'log':
+                self._scale_xlo = math.pow(10, newlo)
+                self._scale_xhi = math.pow(10, newhi)
+                ax.set_xlim(math.pow(10, newlo), math.pow(10, newhi))
+            else:
+                self._scale_xlo = newlo
+                self._scale_xhi = newhi
+                ax.set_xlim(newlo, newhi)
+
+            lo, hi = ax.get_ylim()
+            newlo, newhi = lo - ydelta, hi - ydelta
+            if self.yscale == 'log':
+                if lo > 0:
+                    newlo = math.log10(lo) - ydelta
+                if hi > 0:
+                    newhi = math.log10(hi) - ydelta
+            if  self.yscale == 'log':
+                self._scale_ylo = math.pow(10, newlo)
+                self._scale_yhi = math.pow(10, newhi)
+                ax.set_ylim(math.pow(10, newlo), math.pow(10, newhi))
+            else:
+                self._scale_ylo = newlo
+                self._scale_yhi = newhi
+                ax.set_ylim(newlo, newhi)
+        self.canvas.draw_idle()
+
+    def resetFitView(self):
+        """
+        For fit Dialog initial display
+        """
+        self.xmin = 0.0
+        self.xmax = 0.0
+        self.xminView = 0.0
+        self.xmaxView = 0.0
+        self._scale_xlo = None
+        self._scale_xhi = None
+        self._scale_ylo = None
+        self._scale_yhi = None
+        self.Avalue = None
+        self.Bvalue = None
+        self.ErrAvalue = None
+        self.ErrBvalue = None
+        self.Chivalue = None
+
+    def onWheel(self, event):
+        """
+        Process mouse wheel as zoom events
+
+        :param event: Wheel event
+
+        """
+        ax = event.inaxes
+        step = event.step
+
+        if ax != None:
+            # Event occurred inside a plotting area
+            lo, hi = ax.get_xlim()
+            lo, hi = _rescale(lo, hi, step,
+                              pt=event.xdata, scale=ax.get_xscale())
+            if not self.xscale == 'log' or lo > 0:
+                self._scale_xlo = lo
+                self._scale_xhi = hi
+                ax.set_xlim((lo, hi))
+
+            lo, hi = ax.get_ylim()
+            lo, hi = _rescale(lo, hi, step, pt=event.ydata,
+                              scale=ax.get_yscale())
+            if not self.yscale == 'log' or lo > 0:
+                self._scale_ylo = lo
+                self._scale_yhi = hi
+                ax.set_ylim((lo, hi))
+        else:
+            # Check if zoom happens in the axes
+            xdata, ydata = None, None
+            x, y = event.x, event.y
+
+            for ax in self.axes:
+                insidex, _ = ax.xaxis.contains(event)
+                if insidex:
+                    xdata, _ = ax.transAxes.inverted().transform_point((x, y))
+                insidey, _ = ax.yaxis.contains(event)
+                if insidey:
+                    _, ydata = ax.transAxes.inverted().transform_point((x, y))
+            if xdata is not None:
+                lo, hi = ax.get_xlim()
+                lo, hi = _rescale(lo, hi, step,
+                                  bal=xdata, scale=ax.get_xscale())
+                if not self.xscale == 'log' or lo > 0:
+                    self._scale_xlo = lo
+                    self._scale_xhi = hi
+                    ax.set_xlim((lo, hi))
+            if ydata is not None:
+                lo, hi = ax.get_ylim()
+                lo, hi = _rescale(lo, hi, step, bal=ydata,
+                                  scale=ax.get_yscale())
+                if not self.yscale == 'log' or lo > 0:
+                    self._scale_ylo = lo
+                    self._scale_yhi = hi
+                    ax.set_ylim((lo, hi))
+        self.canvas.draw_idle()
+
+    def returnTrans(self):
+        """
+        Return values and labels used by Fit Dialog
+        """
+        return self.xLabel, self.yLabel, self.Avalue, self.Bvalue, \
+                self.ErrAvalue, self.ErrBvalue, self.Chivalue
+
+    def setTrans(self, xtrans, ytrans):
+        """
+
+        :param xtrans: set x transformation on Property dialog
+        :param ytrans: set y transformation on Property dialog
+
+        """
+        self.prevXtrans = xtrans
+        self.prevYtrans = ytrans
+
+    def onFitting(self, event):
+        """
+        when clicking on linear Fit on context menu , display Fitting Dialog
+        """
+        plot_dict = {}
+        menu = event.GetEventObject()
+        event_id = event.GetId()
+        self.set_selected_from_menu(menu, event_id)
+        plotlist = self.graph.returnPlottable()
+        if self.graph.selected_plottable is not None:
+            for item in plotlist:
+                if item.id == self.graph.selected_plottable:
+                    plot_dict[item] = plotlist[item]
+        else:
+            plot_dict = plotlist
+        from fitDialog import LinearFit
+
+        if len(plot_dict.keys()) > 0:
+            first_item = plot_dict.keys()[0]
+            dlg = LinearFit(parent=None, plottable=first_item,
+                            push_data=self.onFitDisplay,
+                            transform=self.returnTrans,
+                            title='Linear Fit')
+
+            if (self.xmin != 0.0)and (self.xmax != 0.0)\
+                and(self.xminView != 0.0)and (self.xmaxView != 0.0):
+                dlg.setFitRange(self.xminView, self.xmaxView,
+                                self.xmin, self.xmax)
+            dlg.ShowModal()
+
+    def set_selected_from_menu(self, menu, id):
+        """
+        Set selected_plottable from context menu selection
+
+        :param menu: context menu item
+        :param id: menu item id
+        """
+        if len(self.plots) < 1:
+            return
+        name = menu.GetHelpString(id)
+        for plot in self.plots.values():
+            if plot.name == name:
+                self.graph.selected_plottable = plot.id
+                break
+
+    def linear_plottable_fit(self, plot):
+        """
+            when clicking on linear Fit on context menu, display Fitting Dialog
+
+            :param plot: PlotPanel owning the graph
+
+        """
+        from fitDialog import LinearFit
+        if self._fit_dialog is not None:
+            return
+        self._fit_dialog = LinearFit(None, plot, self.onFitDisplay,
+                                     self.returnTrans, -1, 'Linear Fit')
+        # Set the zoom area
+        if self._scale_xhi is not None and self._scale_xlo is not None:
+            self._fit_dialog.set_fit_region(self._scale_xlo, self._scale_xhi)
+        # Register the close event
+        self._fit_dialog.register_close(self._linear_fit_close)
+        # Show a non-model dialog
+        self._fit_dialog.Show()
+
+    def _linear_fit_close(self):
+        """
+        A fit dialog was closed
+        """
+        self._fit_dialog = None
+
+    def _onProperties(self, event):
+        """
+        when clicking on Properties on context menu ,
+        The Property dialog is displayed
+        The user selects a transformation for x or y value and
+        a new plot is displayed
+        """
+        if self._fit_dialog is not None:
+            self._fit_dialog.Destroy()
+            self._fit_dialog = None
+        plot_list = self.graph.returnPlottable()
+        if len(plot_list.keys()) > 0:
+            first_item = plot_list.keys()[0]
+            if first_item.x != []:
+                from PropertyDialog import Properties
+                dial = Properties(self, -1, 'Properties')
+                dial.setValues(self.prevXtrans, self.prevYtrans, self.viewModel)
+                if dial.ShowModal() == wx.ID_OK:
+                    self.xLabel, self.yLabel, self.viewModel = dial.getValues()
+                    if self.viewModel == "Linear y vs x":
+                        self.xLabel = "x"
+                        self.yLabel = "y"
+                        self.viewModel = "--"
+                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
+                    if self.viewModel == "Guinier lny vs x^(2)":
+                        self.xLabel = "x^(2)"
+                        self.yLabel = "ln(y)"
+                        self.viewModel = "--"
+                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
+                    if self.viewModel == "XS Guinier ln(y*x) vs x^(2)":
+                        self.xLabel = "x^(2)"
+                        self.yLabel = "ln(y*x)"
+                        self.viewModel = "--"
+                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
+                    if self.viewModel == "Porod y*x^(4) vs x^(4)":
+                        self.xLabel = "x^(4)"
+                        self.yLabel = "y*x^(4)"
+                        self.viewModel = "--"
+                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
+                    self._onEVT_FUNC_PROPERTY()
+                dial.Destroy()
+
+    def set_yscale(self, scale='linear'):
+        """
+        Set the scale on Y-axis
+
+        :param scale: the scale of y-axis
+
+        """
+        self.subplot.set_yscale(scale, nonposy='clip')
+        self.yscale = scale
+
+    def get_yscale(self):
+        """
+
+        :return: Y-axis scale
+
+        """
+        return self.yscale
+
+    def set_xscale(self, scale='linear'):
+        """
+        Set the scale on x-axis
+
+        :param scale: the scale of x-axis
+
+        """
+        self.subplot.set_xscale(scale)
+        self.xscale = scale
+
+    def get_xscale(self):
+        """
+
+        :return: x-axis scale
+
+        """
+        return self.xscale
+
+    def SetColor(self, rgbtuple):
+        """
+        Set figure and canvas colours to be the same
+
+        """
+        if not rgbtuple:
+            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
+        col = [c / 255.0 for c in rgbtuple]
+        self.figure.set_facecolor(col)
+        self.figure.set_edgecolor(self.color)
+        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))
+
+    def _onSize(self, event):
+        """
+        """
+        self._resizeflag = True
+
+    def _onIdle(self, evt):
+        """
+        """
+        if self._resizeflag:
+            self._resizeflag = False
+            self._SetSize()
+            self.draw()
+
+    def _SetSize(self, pixels=None):
+        """
+        This method can be called to force the Plot to be a desired size,
+         which defaults to the ClientSize of the panel
+
+        """
+        if not pixels:
+            pixels = tuple(self.GetClientSize())
+        self.canvas.SetSize(pixels)
+        self.figure.set_size_inches(float(pixels[0]) / self.figure.get_dpi(),
+                                    float(pixels[1]) / self.figure.get_dpi())
+
+    def draw(self):
+        """
+        Where the actual drawing happens
+
+        """
+        self.figure.canvas.draw_idle()
+
+    def legend_picker(self, legend, event):
+        """
+            Pick up the legend patch
+        """
+        return self.legend.legendPatch.contains(event)
+
+    def get_loc_label(self):
+        """
+        Associates label to a specific legend location
+        """
+        _labels = {}
+        i = 0
+        _labels['best'] = i
+        i += 1
+        _labels['upper right'] = i
+        i += 1
+        _labels['upper left'] = i
+        i += 1
+        _labels['lower left'] = i
+        i += 1
+        _labels['lower right'] = i
+        i += 1
+        _labels['right'] = i
+        i += 1
+        _labels['center left'] = i
+        i += 1
+        _labels['center right'] = i
+        i += 1
+        _labels['lower center'] = i
+        i += 1
+        _labels['upper center'] = i
+        i += 1
+        _labels['center'] = i
+        return _labels
+
+    def onSaveImage(self, evt):
+        """
+        Implement save image
+        """
+        self.toolbar.save_figure(evt)
+
+    def onContextMenu(self, event):
+        """
+        Default context menu for a plot panel
+
+        """
+        # Slicer plot popup menu
+        wx_id = wx.NewId()
+        slicerpop = wx.Menu()
+        slicerpop.Append(wx_id, '&Save image', 'Save image as PNG')
+        wx.EVT_MENU(self, wx_id, self.onSaveImage)
+
+        wx_id = wx.NewId()
+        slicerpop.Append(wx_id, '&Printer setup', 'Set image size')
+        wx.EVT_MENU(self, wx_id, self.onPrinterSetup)
+
+        wx_id = wx.NewId()
+        slicerpop.Append(wx_id, '&Print image', 'Print image ')
+        wx.EVT_MENU(self, wx_id, self.onPrint)
+
+        wx_id = wx.NewId()
+        slicerpop.Append(wx_id, '&Copy', 'Copy to the clipboard')
+        wx.EVT_MENU(self, wx_id, self.OnCopyFigureMenu)
+
+        wx_id = wx.NewId()
+        slicerpop.AppendSeparator()
+        slicerpop.Append(wx_id, '&Properties')
+        wx.EVT_MENU(self, wx_id, self._onProperties)
+
+        wx_id = wx.NewId()
+        slicerpop.AppendSeparator()
+        slicerpop.Append(wx_id, '&Linear Fit')
+        wx.EVT_MENU(self, wx_id, self.onFitting)
+
+        wx_id = wx.NewId()
+        slicerpop.AppendSeparator()
+        slicerpop.Append(wx_id, '&Toggle Legend On/Off', 'Toggle Legend On/Off')
+        wx.EVT_MENU(self, wx_id, self.onLegend)
+
+        loc_menu = wx.Menu()
+        for label in self._loc_labels:
+            wx_id = wx.NewId()
+            loc_menu.Append(wx_id, str(label), str(label))
+            wx.EVT_MENU(self, wx_id, self.onChangeLegendLoc)
+        wx_id = wx.NewId()
+        slicerpop.AppendMenu(wx_id, '&Modify Legend Location', loc_menu)
+
+        wx_id = wx.NewId()
+        slicerpop.Append(wx_id, '&Modify Y Axis Label')
+        wx.EVT_MENU(self, wx_id, self._on_yaxis_label)
+        wx_id = wx.NewId()
+        slicerpop.Append(wx_id, '&Modify X Axis Label')
+        wx.EVT_MENU(self, wx_id, self._on_xaxis_label)
+
+        try:
+            # mouse event
+            pos_evt = event.GetPosition()
+            pos = self.ScreenToClient(pos_evt)
+        except:
+            # toolbar event
+            pos_x, pos_y = self.toolbar.GetPositionTuple()
+            pos = (pos_x, pos_y + 5)
+
+        self.PopupMenu(slicerpop, pos)
+
+    def onToolContextMenu(self, event):
+        """
+        ContextMenu from toolbar
+
+        :param event: toolbar event
+        """
+        # reset postion
+        self.position = None
+        if self.graph.selected_plottable != None:
+            self.graph.selected_plottable = None
+
+        self.onContextMenu(event)
+
+# modified kieranrcampbell ILL june2012
+    def onLegend(self, legOnOff):
+        """
+        Toggles whether legend is visible/not visible
+        """
+        self.legend_on = legOnOff
+        if not self.legend_on:
+            for ax in self.axes:
+                self.remove_legend(ax)
+        else:
+            # sort them by labels
+            handles, labels = self.subplot.get_legend_handles_labels()
+            hl = sorted(zip(handles, labels),
+                        key=operator.itemgetter(1))
+            handles2, labels2 = zip(*hl)
+            self.line_collections_list = handles2
+            self.legend = self.subplot.legend(handles2, labels2,
+                                              prop=FontProperties(size=10),
+                                              loc=self.legendLoc)
+            if self.legend != None:
+                self.legend.set_picker(self.legend_picker)
+                self.legend.set_axes(self.subplot)
+                self.legend.set_zorder(20)
+
+        self.subplot.figure.canvas.draw_idle()
+
+    def onChangeLegendLoc(self, event):
+        """
+        Changes legend loc based on user input
+        """
+        menu = event.GetEventObject()
+        label = menu.GetLabel(event.GetId())
+        self.ChangeLegendLoc(label)
+
+    def ChangeLegendLoc(self, label):
+        """
+        Changes legend loc based on user input
+        """
+        self.legendLoc = label
+        self.legend_pos_loc = None
+        # sort them by labels
+        handles, labels = self.subplot.get_legend_handles_labels()
+        hl = sorted(zip(handles, labels),
+                    key=operator.itemgetter(1))
+        handles2, labels2 = zip(*hl)
+        self.line_collections_list = handles2
+        self.legend = self.subplot.legend(handles2, labels2,
+                                          prop=FontProperties(size=10),
+                                          loc=self.legendLoc)
+        if self.legend != None:
+            self.legend.set_picker(self.legend_picker)
+            self.legend.set_axes(self.subplot)
+            self.legend.set_zorder(20)
+        self.subplot.figure.canvas.draw_idle()
+
+    def remove_legend(self, ax=None):
+        """
+        Remove legend for ax or the current axes.
+        """
+        from pylab import gca
+        if ax is None:
+            ax = gca()
+        ax.legend_ = None
+
+    def _on_addtext(self, event):
+        """
+        Allows you to add text to the plot
+        """
+        pos_x = 0
+        pos_y = 0
+        if self.position != None:
+            pos_x, pos_y = self.position
+        else:
+            pos_x, pos_y = 0.01, 1.00
+
+        textdial = TextDialog(None, -1, 'Add Custom Text')
+        if textdial.ShowModal() == wx.ID_OK:
+            try:
+                FONT = FontProperties()
+                label = textdial.getText()
+                xpos = pos_x
+                ypos = pos_y
+                font = FONT.copy()
+                font.set_size(textdial.getSize())
+                font.set_family(textdial.getFamily())
+                font.set_style(textdial.getStyle())
+                font.set_weight(textdial.getWeight())
+                colour = textdial.getColor()
+                if len(label) > 0 and xpos > 0 and ypos > 0:
+                    new_text = self.subplot.text(str(xpos), str(ypos), label,
+                                                 fontproperties=font,
+                                                 color=colour)
+                    self.textList.append(new_text)
+                    self.subplot.figure.canvas.draw_idle()
+            except:
+                if self.parent != None:
+                    msg = "Add Text: Error. Check your property values..."
+                    wx.PostEvent(self.parent, StatusEvent(status=msg))
+                else:
+                    raise
+        textdial.Destroy()
+        #Have a pop up box come up for user to type in the
+        #text that they want to add...then create text Plottable
+        #based on this and plot it at user designated coordinates
+
+    def onGridOnOff(self, gridon_off):
+        """
+        Allows ON/OFF Grid
+        """
+        self.grid_on = gridon_off
+
+        self.subplot.figure.canvas.draw_idle()
+
+    def _on_xaxis_label(self, event):
+        """
+        Allows you to add text to the plot
+        """
+        xaxis_label, xaxis_unit, xaxis_font, xaxis_color, \
+                     is_ok, is_tick = self._on_axis_label(axis='x')
+        if not is_ok:
+            return
+
+        self.xaxis_label = xaxis_label
+        self.xaxis_unit = xaxis_unit
+        self.xaxis_font = xaxis_font
+        self.xaxis_color = xaxis_color
+        if is_tick:
+            self.xaxis_tick = xaxis_font
+
+        if self.data != None:
+            # 2D
+            self.xaxis(self.xaxis_label, self.xaxis_unit, \
+                        self.xaxis_font, self.xaxis_color, self.xaxis_tick)
+            self.subplot.figure.canvas.draw_idle()
+        else:
+            # 1D
+            self._check_zoom_plot()
+
+    def _check_zoom_plot(self):
+        """
+        Check the zoom range and plot (1D only)
+        """
+        xlo, xhi = self.subplot.get_xlim()
+        ylo, yhi = self.subplot.get_ylim()
+        ## Set the view scale for all plots
+        self._onEVT_FUNC_PROPERTY(False)
+        if self.is_zoomed:
+            # Recover the x,y limits
+            self.subplot.set_xlim((xlo, xhi))
+            self.subplot.set_ylim((ylo, yhi))
+
+    @property
+    def is_zoomed(self):
+        toolbar_zoomed = self.toolbar.GetToolEnabled(self.toolbar.wx_ids['Back'])
+        return self._is_zoomed or toolbar_zoomed
+
+    @is_zoomed.setter
+    def is_zoomed(self, value):
+        self._is_zoomed = value
+
+    def _on_yaxis_label(self, event):
+        """
+        Allows you to add text to the plot
+        """
+        yaxis_label, yaxis_unit, yaxis_font, yaxis_color, \
+                        is_ok, is_tick = self._on_axis_label(axis='y')
+        if not is_ok:
+            return
+
+        self.yaxis_label = yaxis_label
+        self.yaxis_unit = yaxis_unit
+        self.yaxis_font = yaxis_font
+        self.yaxis_color = yaxis_color
+        if is_tick:
+            self.yaxis_tick = yaxis_font
+
+        if self.data != None:
+            # 2D
+            self.yaxis(self.yaxis_label, self.yaxis_unit, \
+                        self.yaxis_font, self.yaxis_color, self.yaxis_tick)
+            self.subplot.figure.canvas.draw_idle()
+        else:
+            # 1D
+            self._check_zoom_plot()
+
+    def _on_axis_label(self, axis='x'):
+        """
+        Modify axes labels
+
+        :param axis: x or y axis [string]
+        """
+        is_ok = True
+        title = 'Modify %s axis label' % axis
+        font = 'serif'
+        colour = 'black'
+        if axis == 'x':
+            label = self.xaxis_label
+            unit = self.xaxis_unit
+        else:
+            label = self.yaxis_label
+            unit = self.yaxis_unit
+        textdial = TextDialog(None, -1, title, label, unit)
+        if textdial.ShowModal() == wx.ID_OK:
+            try:
+                FONT = FontProperties()
+                font = FONT.copy()
+                font.set_size(textdial.getSize())
+                font.set_family(textdial.getFamily())
+                font.set_style(textdial.getStyle())
+                font.set_weight(textdial.getWeight())
+                unit = textdial.getUnit()
+                colour = textdial.getColor()
+                is_tick = textdial.getTickLabel()
+                label_temp = textdial.getText()
+                if label_temp.count("\%s" % "\\") > 0:
+                    if self.parent != None:
+                        msg = "Add Label: Error. Can not use double '\\' "
+                        msg += "characters..."
+                        wx.PostEvent(self.parent, StatusEvent(status=msg))
+                else:
+                    label = label_temp
+            except:
+                if self.parent != None:
+                    msg = "Add Label: Error. Check your property values..."
+                    wx.PostEvent(self.parent, StatusEvent(status=msg))
+                else:
+                    pass
+        else:
+            is_ok = False
+            is_tick = True
+        textdial.Destroy()
+        return label, unit, font, colour, is_ok, is_tick
+
+    def _on_removetext(self, event):
+        """
+        Removes all text from the plot.
+        Eventually, add option to remove specific text boxes
+        """
+        num_text = len(self.textList)
+        if num_text < 1:
+            if self.parent != None:
+                msg = "Remove Text: Nothing to remove.  "
+                wx.PostEvent(self.parent, StatusEvent(status=msg))
+            else:
+                raise
+            return
+        txt = self.textList[num_text - 1]
+        try:
+            text_remove = txt.get_text()
+            txt.remove()
+            if self.parent != None:
+                msg = "Removed Text: '%s'. " % text_remove
+                wx.PostEvent(self.parent, StatusEvent(status=msg))
+        except:
+            if self.parent != None:
+                msg = "Remove Text: Error occurred. "
+                wx.PostEvent(self.parent, StatusEvent(status=msg))
+            else:
+                raise
+        self.textList.remove(txt)
+
+        self.subplot.figure.canvas.draw_idle()
+
+    def properties(self, prop):
+        """
+        Set some properties of the graph.
+        The set of properties is not yet determined.
+
+        """
+        # The particulars of how they are stored and manipulated (e.g., do
+        # we want an inventory internally) is not settled.  I've used a
+        # property dictionary for now.
+        #
+        # How these properties interact with a user defined style file is
+        # even less clear.
+
+        # Properties defined by plot
+        
+        # Ricardo:
+        # A empty label "$$" will prevent the panel from displaying! 
+        
+        if prop["xlabel"]:
+            self.subplot.set_xlabel(r"$%s$"%prop["xlabel"])
+        if prop["ylabel"]:
+            self.subplot.set_ylabel(r"$%s$"%prop["ylabel"])
+        self.subplot.set_title(prop["title"])
+        
+
+    def clear(self):
+        """Reset the plot"""
+        # TODO: Redraw is brutal.  Render to a backing store and swap in
+        # TODO: rather than redrawing on the fly.
+        self.subplot.clear()
+        self.subplot.hold(True)
+
+    def render(self):
+        """Commit the plot after all objects are drawn"""
+        # TODO: this is when the backing store should be swapped in.
+        if self.legend_on:
+            ax = self.subplot
+            ax.texts = self.textList
+            try:
+                handles, labels = ax.get_legend_handles_labels()
+                # sort them by labels
+                hl = sorted(zip(handles, labels),
+                            key=operator.itemgetter(1))
+                handles2, labels2 = zip(*hl)
+                self.line_collections_list = handles2
+                self.legend = ax.legend(handles2, labels2,
+                                        prop=FontProperties(size=10),
+                                        loc=self.legendLoc)
+                if self.legend != None:
+                    self.legend.set_picker(self.legend_picker)
+                    self.legend.set_axes(self.subplot)
+                    self.legend.set_zorder(20)
+
+            except:
+                self.legend = ax.legend(prop=FontProperties(size=10),
+                                        loc=self.legendLoc)
+
+    def xaxis(self, label, units, font=None, color='black', t_font=None):
+        """xaxis label and units.
+
+        Axis labels know about units.
+
+        We need to do this so that we can detect when axes are not
+        commesurate.  Currently this is ignored other than for formatting
+        purposes.
+
+        """
+
+        self.xcolor = color
+        if units.count("{") > 0 and units.count("$") < 2:
+            units = '$' + units + '$'
+        if label.count("{") > 0 and label.count("$") < 2:
+            label = '$' + label + '$'
+        if units != "":
+            label = label + " (" + units + ")"
+        if font:
+            self.subplot.set_xlabel(label, fontproperties=font, color=color)
+            if t_font != None:
+                for tick in self.subplot.xaxis.get_major_ticks():
+                    tick.label.set_fontproperties(t_font)
+                for line in self.subplot.xaxis.get_ticklines():
+                    size = t_font.get_size()
+                    line.set_markersize(size / 3)
+        else:
+            self.subplot.set_xlabel(label, color=color)
+        pass
+
+    def yaxis(self, label, units, font=None, color='black', t_font=None):
+        """yaxis label and units."""
+        self.ycolor = color
+        if units.count("{") > 0 and units.count("$") < 2:
+            units = '$' + units + '$'
+        if label.count("{") > 0 and label.count("$") < 2:
+            label = '$' + label + '$'
+        if units != "":
+            label = label + " (" + units + ")"
+        if font:
+            self.subplot.set_ylabel(label, fontproperties=font, color=color)
+            if t_font != None:
+                for tick_label in self.subplot.get_yticklabels():
+                    tick_label.set_fontproperties(t_font)
+                for line in self.subplot.yaxis.get_ticklines():
+                    size = t_font.get_size()
+                    line.set_markersize(size / 3)
+        else:
+            self.subplot.set_ylabel(label, color=color)
+        pass
+
+    def _connect_to_xlim(self, callback):
+        """Bind the xlim change notification to the callback"""
+        def process_xlim(axes):
+            lo, hi = subplot.get_xlim()
+            callback(lo, hi)
+        self.subplot.callbacks.connect('xlim_changed', process_xlim)
+
+    def interactive_points(self, x, y, dx=None, dy=None, name='', color=0,
+                           symbol=0, markersize=5, zorder=1, id=None,
+                           label=None, hide_error=False):
+        """Draw markers with error bars"""
+        self.subplot.set_yscale('linear')
+        self.subplot.set_xscale('linear')
+        if id is None:
+            id = name
+        from plottable_interactor import PointInteractor
+        p = PointInteractor(self, self.subplot, zorder=zorder, id=id)
+        if p.markersize != None:
+            markersize = p.markersize
+        p.points(x, y, dx=dx, dy=dy, color=color, symbol=symbol, zorder=zorder,
+                 markersize=markersize, label=label, hide_error=hide_error)
+
+        self.subplot.set_yscale(self.yscale, nonposy='clip')
+        self.subplot.set_xscale(self.xscale)
+
+    def interactive_curve(self, x, y, dy=None, name='', color=0,
+                          symbol=0, zorder=1, id=None, label=None):
+        """Draw markers with error bars"""
+        self.subplot.set_yscale('linear')
+        self.subplot.set_xscale('linear')
+        if id is None:
+            id = name
+        from plottable_interactor import PointInteractor
+        p = PointInteractor(self, self.subplot, zorder=zorder, id=id)
+        p.curve(x, y, dy=dy, color=color, symbol=symbol, zorder=zorder,
+                label=label)
+
+        self.subplot.set_yscale(self.yscale, nonposy='clip')
+        self.subplot.set_xscale(self.xscale)
+
+    def plottable_selected(self, id):
+        """
+        Called to register a plottable as selected
+        """
+        #TODO: check that it really is in the list of plottables
+        self.graph.selected_plottable = id
+
+    def points(self, x, y, dx=None, dy=None,
+               color=0, symbol=0, marker_size=5, label=None,
+               id=None, hide_error=False):
+        """Draw markers with error bars"""
+
+        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
+        if dx != None and type(dx) == type(()):
+            dx = nx.vstack((x - dx[0], dx[1] - x)).transpose()
+        if dy != None and type(dy) == type(()):
+            dy = nx.vstack((y - dy[0], dy[1] - y)).transpose()
+        if dx == None and dy == None:
+            self.subplot.plot(x, y, color=self._color(color),
+                              marker=self._symbol(symbol),
+                              markersize=marker_size,
+                              linestyle='',
+                              label=label)
+        else:
+            col = self._color(color)
+            if hide_error:
+                self.subplot.plot(x, y, color=col,
+                                  marker=self._symbol(symbol),
+                                  markersize=marker_size,
+                                  linestyle='',
+                                  label=label)
+            else:
+                self.subplot.errorbar(x, y, yerr=dy, xerr=None,
+                                      ecolor=col, capsize=2, linestyle='',
+                                      barsabove=False,
+                                      mec=col, mfc=col,
+                                      marker=self._symbol(symbol),
+                                      markersize=marker_size,
+                                      lolims=False, uplims=False,
+                                      xlolims=False, xuplims=False, label=label)
+
+        self.subplot.set_yscale(self.yscale, nonposy='clip')
+        self.subplot.set_xscale(self.xscale)
+
+    def _onToggleScale(self, event):
+        """
+        toggle axis and replot image
+
+        """
+        zmin_2D_temp = self.zmin_2D
+        zmax_2D_temp = self.zmax_2D
+        if self.scale == 'log_{10}':
+            self.scale = 'linear'
+            if not self.zmin_2D is None:
+                zmin_2D_temp = math.pow(10, self.zmin_2D)
+            if not self.zmax_2D is None:
+                zmax_2D_temp = math.pow(10, self.zmax_2D)
+        else:
+            self.scale = 'log_{10}'
+            if not self.zmin_2D is None:
+                # min log value: no log(negative)
+                if self.zmin_2D <= 0:
+                    zmin_2D_temp = -32
+                else:
+                    zmin_2D_temp = math.log10(self.zmin_2D)
+            if not self.zmax_2D is None:
+                zmax_2D_temp = math.log10(self.zmax_2D)
+
+        self.image(data=self.data, qx_data=self.qx_data,
+                   qy_data=self.qy_data, xmin=self.xmin_2D,
+                   xmax=self.xmax_2D,
+                   ymin=self.ymin_2D, ymax=self.ymax_2D,
+                   cmap=self.cmap, zmin=zmin_2D_temp,
+                   zmax=zmax_2D_temp)
+
+    def image(self, data, qx_data, qy_data, xmin, xmax, ymin, ymax,
+              zmin, zmax, color=0, symbol=0, markersize=0,
+              label='data2D', cmap=DEFAULT_CMAP):
+        """
+        Render the current data
+
+        """
+        self.data = data
+        self.qx_data = qx_data
+        self.qy_data = qy_data
+        self.xmin_2D = xmin
+        self.xmax_2D = xmax
+        self.ymin_2D = ymin
+        self.ymax_2D = ymax
+        self.zmin_2D = zmin
+        self.zmax_2D = zmax
+        c = self._color(color)
+        # If we don't have any data, skip.
+        if self.data == None:
+            return
+        if self.data.ndim == 1:
+            output = self._build_matrix()
+        else:
+            output = copy.deepcopy(self.data)
+        # check scale
+        if self.scale == 'log_{10}':
+            try:
+                if  self.zmin_2D <= 0  and len(output[output > 0]) > 0:
+                    zmin_temp = self.zmin_2D
+                    output[output > 0] = numpy.log10(output[output > 0])
+                    #In log scale Negative values are not correct in general
+                    #output[output<=0] = math.log(numpy.min(output[output>0]))
+                elif self.zmin_2D <= 0:
+                    zmin_temp = self.zmin_2D
+                    output[output > 0] = numpy.zeros(len(output))
+                    output[output <= 0] = -32
+                else:
+                    zmin_temp = self.zmin_2D
+                    output[output > 0] = numpy.log10(output[output > 0])
+                    #In log scale Negative values are not correct in general
+                    #output[output<=0] = math.log(numpy.min(output[output>0]))
+            except:
+                #Too many problems in 2D plot with scale
+                pass
+
+        else:
+            zmin_temp = self.zmin_2D
+        self.cmap = cmap
+        if self.dimension != 3:
+            #Re-adjust colorbar
+            self.subplot.figure.subplots_adjust(left=0.2, right=.8, bottom=.2)
+
+            im = self.subplot.imshow(output, interpolation='nearest',
+                                     origin='lower',
+                                     vmin=zmin_temp, vmax=self.zmax_2D,
+                                     cmap=self.cmap,
+                                     extent=(self.xmin_2D, self.xmax_2D,
+                                             self.ymin_2D, self.ymax_2D))
+
+            cbax = self.subplot.figure.add_axes([0.84, 0.2, 0.02, 0.7])
+        else:
+            # clear the previous 2D from memory
+            # mpl is not clf, so we do
+            self.subplot.figure.clear()
+
+            self.subplot.figure.subplots_adjust(left=0.1, right=.8, bottom=.1)
+
+            X = self.x_bins[0:-1]
+            Y = self.y_bins[0:-1]
+            X, Y = numpy.meshgrid(X, Y)
+
+            try:
+                # mpl >= 1.0.0
+                ax = self.subplot.figure.gca(projection='3d')
+                #ax.disable_mouse_rotation()
+                cbax = self.subplot.figure.add_axes([0.84, 0.1, 0.02, 0.8])
+                if len(X) > 60:
+                    ax.disable_mouse_rotation()
+            except:
+                # mpl < 1.0.0
+                try:
+                    from mpl_toolkits.mplot3d import Axes3D
+                except:
+                    logging.error("PlotPanel could not import Axes3D")
+                self.subplot.figure.clear()
+                ax = Axes3D(self.subplot.figure)
+                if len(X) > 60:
+                    ax.cla()
+                cbax = None
+            self.subplot.figure.canvas.resizing = False
+            im = ax.plot_surface(X, Y, output, rstride=1, cstride=1, cmap=cmap,
+                                 linewidth=0, antialiased=False)
+            self.subplot.set_axis_off()
+
+        if cbax == None:
+            ax.set_frame_on(False)
+            cb = self.subplot.figure.colorbar(im, shrink=0.8, aspect=20)
+        else:
+            cb = self.subplot.figure.colorbar(im, cax=cbax)
+        cb.update_bruteforce(im)
+        cb.set_label('$' + self.scale + '$')
+        if self.dimension != 3:
+            self.figure.canvas.draw_idle()
+        else:
+            self.figure.canvas.draw()
+
+    def _build_matrix(self):
+        """
+        Build a matrix for 2d plot from a vector
+        Returns a matrix (image) with ~ square binning
+        Requirement: need 1d array formats of
+        self.data, self.qx_data, and self.qy_data
+        where each one corresponds to z, x, or y axis values
+
+        """
+        # No qx or qy given in a vector format
+        if self.qx_data == None or self.qy_data == None \
+                or self.qx_data.ndim != 1 or self.qy_data.ndim != 1:
+            # do we need deepcopy here?
+            return copy.deepcopy(self.data)
+
+        # maximum # of loops to fillup_pixels
+        # otherwise, loop could never stop depending on data
+        max_loop = 1
+        # get the x and y_bin arrays.
+        self._get_bins()
+        # set zero to None
+
+        #Note: Can not use scipy.interpolate.Rbf:
+        # 'cause too many data points (>10000)<=JHC.
+        # 1d array to use for weighting the data point averaging
+        #when they fall into a same bin.
+        weights_data = numpy.ones([self.data.size])
+        # get histogram of ones w/len(data); this will provide
+        #the weights of data on each bins
+        weights, xedges, yedges = numpy.histogram2d(x=self.qy_data,
+                                                    y=self.qx_data,
+                                                    bins=[self.y_bins, self.x_bins],
+                                                    weights=weights_data)
+        # get histogram of data, all points into a bin in a way of summing
+        image, xedges, yedges = numpy.histogram2d(x=self.qy_data,
+                                                  y=self.qx_data,
+                                                  bins=[self.y_bins, self.x_bins],
+                                                  weights=self.data)
+        # Now, normalize the image by weights only for weights>1: 
+        # If weight == 1, there is only one data point in the bin so
+        # that no normalization is required.
+        image[weights > 1] = image[weights > 1] / weights[weights > 1]
+        # Set image bins w/o a data point (weight==0) as None (was set to zero
+        # by histogram2d.)
+        image[weights == 0] = None
+
+        # Fill empty bins with 8 nearest neighbors only when at least
+        #one None point exists
+        loop = 0
+
+        # do while loop until all vacant bins are filled up up
+        #to loop = max_loop
+        while not(numpy.isfinite(image[weights == 0])).all():
+            if loop >= max_loop:  # this protects never-ending loop
+                break
+            image = self._fillup_pixels(image=image, weights=weights)
+            loop += 1
+
+        return image
+
+    def _get_bins(self):
+        """
+        get bins
+        set x_bins and y_bins into self, 1d arrays of the index with
+        ~ square binning
+        Requirement: need 1d array formats of
+        self.qx_data, and self.qy_data
+        where each one corresponds to  x, or y axis values
+        """
+        # No qx or qy given in a vector format
+        if self.qx_data == None or self.qy_data == None \
+                or self.qx_data.ndim != 1 or self.qy_data.ndim != 1:
+            # do we need deepcopy here?
+            return copy.deepcopy(self.data)
+
+        # find max and min values of qx and qy
+        xmax = self.qx_data.max()
+        xmin = self.qx_data.min()
+        ymax = self.qy_data.max()
+        ymin = self.qy_data.min()
+
+        # calculate the range of qx and qy: this way, it is a little
+        # more independent
+        x_size = xmax - xmin
+        y_size = ymax - ymin
+
+        # estimate the # of pixels on each axes
+        npix_y = int(math.floor(math.sqrt(len(self.qy_data))))
+        npix_x = int(math.floor(len(self.qy_data) / npix_y))
+
+        # bin size: x- & y-directions
+        xstep = x_size / (npix_x - 1)
+        ystep = y_size / (npix_y - 1)
+
+        # max and min taking account of the bin sizes
+        xmax = xmax + xstep / 2.0
+        xmin = xmin - xstep / 2.0
+        ymax = ymax + ystep / 2.0
+        ymin = ymin - ystep / 2.0
+
+        # store x and y bin centers in q space
+        x_bins = numpy.linspace(xmin, xmax, npix_x)
+        y_bins = numpy.linspace(ymin, ymax, npix_y)
+
+        #set x_bins and y_bins
+        self.x_bins = x_bins
+        self.y_bins = y_bins
+
+    def _fillup_pixels(self, image=None, weights=None):
+        """
+        Fill z values of the empty cells of 2d image matrix
+        with the average over up-to next nearest neighbor points
+
+        :param image: (2d matrix with some zi = None)
+
+        :return: image (2d array )
+
+        :TODO: Find better way to do for-loop below
+
+        """
+        # No image matrix given
+        if image == None or numpy.ndim(image) != 2 \
+                or numpy.isfinite(image).all() \
+                or weights == None:
+            return image
+        # Get bin size in y and x directions
+        len_y = len(image)
+        len_x = len(image[1])
+        temp_image = numpy.zeros([len_y, len_x])
+        weit = numpy.zeros([len_y, len_x])
+        # do for-loop for all pixels
+        for n_y in range(len(image)):
+            for n_x in range(len(image[1])):
+                # find only null pixels
+                if weights[n_y][n_x] > 0 or numpy.isfinite(image[n_y][n_x]):
+                    continue
+                else:
+                    # find 4 nearest neighbors
+                    # check where or not it is at the corner
+                    if n_y != 0 and numpy.isfinite(image[n_y - 1][n_x]):
+                        temp_image[n_y][n_x] += image[n_y - 1][n_x]
+                        weit[n_y][n_x] += 1
+                    if n_x != 0 and numpy.isfinite(image[n_y][n_x - 1]):
+                        temp_image[n_y][n_x] += image[n_y][n_x - 1]
+                        weit[n_y][n_x] += 1
+                    if n_y != len_y - 1 and numpy.isfinite(image[n_y + 1][n_x]):
+                        temp_image[n_y][n_x] += image[n_y + 1][n_x]
+                        weit[n_y][n_x] += 1
+                    if n_x != len_x - 1 and numpy.isfinite(image[n_y][n_x + 1]):
+                        temp_image[n_y][n_x] += image[n_y][n_x + 1]
+                        weit[n_y][n_x] += 1
+                    # go 4 next nearest neighbors when no non-zero
+                    # neighbor exists
+                    if n_y != 0 and n_x != 0 and\
+                         numpy.isfinite(image[n_y - 1][n_x - 1]):
+                        temp_image[n_y][n_x] += image[n_y - 1][n_x - 1]
+                        weit[n_y][n_x] += 1
+                    if n_y != len_y - 1 and n_x != 0 and \
+                        numpy.isfinite(image[n_y + 1][n_x - 1]):
+                        temp_image[n_y][n_x] += image[n_y + 1][n_x - 1]
+                        weit[n_y][n_x] += 1
+                    if n_y != len_y and n_x != len_x - 1 and \
+                        numpy.isfinite(image[n_y - 1][n_x + 1]):
+                        temp_image[n_y][n_x] += image[n_y - 1][n_x + 1]
+                        weit[n_y][n_x] += 1
+                    if n_y != len_y - 1 and n_x != len_x - 1 and \
+                        numpy.isfinite(image[n_y + 1][n_x + 1]):
+                        temp_image[n_y][n_x] += image[n_y + 1][n_x + 1]
+                        weit[n_y][n_x] += 1
+
+        # get it normalized
+        ind = (weit > 0)
+        image[ind] = temp_image[ind] / weit[ind]
+
+        return image
+
+    def curve(self, x, y, dy=None, color=0, symbol=0, label=None):
+        """Draw a line on a graph, possibly with confidence intervals."""
+        c = self._color(color)
+        self.subplot.set_yscale('linear')
+        self.subplot.set_xscale('linear')
+
+        self.subplot.plot(x, y, color=c, marker='',
+                          linestyle='-', label=label)
+        self.subplot.set_yscale(self.yscale)
+        self.subplot.set_xscale(self.xscale)
+
+    def _color(self, c):
+        """Return a particular colour"""
+        return self.colorlist[c % len(self.colorlist)]
+
+    def _symbol(self, s):
+        """Return a particular symbol"""
+        return self.symbollist[s % len(self.symbollist)]
+
+    def _replot(self, remove_fit=False):
+        """
+        Rescale the plottables according to the latest
+        user selection and update the plot
+
+        :param remove_fit: Fit line will be removed if True
+
+        """
+        self.graph.reset_scale()
+        self._onEVT_FUNC_PROPERTY(remove_fit=remove_fit)
+        #TODO: Why do we have to have the following line?
+        self.fit_result.reset_view()
+        self.graph.render(self)
+        self.subplot.figure.canvas.draw_idle()
+
+    def _onEVT_FUNC_PROPERTY(self, remove_fit=True, show=True):
+        """
+        Receive the x and y transformation from myDialog,
+        Transforms x and y in View
+        and set the scale
+        """
+        # The logic should be in the right order
+        # Delete first, and then get the whole list...
+        if remove_fit:
+            self.graph.delete(self.fit_result)
+        self.ly = None
+        self.q_ctrl = None
+        list = self.graph.returnPlottable()
+        # Changing the scale might be incompatible with
+        # currently displayed data (for instance, going
+        # from ln to log when all plotted values have
+        # negative natural logs).
+        # Go linear and only change the scale at the end.
+        self.set_xscale("linear")
+        self.set_yscale("linear")
+        _xscale = 'linear'
+        _yscale = 'linear'
+        for item in list:
+            item.setLabel(self.xLabel, self.yLabel)
+
+            # control axis labels from the panel itself
+            yname, yunits = item.get_yaxis()
+            if self.yaxis_label != None:
+                yname = self.yaxis_label
+                yunits = self.yaxis_unit
+            else:
+                self.yaxis_label = yname
+                self.yaxis_unit = yunits
+            xname, xunits = item.get_xaxis()
+            if self.xaxis_label != None:
+                xname = self.xaxis_label
+                xunits = self.xaxis_unit
+            else:
+                self.xaxis_label = xname
+                self.xaxis_unit = xunits
+            # Goes through all possible scales
+            if self.xLabel == "x":
+                item.transformX(transform.toX, transform.errToX)
+                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
+            if self.xLabel == "x^(2)":
+                item.transformX(transform.toX2, transform.errToX2)
+                xunits = convert_unit(2, xunits)
+                self.graph._xaxis_transformed("%s^{2}" % xname, "%s" % xunits)
+            if self.xLabel == "x^(4)":
+                item.transformX(transform.toX4, transform.errToX4)
+                xunits = convert_unit(4, xunits)
+                self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
+            if self.xLabel == "ln(x)":
+                item.transformX(transform.toLogX, transform.errToLogX)
+                self.graph._xaxis_transformed("\ln\\ %s" % xname, "%s" % xunits)
+            if self.xLabel == "log10(x)":
+                item.transformX(transform.toX_pos, transform.errToX_pos)
+                _xscale = 'log'
+                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
+            if self.xLabel == "log10(x^(4))":
+                item.transformX(transform.toX4, transform.errToX4)
+                xunits = convert_unit(4, xunits)
+                self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
+                _xscale = 'log'
+            if self.yLabel == "ln(y)":
+                item.transformY(transform.toLogX, transform.errToLogX)
+                self.graph._yaxis_transformed("\ln\\ %s" % yname, "%s" % yunits)
+            if self.yLabel == "y":
+                item.transformY(transform.toX, transform.errToX)
+                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
+            if self.yLabel == "log10(y)":
+                item.transformY(transform.toX_pos, transform.errToX_pos)
+                _yscale = 'log'
+                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
+            if self.yLabel == "y^(2)":
+                item.transformY(transform.toX2, transform.errToX2)
+                yunits = convert_unit(2, yunits)
+                self.graph._yaxis_transformed("%s^{2}" % yname, "%s" % yunits)
+            if self.yLabel == "1/y":
+                item.transformY(transform.toOneOverX, transform.errOneOverX)
+                yunits = convert_unit(-1, yunits)
+                self.graph._yaxis_transformed("1/%s" % yname, "%s" % yunits)
+            if self.yLabel == "y*x^(4)":
+                item.transformY(transform.toYX4, transform.errToYX4)
+                xunits = convert_unit(4, self.xaxis_unit)
+                self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname, xname),
+                                              "%s%s" % (yunits, xunits))
+            if self.yLabel == "1/sqrt(y)":
+                item.transformY(transform.toOneOverSqrtX,
+                                transform.errOneOverSqrtX)
+                yunits = convert_unit(-0.5, yunits)
+                self.graph._yaxis_transformed("1/\sqrt{%s}" % yname,
+                                              "%s" % yunits)
+            if self.yLabel == "ln(y*x)":
+                item.transformY(transform.toLogXY, transform.errToLogXY)
+                self.graph._yaxis_transformed("\ln (%s \ \ %s)" % (yname, xname),
+                                              "%s%s" % (yunits, self.xaxis_unit))
+            if self.yLabel == "ln(y*x^(2))":
+                item.transformY(transform.toLogYX2, transform.errToLogYX2)
+                xunits = convert_unit(2, self.xaxis_unit)
+                self.graph._yaxis_transformed("\ln (%s \ \ %s^{2})" % (yname, xname),
+                                              "%s%s" % (yunits, xunits))
+            if self.yLabel == "ln(y*x^(4))":
+                item.transformY(transform.toLogYX4, transform.errToLogYX4)
+                xunits = convert_unit(4, self.xaxis_unit)
+                self.graph._yaxis_transformed("\ln (%s \ \ %s^{4})" % (yname, xname),
+                                              "%s%s" % (yunits, xunits))
+            if self.yLabel == "log10(y*x^(4))":
+                item.transformY(transform.toYX4, transform.errToYX4)
+                xunits = convert_unit(4, self.xaxis_unit)
+                _yscale = 'log'
+                self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname, xname),
+                                              "%s%s" % (yunits, xunits))
+            if self.viewModel == "Guinier lny vs x^(2)":
+                item.transformX(transform.toX2, transform.errToX2)
+                xunits = convert_unit(2, xunits)
+                self.graph._xaxis_transformed("%s^{2}" % xname, "%s" % xunits)
+                item.transformY(transform.toLogX, transform.errToLogX)
+                self.graph._yaxis_transformed("\ln\ \ %s" % yname, "%s" % yunits)
+            if self.viewModel == "Porod y*x^(4) vs x^(4)":
+                item.transformX(transform.toX4, transform.errToX4)
+                xunits = convert_unit(4, self.xaxis_unit)
+                self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
+                item.transformY(transform.toYX4, transform.errToYX4)
+                self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname, xname),
+                                              "%s%s" % (yunits, xunits))
+            item.transformView()
+
+        # set new label and units
+        yname = self.graph.prop["ylabel"]
+        yunits = ''
+        xname = self.graph.prop["xlabel"]
+        xunits = ''
+
+        self.resetFitView()
+        self.prevXtrans = self.xLabel
+        self.prevYtrans = self.yLabel
+        self.graph.render(self)
+        self.set_xscale(_xscale)
+        self.set_yscale(_yscale)
+
+        self.xaxis(xname, xunits, self.xaxis_font,
+                   self.xaxis_color, self.xaxis_tick)
+        self.yaxis(yname, yunits, self.yaxis_font,
+                   self.yaxis_color, self.yaxis_tick)
+        self.subplot.texts = self.textList
+        if show:
+            self.subplot.figure.canvas.draw_idle()
+
+    def onFitDisplay(self, tempx, tempy, xminView,
+                     xmaxView, xmin, xmax, func):
+        """
+        Add a new plottable into the graph .In this case this plottable
+        will be used to fit some data
+
+        :param tempx: The x data of fit line
+        :param tempy: The y data of fit line
+        :param xminView: the lower bound of fitting range
+        :param xminView: the upper bound of  fitting range
+        :param xmin: the lowest value of data to fit to the line
+        :param xmax: the highest value of data to fit to the line
+
+        """
+        # Saving value to redisplay in Fit Dialog when it is opened again
+        self.Avalue, self.Bvalue, self.ErrAvalue, \
+                      self.ErrBvalue, self.Chivalue = func
+        self.xminView = xminView
+        self.xmaxView = xmaxView
+        self.xmin = xmin
+        self.xmax = xmax
+        #In case need to change the range of data plotted
+        for item in self.graph.returnPlottable():
+            item.onFitRange(None, None)
+        # Create new data plottable with result
+        self.fit_result.x = []
+        self.fit_result.y = []
+        self.fit_result.x = tempx
+        self.fit_result.y = tempy
+        self.fit_result.dx = None
+        self.fit_result.dy = None
+        #Load the view with the new values
+        self.fit_result.reset_view()
+        # Add the new plottable to the graph
+        self.graph.add(self.fit_result)
+        self.graph.render(self)
+        self._offset_graph()
+        self.subplot.figure.canvas.draw_idle()
+
+    def onChangeCaption(self, event):
+        """
+        """
+        if self.parent == None:
+            return
+        # get current caption
+        old_caption = self.window_caption
+        # Get new caption dialog
+        dial = LabelDialog(None, -1, 'Modify Window Title', old_caption)
+        if dial.ShowModal() == wx.ID_OK:
+            new_caption = dial.getText()
+
+            # send to guiframe to change the panel caption
+            caption = self.parent.on_change_caption(self.window_name,
+                                                    old_caption, new_caption)
+
+            # also set new caption in plot_panels list
+            self.parent.plot_panels[self.uid].window_caption = caption
+            # set new caption
+            self.window_caption = caption
+
+        dial.Destroy()
+
+    def onResetGraph(self, event):
+        """
+        Reset the graph by plotting the full range of data
+        """
+        for item in self.graph.returnPlottable():
+            item.onReset()
+        self.graph.render(self)
+        self._onEVT_FUNC_PROPERTY(False)
+        self.is_zoomed = False
+        self.toolbar.update()
+
+    def onPrint(self, event=None):
+        self.toolbar.print_figure(event)
+
+    def onPrinterSetup(self, event=None):
+        """
+        """
+        self.canvas.Printer_Setup(event=event)
+        self.Update()
+
+    def onPrinterPreview(self, event=None):
+        """
+        Matplotlib camvas can no longer print itself.  Thus need to do
+        everything ourselves: need to create a printpreview frame to to
+        see the preview but needs a parent frame object.  Also needs a
+        printout object (just as any printing task).
+        """
+        try:
+            #check if parent is a frame.  If not keep getting the higher
+            #parent till we find a frame
+            _plot = self
+            while not isinstance(_plot, wx.Frame):
+                _plot = _plot.GetParent()
+                assert _plot is not None
+
+            #now create the printpeview object
+            _preview = wx.PrintPreview(PlotPrintout(self.canvas),
+                                       PlotPrintout(self.canvas))
+            #and tie it to a printpreview frame then show it
+            _frame = wx.PreviewFrame(_preview, _plot, "Print Preview", wx.Point(100, 100), wx.Size(600, 650))
+            _frame.Centre(wx.BOTH)
+            _frame.Initialize()
+            _frame.Show(True)
+        except:
+            traceback.print_exc()
+            pass
+
+    def OnCopyFigureMenu(self, evt):
+        """
+        Copy the current figure to clipboard
+        """
+        try:
+            self.toolbar.copy_figure()
+        except:
+            print "Error in copy Image"
+
+
+#---------------------------------------------------------------
+class NoRepaintCanvas(FigureCanvasWxAgg):
+    """
+    We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
+    the draw method is only called for the first two paint events. After that,
+    the canvas will only be redrawn when it is resized.
+
+    """
+    def __init__(self, *args, **kwargs):
+        """
+        """
+        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
+        self._drawn = 0
+
+    def _onPaint(self, evt):
+        """
+        Called when wxPaintEvt is generated
+
+        """
+        if not self._isRealized:
+            self.realize()
+        if self._drawn < 2:
+            self.draw(repaint=False)
+            self._drawn += 1
+        self.gui_repaint(drawDC=wx.PaintDC(self))
Index: src/sas/sasgui/plottools/PropertyDialog.py
===================================================================
--- src/sas/sasgui/plottools/PropertyDialog.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/PropertyDialog.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,95 @@
+"""
+"""
+import wx
+
+class Properties(wx.Dialog):
+    """
+    """
+    def __init__(self, parent, id=-1, title="Select the scale of the graph"):
+        wx.Dialog.__init__(self, parent, id, title)
+        self.parent = parent
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        sizer = wx.GridBagSizer(5, 5)
+
+        x_size = 70
+
+        ix = 1
+        iy = 1
+        sizer.Add(wx.StaticText(self, -1, 'X'), (iy, ix))
+        ix += 2
+        sizer.Add(wx.StaticText(self, -1, 'Y'), (iy, ix))
+        ix += 2
+        sizer.Add(wx.StaticText(self, -1, 'View'), (iy, ix))
+        iy += 1
+        ix = 1
+        self.xvalue = wx.ComboBox(self, -1)
+        x_size += self.xvalue.GetSize()[0]
+        sizer.Add(self.xvalue, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+        ix += 2
+        self.yvalue = wx.ComboBox(self, -1)
+        x_size += self.yvalue.GetSize()[0]
+        sizer.Add(self.yvalue, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+        ix += 2
+        self.view = wx.ComboBox(self, -1)
+        x_size += self.view.GetSize()[0]
+        self.view.SetMinSize((160, 30))
+        sizer.Add(self.view, (iy, ix), (1, 1),
+                  wx.EXPAND | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
+        self.SetMinSize((x_size, 50))
+        vbox.Add(sizer, 0, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+        cancel_button = wx.Button(self, wx.ID_CANCEL, 'Cancel')
+        ok_button = wx.Button(self, wx.ID_OK, "OK")
+        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        sizer_button.Add(ok_button, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
+        sizer_button.Add(cancel_button, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
+        vbox.Add(sizer_button, 0,
+                 wx.EXPAND | wx.TOP | wx.BOTTOM | wx.ADJUST_MINSIZE, 10)
+        # scale value for x
+        self.xvalue.SetValue("ln(x)")
+        self.xvalue.Insert("x", 0)
+        self.xvalue.Insert("x^(2)", 1)
+        self.xvalue.Insert("x^(4)", 2)
+        self.xvalue.Insert("ln(x)", 3)
+        self.xvalue.Insert("log10(x)", 4)
+        self.xvalue.Insert("log10(x^(4))", 5)
+
+        # scale value for y
+        self.yvalue.SetValue("ln(y)")
+        self.yvalue.Insert("y", 0)
+        self.yvalue.Insert("1/y", 1)
+        self.yvalue.Insert("ln(y)", 2)
+        self.yvalue.Insert("y^(2)", 3)
+        self.yvalue.Insert("y*x^(4)", 4)
+        self.yvalue.Insert("1/sqrt(y)", 5)
+        self.yvalue.Insert("log10(y)", 6)
+        self.yvalue.Insert("ln(y*x)", 7)
+        self.yvalue.Insert("ln(y*x^(2))", 8)
+        self.yvalue.Insert("ln(y*x^(4))", 9)
+        self.yvalue.Insert("log10(y*x^(4))", 10)
+        # type of view or model used
+        self.view.SetValue("--")
+        self.view.Insert("--", 0)
+        self.view.Insert("Linear y vs x", 1)
+        self.view.Insert("Guinier lny vs x^(2)", 2)
+        self.view.Insert("XS Guinier ln(y*x) vs x^(2)", 3)
+        self.view.Insert("Porod y*x^(4) vs x^(4)", 4)
+        self.view.Insert("Kratky y*x^(2) vs x", 5)
+        self.SetSizer(vbox)
+        self.Fit()
+        self.Centre()
+
+    def setValues(self, x, y, view):
+        """
+        """
+        return  self.xvalue.SetValue(x), self.yvalue.SetValue(y), \
+                    self.view.SetValue(view)
+
+    def getValues(self):
+        """
+        """
+        return self.xvalue.GetValue(), self.yvalue.GetValue(), \
+                            self.view.GetValue()
Index: src/sas/sasgui/plottools/SimpleFont.py
===================================================================
--- src/sas/sasgui/plottools/SimpleFont.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/SimpleFont.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,114 @@
+
+"""
+This software was developed by Institut Laue-Langevin as part of
+Distributed Data Analysis of Neutron Scattering Experiments (DANSE).
+
+Copyright 2012 Institut Laue-Langevin
+"""
+
+# this is a dead simple dialog for getting font family, size,style and weight
+
+
+import wx
+from matplotlib.font_manager import FontProperties
+
+FAMILY = ['serif', 'sas-serif', 'fantasy', 'monospace']
+SIZE = [8, 9, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72]
+STYLE = ['normal', 'italic']
+WEIGHT = ['light', 'normal', 'bold']
+
+
+class SimpleFont(wx.Dialog):
+    def __init__(self, parent, id, title):
+
+        wx.Dialog.__init__(self, parent, id, title, size=(440, 160))
+        self.parent = parent
+#        self.SetWindowVariant(variant=FONT_VARIANT)
+
+        self.family = FAMILY[1]
+        self.size = SIZE[3]
+        self.style = STYLE[0]
+        self.weight = WEIGHT[1]
+        self.tick_label = None
+        self.tick_label_check = None
+        self.InitUI()
+        self.Centre()
+        self.Show()
+
+    def InitUI(self):
+        self.font_family = wx.ComboBox(self, pos=(80, 10), style=wx.CB_READONLY, size=(100, -1))
+        self.font_style = wx.ComboBox(self, pos=(80, 60), style=wx.CB_READONLY, size=(100, -1))
+        self.font_point = wx.ComboBox(self, pos=(300, 10), style=wx.CB_READONLY, size=(100, -1))
+        self.font_weight = wx.ComboBox(self, pos=(300, 60), style=wx.CB_READONLY, size=(100, -1))
+        self.tick_label_check = wx.CheckBox(self, -1, label='', pos=(80, 100), size=(15, -1))
+        self.tick_label_check.SetValue(False)
+        wx.Button(self, wx.ID_OK, 'OK', pos=(215, 100))
+        wx.Button(self, wx.ID_CANCEL, 'Cancel', pos=(315, 100))
+
+        self._set_family_list()
+        self._set_style_list()
+        self._set_weight_list()
+        self._set_point_list()
+
+        wx.StaticText(self, label='Family:', pos=(10, 12))
+        wx.StaticText(self, label='Size:', pos=(220, 12))
+        wx.StaticText(self, label='Style:', pos=(10, 62))
+        wx.StaticText(self, label='Weight:', pos=(220, 62))
+        tick_label_text = wx.StaticText(self, label='Tick label?', pos=(10, 100))
+        tick_label_text.SetToolTipString("Apply to tick label too.")
+
+    def _set_family_list(self):
+        # list of font family
+        for idx in range(len(FAMILY)):
+            self.font_family.Append(FAMILY[idx], idx)
+
+    def _set_style_list(self):
+        # list of styles
+        for idx in range(len(STYLE)):
+            self.font_style.Append(STYLE[idx], idx)
+
+    def _set_weight_list(self):
+        # list of weights
+        for idx in range(len(WEIGHT)):
+            self.font_weight.Append(WEIGHT[idx], idx)
+
+    def _set_point_list(self):
+        # list of point sizes
+        for idx in range(len(SIZE)):
+            self.font_point.Append(str(SIZE[idx]), idx)
+
+    def get_ticklabel_check(self):
+        """
+        Get tick label check value
+        """
+        self.tick_label = self.tick_label_check.GetValue()
+        return self.tick_label
+
+    def set_ticklabel_check(self, check=False):
+        """
+        Set tick label check value
+        """
+        self.tick_label_check.SetValue(check)
+
+    def set_default_font(self, font):
+
+        if not font:
+            self.font_family.SetSelection(1)
+            self.font_weight.SetSelection(1)
+            self.font_point.SetSelection(3)
+            self.font_style.SetSelection(0)
+        else:
+            self.font_weight.SetStringSelection(str(font.get_weight()))
+            self.font_point.SetStringSelection(str(int(font.get_size())))
+            self.font_family.SetStringSelection(str(font.get_family()[0]))
+            self.font_style.SetStringSelection(str(font.get_style()))
+
+    def get_font(self):
+        font_properties = FontProperties()
+        font = font_properties.copy()
+        font.set_size(str(self.font_point.GetValue()))
+        font.set_name(str(self.font_family.GetValue()))
+        font.set_slant(str(self.font_style.GetValue()))
+        font.set_weight(str(self.font_weight.GetValue()))
+
+        return font
Index: src/sas/sasgui/plottools/SizeDialog.py
===================================================================
--- src/sas/sasgui/plottools/SizeDialog.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/SizeDialog.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,38 @@
+import wx
+
+
+class SizeDialog(wx.Dialog):
+    def __init__(self, parent, id, title):
+        wx.Dialog.__init__(self, parent, id, title, size=(300, 175))
+
+        mainbox = wx.BoxSizer(wx.VERTICAL)
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        textbox = wx.BoxSizer(wx.HORIZONTAL)
+        hbox = wx.BoxSizer(wx.HORIZONTAL)
+        text1 = "Enter in a custom size (> 0 accepted)"
+        msg = wx.StaticText(self, -1, text1, (30, 15), style=wx.ALIGN_CENTRE)
+        msg.SetLabel(text1)
+        self.text_ctrl = wx.TextCtrl(self, -1, '', (100, 50))
+
+        textbox.Add(self.text_ctrl, flag=wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, border=10)
+        vbox.Add(msg, flag=wx.ALL, border=10, proportion=1)
+        vbox.Add(textbox, flag=wx.EXPAND | wx.TOP | wx.BOTTOM | wx.ADJUST_MINSIZE, border=10)
+        self.text_ctrl.SetValue(str(5))
+
+        ok_button = wx.Button(self, wx.ID_OK, 'OK', size=(70, 25))
+        close_button = wx.Button(self, wx.ID_CANCEL, 'Cancel', size=(70, 25))
+
+        hbox.Add(ok_button)
+        hbox.Add((20, 20))
+        hbox.Add(close_button)
+
+        mainbox.Add(vbox, flag=wx.ALL, border=10)
+        mainbox.Add(wx.StaticLine(self), 0, wx.ALL | wx.EXPAND, 5)
+        mainbox.Add(hbox, flag=wx.CENTER, border=10)
+        self.SetSizer(mainbox)
+
+    def getText(self):
+        """
+        Get text typed
+        """
+        return self.text_ctrl.GetValue()
Index: src/sas/sasgui/plottools/TextDialog.py
===================================================================
--- src/sas/sasgui/plottools/TextDialog.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/TextDialog.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,307 @@
+import wx
+import sys
+
+if sys.platform.count("win32") > 0:
+    FONT_VARIANT = 0
+    PNL_WIDTH = 460
+else:
+    FONT_VARIANT = 1
+    PNL_WIDTH = 500
+FAMILY = ['serif', 'sans-serif', 'fantasy', 'monospace']
+SIZE = [8, 9, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72]
+STYLE = ['normal', 'italic']
+WEIGHT = ['light', 'normal', 'bold']
+COLOR = ['black', 'blue', 'green', 'red', 'cyan', 'magenta', 'yellow']
+
+
+class TextDialog(wx.Dialog):
+    def __init__(self, parent, id, title, label='', unit=None):
+        """
+        Dialog window pops- up when selecting 'Add Text' on the toolbar
+        """
+        wx.Dialog.__init__(self, parent, id, title, size=(PNL_WIDTH, 300))
+        self.parent = parent
+        # Font
+        self.SetWindowVariant(variant=FONT_VARIANT)
+        # default
+        self.family = FAMILY[1]
+        self.size = SIZE[3]
+        self.style = STYLE[0]
+        self.weight = WEIGHT[1]
+        self.color = COLOR[0]
+        self.tick_label = False
+        # Dialog interface
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        text_box = wx.BoxSizer(wx.HORIZONTAL)
+        sizer = wx.GridBagSizer(1, 3)
+        _BOX_WIDTH = 70
+        font_description = wx.StaticBox(self, -1, 'Font')
+        font_box = wx.StaticBoxSizer(font_description, wx.VERTICAL)
+        family_box = wx.BoxSizer(wx.HORIZONTAL)
+        style_box = wx.BoxSizer(wx.HORIZONTAL)
+        # tcA
+        if unit != None:
+            styles = wx.TAB_TRAVERSAL
+            height = -1
+            unit_text = wx.StaticText(self, -1, 'Unit :')
+            unit_text.SetToolTipString("Unit of the axis.")
+            self.unit_ctrl = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, -1))
+            self.unit_ctrl.SetValue(str(unit))
+            unit_box = wx.BoxSizer(wx.HORIZONTAL)
+            tick_label_text = wx.StaticText(self, -1, 'Tick label')
+            tick_label_text.SetToolTipString("Apply to tick label too.")
+            self.tick_label_check = wx.CheckBox(self, -1, '', (10, 10))
+            self.tick_label_check.SetValue(False)
+            self.tick_label_check.SetToolTipString("Apply to tick label too.")
+            wx.EVT_CHECKBOX(self, self.tick_label_check.GetId(),
+                            self.on_tick_label)
+            enter_text = 'Enter text:'
+        else:
+            styles = wx.TAB_TRAVERSAL | wx.TE_MULTILINE | wx.TE_LINEWRAP | \
+                              wx.TE_PROCESS_ENTER | wx.SUNKEN_BORDER | wx.HSCROLL
+            height = 60
+            unit_text = None
+            self.unit_ctrl = None
+            unit_box = None
+            tick_label_text = None
+            self.tick_label_check = None
+            enter_text = 'Enter text'
+            if len(label) > 0:
+                enter_text += " (this text won't be auto-updated if modified.):"
+            else:
+                enter_text += ":"
+        self.text_string = wx.TextCtrl(self, -1, size=(PNL_WIDTH - 30, height), style=styles)
+        self.text_string.SetValue(str(label))
+        self.text_string.SetToolTipString("The text that will be displayed.")
+        # font family
+        self.font_family = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+        wx.EVT_COMBOBOX(self.font_family, -1, self.on_family)
+        self.font_family.SetMinSize((_BOX_WIDTH, -1))
+        self._set_family_list()
+        self.font_family.SetSelection(1)
+        self.font_family.SetToolTipString("Font family of the text.")
+        # font weight
+        self.font_weight = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+        wx.EVT_COMBOBOX(self.font_weight, -1, self.on_weight)
+        self.font_weight.SetMinSize((_BOX_WIDTH, -1))
+        self._set_weight_list()
+        self.font_weight.SetSelection(1)
+        self.font_weight.SetToolTipString("Font weight of the text.")
+        # font family
+        self.font_size = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+        wx.EVT_COMBOBOX(self.font_size, -1, self.on_size)
+        self.font_size.SetMinSize((_BOX_WIDTH, -1))
+        self._set_size_list()
+        self.font_size.SetSelection(5)
+        self.font_size.SetToolTipString("Font size of the text.")
+        # font style
+        self.font_style = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+        wx.EVT_COMBOBOX(self.font_style, -1, self.on_style)
+        self.font_style.SetMinSize((_BOX_WIDTH, -1))
+        self._set_style_list()
+        self.font_style.SetSelection(0)
+        self.font_style.SetToolTipString("Font style of the text.")
+        # font color
+        self.font_color = wx.ComboBox(self, -1, style = wx.CB_READONLY)
+        wx.EVT_COMBOBOX(self.font_color, -1, self.on_color)
+        self.font_color.SetMinSize((_BOX_WIDTH, -1))
+        self._set_color_list()
+        self.font_color.SetSelection(0)
+        self.font_color.SetToolTipString("Font color of the text.")
+        # Buttons on the bottom
+        self.static_line_1 = wx.StaticLine(self, -1)
+        self.ok_button = wx.Button(self, wx.ID_OK, 'OK',
+                                   size = (_BOX_WIDTH, 25))
+        self.close_button = wx.Button(self, wx.ID_CANCEL, 'Cancel',
+                                      size = (_BOX_WIDTH, 25))
+
+        # Intro
+        explanation = "Select font properties :"
+        vbox.Add(sizer)
+        ix = 0
+        iy = 1
+        sizer.Add(wx.StaticText(self, -1, explanation), (iy, ix),
+                  (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+        family_box.Add(wx.StaticText(self, -1, 'Family :', size = (50, -1)),
+                       0, wx.TOP, 5)
+        family_box.Add(self.font_family, 0, 0)
+        family_box.Add((_BOX_WIDTH / 2, -1))
+        family_box.Add(wx.StaticText(self, -1, 'Size :', size = (50, -1)),
+                       0, wx.TOP, 5)
+        family_box.Add(self.font_size, 0, 0)
+        if unit_box != None:
+            family_box.Add((_BOX_WIDTH / 2, -1))
+            family_box.Add(tick_label_text, 0, 0)
+            family_box.Add(self.tick_label_check, 0, 0)
+        style_box.Add(wx.StaticText(self, -1, 'Style :', size = (50, -1)),
+                      0, wx.TOP, 5)
+        style_box.Add(self.font_style, 0, 0)
+        style_box.Add((_BOX_WIDTH / 2, -1))
+        style_box.Add(wx.StaticText(self, -1, 'Weight :', size = (50, -1)),
+                      0, wx.TOP, 5)
+        style_box.Add(self.font_weight, 0, 0)
+        style_box.Add((_BOX_WIDTH / 2, -1))
+        style_box.Add(wx.StaticText(self, -1, 'Color :', size = (45, -1)),
+                      0, wx.TOP, 5)
+        style_box.Add(self.font_color, 0, 0)
+        font_box.Add(family_box, 0, 10)
+        font_box.Add((0,5))
+        font_box.Add(style_box, 0, 10)
+        iy += 1
+        ix = 0
+        sizer.Add(font_box, (iy, ix),
+                  (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+        iy += 2
+        ix = 0
+        sizer.Add(wx.StaticText(self, -1, enter_text), (iy, ix),
+                  (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+        text_box.Add((15, 10))
+        text_box.Add(self.text_string)
+        vbox.Add(text_box, 0, wx.EXPAND, 15)
+        if unit_box != None:
+            unit_box.Add(unit_text, 0, 0)
+            unit_box.Add(self.unit_ctrl, 0, 0)
+            vbox.Add((5, 5))
+            vbox.Add(unit_box, 0, wx.LEFT, 15)
+
+        vbox.Add((10, 10))
+        vbox.Add(self.static_line_1, 0, wx.EXPAND, 10)
+        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        sizer_button.Add(self.ok_button, 0,
+                         wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
+        sizer_button.Add(self.close_button, 0,
+                         wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
+        vbox.Add(sizer_button, 0, wx.EXPAND | wx.BOTTOM | wx.TOP, 10)
+        self.SetSizer(vbox)
+        self.Centre()
+
+    def _set_family_list(self):
+        """
+        Set the list of font family
+        """
+        # list of family choices
+        for idx in range(len(FAMILY)):
+            self.font_family.Append(FAMILY[idx], idx)
+
+    def _set_size_list(self):
+        """
+        Set the list of font size
+        """
+        # list of size choices
+        for idx in range(len(SIZE)):
+            self.font_size.Append(str(SIZE[idx]), idx)
+
+    def _set_weight_list(self):
+        """
+        Set the list of font weight
+        """
+        # list of weight choices
+        for idx in range(len(WEIGHT)):
+            self.font_weight.Append(WEIGHT[idx], idx)
+
+    def _set_style_list(self):
+        """
+        Set the list of font style
+        """
+        # list of style choices
+        for idx in range(len(STYLE)):
+            self.font_style.Append(STYLE[idx], idx)
+
+    def _set_color_list(self):
+        """
+        Set the list of font color
+        """
+        # list of tyle choices
+        for idx in range(len(COLOR)):
+            self.font_color.Append(COLOR[idx], idx)
+
+    def on_tick_label(self, event):
+        """
+        Set the font for tick label
+        """
+        event.Skip()
+        self.tick_label = self.tick_label_check.GetValue()
+
+    def on_family(self, event):
+        """
+        Set the family
+        """
+        event.Skip()
+        self.family = self.font_family.GetValue()
+
+    def on_style(self, event):
+        """
+        Set the style
+        """
+        event.Skip()
+        self.style = self.font_style.GetValue()
+
+    def on_weight(self, event):
+        """
+        Set the weight
+        """
+        event.Skip()
+        self.weight = self.font_weight.GetValue()
+
+    def on_size(self, event):
+        """
+        Set the size
+        """
+        event.Skip()
+        self.size = self.font_size.GetValue()
+
+    def on_color(self, event):
+        """
+        Set the color
+        """
+        event.Skip()
+        self.color = self.font_color.GetValue()
+
+    def getText(self):
+        """
+        Returns text string as input by user.
+        """
+        return self.text_string.GetValue()
+
+    def getUnit(self):
+        """
+        Returns unit string as input by user.
+        """
+        return self.unit_ctrl.GetValue()
+
+    def getFamily(self):
+        """
+        Returns font family for the text box
+        """
+        return str(self.family)
+
+    def getStyle(self):
+        """
+        Returns font tyle for the text box
+        """
+        return str(self.style)
+
+    def getWeight(self):
+        """
+        Returns font weight for the text box
+        """
+        return str(self.weight)
+
+    def getSize(self):
+        """
+        Returns font size for the text box
+        """
+        return int(self.size)
+
+    def getColor(self):
+        """
+        Returns font size for the text box
+        """
+        return str(self.color)
+
+    def getTickLabel(self):
+        """
+        Bool for use on tick label
+        """
+        return self.tick_label
Index: src/sas/sasgui/plottools/__init__.py
===================================================================
--- src/sas/sasgui/plottools/__init__.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/__init__.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,3 @@
+import config
+from PlotPanel import PlotPanel
+from plottables import Data1D, Theory1D
Index: src/sas/sasgui/plottools/arrow3d.py
===================================================================
--- src/sas/sasgui/plottools/arrow3d.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/arrow3d.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,67 @@
+"""
+Module that draws multiple arrows in 3D coordinates
+"""
+
+from matplotlib.patches import FancyArrowPatch
+from mpl_toolkits.mplot3d import proj3d
+import time
+# from matplotlib.artist import allow_rasterization
+class Arrow3D(FancyArrowPatch):
+    """
+    Draw 3D arrow
+    """
+
+    def __init__(self, base, xs, ys, zs, colors, *args, **kwargs):
+        """
+        Init
+
+        :Params xs: [[x0, x0+dx0], [x1, x1+dx1], ...]
+        :Params ys: [[y0, y0+dy0], [y1, y1+dy1], ...]
+        :Params zs: [[z0, z0+dz0], [z1, z1+dz1], ...]
+        :Params colors: [[R0, G0, B0], [R1, G1, B1], ...]
+        where R, G, B ranges (0,1)
+        """
+        FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)
+        self.leftdown = False
+        self.t_click = 0
+        self._verts3d = xs, ys, zs
+        self.colors = colors
+        self.base = base
+
+        if base != None:
+            # To turn the updating off during dragging
+            base.canvas.mpl_connect('button_press_event', self.on_left_down)
+            base.canvas.mpl_connect('button_release_event', self.on_left_up)
+
+    def on_left_down(self, event):
+        """
+            Mouse left-down event
+        """
+        self.leftdown = True
+        self.t_click = time.time()
+
+    def on_left_up(self, event):
+        """
+            Mouse left up event
+        """
+        t_up = time.time() - self.t_click
+        # Avoid just clicking
+        if t_up > 0.1:
+            self.leftdown = False
+            self.base.canvas.draw()
+
+    def draw(self, renderer, rasterized=True):
+        """
+        Drawing actually happens here
+        """
+        # Draws only when the dragging finished
+        if self.leftdown:
+            return
+        xs3d, ys3d, zs3d = self._verts3d
+        for i in xrange(len(xs3d)):
+            xs, ys, _ = proj3d.proj_transform(xs3d[i], ys3d[i], zs3d[i], renderer.M)
+            self.set_positions((xs[0], ys[0]), (xs[1], ys[1]))
+            self.set_color(self.colors[i])
+            FancyArrowPatch.draw(self, renderer)
+
+        self.leftdown = False
Index: src/sas/sasgui/plottools/binder.py
===================================================================
--- src/sas/sasgui/plottools/binder.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/binder.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,396 @@
+"""
+Extension to MPL to support the binding of artists to key/mouse events.
+"""
+import sys
+import logging
+
+class Selection(object):
+    """
+    Store and compare selections.
+    """
+    # TODO: We need some way to check in prop matches, preferably
+    # TODO: without imposing structure on prop.
+
+    artist = None
+    prop = {}
+
+    def __init__(self, artist=None, prop={}):
+        self.artist, self.prop = artist, self.prop
+
+    def __eq__(self, other):
+        return self.artist is other.artist
+
+    def __ne__(self, other):
+        return self.artist is not other.artist
+
+    def __nonzero__(self):
+        return self.artist is not None
+
+
+class BindArtist(object):
+    """
+    """
+    # Track keyboard modifiers for events.
+    # TODO: Move keyboard modifier support into the backend.  We cannot
+    # TODO: properly support it from outside the windowing system since there
+    # TODO: is no way to recognized whether shift is held down when the mouse
+    # TODO: first clicks on the the application window.
+    control, shift, alt, meta = False, False, False, False
+
+    # Track doubleclick
+    dclick_threshhold = 0.25
+    _last_button, _last_time = None, 0
+
+    # Mouse/keyboard events we can bind to
+    events = ['enter', 'leave', 'motion', 'click', 'dclick', 'drag', 'release',
+              'scroll', 'key', 'keyup']
+    # TODO: Need our own event structure
+
+    def __init__(self, figure):
+        canvas = figure.canvas
+
+        # Link to keyboard/mouse
+        try:
+            self._connections = [
+                canvas.mpl_connect('motion_notify_event', self._onMotion),
+                canvas.mpl_connect('button_press_event', self._onClick),
+                canvas.mpl_connect('button_release_event', self._onRelease),
+                canvas.mpl_connect('key_press_event', self._onKey),
+                canvas.mpl_connect('key_release_event', self._onKeyRelease),
+                canvas.mpl_connect('scroll_event', self._onScroll)
+            ]
+        except:
+            print "bypassing scroll_event: wrong matplotlib version"
+            self._connections = [
+                canvas.mpl_connect('motion_notify_event', self._onMotion),
+                canvas.mpl_connect('button_press_event', self._onClick),
+                canvas.mpl_connect('button_release_event', self._onRelease),
+                canvas.mpl_connect('key_press_event', self._onKey),
+                canvas.mpl_connect('key_release_event', self._onKeyRelease),
+            ]
+
+        self._current = None
+        self._actions = {}
+        self.canvas = canvas
+        self.figure = figure
+        self.clearall()
+
+    def clear(self, *artists):
+        """
+        self.clear(h1,h2,...)
+            Remove connections for artists h1, h2, ...
+
+        Use clearall() to reset all connections.
+        """
+
+        for h in artists:
+            for a in self.events:
+                if h in self._actions[a]:
+                    del self._actions[a][h]
+            if h in self._artists:
+                self._artists.remove(h)
+        if self._current.artist in artists:
+            self._current = Selection()
+        if self._hasclick.artist in artists:
+            self._hasclick = Selection()
+        if self._haskey.artist in artists:
+            self._haskey = Selection()
+
+    def clearall(self):
+        """
+        Clear connections to all artists.
+
+        Use clear(h1,h2,...) to reset specific artists.
+        """
+        # Don't monitor any actions
+        self._actions = {}
+        for action in self.events:
+            self._actions[action] = {}
+
+        # Need activity state
+        self._artists = []
+        self._current = Selection()
+        self._hasclick = Selection()
+        self._haskey = Selection()
+
+    def disconnect(self):
+        """
+        In case we need to disconnect from the canvas...
+        """
+        try:
+            for cid in self._connections: self.canvas.mpl_disconnect(cid)
+        except:
+            logging.error("Error disconnection canvas: %s" % sys.exc_value)
+        self._connections = []
+
+    def __del__(self):
+        self.disconnect()
+
+    def __call__(self, trigger, artist, action):
+        """Register a callback for an artist to a particular trigger event.
+
+        usage:
+            self.connect(eventname,artist,action)
+
+        where:
+            eventname is a string
+            artist is the particular graph object to respond to the event
+            action(event,**kw) is called when the event is triggered
+
+        The action callback is associated with particular artists.
+        Different artists will have different kwargs.  See documentation
+        on the contains() method for each artist.  One common properties
+        are ind for the index of the item under the cursor, which is
+        returned by Line2D and by collections.
+
+        The following events are supported:
+            enter: mouse cursor moves into the artist or to a new index
+            leave: mouse cursor leaves the artist
+            click: mouse button pressed on the artist
+            drag: mouse button pressed on the artist and cursor moves
+            release: mouse button released for the artist
+            key: key pressed when mouse is on the artist
+            keyrelease: key released for the artist
+
+        The event received by action has a number of attributes:
+            name is the event name which was triggered
+            artist is the object which triggered the event
+            x,y are the screen coordinates of the mouse
+            xdata,ydata are the graph coordinates of the mouse
+            button is the mouse button being pressed/released
+            key is the key being pressed/released
+            shift,control,alt,meta are flags which are true if the
+                corresponding key is pressed at the time of the event.
+            details is a dictionary of artist specific details, such as the
+                id(s) of the point that were clicked.
+
+        When receiving an event, first check the modifier state to be
+        sure it applies.  E.g., the callback for 'press' might be:
+            if event.button == 1 and event.shift: process Shift-click
+
+        TODO: Only receive events with the correct modifiers (e.g., S-click,
+        TODO:   or *-click for any modifiers).
+        TODO: Only receive button events for the correct button (e.g., click1
+        TODO:   release3, or dclick* for any button)
+        TODO: Support virtual artist, so that and artist can be flagged as
+        TODO:   having a tag list and receive the correct events
+        TODO: Support virtual events for binding to button-3 vs shift button-1
+        TODO:   without changing callback code
+        TODO: Attach multiple callbacks to the same event?
+        TODO: Clean up interaction with toolbar modes
+        TODO: push/pushclear/pop context so that binding changes for
+             the duration
+        TODO:   e.g., to support ? context sensitive help
+        """
+        # Check that the trigger is valid
+        if trigger not in self._actions:
+            raise ValueError, "%s invalid --- valid triggers are %s"\
+                 % (trigger, ", ".join(self.events))
+
+        # Register the trigger callback
+        self._actions[trigger][artist] = action
+
+        # Maintain a list of all artists
+        if artist not in self._artists:
+            self._artists.append(artist)
+
+    def trigger(self, actor, action, ev):
+        """
+        Trigger a particular event for the artist.  Fallback to axes,
+        to figure, and to 'all' if the event is not processed.
+        """
+        if action not in self.events:
+            raise ValueError, "Trigger expects " + ", ".join(self.events)
+
+        # Tag the event with modifiers
+        for mod in ('alt', 'control', 'shift', 'meta'):
+            setattr(ev, mod, getattr(self, mod))
+        setattr(ev, 'artist', None)
+        setattr(ev, 'action', action)
+        setattr(ev, 'prop', {})
+
+        # Fallback scheme.  If the event does not return false, pass to parent.
+        processed = False
+        artist, prop = actor.artist, actor.prop
+        if artist in self._actions[action]:
+            ev.artist, ev.prop = artist, prop
+            processed = self._actions[action][artist](ev)
+        if not processed and ev.inaxes in self._actions[action]:
+            ev.artist, ev.prop = ev.inaxes, {}
+            processed = self._actions[action][ev.inaxes](ev)
+        if not processed and self.figure in self._actions[action]:
+            ev.artist, ev.prop = self.figure, {}
+            processed = self._actions[action][self.figure](ev)
+        if not processed and 'all' in self._actions[action]:
+            ev.artist, ev.prop = None, {}
+            processed = self._actions[action]['all'](ev)
+        return processed
+
+    def _find_current(self, event):
+        """
+        Find the artist who will receive the event.  Only search
+        registered artists.  All others are invisible to the mouse.
+        """
+        # TODO: sort by zorder of axes then by zorder within axes
+        self._artists.sort(cmp=lambda x, y: cmp(y.zorder, x.zorder))
+        found = Selection()
+        for artist in self._artists:
+            # TODO: should contains() return false if invisible?
+            if not artist.get_visible():
+                continue
+            # TODO: optimization - exclude artists not inaxes
+            try:
+                inside, prop = artist.contains(event)
+            except:
+                # Probably an old version of matplotlib
+                inside = False
+            if inside:
+                found.artist, found.prop = artist, prop
+                break
+
+        # TODO: how to check if prop is equal?
+        if found != self._current:
+            self.trigger(self._current, 'leave', event)
+            self.trigger(found, 'enter', event)
+        self._current = found
+
+        return found
+
+    def _onMotion(self, event):
+        """
+        Track enter/leave/motion through registered artists; all
+        other artists are invisible.
+        """
+        # # Can't kill double-click on motion since Windows produces
+        # # spurious motion events.
+        # self._last_button = None
+
+        # Dibs on the motion event for the clicked artist
+        if self._hasclick:
+            # Make sure the x,y data use the coordinate system of the
+            # artist rather than the default axes coordinates.
+
+            transform = self._hasclick.artist.get_transform()
+            # x,y = event.xdata,event.ydata
+            x, y = event.x, event.y
+            try:
+                if transform.__class__.__name__ == "IdentityTransform":
+                    x, y = transform.inverted().transform((x, y))
+                else:
+                    # # For interactive plottable apply transform is not working
+                    # # don't know why maybe marker definition
+                    # #transform ="CompositeGenericTransform" crash
+                    pass
+            except:
+                # # CRUFT matplotlib-0.91 support
+                # # exception for transform ="CompositeGenericTransform"
+                # # crashes also here
+                x, y = transform.inverse_xy_tup((x, y))
+
+            # event.xdata, event.ydata = x, y
+            self.trigger(self._hasclick, 'drag', event)
+        else:
+            found = self._find_current(event)
+            self.trigger(found, 'motion', event)
+
+    def _onClick(self, event):
+        """
+        Process button click
+        """
+        import time
+
+        # Check for double-click
+        event_time = time.time()
+        if (event.button != self._last_button) or \
+                (event_time > self._last_time + self.dclick_threshhold):
+            action = 'click'
+        else:
+            action = 'dclick'
+        self._last_button = event.button
+        self._last_time = event_time
+
+        # If an artist is already dragging, feed any additional button
+        # presses to that artist.
+        # TODO: do we want to force a single button model on the user?
+        # TODO: that is, once a button is pressed, no other buttons
+        # TODO: can come through?  I think this belongs in canvas, not here.
+        if self._hasclick:
+            found = self._hasclick
+        else:
+            found = self._find_current(event)
+        # print "button %d pressed"%event.button
+        # Note: it seems like if "click" returns False then hasclick should
+        # not be set.  The problem is that there are two reasons it can
+        # return false: because there is no click action for this artist
+        # or because the click action returned false.  A related problem
+        # is that click actions will go to the canvas if there is no click
+        # action for the artist, even if the artist has a drag. I'll leave
+        # it to future maintainers to sort out this problem.  For now the
+        # recommendation is that users should define click if they have
+        # drag or release on the artist.
+        self.trigger(found, action, event)
+        self._hasclick = found
+
+    def _onDClick(self, event):
+        """
+        Process button double click
+        """
+        # If an artist is already dragging, feed any additional button
+        # presses to that artist.
+        # TODO: do we want to force a single button model on the user?
+        # TODO: that is, once a button is pressed, no other buttons
+        # TODO: can come through?  I think this belongs in canvas, not here.
+        if self._hasclick:
+            found = self._hasclick
+        else:
+            found = self._find_current(event)
+        self.trigger(found, 'dclick', event)
+        self._hasclick = found
+
+    def _onRelease(self, event):
+        """
+        Process release release
+        """
+        self.trigger(self._hasclick, 'release', event)
+        self._hasclick = Selection()
+
+    def _onKey(self, event):
+        """
+        Process key click
+        """
+        # TODO: Do we really want keyboard focus separate from mouse focus?
+        # TODO: Do we need an explicit focus command for keyboard?
+        # TODO: Can we tab between items?
+        # TODO: How do unhandled events get propogated to axes, figure and
+        # TODO: finally to application?  Do we need to implement a full tags
+        # TODO: architecture a la Tk?
+        # TODO: Do modifiers cause a grab?  Does the artist see the modifiers?
+        if event.key in ('alt', 'meta', 'control', 'shift'):
+            setattr(self, event.key, True)
+            return
+
+        if self._haskey:
+            found = self._haskey
+        else:
+            found = self._find_current(event)
+        self.trigger(found, 'key', event)
+        self._haskey = found
+
+    def _onKeyRelease(self, event):
+        """
+        Process key release
+        """
+        if event.key in ('alt', 'meta', 'control', 'shift'):
+            setattr(self, event.key, False)
+            return
+
+        if self._haskey:
+            self.trigger(self._haskey, 'keyup', event)
+        self._haskey = Selection()
+
+    def _onScroll(self, event):
+        """
+        Process scroll event
+        """
+        found = self._find_current(event)
+        self.trigger(found, 'scroll', event)
Index: src/sas/sasgui/plottools/canvas.py
===================================================================
--- src/sas/sasgui/plottools/canvas.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/canvas.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,269 @@
+"""
+This module implements a faster canvas for plotting.
+it ovewrites some matplolib methods to allow printing on sys.platform=='win32'
+"""
+import wx
+import sys
+import logging
+from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
+from matplotlib.backend_bases import MouseEvent, RendererBase
+from matplotlib.backends.backend_wx import GraphicsContextWx, PrintoutWx
+from matplotlib.backends.backend_wx import RendererWx
+
+
+def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None):
+    """
+    Draw the image instance into the current axes;
+
+    :param x: is the distance in pixels from the left hand side of the canvas.
+    :param y: the distance from the origin.  That is, if origin is
+        upper, y is the distance from top.  If origin is lower, y
+        is the distance from bottom
+    :param im: the class`matplotlib._image.Image` instance
+    :param bbox: a class `matplotlib.transforms.Bbox` instance for clipping, or
+        None
+
+    """
+    pass
+
+
+def select(self):
+    """
+    """
+    pass
+
+
+def unselect(self):
+    """
+    """
+    pass
+
+
+def OnPrintPage(self, page):
+    """
+    override printPage of matplotlib
+    """
+    self.canvas.draw()
+    dc = self.GetDC()
+    try:
+        (ppw, pph) = self.GetPPIPrinter()  # printer's pixels per in
+    except:
+        ppw = 1
+        pph = 1
+    (pgw, _) = self.GetPageSizePixels()  # page size in pixels
+    (dcw, _) = dc.GetSize()
+    (grw, _) = self.canvas.GetSizeTuple()
+
+    # save current figure dpi resolution and bg color,
+    # so that we can temporarily set them to the dpi of
+    # the printer, and the bg color to white
+    bgcolor = self.canvas.figure.get_facecolor()
+    fig_dpi = self.canvas.figure.dpi
+
+    # draw the bitmap, scaled appropriately
+    vscale = float(ppw) / fig_dpi
+
+    # set figure resolution,bg color for printer
+    self.canvas.figure.dpi = ppw
+    self.canvas.figure.set_facecolor('#FFFFFF')
+
+    renderer = RendererWx(self.canvas.bitmap, self.canvas.figure.dpi)
+    self.canvas.figure.draw(renderer)
+    self.canvas.bitmap.SetWidth(int(self.canvas.bitmap.GetWidth() * vscale))
+    self.canvas.bitmap.SetHeight(int(self.canvas.bitmap.GetHeight() * vscale))
+    self.canvas.draw()
+
+    # page may need additional scaling on preview
+    page_scale = 1.0
+    if self.IsPreview():
+        page_scale = float(dcw) / pgw
+
+    # get margin in pixels = (margin in in) * (pixels/in)
+    top_margin = int(self.margin * pph * page_scale)
+    left_margin = int(self.margin * ppw * page_scale)
+
+    # set scale so that width of output is self.width inches
+    # (assuming grw is size of graph in inches....)
+    user_scale = (self.width * fig_dpi * page_scale) / float(grw)
+    dc.SetDeviceOrigin(left_margin, top_margin)
+    dc.SetUserScale(user_scale, user_scale)
+
+    # this cute little number avoid API inconsistencies in wx
+    try:
+        dc.DrawBitmap(self.canvas.bitmap, 0, 0)
+    except:
+        try:
+            dc.DrawBitmap(self.canvas.bitmap, (0, 0))
+        except:
+            logging.error(sys.exc_value)
+
+    # restore original figure  resolution
+    self.canvas.figure.set_facecolor(bgcolor)
+    # # used to be self.canvas.figure.dpi.set( fig_dpi)
+    self.canvas.figure.dpi = fig_dpi
+    self.canvas.draw()
+    return True
+
+GraphicsContextWx.select = select
+GraphicsContextWx.unselect = unselect
+PrintoutWx.OnPrintPage = OnPrintPage
+RendererBase.draw_image = draw_image
+
+
+class FigureCanvas(FigureCanvasWxAgg):
+    """
+    Add features to the wx agg canvas for better support of AUI and
+    faster plotting.
+    """
+
+    def __init__(self, *args, **kw):
+        super(FigureCanvas, self).__init__(*args, **kw)
+        self._isRendered = False
+
+        # Create an timer for handling draw_idle requests
+        # If there are events pending when the timer is
+        # complete, reset the timer and continue.  The
+        # alternative approach, binding to wx.EVT_IDLE,
+        # doesn't behave as nicely.
+        self.idletimer = wx.CallLater(1, self._onDrawIdle)
+        # panel information
+        self.panel = None
+        self.resizing = False
+        self.xaxis = None
+        self.yaxis = None
+        self.ndraw = 0
+        # Support for mouse wheel
+        self.Bind(wx.EVT_MOUSEWHEEL, self._onMouseWheel)
+
+    def set_panel(self, panel):
+        """
+        Set axes
+        """
+        # set panel
+        self.panel = panel
+        # set axes
+        self.xaxis = panel.subplot.xaxis
+        self.yaxis = panel.subplot.yaxis
+
+    def draw_idle(self, *args, **kwargs):
+        """
+        Render after a delay if no other render requests have been made.
+        """
+        self.panel.subplot.grid(self.panel.grid_on)
+        if self.panel.legend != None and self.panel.legend_pos_loc:
+            self.panel.legend._loc = self.panel.legend_pos_loc
+        self.idletimer.Restart(5, *args, **kwargs)  # Delay by 5 ms
+
+    def _onDrawIdle(self, *args, **kwargs):
+        """
+        """
+        if False and wx.GetApp().Pending():
+            self.idletimer.Restart(5, *args, **kwargs)
+        else:
+            # Draw plot, changes resizing too
+            self.draw(*args, **kwargs)
+            self.resizing = False
+
+    def _get_axes_switch(self):
+        """
+        """
+        # Check resize whether or not True
+        if self.panel.dimension == 3:
+            return
+
+        # This is for fast response when plot is being resized
+        if not self.resizing:
+            self.xaxis.set_visible(True)
+            self.yaxis.set_visible(True)
+            self.panel.schedule_full_draw('del')
+        else:
+            self.xaxis.set_visible(False)
+            self.yaxis.set_visible(False)
+            self.panel.schedule_full_draw('append')
+        # set the resizing back to default= False
+        self.set_resizing(False)
+
+    def set_resizing(self, resizing=False):
+        """
+        Setting the resizing
+        """
+        self.resizing = resizing
+        self.panel.set_resizing(False)
+
+    def draw(self, drawDC=None):
+        """
+        Render the figure using agg.
+        """
+        # Only draw if window is shown, otherwise graph will bleed through
+        # on the notebook style AUI widgets.
+        #    raise
+        fig = FigureCanvasWxAgg
+        if self.IsShownOnScreen() and self.ndraw != 1:
+            self._isRendered = True
+            self._get_axes_switch()
+            # import time
+            # st = time.time()
+            try:
+                fig.draw(self)
+            except ValueError:
+                logging.error(sys.exc_value)
+        else:
+            self._isRendered = False
+        if self.ndraw <= 1:
+            self.ndraw += 1
+
+    def _onMouseWheel(self, evt):
+        """Translate mouse wheel events into matplotlib events"""
+        # Determine mouse location
+        _, h = self.figure.canvas.get_width_height()
+        x = evt.GetX()
+        y = h - evt.GetY()
+
+        # Convert delta/rotation/rate into a floating point step size
+        delta = evt.GetWheelDelta()
+        rotation = evt.GetWheelRotation()
+        rate = evt.GetLinesPerAction()
+        # print "delta,rotation,rate",delta,rotation,rate
+        step = rate * float(rotation) / delta
+
+        # Convert to mpl event
+        evt.Skip()
+        self.scroll_event(x, y, step, guiEvent=evt)
+
+    def scroll_event(self, x, y, step=1, guiEvent=None):
+        """
+        Backend derived classes should call this function on any
+        scroll wheel event.  x,y are the canvas coords: 0,0 is lower,
+        left.  button and key are as defined in MouseEvent
+        """
+        button = 'up' if step >= 0 else 'down'
+        self._button = button
+        s = 'scroll_event'
+        event = MouseEvent(s, self, x, y, button, self._key, guiEvent=guiEvent)
+        setattr(event, 'step', step)
+        self.callbacks.process(s, event)
+        if step != 0:
+            self.panel.is_zoomed = True
+
+    def _onRightButtonDown(self, evt):
+        """
+        Overload the right button down call back to avoid a problem
+        with the context menu over matplotlib plots on linux.
+
+        :TODO: Investigate what the root cause of the problem is.
+
+        """
+        if sys.platform == 'linux2' or self.panel.dimension == 3:
+            evt.Skip()
+        else:
+            FigureCanvasWxAgg._onRightButtonDown(self, evt)
+            # This solves the focusing on rightclick.
+            # Todo: better design
+            self.panel.parent.set_plot_unfocus()
+            self.panel.on_set_focus(None)
+        return
+
+    # CRUFT: wx 3.0.0.0 on OS X doesn't release the mouse on leaving window
+    def _onLeave(self, evt):
+        if self.HasCapture(): self.ReleaseMouse()
+        super(FigureCanvas, self)._onLeave(evt)
Index: src/sas/sasgui/plottools/config.py
===================================================================
--- src/sas/sasgui/plottools/config.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/config.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,67 @@
+# This program is public domain
+"""
+Configure plotter for plottools.
+
+This must be imported first in __init__.py for plottools.
+
+If your application uses matplotlib outside plottools, then
+please do the following at the start of your application:
+
+    # Select matplotlib version and backend
+    import sas.sasgui.plottools.config
+
+Note that plottools requires particular versions of matplotlib
+and a particular backend.  As of this writing it is the WXAgg
+backend for matplotlib>=0.98.
+
+The plottools package uses pkg_resources if available to select
+the correct version of matplotlib.  If you need multiple matplotlib
+versions in your path, be sure to use "easy_install -m" for all
+of them.  If a version is installed without "-m" that does not
+meet the requirements, then pkg_resources.require() will fail,
+even if you have installed a suitable version with "-m".  In this
+case you will need to fix up your site-packages directory,
+probably by removing site-packages/matplotlib and the associated
+egg file for that version, and reinstalling with "-m".  You may
+also need to edit site-packages/easy-install.pth.
+"""
+import sys
+
+__all__ = []
+
+plot_version = "0.98"
+plot_backend = "WXAgg"
+
+# Sort out matplotlib version
+import matplotlib
+try:
+    import pkg_resources
+    pkg_resources.require("matplotlib>=" + plot_version)
+except:
+    from distutils.version import LooseVersion as Version
+    if Version(matplotlib.__version__) < Version(plot_version):
+        msg = "Matplotlib version must be %s or newer" % (plot_version, )
+        raise ImportError(msg)
+
+# Sort out matplotlib backend
+#import matplotlib
+if 'matplotlib.backends' not in sys.modules:
+    # if no backend yet, be sure to use the correct one
+    matplotlib.use(plot_backend)
+elif matplotlib.get_backend() != plot_backend:
+    # if a backend has already been selected, make sure it is the correct one.
+    raise ImportError("Matplotlib not using backend " + plot_backend)
+
+# set global plot style
+param = 'legend.handletextpad'
+if param not in matplotlib.rcParams: param = 'legend.handletextsep'
+matplotlib.rcParams[param] = 0.05
+matplotlib.rcParams['legend.numpoints'] = 1
+
+# this should happen after initial matplotlib configuration
+from .toolbar import NavigationToolBar
+from matplotlib.backends import backend_wxagg
+backend_wxagg.NavigationToolbar2WxAgg = NavigationToolBar
+
+# CRUFT: bumps 0.7.5.6 and older uses wrong toolbar
+backend_wxagg.NavigationToolbar2Wx = NavigationToolBar
Index: src/sas/sasgui/plottools/convert_units.py
===================================================================
--- src/sas/sasgui/plottools/convert_units.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/convert_units.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,78 @@
+"""
+    Convert units to strings that can be displayed
+    This is a cleaned up version of unitConverter.py
+"""
+import re
+import string
+
+def convert_unit(power, unit):
+    """
+        Convert units to strings that can be displayed
+    """
+    if power != 0:
+        if string.find(unit, "^") != -1:  # if the unit contains a powerer ^
+            toks = re.split("\^", unit)
+            if string.find(toks[0], "/") != -1 or \
+                string.find(toks[0], "-") != -1:
+                if power == 1:
+                    unit = unit
+                else:
+                    unit = "(" + unit + ")" + "^{" + str(power) + "}"
+            else:
+                if string.find(toks[1], "{") != -1:  # if found a {
+                    find_power_toks = re.split("{", toks[1])
+                    if string.find(find_power_toks[1], "}") != -1:  # found }
+                        unit_toks = re.split("}", find_power_toks[1])
+                        if string.find(unit_toks[0], ".") != -1:
+                            powerer = float(unit_toks[0]) * power
+                        elif string.find(unit_toks[0], "/") != -1:
+                            power_toks = re.split("/", unit_toks[0])
+                            powerer = power * int(power_toks[0])\
+                                            / int(power_toks[1])
+                        else:
+                            powerer = int(unit_toks[0]) * power
+
+                        if powerer == 1.0:
+                            unit = toks[0]
+                        elif powerer == 0.5:
+                            unit = toks[0] + "^{1/2}"
+                        elif powerer == -0.5:
+                            unit = toks[0] + "^{-1/2}"
+                        else:
+                            unit = toks[0] + "^{" + str(powerer) + "}"
+                else:
+                    raise ValueError, "missing } in unit expression"
+        else:  # no powerer
+            if  power != 1:
+                unit = "(" + unit + ")" + "^{" + str(power) + "}"
+    else:
+        raise ValueError, "empty unit ,enter a powerer different from zero"
+    return unit
+
+
+if __name__ == "__main__":
+    # pylint: disable=invalid-name
+    # Input   ->  new scale  ->  Output
+    unit1 = "A^{-1} "  #             x                    A^{-1}
+    unit2 = "A"  #                   x                     A
+    unit3 = "A"  #                   x^2                  A^{2}
+    unit4 = "A "  #                  1/x                  A^{-1}
+    unit5 = "A^{0.5} "  #        x^2                      A
+    unit9 = "m^{1/2}"  #         x^2               m
+
+    # If you don't recognize the pattern, give up
+    # and just put some parentheses around the unit and write the transoformation:
+
+    unit6 = "m/s"  #                x^2               (m/s)^{2}
+    unit7 = "m/s^{2}"  #         1/x                 (m/s^{2})^{-1}
+    unit8 = "m/s^{4}"  #         x^2               (m/s^{4})^{2}
+
+    print "this unit1 %s ,its powerer %s , and value %s" % (unit1, 1, convert_unit(1, unit1))
+    print "this unit2 %s ,its powerer %s , and value %s" % (unit2, 1, convert_unit(1, unit2))
+    print "this unit3 %s ,its powerer %s , and value %s" % (unit3, 2, convert_unit(2, unit3))
+    print "this unit4 %s ,its powerer %s , and value %s" % (unit4, -1, convert_unit(-1, unit4))
+    print "this unit5 %s ,its powerer %s , and value %s" % (unit5, 2, convert_unit(2, unit5))
+    print "this unit6 %s ,its powerer %s , and value %s" % (unit6, 2, convert_unit(2, unit6))
+    print "this unit7 %s ,its powerer %s , and value %s" % (unit7, -1, convert_unit(-1, unit7))
+    print "this unit8 %s ,its powerer %s , and value %s" % (unit8, 2, convert_unit(2, unit8))
+    print "this unit9 %s ,its powerer %s , and value %s" % (unit9, 2, convert_unit(2, unit9))
Index: src/sas/sasgui/plottools/fitDialog.py
===================================================================
--- src/sas/sasgui/plottools/fitDialog.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/fitDialog.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,697 @@
+import wx
+from plottables import Theory1D
+import math
+import numpy
+import fittings
+import transform
+import sys
+
+# Linear fit panel size
+if sys.platform.count("win32") > 0:
+    FONT_VARIANT = 0
+    PNL_WIDTH = 450
+    PNL_HEIGHT = 500
+else:
+    FONT_VARIANT = 1
+    PNL_WIDTH = 500
+    PNL_HEIGHT = 500
+RG_ON = True
+
+def format_number(value, high=False):
+    """
+    Return a float in a standardized, human-readable formatted string
+    """
+    try:
+        value = float(value)
+    except:
+        output = "NaN"
+        return output.lstrip().rstrip()
+
+    if high:
+        output = "%-6.4g" % value
+
+    else:
+        output = "%-5.3g" % value
+    return output.lstrip().rstrip()
+
+
+class LinearFit(wx.Dialog):
+    def __init__(self, parent, plottable, push_data, transform, title):
+        """
+        Dialog window pops- up when select Linear fit on Context menu
+        Displays fitting parameters
+        """
+        wx.Dialog.__init__(self, parent, title=title,
+                           size=(PNL_WIDTH, 350))
+        self.parent = parent
+        self.transform = transform
+        # Font
+        self.SetWindowVariant(variant=FONT_VARIANT)
+        # Registered owner for close event
+        self._registered_close = None
+
+        # dialog panel self call function to plot the fitting function
+        self.push_data = push_data
+        # dialog self plottable
+        self.plottable = plottable
+        self.rg_on = False
+        # Receive transformations of x and y
+        self.xLabel, self.yLabel, self.Avalue, self.Bvalue, \
+               self.ErrAvalue, self.ErrBvalue, self.Chivalue = self.transform()
+
+        # Dialog interface
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        sizer = wx.GridBagSizer(5, 5)
+        _BOX_WIDTH = 100
+
+        self.tcA = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
+        self.tcA.SetToolTipString("Fit value for the slope parameter.")
+        self.tcErrA = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
+        self.tcErrA.SetToolTipString("Error on the slope parameter.")
+        self.tcB = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
+        self.tcA.SetToolTipString("Fit value for the constant parameter.")
+        self.tcErrB = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
+        self.tcErrB.SetToolTipString("Error on the constant parameter.")
+        self.tcChi = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
+        self.tcChi.SetToolTipString("Chi^2 over degrees of freedom.")
+        self.xminFit = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
+        msg = "Enter the minimum value on "
+        msg += "the x-axis to be included in the fit."
+        self.xminFit.SetToolTipString(msg)
+        self.xmaxFit = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
+        msg = "Enter the maximum value on "
+        msg += " the x-axis to be included in the fit."
+        self.xmaxFit.SetToolTipString(msg)
+        self.initXmin = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
+        msg = "Minimum value on the x-axis for the plotted data."
+        self.initXmin.SetToolTipString(msg)
+        self.initXmax = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))
+        msg = "Maximum value on the x-axis for the plotted data."
+        self.initXmax.SetToolTipString(msg)
+
+        # Make the info box not editable
+        # _BACKGROUND_COLOR = '#ffdf85'
+        _BACKGROUND_COLOR = self.GetBackgroundColour()
+        self.initXmin.SetEditable(False)
+        self.initXmin.SetBackgroundColour(_BACKGROUND_COLOR)
+        self.initXmax.SetEditable(False)
+        self.initXmax.SetBackgroundColour(_BACKGROUND_COLOR)
+
+        # Buttons on the bottom
+        self.bg_on = False
+        self.static_line_1 = wx.StaticLine(self, -1)
+        self.btFit = wx.Button(self, -1, 'Fit')
+        self.btFit.Bind(wx.EVT_BUTTON, self._onFit)
+        self.btFit.SetToolTipString("Perform fit.")
+        self.btClose = wx.Button(self, wx.ID_CANCEL, 'Close')
+        self.btClose.Bind(wx.EVT_BUTTON, self._on_close)
+        if RG_ON:
+            if (self.yLabel == "ln(y)" or self.yLabel == "ln(y*x)") and \
+                    (self.xLabel == "x^(2)"):
+                self.rg_on = True
+            if (self.xLabel == "x^(4)") and (self.yLabel == "y*x^(4)"):
+                self.bg_on = True
+        # Intro
+        explanation = "Perform fit for y(x) = ax + b"
+        if self.bg_on:
+            param_a = 'Background (= Parameter a)'
+        else:
+            param_a = 'Parameter a'
+        vbox.Add(sizer)
+        ix = 0
+        iy = 1
+        sizer.Add(wx.StaticText(self, -1, explanation), (iy, ix),
+                  (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+        iy += 2
+        sizer.Add(wx.StaticText(self, -1, param_a), (iy, ix),
+                  (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+        ix += 1
+        sizer.Add(self.tcA, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ix += 1
+        sizer.Add(wx.StaticText(self, -1, '+/-'),
+                  (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ix += 1
+        sizer.Add(self.tcErrA, (iy, ix), (1, 1),
+                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        iy += 1
+        ix = 0
+        sizer.Add(wx.StaticText(self, -1, 'Parameter b'), (iy, ix), (1, 1),
+                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+        ix += 1
+        sizer.Add(self.tcB, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ix += 1
+        sizer.Add(wx.StaticText(self, -1, '+/-'), (iy, ix),
+                  (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ix += 1
+        sizer.Add(self.tcErrB, (iy, ix), (1, 1),
+                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        iy += 1
+        ix = 0
+        sizer.Add(wx.StaticText(self, -1, 'Chi2/dof'), (iy, ix), (1, 1),
+                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+        ix += 1
+        sizer.Add(self.tcChi, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        iy += 2
+        ix = 1
+        sizer.Add(wx.StaticText(self, -1, 'Min'), (iy, ix), (1, 1),
+                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ix += 2
+        sizer.Add(wx.StaticText(self, -1, 'Max'), (iy, ix),
+                  (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+        iy += 1
+        ix = 0
+        sizer.Add(wx.StaticText(self, -1, 'Maximum range (linear scale)'),
+                  (iy, ix), (1, 1),
+                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+        ix += 1
+        sizer.Add(self.initXmin, (iy, ix), (1, 1),
+                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ix += 2
+        sizer.Add(self.initXmax, (iy, ix), (1, 1),
+                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+        iy += 1
+        ix = 0
+        sizer.Add(wx.StaticText(self, -1, 'Fit range of ' + self.xLabel),
+                  (iy, ix), (1, 1),
+                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+        ix += 1
+        sizer.Add(self.xminFit, (iy, ix), (1, 1),
+                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ix += 2
+        sizer.Add(self.xmaxFit, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        if self.rg_on:
+            self.SetSize((PNL_WIDTH, PNL_HEIGHT))
+            I0_stxt = wx.StaticText(self, -1, 'I(q=0)')
+            self.I0_tctr = wx.TextCtrl(self, -1, '')
+            self.I0_tctr.SetEditable(False)
+            self.I0_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
+            self.I0err_tctr = wx.TextCtrl(self, -1, '')
+            self.I0err_tctr.SetEditable(False)
+            self.I0err_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
+            Rg_stxt = wx.StaticText(self, -1, 'Rg [A]')
+            Rg_stxt.Show(self.yLabel == "ln(y)")
+            self.Rg_tctr = wx.TextCtrl(self, -1, '')
+            self.Rg_tctr.SetEditable(False)
+            self.Rg_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
+            self.Rg_tctr.Show(self.yLabel == "ln(y)")
+            self.Rgerr_tctr = wx.TextCtrl(self, -1, '')
+            self.Rgerr_tctr.SetEditable(False)
+            self.Rgerr_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
+            self.Rgerr_tctr.Show(self.yLabel == "ln(y)")
+            self.Rgerr_pm = wx.StaticText(self, -1, '+/-')
+            self.Rgerr_pm.Show(self.yLabel == "ln(y)")
+            Diameter_stxt = wx.StaticText(self, -1, 'Rod Diameter [A]')
+            Diameter_stxt.Show(self.yLabel == "ln(y*x)")
+            self.Diameter_tctr = wx.TextCtrl(self, -1, '')
+            self.Diameter_tctr.SetEditable(False)
+            self.Diameter_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
+            self.Diameter_tctr.Show(self.yLabel == "ln(y*x)")
+            self.Diameter_pm = wx.StaticText(self, -1, '+/-')
+            self.Diameter_pm.Show(self.yLabel == "ln(y*x)")
+            self.Diametererr_tctr = wx.TextCtrl(self, -1, '')
+            self.Diametererr_tctr.SetEditable(False)
+            self.Diametererr_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
+            self.Diametererr_tctr.Show(self.yLabel == "ln(y*x)")
+            RgQmin_stxt = wx.StaticText(self, -1, 'Rg*Qmin')
+            self.RgQmin_tctr = wx.TextCtrl(self, -1, '')
+            self.RgQmin_tctr.SetEditable(False)
+            self.RgQmin_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
+            RgQmax_stxt = wx.StaticText(self, -1, 'Rg*Qmax')
+            self.RgQmax_tctr = wx.TextCtrl(self, -1, '')
+            self.RgQmax_tctr.SetEditable(False)
+            self.RgQmax_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
+
+            iy += 2
+            ix = 0
+            sizer.Add(I0_stxt, (iy, ix), (1, 1),
+                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+            ix += 1
+            sizer.Add(self.I0_tctr, (iy, ix), (1, 1),
+                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            ix += 1
+            sizer.Add(wx.StaticText(self, -1, '+/-'), (iy, ix),
+                      (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            ix += 1
+            sizer.Add(self.I0err_tctr, (iy, ix), (1, 1),
+                      wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+            iy += 1
+            ix = 0
+            sizer.Add(Rg_stxt, (iy, ix), (1, 1),
+                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+            ix += 1
+            sizer.Add(self.Rg_tctr, (iy, ix), (1, 1),
+                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+            ix += 1
+            sizer.Add(self.Rgerr_pm, (iy, ix),
+                      (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            ix += 1
+            sizer.Add(self.Rgerr_tctr, (iy, ix), (1, 1),
+                      wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            iy += 1
+            ix = 0
+            sizer.Add(Diameter_stxt, (iy, ix), (1, 1),
+                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+            ix += 1
+            sizer.Add(self.Diameter_tctr, (iy, ix), (1, 1),
+                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+            ix += 1
+            sizer.Add(self.Diameter_pm, (iy, ix),
+                      (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            ix += 1
+            sizer.Add(self.Diametererr_tctr, (iy, ix), (1, 1),
+                      wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            iy += 1
+            ix = 0
+            sizer.Add(RgQmin_stxt, (iy, ix), (1, 1),
+                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+            ix += 1
+            sizer.Add(self.RgQmin_tctr, (iy, ix), (1, 1),
+                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            iy += 1
+            ix = 0
+            sizer.Add(RgQmax_stxt, (iy, ix), (1, 1),
+                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+            ix += 1
+            sizer.Add(self.RgQmax_tctr, (iy, ix), (1, 1),
+                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+        iy += 1
+        ix = 1
+
+        vbox.Add(self.static_line_1, 0, wx.EXPAND, 0)
+        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        sizer_button.Add(self.btFit, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
+        sizer_button.Add(self.btClose, 0,
+                         wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
+        vbox.Add(sizer_button, 0, wx.EXPAND | wx.BOTTOM | wx.TOP, 10)
+
+        sizer.Add(self.btFit, (iy, ix), (1, 1), wx.LEFT | wx.ADJUST_MINSIZE, 0)
+        # panel.SetSizer(sizer)
+        self.SetSizer(vbox)
+        self.Centre()
+        # Receives the type of model for the fitting
+        from LineModel import LineModel
+        self.model = LineModel()
+        # Display the fittings values
+        self.default_A = self.model.getParam('A')
+        self.default_B = self.model.getParam('B')
+        self.cstA = fittings.Parameter(self.model, 'A', self.default_A)
+        self.cstB = fittings.Parameter(self.model, 'B', self.default_B)
+
+        # Set default value of parameter in fit dialog
+        if self.Avalue == None:
+            self.tcA.SetValue(format_number(self.default_A))
+        else:
+            self.tcA.SetLabel(format_number(self.Avalue))
+        if self.Bvalue == None:
+            self.tcB.SetValue(format_number(self.default_B))
+        else:
+            self.tcB.SetLabel(format_number(self.Bvalue))
+        if self.ErrAvalue == None:
+            self.tcErrA.SetLabel(format_number(0.0))
+        else:
+            self.tcErrA.SetLabel(format_number(self.ErrAvalue))
+        if self.ErrBvalue == None:
+            self.tcErrB.SetLabel(format_number(0.0))
+        else:
+            self.tcErrB.SetLabel(format_number(self.ErrBvalue))
+        if self.Chivalue == None:
+            self.tcChi.SetLabel(format_number(0.0))
+        else:
+            self.tcChi.SetLabel(format_number(self.Chivalue))
+        if self.plottable.x != []:
+            # store the values of View in self.x,self.y,self.dx,self.dy
+            self.x, self.y, self.dx, \
+                     self.dy = self.plottable.returnValuesOfView()
+            try:
+                self.mini = self.floatForwardTransform(min(self.x))
+            except:
+                self.mini = "Invalid"
+            try:
+                self.maxi = self.floatForwardTransform(max(self.x))
+            except:
+                self.maxi = "Invalid"
+
+            self.initXmin.SetValue(format_number(min(self.plottable.x)))
+            self.initXmax.SetValue(format_number(max(self.plottable.x)))
+            self.mini = min(self.x)
+            self.maxi = max(self.x)
+            self.xminFit.SetValue(format_number(self.mini))
+            self.xmaxFit.SetValue(format_number(self.maxi))
+
+    def register_close(self, owner):
+        """
+        Method to register the close event to a parent
+        window that needs notification when the dialog
+        is closed
+
+        :param owner: parent window
+
+        """
+        self._registered_close = owner
+
+    def _on_close(self, event):
+        """
+        Close event.
+        Notify registered owner if available.
+        """
+        event.Skip()
+        if self._registered_close is not None:
+            self._registered_close()
+
+    def _onFit(self, event):
+        """
+        Performs the fit. Receive an event when clicking on
+        the button Fit.Computes chisqr ,
+        A and B parameters of the best linear fit y=Ax +B
+        Push a plottable to
+        """
+        tempx = []
+        tempy = []
+        tempdy = []
+
+        # Check if View contains a x array .we online fit when x exits
+        # makes transformation for y as a line to fit
+        if self.x != []:
+            if self.checkFitValues(self.xminFit) == True:
+                # Check if the field of Fit Dialog contain values
+                # and use the x max and min of the user
+                if not self._checkVal(self.xminFit, self.xmaxFit):
+                    return
+                xminView = float(self.xminFit.GetValue())
+                xmaxView = float(self.xmaxFit.GetValue())
+                xmin = xminView
+                xmax = xmaxView
+                # Store the transformed values of view x, y,dy
+                # in variables  before the fit
+                if self.yLabel.lower() == "log10(y)":
+                    if self.xLabel.lower() == "log10(x)":
+                        for i in range(len(self.x)):
+                            if self.x[i] >= math.log10(xmin):
+                                tempy.append(math.log10(self.y[i]))
+                                tempdy.append(transform.errToLogX(self.y[i], 0, self.dy[i], 0))
+                    else:
+                        for i in range(len(self.y)):
+                            tempy.append(math.log10(self.y[i]))
+                            tempdy.append(transform.errToLogX(self.y[i], 0, self.dy[i], 0))
+                else:
+                    tempy = self.y
+                    tempdy = self.dy
+
+                if self.xLabel.lower() == "log10(x)":
+                    for x_i in self.x:
+                        if x_i >= math.log10(xmin):
+                            tempx.append(math.log10(x_i))
+                else:
+                    tempx = self.x
+
+                # Find the fitting parameters
+                # Always use the same defaults, so that fit history
+                # doesn't play a role!
+                self.cstA = fittings.Parameter(self.model, 'A', self.default_A)
+                self.cstB = fittings.Parameter(self.model, 'B', self.default_B)
+
+                if self.xLabel.lower() == "log10(x)":
+                    tempdy = numpy.asarray(tempdy)
+                    tempdy[tempdy == 0] = 1
+                    chisqr, out, cov = fittings.sasfit(self.model,
+                                                       [self.cstA, self.cstB],
+                                                       tempx, tempy,
+                                                       tempdy,
+                                                       math.log10(xmin),
+                                                       math.log10(xmax))
+                else:
+                    tempdy = numpy.asarray(tempdy)
+                    tempdy[tempdy == 0] = 1
+                    chisqr, out, cov = fittings.sasfit(self.model,
+                                                       [self.cstA, self.cstB],
+                                                       tempx, tempy, tempdy,
+                                                       xminView, xmaxView)
+                # Use chi2/dof
+                if len(tempx) > 0:
+                    chisqr = chisqr / len(tempx)
+
+                # Check that cov and out are iterable before displaying them
+                if cov == None:
+                    errA = 0.0
+                    errB = 0.0
+                else:
+                    errA = math.sqrt(cov[0][0])
+                    errB = math.sqrt(cov[1][1])
+                if out == None:
+                    cstA = 0.0
+                    cstB = 0.0
+                else:
+                    cstA = out[0]
+                    cstB = out[1]
+                # Reset model with the right values of A and B
+                self.model.setParam('A', float(cstA))
+                self.model.setParam('B', float(cstB))
+
+                tempx = []
+                tempy = []
+                y_model = 0.0
+                # load tempy with the minimum transformation
+
+                if self.xLabel == "log10(x)":
+                    y_model = self.model.run(math.log10(xmin))
+                    tempx.append(xmin)
+                else:
+                    y_model = self.model.run(xminView)
+                    tempx.append(xminView)
+
+                if self.yLabel == "log10(y)":
+                    tempy.append(math.pow(10, y_model))
+                else:
+                    tempy.append(y_model)
+
+                # load tempy with the maximum transformation
+                if self.xLabel == "log10(x)":
+                    y_model = self.model.run(math.log10(xmax))
+                    tempx.append(xmax)
+                else:
+                    y_model = self.model.run(xmaxView)
+                    tempx.append(xmaxView)
+
+                if self.yLabel == "log10(y)":
+                    tempy.append(math.pow(10, y_model))
+                else:
+                    tempy.append(y_model)
+                # Set the fit parameter display when  FitDialog is opened again
+                self.Avalue = cstB
+                self.Bvalue = cstA
+                self.ErrAvalue = errA
+                self.ErrBvalue = errB
+                self.Chivalue = chisqr
+                self.push_data(tempx, tempy, xminView, xmaxView,
+                               xmin, xmax, self._ongetValues())
+
+                # Display the fitting value on the Fit Dialog
+                self._onsetValues(cstB, cstA, errA, errB, chisqr)
+
+    def _onsetValues(self, cstA, cstB, errA, errB, Chi):
+        """
+        Display  the value on fit Dialog
+        """
+        rg = None
+        self.tcA.SetValue(format_number(cstA))
+        self.tcB.SetValue(format_number(cstB))
+        self.tcErrA.SetValue(format_number(errA))
+        self.tcErrB.SetValue(format_number(errB))
+        self.tcChi.SetValue(format_number(Chi))
+        if self.rg_on:
+            if self.Rg_tctr.IsShown():
+                rg = numpy.sqrt(-3 * float(cstA))
+                value = format_number(rg)
+                self.Rg_tctr.SetValue(value)
+                if self.I0_tctr.IsShown():
+                    val = numpy.exp(cstB)
+                    self.I0_tctr.SetValue(format_number(val))
+            if self.Rgerr_tctr.IsShown():
+                if rg != None and rg != 0:
+                    value = format_number(3 * float(cstA) / (2 * rg))
+                else:
+                    value = ''
+                self.Rgerr_tctr.SetValue(value)
+                if self.I0err_tctr.IsShown():
+                    val = numpy.abs(numpy.exp(cstB) - numpy.exp(cstB + errB))
+                    self.I0err_tctr.SetValue(format_number(val))
+            if self.Diameter_tctr.IsShown():
+                rg = 4 * numpy.sqrt(-float(cstA))
+                value = format_number(rg)
+                self.Diameter_tctr.SetValue(value)
+            if self.Diametererr_tctr.IsShown():
+                if rg != None and rg != 0:
+                    value = format_number(8 * float(cstA) / rg)
+                else:
+                    value = ''
+                self.Diametererr_tctr.SetValue(value)
+            if self.RgQmin_tctr.IsShown():
+                value = format_number(rg * self.mini)
+                self.RgQmin_tctr.SetValue(value)
+            if self.RgQmax_tctr.IsShown():
+                value = format_number(rg * self.maxi)
+                self.RgQmax_tctr.SetValue(value)
+
+    def _ongetValues(self):
+        """
+        Display  the value on fit Dialog
+        """
+        return self.Avalue, self.Bvalue, self.ErrAvalue, \
+                            self.ErrBvalue, self.Chivalue
+
+    def _checkVal(self, usermin, usermax):
+        """
+        Ensure that fields parameter contains a min and a max value
+        within x min and x max range
+        """
+        self.mini = float(self.xminFit.GetValue())
+        self.maxi = float(self.xmaxFit.GetValue())
+        flag = True
+        try:
+            mini = float(usermin.GetValue())
+            maxi = float(usermax.GetValue())
+            if mini < maxi:
+                usermin.SetBackgroundColour(wx.WHITE)
+                usermin.Refresh()
+            else:
+                flag = False
+                usermin.SetBackgroundColour("pink")
+                usermin.Refresh()
+        except:
+            # Check for possible values entered
+            flag = False
+            usermin.SetBackgroundColour("pink")
+            usermin.Refresh()
+
+        return flag
+
+    def floatForwardTransform(self, x):
+        """
+        transform a float.
+        """
+        # TODO: refactor this with proper object-oriented design
+        # This code stinks.
+        if self.xLabel == "x":
+            return transform.toX(x)
+        if self.xLabel == "x^(2)":
+            return transform.toX2(x)
+        if self.xLabel == "ln(x)":
+            return transform.toLogX(x)
+        if self.xLabel == "log10(x)":
+            return math.log10(x)
+
+    def floatTransform(self, x):
+        """
+        transform a float.It is use to determine the x.
+        View min and x.View max for values
+        not in x
+        """
+        # TODO: refactor this with proper object-oriented design
+        # This code stinks.
+        if self.xLabel == "x":
+            return transform.toX(x)
+        if self.xLabel == "x^(2)":
+            return transform.toX2(x)
+        if self.xLabel == "ln(x)":
+            return transform.toLogX(x)
+        if self.xLabel == "log10(x)":
+            if x > 0:
+                return x
+            else:
+                raise ValueError, "cannot compute log of a negative number"
+
+    def floatInvTransform(self, x):
+        """
+        transform a float.It is use to determine the x.View min and x.View
+        max for values not in x
+
+        """
+        # TODO: refactor this. This is just a hack to make the
+        # functionality work without rewritting the whole code
+        # with good design (which really should be done...).
+        if self.xLabel == "x^(2)":
+            return math.sqrt(x)
+        elif self.xLabel == "log10(x)":
+            return math.pow(10, x)
+        elif self.xLabel == "ln(x)":
+            return math.exp(x)
+        return x
+
+    def checkFitValues(self, item):
+        """
+            Check the validity of input values
+        """
+        flag = True
+        value = item.GetValue()
+        # Check for possible values entered
+        if self.xLabel == "log10(x)":
+            if float(value) > 0:
+                item.SetBackgroundColour(wx.WHITE)
+                item.Refresh()
+            else:
+                flag = False
+                item.SetBackgroundColour("pink")
+                item.Refresh()
+        return flag
+
+    def setFitRange(self, xmin, xmax, xminTrans, xmaxTrans):
+        """
+        Set fit parameters
+        """
+        self.xminFit.SetValue(format_number(xmin))
+        self.xmaxFit.SetValue(format_number(xmax))
+
+    def set_fit_region(self, xmin, xmax):
+        """
+        Set the fit region
+        :param xmin: minimum x-value to be included in fit
+        :param xmax: maximum x-value to be included in fit
+        """
+        # Check values
+        try:
+            float(xmin)
+            float(xmax)
+        except:
+            msg = "LinearFit.set_fit_region: fit range must be floats"
+            raise ValueError, msg
+        self.xminFit.SetValue(format_number(xmin))
+        self.xmaxFit.SetValue(format_number(xmax))
+
+
+class MyApp(wx.App):
+    """
+        Test application
+    """
+    def OnInit(self):
+        """
+            Test application initialization
+        """
+        wx.InitAllImageHandlers()
+        plot = Theory1D([], [])
+        dialog = LinearFit(parent=None, plottable=plot,
+                           push_data=self.onFitDisplay,
+                           transform=self.returnTrans,
+                           title='Linear Fit')
+        if dialog.ShowModal() == wx.ID_OK:
+            pass
+        dialog.Destroy()
+        return 1
+
+    def onFitDisplay(self, tempx, tempy, xminView, xmaxView, xmin, xmax, func):
+        """
+            Test application dummy method
+        """
+        pass
+
+    def returnTrans(self):
+        """
+            Test application dummy method
+        """
+        return '', '', 0, 0, 0, 0, 0
Index: src/sas/sasgui/plottools/fittings.py
===================================================================
--- src/sas/sasgui/plottools/fittings.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/fittings.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,95 @@
+"""
+"""
+from scipy import optimize
+
+
+class Parameter(object):
+    """
+    Class to handle model parameters
+    """
+    def __init__(self, model, name, value=None):
+        self.model = model
+        self.name = name
+        if not value == None:
+            self.model.setParam(self.name, value)
+
+    def set(self, value):
+        """
+            Set the value of the parameter
+        """
+        self.model.setParam(self.name, value)
+
+    def __call__(self):
+        """
+            Return the current value of the parameter
+        """
+        return self.model.getParam(self.name)
+
+
+def sasfit(model, pars, x, y, err_y, qmin=None, qmax=None):
+    """
+    Fit function
+
+    :param model: sas model object
+    :param pars: list of parameters
+    :param x: vector of x data
+    :param y: vector of y data
+    :param err_y: vector of y errors
+    """
+    def f(params):
+        """
+        Calculates the vector of residuals for each point
+        in y for a given set of input parameters.
+
+        :param params: list of parameter values
+        :return: vector of residuals
+        """
+        i = 0
+        for p in pars:
+            p.set(params[i])
+            i += 1
+
+        residuals = []
+        for j in range(len(x)):
+            if x[j] >= qmin and x[j] <= qmax:
+                residuals.append((y[j] - model.runXY(x[j])) / err_y[j])
+        return residuals
+
+    def chi2(params):
+        """
+        Calculates chi^2
+
+        :param params: list of parameter values
+
+        :return: chi^2
+
+        """
+        sum = 0
+        res = f(params)
+        for item in res:
+            sum += item * item
+        return sum
+
+    p = [param() for param in pars]
+    out, cov_x, info, mesg, success = optimize.leastsq(f, p, full_output=1)
+    # Calculate chi squared
+    if len(pars) > 1:
+        chisqr = chi2(out)
+    elif len(pars) == 1:
+        chisqr = chi2([out])
+
+    return chisqr, out, cov_x
+
+
+def calcCommandline(event):
+    # Testing implementation
+    # Fit a Line model
+    from LineModel import LineModel
+    line = LineModel()
+    cstA = Parameter(line, 'A', event.cstA)
+    cstB = Parameter(line, 'B', event.cstB)
+    y = line.run()
+    chisqr, out, cov = sasfit(line, [cstA, cstB], event.x, y, 0)
+    # print "Output parameters:", out
+    print "The right answer is [70.0, 1.0]"
+    print chisqr, out, cov
Index: src/sas/sasgui/plottools/plottable_interactor.py
===================================================================
--- src/sas/sasgui/plottools/plottable_interactor.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/plottable_interactor.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,231 @@
+"""
+    This module allows more interaction with the plot
+"""
+from BaseInteractor import _BaseInteractor
+
+class PointInteractor(_BaseInteractor):
+    """
+    """
+    def __init__(self, base, axes, color='black', zorder=3, id=''):
+        """
+        """
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        self.zorder = zorder
+        self.id = id
+        self.color = color
+        self.colorlist = ['b', 'g', 'r', 'c', 'm', 'y', 'k']
+        self.symbollist = ['o', 'x', '^', 'v', '<', '>',
+                           '+', 's', 'd', 'D', 'h', 'H', 'p', '-', '--',
+                           'vline', 'step']
+        self.markersize = None
+        self.marker = None
+        self.marker2 = None
+        self._button_down = False
+        self._context_menu = False
+        self._dragged = False
+        self.connect_markers([self.axes])
+
+    def _color(self, c):
+        """Return a particular colour"""
+        return self.colorlist[c % len(self.colorlist)]
+
+    def _symbol(self, s):
+        """Return a particular symbol"""
+        return self.symbollist[s % len(self.symbollist)]
+
+    def points(self, x, y, dx=None, dy=None, color=0, symbol=0, zorder=1,
+               markersize=5, label=None, hide_error=False):
+        """
+        """
+        # Draw curve
+        if self._symbol(symbol) == '-' or self._symbol(symbol) == '--':
+            l_width = markersize * 0.4
+            return self.curve(x=x, y=y, color=color, symbol=symbol,
+                              label=label, width=l_width)
+            # return
+        if self._symbol(symbol) == 'vline':
+            l_width = markersize * 0.4
+            return self.vline(x=x, y=y, color=color, label=label, width=l_width)
+        if self._symbol(symbol) == 'step':
+            l_width = markersize * 0.4
+            return self.step(x=x, y=y, color=color, label=label, width=l_width)
+        if not self.marker == None:
+            self.base.connect.clear([self.marker])
+        self.color = self._color(color)
+        if self.markersize != None:
+            markersize = self.markersize
+        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
+        if dx != None and type(dx) == type(()):
+            dx = nx.vstack((x - dx[0], dx[1] - x)).transpose()
+        if dy != None and type(dy) == type(()):
+            dy = nx.vstack((y - dy[0], dy[1] - y)).transpose()
+
+        if dx == None and dy == None:
+            # zorder = 1
+            self.marker = self.axes.plot(x, y, color=self.color,
+                                         marker=self._symbol(symbol),
+                                         markersize=markersize,
+                                         linestyle='', label=label,
+                                         zorder=zorder)[0]
+        else:
+
+            if hide_error:
+                # zorder = 1
+                self.marker = self.axes.plot(x, y, color=self.color,
+                                             marker=self._symbol(symbol),
+                                             markersize=markersize,
+                                             linestyle='', label=label,
+                                             zorder=1)[0]
+            else:
+                # zorder = 2
+                self.marker = self.axes.errorbar(x, y, yerr=dy,
+                                                 xerr=None,
+                                                 ecolor=self.color,
+                                                 color=self.color,
+                                                 capsize=2,
+                                                 linestyle='',
+                                                 barsabove=False,
+                                                 marker=self._symbol(symbol),
+                                                 markersize=markersize,
+                                                 lolims=False, uplims=False,
+                                                 xlolims=False, xuplims=False,
+                                                 label=label,
+                                                 zorder=1)[0]
+
+        self.connect_markers([self.marker])
+        self.update()
+
+    def curve(self, x, y, dy=None, color=0, symbol=0, zorder=10,
+              label=None, width=2.0):
+        """
+        """
+        if not self.marker == None:
+            self.base.connect.clear([self.marker])
+        self.color = self._color(color)
+        self.marker = self.axes.plot(x, y, color=self.color, lw=width,
+                                     marker='', linestyle=self._symbol(symbol),
+                                     label=label, zorder=zorder)[0]
+
+        self.connect_markers([self.marker])
+        self.update()
+
+
+    def vline(self, x, y, dy=None, color=0, symbol=0, zorder=1,
+              label=None, width=2.0):
+        """
+        """
+        if not self.marker == None:
+            self.base.connect.clear([self.marker])
+        self.color = self._color(color)
+        if min(y) < 0:
+            y_min = 0.0
+        else:
+            y_min = min(y) * 9 / 10
+        self.marker = self.axes.vlines(x=x, ymin=y_min, ymax=y,
+                                       color=self.color,
+                                       linestyle='-', label=label,
+                                       lw=width, zorder=zorder)
+        self.connect_markers([self.marker])
+        self.update()
+
+    def step(self, x, y, dy=None, color=0, symbol=0, zorder=1,
+             label=None, width=2.0):
+        """
+        """
+        if not self.marker == None:
+            self.base.connect.clear([self.marker])
+        self.color = self._color(color)
+        self.marker = self.axes.step(x, y, color=self.color,
+                                     marker='',
+                                     linestyle='-', label=label,
+                                     lw=width, zorder=zorder)[0]
+        self.connect_markers([self.marker])
+        self.update()
+
+    def connect_markers(self, markers):
+        """
+        Connect markers to callbacks
+        """
+        for h in markers:
+            connect = self.base.connect
+            connect('enter', h, self._on_enter)
+            connect('leave', h, self._on_leave)
+            connect('click', h, self._on_click)
+            connect('release', h, self._on_release)
+            connect('key', h, self.onKey)
+
+    def clear(self):
+        print "plottable_interactor.clear()"
+
+    def _on_click(self, evt):
+        """
+        Called when a mouse button is clicked
+        from within the boundaries of an artist.
+        """
+        if self._context_menu == True:
+            self._context_menu = False
+            evt.artist = self.marker
+            self._on_leave(evt)
+
+    def _on_release(self, evt):
+        """
+        Called when a mouse button is released
+        within the boundaries of an artist
+        """
+        # Check to see whether we are about to pop
+        # the context menu up
+        if evt.button == 3:
+            self._context_menu = True
+
+    def _on_enter(self, evt):
+        """
+        Called when we are entering the boundaries
+        of an artist.
+        """
+        if not evt.artist.__class__.__name__ == "AxesSubplot":
+            self.base.plottable_selected(self.id)
+
+            if evt.artist.get_color() == 'y':
+                try:
+                    evt.artist.set_color('b')
+                except:
+                    evt.artist.set_color_cycle('b')
+                if hasattr(evt.artist, "set_facecolor"):
+                    evt.artist.set_facecolor('b')
+                if hasattr(evt.artist, "set_edgecolor"):
+                    evt.artist.set_edgecolor('b')
+            else:
+                try:
+                    evt.artist.set_color('y')
+                except:
+                    evt.artist.set_color_cycle('y')
+                if hasattr(evt.artist, "set_facecolor"):
+                    evt.artist.set_facecolor('y')
+                if hasattr(evt.artist, "set_edgecolor"):
+                    evt.artist.set_edgecolor('y')
+
+            self.axes.figure.canvas.draw_idle()
+
+    def _on_leave(self, evt):
+        """
+        Called when we are leaving the boundaries
+        of an artist.
+        """
+        if not evt.artist.__class__.__name__ == "AxesSubplot":
+            if self._context_menu == False:
+                self.base.plottable_selected(None)
+                try:
+                    evt.artist.set_color(self.color)
+                except:
+                    evt.artist.set_color_cycle(self.color)
+                if hasattr(evt.artist, "set_facecolor"):
+                    evt.artist.set_facecolor(self.color)
+                if hasattr(evt.artist, "set_edgecolor"):
+                    evt.artist.set_edgecolor(self.color)
+                self.axes.figure.canvas.draw_idle()
+
+    def update(self):
+        """
+        Update
+        """
+        pass
Index: src/sas/sasgui/plottools/plottables.py
===================================================================
--- src/sas/sasgui/plottools/plottables.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/plottables.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,1284 @@
+"""
+Prototype plottable object support.
+
+The main point of this prototype is to provide a clean separation between
+the style (plotter details: color, grids, widgets, etc.) and substance
+(application details: which information to plot).  Programmers should not be
+dictating line colours and plotting symbols.
+
+Unlike the problem of style in CSS or Word, where most paragraphs look
+the same, each line on a graph has to be distinguishable from its neighbours.
+Our solution is to provide parametric styles, in which a number of
+different classes of object (e.g., reflectometry data, reflectometry
+theory) representing multiple graph primitives cycle through a colour
+palette provided by the underlying plotter.
+
+A full treatment would provide perceptual dimensions of prominence and
+distinctiveness rather than a simple colour number.
+
+"""
+
+# Design question: who owns the color?
+# Is it a property of the plottable?
+# Or of the plottable as it exists on the graph?
+# Or if the graph?
+# If a plottable can appear on multiple graphs, in some case the
+# color should be the same on each graph in which it appears, and
+# in other cases (where multiple plottables from different graphs
+# coexist), the color should be assigned by the graph.  In any case
+# once a plottable is placed on the graph its color should not
+# depend on the other plottables on the graph.  Furthermore, if
+# a plottable is added and removed from a graph and added again,
+# it may be nice, but not necessary, to have the color persist.
+#
+# The safest approach seems to be to give ownership of color
+# to the graph, which will allocate the colors along with the
+# plottable.  The plottable will need to return the number of
+# colors that are needed.
+#
+# The situation is less clear for symbols.  It is less clear
+# how much the application requires that symbols be unique across
+# all plots on the graph.
+
+# Support for ancient python versions
+import copy
+import numpy
+import sys
+import logging
+
+if 'any' not in dir(__builtins__):
+    def any(L):
+        for cond in L:
+            if cond:
+                return True
+        return False
+
+    def all(L):
+        for cond in L:
+            if not cond:
+                return False
+        return True
+
+
+class Graph(object):
+    """
+    Generic plottables graph structure.
+
+    Plot styles are based on color/symbol lists.  The user gets to select
+    the list of colors/symbols/sizes to choose from, not the application
+    developer.  The programmer only gets to add/remove lines from the
+    plot and move to the next symbol/color.
+
+    Another dimension is prominence, which refers to line sizes/point sizes.
+
+    Axis transformations allow the user to select the coordinate view
+    which provides clarity to the data.  There is no way we can provide
+    every possible transformation for every application generically, so
+    the plottable objects themselves will need to provide the transformations.
+    Here are some examples from reflectometry: ::
+
+       independent: x -> f(x)
+          monitor scaling: y -> M*y
+          log:  y -> log(y if y > min else min)
+          cos:  y -> cos(y*pi/180)
+       dependent:   x -> f(x,y)
+          Q4:      y -> y*x^4
+          fresnel: y -> y*fresnel(x)
+       coordinated: x,y = f(x,y)
+          Q:    x -> 2*pi/L (cos(x*pi/180) - cos(y*pi/180))
+                y -> 2*pi/L (sin(x*pi/180) + sin(y*pi/180))
+       reducing: x,y = f(x1,x2,y1,y2)
+          spin asymmetry: x -> x1, y -> (y1 - y2)/(y1 + y2)
+          vector net: x -> x1, y -> y1*cos(y2*pi/180)
+
+    Multiple transformations are possible, such as Q4 spin asymmetry
+
+    Axes have further complications in that the units of what are being
+    plotted should correspond to the units on the axes.  Plotting multiple
+    types on the same graph should be handled gracefully, e.g., by creating
+    a separate tab for each available axis type, breaking into subplots,
+    showing multiple axes on the same plot, or generating inset plots.
+    Ultimately the decision should be left to the user.
+
+    Graph properties such as grids/crosshairs should be under user control,
+    as should the sizes of items such as axis fonts, etc.  No direct
+    access will be provided to the application.
+
+    Axis limits are mostly under user control.  If the user has zoomed or
+    panned then those limits are preserved even if new data is plotted.
+    The exception is when, e.g., scanning through a set of related lines
+    in which the user may want to fix the limits so that user can compare
+    the values directly.  Another exception is when creating multiple
+    graphs sharing the same limits, though this case may be important
+    enough that it is handled by the graph widget itself.  Axis limits
+    will of course have to understand the effects of axis transformations.
+
+    High level plottable objects may be composed of low level primitives.
+    Operations such as legend/hide/show copy/paste, etc. need to operate
+    on these primitives as a group.  E.g., allowing the user to have a
+    working canvas where they can drag lines they want to save and annotate
+    them.
+
+    Graphs need to be printable.  A page layout program for entire plots
+    would be nice.
+
+    """
+    def _xaxis_transformed(self, name, units):
+        """
+        Change the property of the x axis
+        according to an axis transformation
+        (as opposed to changing the basic properties)
+        """
+        if units != "":
+            name = "%s (%s)" % (name, units)
+        self.prop["xlabel"] = name
+        self.prop["xunit"] = units
+
+    def _yaxis_transformed(self, name, units):
+        """
+        Change the property of the y axis
+        according to an axis transformation
+        (as opposed to changing the basic properties)
+        """
+        if units != "":
+            name = "%s (%s)" % (name, units)
+        self.prop["ylabel"] = name
+        self.prop["yunit"] = units
+
+    def xaxis(self, name, units):
+        """
+        Properties of the x axis.
+        """
+        if units != "":
+            name = "%s (%s)" % (name, units)
+        self.prop["xlabel"] = name
+        self.prop["xunit"] = units
+        self.prop["xlabel_base"] = name
+        self.prop["xunit_base"] = units
+
+    def yaxis(self, name, units):
+        """
+        Properties of the y axis.
+        """
+        if units != "":
+            name = "%s (%s)" % (name, units)
+        self.prop["ylabel"] = name
+        self.prop["yunit"] = units
+        self.prop["ylabel_base"] = name
+        self.prop["yunit_base"] = units
+
+    def title(self, name):
+        """
+        Graph title
+        """
+        self.prop["title"] = name
+
+    def get(self, key):
+        """
+        Get the graph properties
+        """
+        if key == "color":
+            return self.color
+        elif key == "symbol":
+            return self.symbol
+        else:
+            return self.prop[key]
+
+    def set(self, **kw):
+        """
+        Set the graph properties
+        """
+        for key in kw:
+            if key == "color":
+                self.color = kw[key] % len(self.colorlist)
+            elif key == "symbol":
+                self.symbol = kw[key] % len(self.symbollist)
+            else:
+                self.prop[key] = kw[key]
+
+    def isPlotted(self, plottable):
+        """Return True is the plottable is already on the graph"""
+        if plottable in self.plottables:
+            return True
+        return False
+
+    def add(self, plottable, color=None):
+        """Add a new plottable to the graph"""
+        # record the colour associated with the plottable
+        if not plottable in self.plottables:
+            if color is not None:
+                self.plottables[plottable] = color
+            else:
+                self.color += plottable.colors()
+                self.plottables[plottable] = self.color
+
+    def changed(self):
+        """Detect if any graphed plottables have changed"""
+        return any([p.changed() for p in self.plottables])
+
+    def get_range(self):
+        """
+        Return the range of all displayed plottables
+        """
+        min_value = None
+        max_value = None
+        for p in self.plottables:
+            if p.hidden == True:
+                continue
+            if not p.x == None:
+                for x_i in p.x:
+                    if min_value == None or x_i < min_value:
+                        min_value = x_i
+                    if max_value == None or x_i > max_value:
+                        max_value = x_i
+        return min_value, max_value
+
+    def replace(self, plottable):
+        """Replace an existing plottable from the graph"""
+        selected_color = None
+        selected_plottable = None
+        for p in self.plottables.keys():
+            if plottable.id == p.id:
+                selected_plottable = p
+                selected_color = self.plottables[p]
+                break
+        if  selected_plottable is not None and selected_color is not None:
+            del self.plottables[selected_plottable]
+            self.plottables[plottable] = selected_color
+
+    def delete(self, plottable):
+        """Remove an existing plottable from the graph"""
+        if plottable in self.plottables:
+            del self.plottables[plottable]
+            self.color = len(self.plottables)
+
+    def reset_scale(self):
+        """
+        Resets the scale transformation data to the underlying data
+        """
+        for p in self.plottables:
+            p.reset_view()
+
+    def reset(self):
+        """Reset the graph."""
+        self.color = -1
+        self.symbol = 0
+        self.prop = {"xlabel": "", "xunit": None,
+                     "ylabel": "", "yunit": None,
+                     "title": ""}
+        self.plottables = {}
+
+    def _make_labels(self):
+        """
+        """
+        # Find groups of related plottables
+        sets = {}
+        for p in self.plottables:
+            if p.__class__ in sets:
+                sets[p.__class__].append(p)
+            else:
+                sets[p.__class__] = [p]
+        # Ask each plottable class for a set of unique labels
+        labels = {}
+        for c in sets:
+            labels.update(c.labels(sets[c]))
+        return labels
+
+    def get_plottable(self, name):
+        """
+        Return the plottable with the given
+        name if it exists. Otherwise return None
+        """
+        for item in self.plottables:
+            if item.name == name:
+                return item
+        return None
+
+    def returnPlottable(self):
+        """
+        This method returns a dictionary of plottables contained in graph
+        It is just by Plotpanel to interact with the complete list of plottables
+        inside the graph.
+        """
+        return self.plottables
+
+    def render(self, plot):
+        """Redraw the graph"""
+        plot.connect.clearall()
+        plot.clear()
+        plot.properties(self.prop)
+        labels = self._make_labels()
+        for p in self.plottables:
+            if p.custom_color is not None:
+                p.render(plot, color=p.custom_color, symbol=0,
+                         markersize=p.markersize, label=labels[p])
+            else:
+                p.render(plot, color=self.plottables[p], symbol=0,
+                         markersize=p.markersize, label=labels[p])
+        plot.render()
+
+    def __init__(self, **kw):
+        self.reset()
+        self.set(**kw)
+        # Name of selected plottable, if any
+        self.selected_plottable = None
+
+
+# Transform interface definition
+# No need to inherit from this class, just need to provide
+# the same methods.
+class Transform(object):
+    """
+    Define a transform plugin to the plottable architecture.
+
+    Transforms operate on axes.  The plottable defines the
+    set of transforms available for it, and the axes on which
+    they operate.  These transforms can operate on the x axis
+    only, the y axis only or on the x and y axes together.
+
+    This infrastructure is not able to support transformations
+    such as log and polar plots as these require full control
+    over the drawing of axes and grids.
+
+    A transform has a number of attributes.
+
+    name
+      user visible name for the transform.  This will
+      appear in the context menu for the axis and the transform
+      menu for the graph.
+
+    type
+      operational axis.  This determines whether the
+      transform should appear on x,y or z axis context
+      menus, or if it should appear in the context menu for
+      the graph.
+
+    inventory
+      (not implemented)
+      a dictionary of user settable parameter names and
+      their associated types.  These should appear as keyword
+      arguments to the transform call.  For example, Fresnel
+      reflectivity requires the substrate density:
+      ``{ 'rho': type.Value(10e-6/units.angstrom**2) }``
+      Supply reasonable defaults in the callback so that
+      limited plotting clients work even though they cannot
+      set the inventory.
+
+    """
+    def __call__(self, plottable, **kwargs):
+        """
+        Transform the data.  Whenever a plottable is added
+        to the axes, the infrastructure will apply all required
+        transforms.  When the user selects a different representation
+        for the axes (via menu, script, or context menu), all
+        plottables on the axes will be transformed.  The
+        plottable should store the underlying data but set
+        the standard x,dx,y,dy,z,dz attributes appropriately.
+
+        If the call raises a NotImplemented error the dataline
+        will not be plotted.  The associated string will usually
+        be 'Not a valid transform', though other strings are possible.
+        The application may or may not display the message to the
+        user, along with an indication of which plottable was at fault.
+
+        """
+        raise NotImplemented, "Not a valid transform"
+
+    # Related issues
+    # ==============
+    #
+    # log scale:
+    #    All axes have implicit log/linear scaling options.
+    #
+    # normalization:
+    #    Want to display raw counts vs detector efficiency correction
+    #    Want to normalize by time/monitor/proton current/intensity.
+    #    Want to display by eg. counts per 3 sec or counts per 10000 monitor.
+    #    Want to divide by footprint (ab initio, fitted or measured).
+    #    Want to scale by attenuator values.
+    #
+    # compare/contrast:
+    #    Want to average all visible lines with the same tag, and
+    #    display difference from one particular line.  Not a transform
+    #    issue?
+    #
+    # multiline graph:
+    #    How do we show/hide data parts.  E.g., data or theory, or
+    #    different polarization cross sections?  One way is with
+    #    tags: each plottable has a set of tags and the tags are
+    #    listed as check boxes above the plotting area.  Click a
+    #    tag and all plottables with that tag are hidden on the
+    #    plot and on the legend.
+    #
+    # nonconformant y-axes:
+    #    What do we do with temperature vs. Q and reflectivity vs. Q
+    #    on the same graph?
+    #
+    # 2D -> 1D:
+    #    Want various slices through the data.  Do transforms apply
+    #    to the sliced data as well?
+
+
+class Plottable(object):
+    """
+    """
+    # Short ascii name to refer to the plottable in a menu
+    short_name = None
+    # Fancy name
+    name = None
+    # Data
+    x = None
+    y = None
+    dx = None
+    dy = None
+    # Parameter to allow a plot to be part of the list without being displayed
+    hidden = False
+    # Flag to set whether a plottable has an interactor or not
+    interactive = True
+    custom_color = None
+    markersize = 5  # default marker size is 'size 5'
+
+    def __init__(self):
+        self.view = View()
+        self._xaxis = ""
+        self._xunit = ""
+        self._yaxis = ""
+        self._yunit = ""
+
+    def __setattr__(self, name, value):
+        """
+        Take care of changes in View when data is changed.
+        This method is provided for backward compatibility.
+        """
+        object.__setattr__(self, name, value)
+        if name in ['x', 'y', 'dx', 'dy']:
+            self.reset_view()
+            # print "self.%s has been called" % name
+
+    def set_data(self, x, y, dx=None, dy=None):
+        """
+        """
+        self.x = x
+        self.y = y
+        self.dy = dy
+        self.dx = dx
+        self.transformView()
+
+    def xaxis(self, name, units):
+        """
+        Set the name and unit of x_axis
+
+        :param name: the name of x-axis
+        :param units: the units of x_axis
+
+        """
+        self._xaxis = name
+        self._xunit = units
+
+    def yaxis(self, name, units):
+        """
+        Set the name and unit of y_axis
+
+        :param name: the name of y-axis
+        :param units: the units of y_axis
+
+        """
+        self._yaxis = name
+        self._yunit = units
+
+    def get_xaxis(self):
+        """Return the units and name of x-axis"""
+        return self._xaxis, self._xunit
+
+    def get_yaxis(self):
+        """ Return the units and name of y- axis"""
+        return self._yaxis, self._yunit
+
+    @classmethod
+    def labels(cls, collection):
+        """
+        Construct a set of unique labels for a collection of plottables of
+        the same type.
+
+        Returns a map from plottable to name.
+
+        """
+        n = len(collection)
+        label_dict = {}
+        if n > 0:
+            basename = str(cls).split('.')[-1]
+            if n == 1:
+                label_dict[collection[0]] = basename
+            else:
+                for i in xrange(len(collection)):
+                    label_dict[collection[i]] = "%s %d" % (basename, i)
+        return label_dict
+
+    # #Use the following if @classmethod doesn't work
+    # labels = classmethod(labels)
+    def setLabel(self, labelx, labely):
+        """
+        It takes a label of the x and y transformation and set View parameters
+
+        :param transx: The label of x transformation is sent by Properties Dialog
+        :param transy: The label of y transformation is sent Properties Dialog
+
+        """
+        self.view.xLabel = labelx
+        self.view.yLabel = labely
+
+    def set_View(self, x, y):
+        """Load View"""
+        self.x = x
+        self.y = y
+        self.reset_view()
+
+    def reset_view(self):
+        """Reload view with new value to plot"""
+        self.view = View(self.x, self.y, self.dx, self.dy)
+        self.view.Xreel = self.view.x
+        self.view.Yreel = self.view.y
+        self.view.DXreel = self.view.dx
+        self.view.DYreel = self.view.dy
+
+    def render(self, plot):
+        """
+        The base class makes sure the correct units are being used for
+        subsequent plottable.
+
+        For now it is assumed that the graphs are commensurate, and if you
+        put a Qx object on a Temperature graph then you had better hope
+        that it makes sense.
+
+        """
+        plot.xaxis(self._xaxis, self._xunit)
+        plot.yaxis(self._yaxis, self._yunit)
+
+    def is_empty(self):
+        """
+        Returns True if there is no data stored in the plottable
+        """
+        if not self.x == None and len(self.x) == 0 \
+            and not self.y == None and len(self.y) == 0:
+            return True
+        return False
+
+    def colors(self):
+        """Return the number of colors need to render the object"""
+        return 1
+
+    def transformView(self):
+        """
+        It transforms x, y before displaying
+        """
+        self.view.transform(self.x, self.y, self.dx, self.dy)
+
+    def returnValuesOfView(self):
+        """
+        Return View parameters and it is used by Fit Dialog
+        """
+        return self.view.returnXview()
+
+    def check_data_PlottableX(self):
+        """
+        Since no transformation is made for log10(x), check that
+        no negative values is plot in log scale
+        """
+        self.view.check_data_logX()
+
+    def check_data_PlottableY(self):
+        """
+        Since no transformation is made for log10(y), check that
+        no negative values is plot in log scale
+        """
+        self.view.check_data_logY()
+
+    def transformX(self, transx, transdx):
+        """
+        Receive pointers to function that transform x and dx
+        and set corresponding View pointers
+
+        :param transx: pointer to function that transforms x
+        :param transdx: pointer to function that transforms dx
+
+        """
+        self.view.setTransformX(transx, transdx)
+
+    def transformY(self, transy, transdy):
+        """
+        Receive pointers to function that transform y and dy
+        and set corresponding View pointers
+
+        :param transy: pointer to function that transforms y
+        :param transdy: pointer to function that transforms dy
+
+        """
+        self.view.setTransformY(transy, transdy)
+
+    def onReset(self):
+        """
+        Reset x, y, dx, dy view with its parameters
+        """
+        self.view.onResetView()
+
+    def onFitRange(self, xmin=None, xmax=None):
+        """
+        It limits View data range to plot from min to max
+
+        :param xmin: the minimum value of x to plot.
+        :param xmax: the maximum value of x to plot
+
+        """
+        self.view.onFitRangeView(xmin, xmax)
+
+
+class View(object):
+    """
+    Representation of the data that might include a transformation
+    """
+    x = None
+    y = None
+    dx = None
+    dy = None
+
+    def __init__(self, x=None, y=None, dx=None, dy=None):
+        """
+        """
+        self.x = x
+        self.y = y
+        self.dx = dx
+        self.dy = dy
+        # To change x range to the reel range
+        self.Xreel = self.x
+        self.Yreel = self.y
+        self.DXreel = self.dx
+        self.DYreel = self.dy
+        # Labels of x and y received from Properties Dialog
+        self.xLabel = ""
+        self.yLabel = ""
+        # Function to transform x, y, dx and dy
+        self.funcx = None
+        self.funcy = None
+        self.funcdx = None
+        self.funcdy = None
+
+    def transform(self, x=None, y=None, dx=None, dy=None):
+        """
+        Transforms the x,y,dx and dy vectors and stores
+         the output in View parameters
+
+        :param x: array of x values
+        :param y: array of y values
+        :param dx: array of  errors values on x
+        :param dy: array of error values on y
+
+        """
+        # Sanity check
+        # Do the transofrmation only when x and y are empty
+        has_err_x = not (dx == None or len(dx) == 0)
+        has_err_y = not (dy == None or len(dy) == 0)
+
+        if(x != None) and (y != None):
+            if not dx == None and not len(dx) == 0 and not len(x) == len(dx):
+                msg = "Plottable.View: Given x and dx are not"
+                msg += " of the same length"
+                raise ValueError, msg
+            # Check length of y array
+            if not len(y) == len(x):
+                msg = "Plottable.View: Given y "
+                msg += "and x are not of the same length"
+                raise ValueError, msg
+
+            if not dy == None and not len(dy) == 0 and not len(y) == len(dy):
+                msg = "Plottable.View: Given y and dy are not of the same "
+                msg += "length: len(y)=%s, len(dy)=%s" % (len(y), len(dy))
+                raise ValueError, msg
+            self.x = []
+            self.y = []
+            if has_err_x:
+                self.dx = []
+            else:
+                self.dx = None
+            if has_err_y:
+                self.dy = []
+            else:
+                self.dy = None
+            tempx = []
+            tempy = []
+            if not has_err_x:
+                dx = numpy.zeros(len(x))
+            if not has_err_y:
+                dy = numpy.zeros(len(y))
+            for i in range(len(x)):
+                try:
+                    tempx = self.funcx(x[i], y[i])
+                    tempy = self.funcy(y[i], x[i])
+                    if has_err_x:
+                        tempdx = self.funcdx(x[i], y[i], dx[i], dy[i])
+                    if has_err_y:
+                        tempdy = self.funcdy(y[i], x[i], dy[i], dx[i])
+                    self.x.append(tempx)
+                    self.y.append(tempy)
+                    if has_err_x:
+                        self.dx.append(tempdx)
+                    if has_err_y:
+                        self.dy.append(tempdy)
+                except:
+                    tempx = x[i]
+                    tempy = y[i]
+                    tempdy = dy[i]
+            # Sanity check
+            if not len(self.x) == len(self.y):
+                msg = "Plottable.View: transformed x "
+                msg += "and y are not of the same length"
+                raise ValueError, msg
+            if has_err_x and not (len(self.x) and len(self.dx)):
+                msg = "Plottable.View: transformed x and dx"
+                msg += " are not of the same length"
+                raise ValueError, msg
+            if has_err_y and not (len(self.y) and len(self.dy)):
+                msg = "Plottable.View: transformed y"
+                msg += " and dy are not of the same length"
+                raise ValueError, msg
+            # Check that negative values are not plot on x and y axis for
+            # log10 transformation
+            self.check_data_logX()
+            self.check_data_logY()
+            # Store x ,y dx,and dy in their full range for reset
+            self.Xreel = self.x
+            self.Yreel = self.y
+            self.DXreel = self.dx
+            self.DYreel = self.dy
+
+    def onResetView(self):
+        """
+        Reset x,y,dx and y in their full range  and in the initial scale
+        in case their previous range has changed
+        """
+        self.x = self.Xreel
+        self.y = self.Yreel
+        self.dx = self.DXreel
+        self.dy = self.DYreel
+
+    def setTransformX(self, funcx, funcdx):
+        """
+        Receive pointers to function that transform x and dx
+        and set corresponding View pointers
+
+        :param transx: pointer to function that transforms x
+        :param transdx: pointer to function that transforms dx
+        """
+        self.funcx = funcx
+        self.funcdx = funcdx
+
+    def setTransformY(self, funcy, funcdy):
+        """
+        Receive pointers to function that transform y and dy
+        and set corresponding View pointers
+
+        :param transx: pointer to function that transforms y
+        :param transdx: pointer to function that transforms dy
+        """
+        self.funcy = funcy
+        self.funcdy = funcdy
+
+    def returnXview(self):
+        """
+        Return View  x,y,dx,dy
+        """
+        return self.x, self.y, self.dx, self.dy
+
+    def check_data_logX(self):
+        """
+        Remove negative value in x vector to avoid plotting negative
+        value of Log10
+        """
+        tempx = []
+        tempdx = []
+        tempy = []
+        tempdy = []
+        if self.dx == None:
+            self.dx = numpy.zeros(len(self.x))
+        if self.dy == None:
+            self.dy = numpy.zeros(len(self.y))
+        if self.xLabel == "log10(x)":
+            for i in range(len(self.x)):
+                try:
+                    if self.x[i] > 0:
+                        tempx.append(self.x[i])
+                        tempdx.append(self.dx[i])
+                        tempy.append(self.y[i])
+                        tempdy.append(self.dy[i])
+                except:
+                    logging.error("check_data_logX: skipping point x %g", self.x[i])
+                    logging.error(sys.exc_value)
+            self.x = tempx
+            self.y = tempy
+            self.dx = tempdx
+            self.dy = tempdy
+
+    def check_data_logY(self):
+        """
+        Remove negative value in y vector
+        to avoid plotting negative value of Log10
+
+        """
+        tempx = []
+        tempdx = []
+        tempy = []
+        tempdy = []
+        if self.dx == None:
+            self.dx = numpy.zeros(len(self.x))
+        if self.dy == None:
+            self.dy = numpy.zeros(len(self.y))
+        if self.yLabel == "log10(y)":
+            for i in range(len(self.x)):
+                try:
+                    if self.y[i] > 0:
+                        tempx.append(self.x[i])
+                        tempdx.append(self.dx[i])
+                        tempy.append(self.y[i])
+                        tempdy.append(self.dy[i])
+                except:
+                    logging.error("check_data_logY: skipping point %g", self.y[i])
+                    logging.error(sys.exc_value)
+
+            self.x = tempx
+            self.y = tempy
+            self.dx = tempdx
+            self.dy = tempdy
+
+    def onFitRangeView(self, xmin=None, xmax=None):
+        """
+        It limits View data range to plot from min to max
+
+        :param xmin: the minimum value of x to plot.
+        :param xmax: the maximum value of x to plot
+
+        """
+        tempx = []
+        tempdx = []
+        tempy = []
+        tempdy = []
+        if self.dx == None:
+            self.dx = numpy.zeros(len(self.x))
+        if self.dy == None:
+            self.dy = numpy.zeros(len(self.y))
+        if xmin != None and xmax != None:
+            for i in range(len(self.x)):
+                if self.x[i] >= xmin and self.x[i] <= xmax:
+                    tempx.append(self.x[i])
+                    tempdx.append(self.dx[i])
+                    tempy.append(self.y[i])
+                    tempdy.append(self.dy[i])
+            self.x = tempx
+            self.y = tempy
+            self.dx = tempdx
+            self.dy = tempdy
+
+
+class Data2D(Plottable):
+    """
+    2D data class for image plotting
+    """
+    def __init__(self, image=None, qx_data=None, qy_data=None,
+                 err_image=None, xmin=None, xmax=None, ymin=None,
+                 ymax=None, zmin=None, zmax=None):
+        """
+        Draw image
+        """
+        Plottable.__init__(self)
+        self.name = "Data2D"
+        self.label = None
+        self.data = image
+        self.qx_data = qx_data
+        self.qy_data = qx_data
+        self.err_data = err_image
+        self.source = None
+        self.detector = []
+
+        # # Units for Q-values
+        self.xy_unit = 'A^{-1}'
+        # # Units for I(Q) values
+        self.z_unit = 'cm^{-1}'
+        self._zaxis = ''
+        # x-axis unit and label
+        self._xaxis = '\\rm{Q_{x}}'
+        self._xunit = 'A^{-1}'
+        # y-axis unit and label
+        self._yaxis = '\\rm{Q_{y}}'
+        self._yunit = 'A^{-1}'
+
+        # ## might remove that later
+        # # Vector of Q-values at the center of each bin in x
+        self.x_bins = []
+        # # Vector of Q-values at the center of each bin in y
+        self.y_bins = []
+
+        # x and y boundaries
+        self.xmin = xmin
+        self.xmax = xmax
+        self.ymin = ymin
+        self.ymax = ymax
+
+        self.zmin = zmin
+        self.zmax = zmax
+        self.id = None
+
+    def xaxis(self, label, unit):
+        """
+        set x-axis
+
+        :param label: x-axis label
+        :param unit: x-axis unit
+
+        """
+        self._xaxis = label
+        self._xunit = unit
+
+    def yaxis(self, label, unit):
+        """
+        set y-axis
+
+        :param label: y-axis label
+        :param unit: y-axis unit
+
+        """
+        self._yaxis = label
+        self._yunit = unit
+
+    def zaxis(self, label, unit):
+        """
+        set z-axis
+
+        :param label: z-axis label
+        :param unit: z-axis unit
+
+        """
+        self._zaxis = label
+        self._zunit = unit
+
+    def setValues(self, datainfo=None):
+        """
+        Use datainfo object to initialize data2D
+
+        :param datainfo: object
+
+        """
+        self.image = copy.deepcopy(datainfo.data)
+        self.qx_data = copy.deepcopy(datainfo.qx_data)
+        self.qy_data = copy.deepcopy(datainfo.qy_data)
+        self.err_image = copy.deepcopy(datainfo.err_data)
+
+        self.xy_unit = datainfo.Q_unit
+        self.z_unit = datainfo.I_unit
+        self._zaxis = datainfo._zaxis
+
+        self.xaxis(datainfo._xunit, datainfo._xaxis)
+        self.yaxis(datainfo._yunit, datainfo._yaxis)
+        # x and y boundaries
+        self.xmin = datainfo.xmin
+        self.xmax = datainfo.xmax
+        self.ymin = datainfo.ymin
+        self.ymax = datainfo.ymax
+        # # Vector of Q-values at the center of each bin in x
+        self.x_bins = datainfo.x_bins
+        # # Vector of Q-values at the center of each bin in y
+        self.y_bins = datainfo.y_bins
+
+    def set_zrange(self, zmin=None, zmax=None):
+        """
+        """
+        if zmin < zmax:
+            self.zmin = zmin
+            self.zmax = zmax
+        else:
+            raise "zmin is greater or equal to zmax "
+
+    def render(self, plot, **kw):
+        """
+        Renders the plottable on the graph
+
+        """
+        plot.image(self.data, self.qx_data, self.qy_data,
+                   self.xmin, self.xmax, self.ymin,
+                   self.ymax, self.zmin, self.zmax, **kw)
+
+    def changed(self):
+        """
+        """
+        return False
+
+    @classmethod
+    def labels(cls, collection):
+        """Build a label mostly unique within a collection"""
+        label_dict = {}
+        for item in collection:
+            if item.label == "Data2D":
+                item.label = item.name
+            label_dict[item] = item.label
+        return label_dict
+
+
+class Data1D(Plottable):
+    """
+    Data plottable: scatter plot of x,y with errors in x and y.
+    """
+
+    def __init__(self, x, y, dx=None, dy=None):
+        """
+        Draw points specified by x[i],y[i] in the current color/symbol.
+        Uncertainty in x is given by dx[i], or by (xlo[i],xhi[i]) if the
+        uncertainty is asymmetric.  Similarly for y uncertainty.
+
+        The title appears on the legend.
+        The label, if it is different, appears on the status bar.
+        """
+        Plottable.__init__(self)
+        self.name = "data"
+        self.label = "data"
+        self.x = x
+        self.y = y
+        self.dx = dx
+        self.dy = dy
+        self.source = None
+        self.detector = None
+        self.xaxis('', '')
+        self.yaxis('', '')
+        self.view = View(self.x, self.y, self.dx, self.dy)
+        self.symbol = 0
+        self.custom_color = None
+        self.markersize = 5
+        self.id = None
+        self.zorder = 1
+        self.hide_error = False
+
+    def render(self, plot, **kw):
+        """
+        Renders the plottable on the graph
+        """
+        if self.interactive == True:
+            kw['symbol'] = self.symbol
+            kw['id'] = self.id
+            kw['hide_error'] = self.hide_error
+            kw['markersize'] = self.markersize
+            plot.interactive_points(self.view.x, self.view.y,
+                                    dx=self.view.dx, dy=self.view.dy,
+                                    name=self.name, zorder=self.zorder, **kw)
+        else:
+            kw['id'] = self.id
+            kw['hide_error'] = self.hide_error
+            kw['symbol'] = self.symbol
+            kw['color'] = self.custom_color
+            kw['markersize'] = self.markersize
+            plot.points(self.view.x, self.view.y, dx=self.view.dx,
+                        dy=self.view.dy, zorder=self.zorder,
+                        marker=self.symbollist[self.symbol], **kw)
+
+    def changed(self):
+        return False
+
+    @classmethod
+    def labels(cls, collection):
+        """Build a label mostly unique within a collection"""
+        label_dict = {}
+        for item in collection:
+            if item.label == "data":
+                item.label = item.name
+            label_dict[item] = item.label
+        return label_dict
+
+
+class Theory1D(Plottable):
+    """
+    Theory plottable: line plot of x,y with confidence interval y.
+    """
+    def __init__(self, x, y, dy=None):
+        """
+        Draw lines specified in x[i],y[i] in the current color/symbol.
+        Confidence intervals in x are given by dx[i] or by (xlo[i],xhi[i])
+        if the limits are asymmetric.
+
+        The title is the name that will show up on the legend.
+        """
+        Plottable.__init__(self)
+        msg = "Theory1D is no longer supported, please use Data1D and change symbol.\n"
+        raise DeprecationWarning, msg
+
+class Fit1D(Plottable):
+    """
+    Fit plottable: composed of a data line plus a theory line.  This
+    is treated like a single object from the perspective of the graph,
+    except that it will have two legend entries, one for the data and
+    one for the theory.
+
+    The color of the data and theory will be shared.
+
+    """
+    def __init__(self, data=None, theory=None):
+        """
+        """
+        Plottable.__init__(self)
+        self.data = data
+        self.theory = theory
+
+    def render(self, plot, **kw):
+        """
+        """
+        self.data.render(plot, **kw)
+        self.theory.render(plot, **kw)
+
+    def changed(self):
+        """
+        """
+        return self.data.changed() or self.theory.changed()
+
+
+# ---------------------------------------------------------------
+class Text(Plottable):
+    """
+    """
+    def __init__(self, text=None, xpos=0.5, ypos=0.9, name='text'):
+        """
+        Draw the user-defined text in plotter
+        We can specify the position of text
+        """
+        Plottable.__init__(self)
+        self.name = name
+        self.text = text
+        self.xpos = xpos
+        self.ypos = ypos
+
+    def render(self, plot, **kw):
+        """
+        """
+        from matplotlib import transforms
+
+        xcoords = transforms.blended_transform_factory(plot.subplot.transAxes,
+                                                       plot.subplot.transAxes)
+        plot.subplot.text(self.xpos,
+                          self.ypos,
+                          self.text,
+                          label=self.name,
+                          transform=xcoords)
+
+    def setText(self, text):
+        """Set the text string."""
+        self.text = text
+
+    def getText(self, text):
+        """Get the text string."""
+        return self.text
+
+    def set_x(self, x):
+        """
+        Set the x position of the text
+        ACCEPTS: float
+        """
+        self.xpos = x
+
+    def set_y(self, y):
+        """
+        Set the y position of the text
+        ACCEPTS: float
+        """
+        self.ypos = y
+
+
+# ---------------------------------------------------------------
+class Chisq(Plottable):
+    """
+    Chisq plottable plots the chisq
+    """
+    def __init__(self, chisq=None):
+        """
+        Draw the chisq in plotter
+        We can specify the position of chisq
+        """
+        Plottable.__init__(self)
+        self.name = "chisq"
+        self._chisq = chisq
+        self.xpos = 0.5
+        self.ypos = 0.9
+
+    def render(self, plot, **kw):
+        """
+        """
+        if  self._chisq == None:
+            chisqTxt = r'$\chi^2=$'
+        else:
+            chisqTxt = r'$\chi^2=%g$' % (float(self._chisq))
+
+        from matplotlib import transforms
+
+        xcoords = transforms.blended_transform_factory(plot.subplot.transAxes,
+                                                      plot.subplot.transAxes)
+        plot.subplot.text(self.xpos,
+                          self.ypos,
+                          chisqTxt, label='chisq',
+                          transform=xcoords)
+
+    def setChisq(self, chisq):
+        """
+        Set the chisq value.
+        """
+        self._chisq = chisq
+
+
+######################################################
+
+def sample_graph():
+    import numpy as nx
+
+    # Construct a simple graph
+    if False:
+        x = nx.array([1, 2, 3, 4, 5, 6], 'd')
+        y = nx.array([4, 5, 6, 5, 4, 5], 'd')
+        dy = nx.array([0.2, 0.3, 0.1, 0.2, 0.9, 0.3])
+    else:
+        x = nx.linspace(0, 1., 10000)
+        y = nx.sin(2 * nx.pi * x * 2.8)
+        dy = nx.sqrt(100 * nx.abs(y)) / 100
+    data = Data1D(x, y, dy=dy)
+    data.xaxis('distance', 'm')
+    data.yaxis('time', 's')
+    graph = Graph()
+    graph.title('Walking Results')
+    graph.add(data)
+    graph.add(Theory1D(x, y, dy=dy))
+    return graph
+
+
+def demo_plotter(graph):
+    import wx
+    from pylab_plottables import Plotter
+    # from mplplotter import Plotter
+
+    # Make a frame to show it
+    app = wx.PySimpleApp()
+    frame = wx.Frame(None, -1, 'Plottables')
+    plotter = Plotter(frame)
+    frame.Show()
+
+    # render the graph to the pylab plotter
+    graph.render(plotter)
+
+    class GraphUpdate(object):
+        callnum = 0
+
+        def __init__(self, graph, plotter):
+            self.graph, self.plotter = graph, plotter
+
+        def __call__(self):
+            if self.graph.changed():
+                self.graph.render(self.plotter)
+                return True
+            return False
+
+        def onIdle(self, event):
+            self.callnum = self.callnum + 1
+            if self.__call__():
+                pass  # event.RequestMore()
+    update = GraphUpdate(graph, plotter)
+    frame.Bind(wx.EVT_IDLE, update.onIdle)
+    app.MainLoop()
Index: src/sas/sasgui/plottools/toolbar.py
===================================================================
--- src/sas/sasgui/plottools/toolbar.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/toolbar.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,172 @@
+"""
+    This module overwrites matplotlib toolbar
+"""
+import wx
+from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg
+from matplotlib.backends.backend_wx import _load_bitmap
+import logging
+
+# Event binding code changed after version 2.5
+if wx.VERSION_STRING >= '2.5':
+    def bind(actor, event, action, **kw):
+        actor.Bind(event, action, **kw)
+else:
+    def bind(actor, event, action, id=None):
+        if id is not None:
+            event(actor, id, action)
+        else:
+            event(actor, action)
+
+class NavigationToolBar(NavigationToolbar2WxAgg):
+    _NTB2_HOME = wx.NewId()
+    _NTB2_BACK = wx.NewId()
+    _NTB2_FORWARD = wx.NewId()
+    _NTB2_PAN = wx.NewId()
+    _NTB2_ZOOM = wx.NewId()
+    _NTB2_SAVE = wx.NewId()
+    _NTB2_PRINT = wx.NewId()
+    _NTB2_RESET = wx.NewId()
+    _NTB2_COPY = wx.NewId()
+    """
+    Overwrite matplotlib toolbar
+    """
+    def __init__(self, canvas, parent=None):
+        NavigationToolbar2WxAgg.__init__(self, canvas)
+
+    # CRUFT: mpl 1.1 uses save rather than save_figure
+    try: save_figure = NavigationToolbar2WxAgg.save
+    except AttributeError: pass
+    
+    def _init_toolbar(self):
+        self._parent = self.canvas.GetParent()
+
+        # for mpl 1.2+ compatibility
+        self.wx_ids = {}
+        self.wx_ids['Back'] = self._NTB2_BACK
+        self.wx_ids['Forward'] = self._NTB2_FORWARD
+        self.wx_ids['Pan'] = self._NTB2_PAN
+        self.wx_ids['Zoom'] = self._NTB2_ZOOM
+
+        self.SetToolBitmapSize(wx.Size(24, 24))
+
+        context_tip = 'Graph Menu: \n'
+        context_tip += '    For more menu options, \n'
+        context_tip += '    right-click the data symbols.'
+        context = wx.ArtProvider.GetBitmap(wx.ART_LIST_VIEW, wx.ART_TOOLBAR)
+        self.AddSimpleTool(self._NTB2_HOME, context, context_tip, context_tip)
+
+        self.InsertSeparator(1)
+
+        self.AddSimpleTool(self._NTB2_BACK, _load_bitmap('back.png'),
+                           'Back', 'Back navigation view')
+        self.AddSimpleTool(self._NTB2_FORWARD, _load_bitmap('forward.png'),
+                           'Forward', 'Forward navigation view')
+        # todo: get new bitmap
+        self.AddCheckTool(self._NTB2_PAN, _load_bitmap('move.png'),
+                          shortHelp='Pan',
+                          longHelp='Pan with left, zoom with right')
+        self.AddCheckTool(self._NTB2_ZOOM, _load_bitmap('zoom_to_rect.png'),
+                          shortHelp='Zoom', longHelp='Zoom to rectangle')
+
+        self.AddSeparator()
+        self.AddSimpleTool(self._NTB2_SAVE, _load_bitmap('filesave.png'),
+                           'Save', 'Save plot contents to file')
+
+        print_bmp = wx.ArtProvider.GetBitmap(wx.ART_PRINT, wx.ART_TOOLBAR)
+        self.AddSimpleTool(self._NTB2_PRINT, print_bmp, 'Print', 'Print plot')
+
+        reset_bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_HOME, wx.ART_TOOLBAR)
+        self.AddSimpleTool(self._NTB2_RESET, reset_bmp, 'Reset', 'Reset graph range')
+
+        bind(self, wx.EVT_TOOL, self.context_menu, id=self._NTB2_HOME)
+        bind(self, wx.EVT_TOOL, self.forward, id=self._NTB2_FORWARD)
+        bind(self, wx.EVT_TOOL, self.back, id=self._NTB2_BACK)
+        bind(self, wx.EVT_TOOL, self.zoom, id=self._NTB2_ZOOM)
+        bind(self, wx.EVT_TOOL, self.pan, id=self._NTB2_PAN)
+        bind(self, wx.EVT_TOOL, self.save_figure, id=self._NTB2_SAVE)
+        bind(self, wx.EVT_TOOL, self.print_figure, id=self._NTB2_PRINT)
+        bind(self, wx.EVT_TOOL, self.home, id=self._NTB2_RESET)
+
+        self.Realize()
+
+    def on_menu(self, event):
+        try:
+            self._parent.onToolContextMenu(event=event)
+        except:
+            logging.error("Plot toolbar could not show menu")
+
+    def context_menu(self, event):
+        """
+        Default context menu for a plot panel
+
+        """
+        # Slicer plot popup menu
+        popup = wx.Menu()
+        popup.Append(self._NTB2_SAVE, '&Save image', 'Save image as PNG')
+        wx.EVT_MENU(self, self._NTB2_SAVE, self.save_figure)
+
+        popup.Append(self._NTB2_PRINT, '&Print image', 'Print image ')
+        wx.EVT_MENU(self, self._NTB2_PRINT, self.print_figure)
+
+        popup.Append(self._NTB2_COPY, '&Copy to Clipboard', 'Copy image to the clipboard')
+        wx.EVT_MENU(self, self._NTB2_COPY, self.copy_figure)
+
+        # Show the popup menu relative to the location of the toolbar
+        self.PopupMenu(popup, (0,0))
+
+
+    def print_figure(self, event):
+        try:
+            _printer = wx.Printer()
+            _printer.Print(self.canvas, PlotPrintout(self.canvas), True)
+        except:
+            import traceback
+            logging.error(traceback.format_exc())
+
+    def copy_figure(self, event):
+        copy_image_to_clipboard(self.canvas)
+
+class PlotPrintout(wx.Printout):
+    """
+    Create the wx.Printout object for matplotlib figure from the PlotPanel.
+    Provides the required OnPrintPage and HasPage overrides.  Other methods
+    may be added/overriden in the future.
+    :TODO: this needs LOTS of TLC .. but fixes immediate problem
+    """
+    def __init__(self, canvas):
+        """
+        Initialize wx.Printout and get passed figure object
+        """
+        wx.Printout.__init__(self)
+        self.canvas = canvas
+
+    def OnPrintPage(self, page):
+        """
+        Most rudimentry OnPrintPage overide.  instatiates a dc object, gets
+        its size, gets the size of the figure object, scales it to the dc
+        canvas size keeping the aspect ratio intact, then prints as bitmap
+        """
+        _dc = self.GetDC()
+        (_dcX, _dcY) = _dc.GetSizeTuple()
+        (_bmpX,_bmpY) = self.canvas.GetSize()
+        _scale = min(_dcX/_bmpX, _dcY/_bmpY)
+        _dc.SetUserScale(_scale, _scale)
+        _dc.DrawBitmap(self.canvas.bitmap, 0, 0, False,)
+        return True
+
+    def GetPageInfo(self):
+        """
+        just sets the page to 1 - no flexibility for now
+        """
+        return (1, 1, 1, 1)
+
+
+def copy_image_to_clipboard(canvas):
+    bmp = wx.BitmapDataObject()
+    bmp.SetBitmap(canvas.bitmap)
+
+    wx.TheClipboard.Open()
+    wx.TheClipboard.SetData(bmp)
+    wx.TheClipboard.Close()
+
+
Index: src/sas/sasgui/plottools/transform.py
===================================================================
--- src/sas/sasgui/plottools/transform.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
+++ src/sas/sasgui/plottools/transform.py	(revision d7bb5267934b5676c90b1655346a3793a8022965)
@@ -0,0 +1,413 @@
+import math
+
+
+def toX(x, y=None):
+    """
+    This function is used to load value on Plottable.View
+
+    :param x: Float value
+
+    :return: x
+
+    """
+    return x
+
+
+def toX_pos(x, y=None):
+    """
+    This function is used to load value on Plottable.View
+
+    :param x: Float value
+
+    :return: x
+
+    """
+    if not x > 0:
+        raise ValueError, "Transformation only accepts positive values."
+    else:
+        return x
+
+
+def toX2(x, y=None):
+    """
+    This function is used to load value on Plottable.View
+
+    Calculate x^(2)
+
+    :param x: float value
+
+    """
+    return x * x
+
+
+def fromX2(x, y=None):
+    """
+    This function is used to load value on Plottable.View
+    Calculate square root of x
+
+    :param x: float value
+
+    """
+    if not x >= 0:
+        raise ValueError, "square root of a negative value "
+    else:
+        return math.sqrt(x)
+
+
+def toX4(x, y=None):
+    """
+    This function is used to load value on Plottable.View
+
+    Calculate x^(4)
+
+    :param x: float value
+
+    """
+    return x * x * x * x
+
+
+def fromX4(x, y=None):
+    """
+    This function is used to load value on Plottable.View
+    Calculate square root of x
+
+    :param x: float value
+
+    """
+    if not x >= 0:
+        raise ValueError, "double square root of a negative value "
+    else:
+        return math.sqrt(math.sqrt(x))
+
+
+def toLogX(x, y=None):
+    """
+    This function is used to load value on Plottable.View
+    calculate log x
+
+    :param x: float value
+
+    """
+    if not x > 0:
+        raise ValueError, "Log(x)of a negative value "
+    else:
+        return math.log(x)
+
+def toOneOverX(x, y=None):
+    """
+    """
+    if x != 0:
+        return 1 / x
+    else:
+        raise ValueError, "cannot divide by zero"
+
+
+def toOneOverSqrtX(y, x=None):
+    """
+    """
+    if y > 0:
+        return 1 / math.sqrt(y)
+    else:
+        raise ValueError, "transform.toOneOverSqrtX: cannot be computed"
+
+
+def toLogYX2(y, x):
+    """
+    """
+    if (y * (x ** 2)) > 0:
+        return math.log(y * (x ** 2))
+    else:
+        raise ValueError, "transform.toLogYX2: cannot be computed"
+
+
+def toLogYX4(y, x):
+    """
+    """
+    if (math.pow(x, 4) * y) > 0:
+        return math.log(math.pow(x, 4) * y)
+    else:
+        raise ValueError, "transform.toLogYX4: input error"
+
+
+def toYX4(y, x):
+    """
+    """
+    return math.pow(x, 4) * y
+
+def toYX2(y, x):
+    """
+    """
+    return math.pow(x, 2) * y
+
+def toLogXY(y, x):
+    """
+    This function is used to load value on Plottable.View
+    calculate log x
+
+    :param x: float value
+
+    """
+    if not (x * y) > 0:
+        raise ValueError, "Log(X*Y)of a negative value "
+    else:
+        return math.log(x * y)
+
+
+def errToX(x, y=None, dx=None, dy=None):
+    """
+    calculate error of x**2
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if dx == None:
+        dx = 0
+    return dx
+
+
+def errToX_pos(x, y=None, dx=None, dy=None):
+    """
+    calculate error of x**2
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if dx == None:
+        dx = 0
+    return dx
+
+
+def errToX2(x, y=None, dx=None, dy=None):
+    """
+    calculate error of x**2
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if  dx != None:
+        err = 2 * x * dx
+        return math.fabs(err)
+    else:
+        return 0.0
+
+
+def errFromX2(x, y=None, dx=None, dy=None):
+    """
+    calculate error of sqrt(x)
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if x > 0:
+        if dx != None:
+            err = dx / (2 * math.sqrt(x))
+        else:
+            err = 0
+        return math.fabs(err)
+    else:
+        msg = "transform.errFromX2: can't compute error of negative x"
+        raise ValueError, msg
+
+
+def errToX4(x, y=None, dx=None, dy=None):
+    """
+    calculate error of x**4
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if dx != None:
+        err = 4 * math.pow(x, 3) * dx
+        return math.fabs(err)
+    else:
+        return 0.0
+
+
+def errFromX4(x, y=None, dx=None, dy=None):
+    """
+    calculate error of x^1/4
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if x > 0:
+        if dx != None:
+            err = dx / (4 * math.pow(x, 3 / 4))
+        else:
+            err = 0
+        return math.fabs(err)
+    else:
+        msg = "transform.errFromX4: can't compute error of negative x"
+        raise ValueError, msg
+
+
+def errToLog10X(x, y=None, dx=None, dy=None):
+    """
+    calculate error of Log(x)
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if dx == None:
+        dx = 0
+
+    # Check that the point on the graph is positive
+    # within errors
+    if not (x - dx) > 0:
+        msg = "Transformation does not accept"
+        msg += " point that are consistent with zero."
+        raise ValueError, msg
+    if x != 0:
+        dx = dx / (x * math.log(10))
+    else:
+        raise ValueError, "errToLogX: divide by zero"
+    return dx
+
+
+def errToLogX(x, y=None, dx=None, dy=None):
+    """
+    calculate error of Log(x)
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if dx == None:
+        dx = 0
+
+    # Check that the x point on the graph is zero
+    if x != 0:
+        dx = dx / x
+    else:
+        raise ValueError, "errToLogX: divide by zero"
+    return dx
+
+
+def errToYX2(x, y, dx=None, dy=None):
+    """
+    """
+    if dx == None:
+        dx = 0
+    if dy == None:
+        dy = 0
+    err = math.sqrt((2 * x * y * dx) ** 2 + ((x ** 2) * dy) ** 2)
+    return err
+
+
+def errToLogXY(x, y, dx=None, dy=None):
+    """
+    calculate error of Log(xy)
+
+    """
+    # Check that the point on the graph is positive
+    # within errors
+    if not (x - dx) > 0 or not (y - dy) > 0:
+        msg = "Transformation does not accept point "
+        msg += " that are consistent with zero."
+        raise ValueError, msg
+    if x != 0 and y != 0:
+        if dx == None:
+            dx = 0
+        if dy == None:
+            dy = 0
+        err = (dx / x) ** 2 + (dy / y) ** 2
+    else:
+        raise ValueError, "cannot compute this error"
+
+    return math.sqrt(math.fabs(err))
+
+
+def errToLogYX2(x, y, dx=None, dy=None):
+    """
+    calculate error of Log(yx**2)
+
+    """
+    # Check that the point on the graph is positive
+    # within errors
+    if not (x - dx) > 0 or not (y - dy) > 0:
+        msg = "Transformation does not accept point"
+        msg += " that are consistent with zero."
+        raise ValueError, msg
+    if x > 0 and y > 0:
+        if dx == None:
+            dx = 0
+        if dy == None:
+            dy = 0
+        err = (2.0 * dx / x) ** 2 + (dy / y) ** 2
+    else:
+        raise ValueError, "cannot compute this error"
+    return math.sqrt(math.fabs(err))
+
+
+def errOneOverX(x, y=None, dx=None, dy=None):
+    """
+    calculate error on 1/x
+
+    """
+    if x != 0:
+        if dx == None:
+            dx = 0
+        err = dx / x ** 2
+    else:
+        raise ValueError, "Cannot compute this error"
+    return math.fabs(err)
+
+
+def errOneOverSqrtX(x, y=None, dx=None, dy=None):
+    """
+    Calculate error on 1/sqrt(x)
+
+    """
+    if x > 0:
+        if dx == None:
+            dx = 0
+        err = -1 / 2 * math.pow(x, -3.0 / 2.0) * dx
+    else:
+        raise ValueError, "Cannot compute this error"
+    return math.fabs(err)
+
+
+def errToLogYX4(x, y=None, dx=None, dy=None):
+    """
+    error for ln(y*x^(4))
+
+    :param x: float value
+
+    """
+    # Check that the point on the graph is positive
+    # within errors
+    if (not (x - dx) > 0) or (not (y - dy) > 0):
+        msg = "Transformation does not accept point "
+        msg += " that are consistent with zero."
+        raise ValueError, msg
+    if dx == None:
+        dx = 0
+    if dy == None:
+        dy = 0
+    err = math.sqrt((4.0 * dx / x) ** 2 + (dy / y) ** 2)
+    return err
+
+
+def errToYX4(x, y=None, dx=None, dy=None):
+    """
+    error for (y*x^(4))
+
+    :param x: float value
+
+    """
+    # Check that the point on the graph is positive
+    # within errors
+
+    if dx == None:
+        dx = 0
+    if dy == None:
+        dy = 0
+    err = math.sqrt((dy * pow(x, 4)) ** 2 + (4 * y * dx * math.pow(x, 3)) ** 2)
+    return err
