Index: guiframe/local_perspectives/plotting/Arc.py
===================================================================
--- guiframe/local_perspectives/plotting/Arc.py	(revision b06ef8cb70788b49507ed818acd3b911d08edcbc)
+++ guiframe/local_perspectives/plotting/Arc.py	(revision b06ef8cb70788b49507ed818acd3b911d08edcbc)
@@ -0,0 +1,151 @@
+#from config import printEVT
+from BaseInteractor import _BaseInteractor
+from copy import deepcopy
+import math
+
+#from Plotter1D import AddPlotEvent
+import SlicerParameters
+import wx   
+#import radii 
+#(FunctionRadiusEvent, EVT_FUNC_RAD) = wx.lib.newevent.NewEvent()      
+class ArcInteractor(_BaseInteractor):
+    """
+         Select an annulus through a 2D plot
+    """
+    def __init__(self,base,axes,color='black', zorder=5, r=1.0,theta1=math.pi/8,
+                 theta2=math.pi/4):
+        
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        self.markers = []
+        self.axes = axes
+        self._inner_mouse_x = r
+        self._inner_mouse_y = 0
+        
+        self._inner_save_x  = r
+        self._inner_save_y  = 0
+        
+        self.scale = 10.0
+        
+        self.theta1=theta1
+        self.theta2=theta2
+      
+        [self.inner_circle] = self.axes.plot([],[],
+                                      linestyle='-', marker='',
+                                      color=self.color)
+        self.npts = 20
+            
+        self.connect_markers([self.inner_circle])
+        self.update()
+
+    def set_layer(self, n):
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        self.clear_markers()
+        try:
+            self.inner_marker.remove()
+            self.inner_circle.remove()
+        except:
+            # Old version of matplotlib
+            for item in range(len(self.axes.lines)):
+                del self.axes.lines[0]
+        
+        
+        
+    def get_radius(self):
+        radius =math.sqrt(math.pow(self._inner_mouse_x, 2)+math.pow(self._inner_mouse_y, 2))
+        return radius
+        
+    def update(self,theta1=None,theta2=None):
+        """
+        Draw the new roughness on the graph.
+        """
+        # Plot inner circle
+        x = []
+        y = []
+        if theta1 !=None:
+            self.theta1= theta1
+        if theta2 !=None:
+            self.theta2= theta2
+        #print "ring update theta1 theta2", self.theta1, self.theta2
+        while self.theta2 < self.theta1: self.theta2 += 2*math.pi
+        npts = int((self.theta2 - self.theta1)/(math.pi/120))
+        for i in range(self.npts):
+            
+            phi =(self.theta2-self.theta1)/(self.npts-1)*i +self.theta1
+            #delta= phi1-phi
+            r=  math.sqrt(math.pow(self._inner_mouse_x, 2)+math.pow(self._inner_mouse_y, 2))
+            xval = 1.0*r*math.cos(phi) 
+            yval = 1.0*r*math.sin(phi) 
+            #xval = 1.0*self._inner_mouse_x*math.cos(phi) 
+            #yval = 1.0*self._inner_mouse_x*math.sin(phi)
+          
+           
+            x.append(xval)
+            y.append(yval)
+        
+        #self.inner_marker.set(xdata=[self._inner_mouse_x],ydata=[0])
+        self.inner_circle.set_data(x, y) 
+        
+       
+        if self._inner_mouse_x <0:
+            self._inner_mouse_x= 0
+            self._inner_mouse_y= 0
+        r=  math.sqrt(math.pow(self._inner_mouse_x, 2)+math.pow(self._inner_mouse_y, 2))
+        x1=  r*math.cos(self.theta1)
+        y1= r*math.sin(self.theta1)
+        #x1= self._inner_mouse_x*math.cos(self.theta1)
+        #y1= self._inner_mouse_x*math.sin(self.theta1)
+        #x2= r2*math.cos(self.theta1)
+        #y2= r2*math.sin(self.theta1)
+       
+            
+       
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        #self._inner_save_x = self._inner_mouse_x
+        #self._inner_save_y = self._inner_mouse_y
+        self._inner_save_x = self._inner_mouse_x
+        self._inner_save_y = self._inner_mouse_y
+        self.base.freeze_axes()
+
+    def moveend(self, ev):
+        self.base.moveend(ev)
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self._inner_mouse_x = self._inner_save_x
+        self._inner_mouse_y = self._inner_save_y
+       
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        #print "ring move x, y", x,y
+        self._inner_mouse_x = x
+        self._inner_mouse_y = y
+      
+        self.base.base.update()
+        
+    def set_cursor(self, x, y):
+        self.move(x, y, None)
+        self.update()
+        
+        
+    def get_params(self):
+        params = {}
+        params["radius"] = self._inner_mouse_x
+        return params
+    
+    def set_params(self, params):
+
+        x = params["radius"] 
+        self.set_cursor(x, self._inner_mouse_y)
+        
+    
Index: guiframe/local_perspectives/plotting/BaseInteractor.py
===================================================================
--- guiframe/local_perspectives/plotting/BaseInteractor.py	(revision b06ef8cb70788b49507ed818acd3b911d08edcbc)
+++ guiframe/local_perspectives/plotting/BaseInteractor.py	(revision b06ef8cb70788b49507ed818acd3b911d08edcbc)
@@ -0,0 +1,157 @@
+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:
+    """
+    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
+        
+    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,prop = 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: guiframe/local_perspectives/plotting/DataPanel.py
===================================================================
--- guiframe/local_perspectives/plotting/DataPanel.py	(revision ce64735ccfa8f53d6e34d9185b54b9f1ab5cb801)
+++ guiframe/local_perspectives/plotting/DataPanel.py	(revision b06ef8cb70788b49507ed818acd3b911d08edcbc)
@@ -17,4 +17,14 @@
 from sans.guicomm.events import EVT_NEW_PLOT
 from sans.guicomm.events import StatusEvent ,NewPlotEvent
+
+from binder import BindArtist
+#from SlicerParameters import SlicerEvent
+#(InternalEvent, EVT_INTERNAL)   = wx.lib.newevent.NewEvent()
+DEFAULT_QMAX = 0.05
+
+DEFAULT_QSTEP = 0.001
+DEFAULT_BEAM = 0.005
+
+import pylab
 
 class PanelMenu(wx.Menu):
@@ -344,5 +354,6 @@
         ## Plottables
         self.plots = {}
-        self.data = data2d
+        self.data2D= data2d
+        self.data = data2d.data
         ## Unique ID (from gui_manager)
         self.uid = None
@@ -350,5 +361,20 @@
         ## Action IDs for internal call-backs
         self.action_ids = {}
-        
+        self.connect = BindArtist(self.subplot.figure)
+        
+        # Beam stop
+        self.beamstop_radius = DEFAULT_BEAM
+        # Slicer
+        
+        self.qmax = data2d.xmax
+        self.imax= data2d.ymax
+        self.qstep = DEFAULT_QSTEP
+        self.x = pylab.arange(-self.qmax, self.qmax+self.qstep*0.01, self.qstep)
+        self.y = pylab.arange(-self.imax, self.imax+self.qstep*0.01, self.qstep)
+
+        self.slicer_z = 5
+        self.slicer = None
+        #self.parent.Bind(EVT_INTERNAL, self._onEVT_INTERNAL)
+        self.axes_frozen = False
         ## Graph        
         self.graph = Graph()
@@ -427,11 +453,92 @@
       
         id = wx.NewId()
+        slicerpop.Append(id, '&Sector')
+        wx.EVT_MENU(self, id, self.onSector) 
+        
+      
+        
+        id = wx.NewId()
+        slicerpop.Append(id, '&Clear slicer')
+        wx.EVT_MENU(self, id,  self.onClearSlicer) 
+        
+        id = wx.NewId()
+        slicerpop.Append(id, '&Edit Parameters')
+        wx.EVT_MENU(self, id, self._onEditDetector) 
+        
+        slicerpop.AppendSeparator()
+        
+        id = wx.NewId()
+        slicerpop.Append(id, '&Save image')
+        wx.EVT_MENU(self, id, self.onSaveImage) 
+     
+        id = wx.NewId()
         slicerpop.Append(id, '&Toggle Linear/Log scale')
         wx.EVT_MENU(self, id, self._onToggleScale) 
-
+         
         pos = event.GetPosition()
         pos = self.ScreenToClient(pos)
         self.PopupMenu(slicerpop, pos)
-    
+   
+    def _setSlicer(self, slicer):
+        # Clear current slicer
+        #printEVT("Plotter2D._setSlicer %s" % slicer)
+        
+        if not self.slicer == None:  
+            self.slicer.clear()            
+            
+        self.slicer_z += 1
+        self.slicer = slicer(self, self.subplot, zorder=self.slicer_z)
+        self.subplot.set_ylim(-self.qmax, self.qmax)
+        self.subplot.set_xlim(-self.qmax, self.qmax)
+        self.update()
+        self.slicer.update()
+        
+        
+    def get_corrected_data(self):
+        # Protect against empty data set
+        if self.data == None:
+            return None
+        import copy
+        output = copy.deepcopy(self.data)
+        return output
+    def freeze_axes(self):
+        self.axes_frozen = True
+        
+    def thaw_axes(self):
+        self.axes_frozen = False
+        
+    def onMouseMotion(self,event):
+        pass
+    def onWheel(self, event):
+        pass    
+    def update(self, draw=True):
+        """
+            Respond to changes in the model by recalculating the 
+            profiles and resetting the widgets.
+        """
+        #self.slicer.update()
+        self.draw()
+            
+  
+    def onSector(self, event):
+        
+        from SectorSlicer import SectorInteractor
+              
+        self.slicer_z += 1
+        self.slicer = SectorInteractor(self, self.subplot, zorder=self.slicer_z)
+        self.subplot.set_ylim(-self.qmax, self.qmax)
+        self.subplot.set_xlim(-self.qmax, self.qmax)
+        self.update()
+        self.slicer.update()
+        
+       
+    def onClearSlicer(self, event):
+        print "on clear"  
+        
+          
+    def _onEditDetector(self, event):
+        print "on parameter"
+        
+        
     def _onToggleScale(self, event):
         """
@@ -524,18 +631,4 @@
    
 
-    def onSectorSlicer(self, event):
-        print "onLineSlicer"
-        import math
-        from DataLoader.manipulations import SectorPhi
-        for item in self.graph.plottables:
-            r= SectorPhi(005,.01, 0.0, math.pi/2.0)
-            print r(item)
-            data=r(item)
-        new_plot= Data1D(x=data.x,y=data.y,dy=data.dy )
-        new_plot.name = "sector"
-        new_plot.group_id= "sector"
-        #wx.PostEvent(self.parent, NewPlotEvent(plot=r(item), title="Analytical model"))
-        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Analytical model"))
-        
-        
-        
+        
+        
Index: guiframe/local_perspectives/plotting/Edge.py
===================================================================
--- guiframe/local_perspectives/plotting/Edge.py	(revision b06ef8cb70788b49507ed818acd3b911d08edcbc)
+++ guiframe/local_perspectives/plotting/Edge.py	(revision b06ef8cb70788b49507ed818acd3b911d08edcbc)
@@ -0,0 +1,130 @@
+#from config import printEVT
+from BaseInteractor import _BaseInteractor
+from copy import deepcopy
+import math
+
+#from Plotter1D import AddPlotEvent
+import SlicerParameters
+import wx     
+
+import wx.lib.newevent
+       
+class RadiusInteractor(_BaseInteractor):
+    """
+         Select an annulus through a 2D plot
+    """
+    def __init__(self,base,axes,color='black', zorder=5, arc1=None,arc2=None,
+                 theta=math.pi/8):
+        
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        self.markers = []
+        self.axes = axes
+        
+        self.r1 = arc1.get_radius()
+        self.r2 = arc2.get_radius()
+        #print "radius init", self.r1, self.r2
+        self.theta=theta
+        self.save_theta= theta
+        #self.scale = 10.0
+        
+        self.arc1= arc1
+        self.arc2=arc2
+        #raise "Version error", message
+        x1= self.r1*math.cos(self.theta)
+        y1= self.r1*math.sin(self.theta)
+        x2= self.r2*math.cos(self.theta)
+        y2= self.r2*math.sin(self.theta)
+        self.line = self.axes.plot([x1,x2],[y1,y2],
+                                      linestyle='-', marker='',
+                                      color=self.color,
+                                      visible=True)[0]
+       
+        self.npts = 20
+      
+        self.connect_markers([self.line])
+        self.update()
+        
+        
+    def set_layer(self, n):
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        self.clear_markers()
+        try:
+            self.line.remove()
+          
+        except:
+            # Old version of matplotlib
+            for item in range(len(self.axes.lines)):
+                del self.axes.lines[0]
+        
+        
+        
+    def get_radius(self):
+        return self.theta
+        
+    def update(self,r1=None, r2=None):
+        """
+        Draw the new roughness on the graph.
+        """
+        # Plot inner circle
+        if r1 !=None:
+            self.r1=r1
+        if r2!=None:
+            self.r2=r2
+        #print "update line ",self.r1,self.r2
+        #if self.theta <0:
+        #    self.theta= 2*math.pi + self.theta
+        x1= self.r1*math.cos(self.theta)
+        y1= self.r1*math.sin(self.theta)
+        x2= self.r2*math.cos(self.theta)
+        y2= self.r2*math.sin(self.theta)  
+             
+        self.line.set(xdata=[x1,x2], ydata=[y1,y2])
+        return 1
+ 
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.save_theta= self.theta
+        self.base.freeze_axes()
+   
+    def moveend(self, ev):
+        self.base.moveend(ev)
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.theta = self.save_theta
+
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        self.theta= math.atan2(y,x)
+        
+        
+        self.base.base.update()
+        
+    def set_cursor(self, x, y):
+        self.move(x, y, None)
+        self.update()
+        
+        
+    def get_params(self):
+        params = {}
+        params["radius1"] = self.r1
+        params["radius2"] = self.r2
+        return params
+    
+    def set_params(self, params):
+
+        x1 = params["radius1"] 
+        x2 = params["radius2"] 
+        self.set_cursor(x, self._inner_mouse_y)
+        
+    
Index: guiframe/local_perspectives/plotting/SectorSlicer.py
===================================================================
--- guiframe/local_perspectives/plotting/SectorSlicer.py	(revision b06ef8cb70788b49507ed818acd3b911d08edcbc)
+++ guiframe/local_perspectives/plotting/SectorSlicer.py	(revision b06ef8cb70788b49507ed818acd3b911d08edcbc)
@@ -0,0 +1,243 @@
+#TODO: the line slicer should listen to all 2DREFRESH events, get the data and slice it
+#      before pushing a new 1D data update.
+
+#
+#TODO: NEED MAJOR REFACTOR
+#
+
+
+# Debug printout
+
+from BaseInteractor import _BaseInteractor
+from copy import deepcopy
+import math
+
+
+import SlicerParameters
+import wx
+
+class SectorInteractor(_BaseInteractor):
+    """
+         Select an annulus through a 2D plot
+    """
+    def __init__(self,base,axes,color='black', zorder=3):
+        
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        self.markers = []
+        self.axes = axes
+        self.qmax = self.base.qmax
+        self.connect = self.base.connect
+        
+        ## Number of points on the plot
+        self.nbins = 20
+        theta1=math.pi/8
+        theta2=math.pi/2
+        r1=self.qmax/2.0
+        r2=self.qmax/1.8
+        # Inner circle
+        from Arc import ArcInteractor
+        self.inner_circle = ArcInteractor(self, self.base.subplot, zorder=zorder, r=self.qmax/2.0,theta1= theta1,
+                                           theta2=theta2)
+        self.inner_circle.qmax = self.base.qmax
+        self.outer_circle = ArcInteractor(self, self.base.subplot, zorder=zorder+1, r=self.qmax/1.8,theta1= theta1,
+                                           theta2=theta2)
+        self.outer_circle.qmax = self.base.qmax*1.2
+        #self.outer_circle.set_cursor(self.base.qmax/1.8, 0)
+        from Edge import RadiusInteractor
+        self.inner_radius= RadiusInteractor(self, self.base.subplot, zorder=zorder+1,
+                                             arc1=self.inner_circle,
+                                             arc2=self.outer_circle,
+                                            theta=math.pi/8)
+        self.outer_radius= RadiusInteractor(self, self.base.subplot, zorder=zorder+1,
+                                             arc1=self.inner_circle,
+                                             arc2=self.outer_circle,
+                                            theta=math.pi/2)
+        self.update()
+        #self._post_data()
+        
+        # Bind to slice parameter events
+        #self.base.parent.Bind(SlicerParameters.EVT_SLICER_PARS, self._onEVT_SLICER_PARS)
+
+
+    def _onEVT_SLICER_PARS(self, event):
+        #printEVT("AnnulusSlicer._onEVT_SLICER_PARS")
+        event.Skip()
+        if event.type == self.__class__.__name__:
+            self.set_params(event.params)
+            self.base.update()
+
+    def update_and_post(self):
+        self.update()
+        self._post_data()
+
+    def save_data(self, path, image, x, y):
+        output = open(path, 'w')
+        
+        data_x, data_y = self.get_data(image, x, y)
+        
+        output.write("<phi>  <average>\n")
+        for i in range(len(data_x)):
+            output.write("%g  %g\n" % (data_x[i], data_y[i]))
+        output.close()
+
+    def set_layer(self, n):
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        self.clear_markers()
+        #self.outer_circle.clear()
+        self.inner_circle.clear()
+        #self.base.connect.disconnect()
+        self.base.parent.Unbind(SlicerParameters.EVT_SLICER_PARS)
+        
+    def update(self):
+        """
+        Respond to changes in the model by recalculating the profiles and
+        resetting the widgets.
+        """
+        # Update locations        
+        self.inner_circle.update()
+        self.outer_circle.update()
+        r1=self.inner_circle.get_radius()
+        r2=self.outer_circle.get_radius()
+        print"annulus update",r1, r2
+        if self.inner_radius.update(r1,r2)==1:
+            print "went here"
+            self.inner_circle.update(theta1=self.inner_radius.get_radius(), theta2=None)
+            self.outer_circle.update(theta1=self.inner_radius.get_radius(),theta2=None)
+        if self.outer_radius.update(r1,r2)==1:
+             #self.outer_circle.update(self.outer_radius.get_radius())
+             self.inner_circle.update(theta1=None, theta2=self.outer_radius.get_radius())
+             self.outer_circle.update(theta1=None,theta2=self.outer_radius.get_radius())
+    def get_data(self, image, x, y):
+        """ 
+            Return a 1D vector corresponding to the slice
+            @param image: data matrix
+            @param x: x matrix
+            @param y: y matrix
+        """
+        # If we have no data, just return
+        if image == None:
+            return        
+        
+        nbins = self.nbins
+        
+        data_x = nbins*[0]
+        data_y = nbins*[0]
+        counts = nbins*[0]
+        length = len(image)
+        
+        
+        for i_x in range(length):
+            for i_y in range(length):
+                        
+                q = math.sqrt(x[i_x]*x[i_x] + y[i_y]*y[i_y])
+                if (q>self.inner_circle._inner_mouse_x \
+                    and q<self.outer_circle._inner_mouse_x) \
+                    or (q<self.inner_circle._inner_mouse_x \
+                    and q>self.outer_circle._inner_mouse_x):
+                            
+                    i_bin = int(math.ceil(nbins*(math.atan2(y[i_y], x[i_x])+math.pi)/(2.0*math.pi)) - 1)
+                    
+                    
+                    #data_y[i_bin] += math.exp(image[i_x][i_y])
+                    data_y[i_bin] += image[i_y][i_x]
+                    counts[i_bin] += 1.0
+                    
+        for i in range(nbins):
+            data_x[i] = (1.0*i+0.5)*2.0*math.pi/nbins
+            if counts[i]>0:
+                data_y[i] = data_y[i]/counts[i]
+        
+        return data_x, data_y
+
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.base.freeze_axes()
+        self.inner_circle.save(ev)
+        #self.outer_circle.save(ev)
+
+    def _post_data(self):
+        # Compute data
+        data = self.base.get_corrected_data()
+        # If we have no data, just return
+        if data == None:
+            return
+
+        data_x, data_y = self.get_data(data, self.base.x, self.base.y)
+        
+        name = "Ring"
+        if hasattr(self.base, "name"):
+            name += " %s" % self.base.name
+        
+        wx.PostEvent(self.base.parent, AddPlotEvent(name=name,
+                                               x = data_x,
+                                               y = data_y,
+                                               qmin = self.inner_circle._inner_mouse_x,
+                                               qmax = self.outer_circle._inner_mouse_x,
+                                               yscale = 'log',
+                                               variable = 'ANGLE',
+                                               ylabel = "\\rm{Intensity} ",
+                                               yunits = "cm^{-1}",
+                                               xlabel = "\\rm{\phi}",
+                                               xunits = "rad",
+                                               parent = self.base.__class__.__name__))
+                                               
+        
+    def moveend(self, ev):
+        self.base.thaw_axes()
+        
+        # Post paramters
+        event = SlicerParameters.SlicerParameterEvent()
+        event.type = self.__class__.__name__
+        event.params = self.get_params()
+        wx.PostEvent(self.base.parent, event)
+
+        self._post_data()
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.inner_circle.restore()
+        #self.outer_circle.restore()
+
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        pass
+        
+    def set_cursor(self, x, y):
+        pass
+        
+    def get_params(self):
+        params = {}
+        params["inner_radius"] = self.inner_circle._inner_mouse_x
+        #params["outer_radius"] = self.outer_circle._inner_mouse_x
+        params["nbins"] = self.nbins
+        return params
+    
+    def set_params(self, params):
+        
+        inner = params["inner_radius"] 
+        #outer = params["outer_radius"] 
+        self.nbins = int(params["nbins"])
+        self.inner_circle.set_cursor(inner, self.inner_circle._inner_mouse_y)
+        #self.outer_circle.set_cursor(outer, self.outer_circle._inner_mouse_y)
+        self._post_data()
+        
+    def freeze_axes(self):
+        self.base.freeze_axes()
+        
+    def thaw_axes(self):
+        self.base.thaw_axes()
+
+    def draw(self):
+        self.base.draw()
+
+    
Index: guiframe/local_perspectives/plotting/SlicerParameters.py
===================================================================
--- guiframe/local_perspectives/plotting/SlicerParameters.py	(revision b06ef8cb70788b49507ed818acd3b911d08edcbc)
+++ guiframe/local_perspectives/plotting/SlicerParameters.py	(revision b06ef8cb70788b49507ed818acd3b911d08edcbc)
@@ -0,0 +1,115 @@
+"""
+    Panel class to show the slicer parameters 
+"""
+
+import wx
+import wx.lib.newevent
+from copy import deepcopy
+
+(SlicerEvent, EVT_SLICER)   = wx.lib.newevent.NewEvent()
+(SlicerParameterEvent, EVT_SLICER_PARS)   = wx.lib.newevent.NewEvent()
+
+class SlicerParameterPanel(wx.Panel):
+    #TODO: show units
+    #TODO: order parameters properly
+    
+    def __init__(self, parent, *args, **kwargs):
+        wx.Panel.__init__(self, parent, *args, **kwargs)
+        self.params = {}
+        self.parent = parent
+        self.type = None
+        self.listeners = []
+        self.parameters = []
+        self.bck = wx.GridBagSizer(5,5)
+        self.SetSizer(self.bck)
+               
+        title = wx.StaticText(self, -1, "Right-click on 2D plot for slicer options", style=wx.ALIGN_LEFT)
+        self.bck.Add(title, (0,0), (1,2), flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
+        
+        # Bindings
+        self.parent.Bind(EVT_SLICER, self.onEVT_SLICER)
+        self.parent.Bind(EVT_SLICER_PARS, self.onParamChange)
+
+    def onEVT_SLICER(self, event):
+        """
+            Process EVT_SLICER events
+            When the slicer changes, update the panel
+            
+            @param event: EVT_SLICER event
+        """
+        event.Skip()
+        if event.obj_class==None:
+            self.set_slicer(None, None)
+        else:
+            self.set_slicer(event.type, event.params)
+        
+    def set_slicer(self, type, params):
+        """
+            Rebuild the panel
+        """
+        self.bck.Clear(True)  
+        self.type = type  
+        
+        if type==None:
+            title = wx.StaticText(self, -1, "Right-click on 2D plot for slicer options", style=wx.ALIGN_LEFT)
+            self.bck.Add(title, (0,0), (1,2), flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
+
+        else:
+            title = wx.StaticText(self, -1, "Slicer Parameters", style=wx.ALIGN_LEFT)
+            self.bck.Add(title, (0,0), (1,2), flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
+            
+            n = 1
+            self.parameters = []
+            #params = slicer.get_params()
+            keys = params.keys()
+            keys.sort()
+            
+            for item in keys:
+                n += 1
+                text = wx.StaticText(self, -1, item, style=wx.ALIGN_LEFT)
+                self.bck.Add(text, (n-1,0), flag = wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
+                ctl = wx.TextCtrl(self, -1, size=(80,20), style=wx.TE_PROCESS_ENTER)
+                
+                ctl.SetToolTipString("Modify the value of %s to change the 2D slicer" % item)
+                
+                
+                ctl.SetValue(str(params[item]))
+                self.Bind(wx.EVT_TEXT_ENTER, self.onTextEnter)
+                ctl.Bind(wx.EVT_KILL_FOCUS, self.onTextEnter)
+                self.parameters.append([item, ctl])
+                self.bck.Add(ctl, (n-1,1), flag=wx.TOP|wx.BOTTOM, border = 0)
+
+        self.bck.Layout()
+        self.bck.Fit(self)
+        self.parent.GetSizer().Layout()
+
+    def onParamChange(self, evt):
+        evt.Skip()
+        if evt.type == "UPDATE":
+            for item in self.parameters:              
+                if item[0] in evt.params:
+                    item[1].SetValue("%-5.3g" %evt.params[item[0]])
+                    item[1].Refresh()
+        
+    def onTextEnter(self, evt): 
+        """
+            Parameters have changed
+        """ 
+        params = {}
+        has_error = False
+        for item in self.parameters:
+            try:
+                params[item[0]] = float(item[1].GetValue())
+                item[1].SetBackgroundColour(
+                        wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
+                item[1].Refresh()
+            except:
+                has_error = True
+                item[1].SetBackgroundColour("pink")
+                item[1].Refresh()
+
+        if has_error==False:
+            # Post parameter event
+            event = SlicerParameterEvent(type=self.type, params=params)
+            wx.PostEvent(self.parent, event)
+        
Index: guiframe/local_perspectives/plotting/binder.py
===================================================================
--- guiframe/local_perspectives/plotting/binder.py	(revision b06ef8cb70788b49507ed818acd3b911d08edcbc)
+++ guiframe/local_perspectives/plotting/binder.py	(revision b06ef8cb70788b49507ed818acd3b911d08edcbc)
@@ -0,0 +1,384 @@
+"""
+Extension to MPL to support the binding of artists to key/mouse events.
+"""
+
+class Selection:
+    """
+    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:
+
+    # 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),
+            ]
+            
+
+        # Turn off picker if it hasn't already been done
+        try:
+            canvas.mpl_disconnect(canvas.button_pick_id)
+            canvas.mpl_disconnect(canvas.scroll_pick_id)
+        except: 
+            pass
+        
+        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: 
+            pass
+        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
+        #print "==> added",artist,[artist],"to",trigger,":",self._actions[trigger].keys()
+
+        # 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))
+        # print "search"," ".join([str(h) for h in self._artists])
+        found = Selection()
+        #print "searching in",self._artists
+        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
+        #print "found",found.artist
+        
+        # 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
+            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)
+            #print "found",found.artist
+            self.trigger(found,'motion',event)
+
+    def _onClick(self,event):
+        """
+        Process button click
+        """
+        import time
+        
+        # Check for double-click
+        event_time = time.time()
+        #print event_time,self._last_time,self.dclick_threshhold
+        #print (event_time > self._last_time + self.dclick_threshhold)
+        #print event.button,self._last_button
+        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: guiframe/local_perspectives/plotting/plotting.py
===================================================================
--- guiframe/local_perspectives/plotting/plotting.py	(revision c417227254f7cc42adef5e0917cdf92cd084b821)
+++ guiframe/local_perspectives/plotting/plotting.py	(revision b06ef8cb70788b49507ed818acd3b911d08edcbc)
@@ -12,438 +12,10 @@
 import wx
 import sys
-import danse.common.plottools
-from danse.common.plottools.PlotPanel import PlotPanel
-from danse.common.plottools.plottables import Graph,Data1D
+#import danse.common.plottools
+#from danse.common.plottools.PlotPanel import PlotPanel
+#from danse.common.plottools.plottables import Graph,Data1D
 from sans.guicomm.events import EVT_NEW_PLOT
 from sans.guicomm.events import StatusEvent 
-class PanelMenu(wx.Menu):
-    plots = None
-    graph = None
-    
-    def set_plots(self, plots):
-        self.plots = plots
-    
-    def set_graph(self, graph):
-        self.graph = graph
-        
-class View1DPanel1D(PlotPanel):
-    """
-        Plot panel for use with the GUI manager
-    """
-    
-    ## Internal name for the AUI manager
-    window_name = "plotpanel"
-    ## Title to appear on top of the window
-    window_caption = "Plot Panel"
-    ## Flag to tell the GUI manager that this panel is not
-    #  tied to any perspective
-    ALWAYS_ON = True
-    ## Group ID
-    group_id = None
-    
-    def __init__(self, parent, id = -1, color = None,\
-        dpi = None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
-        """
-            Initialize the panel
-        """
-        PlotPanel.__init__(self, parent, id = id, style = style, **kwargs)
-        
-        ## Reference to the parent window
-        self.parent = parent
-        ## Plottables
-        self.plots = {}
-        
-        ## Unique ID (from gui_manager)
-        self.uid = None
-        
-        ## Action IDs for internal call-backs
-        self.action_ids = {}
-        
-        ## Graph        
-        self.graph = Graph()
-        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
-        self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
-        self.graph.render(self)
-   
-    def _reset(self):
-        """
-            Resets internal data and graph
-        """    
-        self.graph.reset()
-        self.plots      = {}
-        self.action_ids = {}
-    
-    def _onEVT_1DREPLOT(self, event):
-        """
-            Data is ready to be displayed
-            @param event: data event
-        """
-        #TODO: Check for existence of plot attribute
 
-        # Check whether this is a replot. If we ask for a replot
-        # and the plottable no longer exists, ignore the event.
-        if hasattr(event, "update") and event.update==True \
-            and event.plot.name not in self.plots.keys():
-            return
-        
-        if hasattr(event, "reset"):
-            self._reset()
-        
-        is_new = True
-        if event.plot.name in self.plots.keys():
-            # Check whether the class of plottable changed
-            #print "panel1D ",event.plot.__class_
-            #print "event panel 1d",self.plots[event.plot.name].__class__
-            if not event.plot.__class__==self.plots[event.plot.name].__class__:
-                self.graph.delete(self.plots[event.plot.name])
-            else:
-                is_new = False
-        
-        if is_new:
-            self.plots[event.plot.name] = event.plot
-            self.graph.add(self.plots[event.plot.name])
-        else:
-            self.plots[event.plot.name].x = event.plot.x    
-            self.plots[event.plot.name].y = event.plot.y    
-            self.plots[event.plot.name].dy = event.plot.dy  
-            if hasattr(event.plot, 'dx') and hasattr(self.plots[event.plot.name], 'dx'):
-                self.plots[event.plot.name].dx = event.plot.dx    
- 
-        
-        # Check axis labels
-        #TODO: Should re-factor this
-        #if event.plot._xunit != self.graph.prop["xunit"]:
-        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
-            
-        #if event.plot._yunit != self.graph.prop["yunit"]:
-        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
-      
-        # Set the view scale for all plots
-        self._onEVT_FUNC_PROPERTY()
-      
-        self.graph.render(self)
-        self.subplot.figure.canvas.draw_idle()
-
-    def onLeftDown(self,event): 
-        """ left button down and ready to drag"""
-           
-        PlotPanel.onLeftDown(self, event)
-        ax = event.inaxes
-        if ax != None:
-            position = "x: %8.3g    y: %8.3g" % (event.xdata, event.ydata)
-            wx.PostEvent(self.parent, StatusEvent(status=position))
-
-    def _onRemove(self, event):
-        """
-        """
-        if not self.graph.selected_plottable == None:
-            print self.graph.selected_plottable
-            
-            
-            self.graph.delete(self.plots[self.graph.selected_plottable])
-            del self.plots[self.graph.selected_plottable]
-            self.graph.render(self)
-            self.subplot.figure.canvas.draw_idle()    
-            
-
-    def onContextMenu(self, event):
-        """
-            1D plot context menu
-            @param event: wx context event
-        """
-        #slicerpop = wx.Menu()
-        slicerpop = PanelMenu()
-        slicerpop.set_plots(self.plots)
-        slicerpop.set_graph(self.graph)
-                
-        # Option to save the data displayed
-        
-        #for plot in self.graph.plottables:
-        if self.graph.selected_plottable in self.plots:
-            plot = self.plots[self.graph.selected_plottable]
-            id = wx.NewId()
-            name = plot.name
-            slicerpop.Append(id, "&Save %s points" % name)
-            self.action_ids[str(id)] = plot
-            wx.EVT_MENU(self, id, self._onSave)
-                
-            # Option to delete plottable
-            id = wx.NewId()
-            slicerpop.Append(id, "Remove %s curve" % name)
-            self.action_ids[str(id)] = plot
-            wx.EVT_MENU(self, id, self._onRemove)
-            
-            # Option to hide
-            #TODO: implement functionality to hide a plottable (legend click)
-            slicerpop.AppendSeparator()
-                
-        # Various plot options
-        id = wx.NewId()
-        slicerpop.Append(id,'&Save image', 'Save image as PNG')
-        wx.EVT_MENU(self, id, self.onSaveImage)
-        
-        
-        item_list = self.parent.get_context_menu(self.graph)
-        if (not item_list==None) and (not len(item_list)==0):
-                slicerpop.AppendSeparator()
-                for item in item_list:
-                    try:
-                        id = wx.NewId()
-                        slicerpop.Append(id, item[0], item[1])
-                        wx.EVT_MENU(self, id, item[2])
-                    except:
-                        print sys.exc_value
-                        print RuntimeError, "View1DPanel.onContextMenu: bad menu item"
-        
-        slicerpop.AppendSeparator()
-        
-        if self.graph.selected_plottable in self.plots:
-            if self.plots[self.graph.selected_plottable].__class__.__name__=="Theory1D":
-                id = wx.NewId()
-                slicerpop.Append(id, '&Add errors to data')
-                wx.EVT_MENU(self, id, self._on_add_errors)
-            else:
-                id = wx.NewId()
-                slicerpop.Append(id, '&Linear fit')
-                wx.EVT_MENU(self, id, self.onFitting)
-                
-        
-
-        id = wx.NewId()
-        slicerpop.Append(id, '&Change scale')
-        wx.EVT_MENU(self, id, self._onProperties)
-        
-        id = wx.NewId()
-        #slicerpop.AppendSeparator()
-        slicerpop.Append(id, '&Reset Graph')
-        wx.EVT_MENU(self, id, self.onResetGraph)        
-
-        pos = event.GetPosition()
-        pos = self.ScreenToClient(pos)
-        self.PopupMenu(slicerpop, pos)
-    
-    
-    def _on_add_errors(self, evt):
-        """
-            Compute reasonable errors for a data set without 
-            errors and transorm the plottable to a Data1D
-        """
-        import math
-        import numpy
-        import time
-        
-        if not self.graph.selected_plottable == None:
-            length = len(self.plots[self.graph.selected_plottable].x)
-            dy = numpy.zeros(length)
-            for i in range(length):
-                dy[i] = math.sqrt(self.plots[self.graph.selected_plottable].y[i])
-                
-            new_plot = Data1D(self.plots[self.graph.selected_plottable].x,
-                              self.plots[self.graph.selected_plottable].y,
-                              dy=dy)
-            new_plot.interactive = True
-            new_plot.name = self.plots[self.graph.selected_plottable].name 
-            if hasattr(self.plots[self.graph.selected_plottable], "group_id"):
-                new_plot.group_id = self.plots[self.graph.selected_plottable].group_id
-            else:
-                new_plot.group_id = str(time.time())
-            
-            label, unit = self.plots[self.graph.selected_plottable].get_xaxis()
-            new_plot.xaxis(label, unit)
-            label, unit = self.plots[self.graph.selected_plottable].get_yaxis()
-            new_plot.yaxis(label, unit)
-            
-            self.graph.delete(self.plots[self.graph.selected_plottable])
-            
-            self.graph.add(new_plot)
-            self.plots[self.graph.selected_plottable]=new_plot
-            
-            self.graph.render(self)
-            self.subplot.figure.canvas.draw_idle()    
-    
-    def _onSave(self, evt):
-        """
-            Save a data set to a text file
-            @param evt: Menu event
-        """
-        import os
-        id = str(evt.GetId())
-        if id in self.action_ids:         
-            
-            path = None
-            dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.txt", wx.SAVE)
-            if dlg.ShowModal() == wx.ID_OK:
-                path = dlg.GetPath()
-                mypath = os.path.basename(path)
-                print path
-            dlg.Destroy()
-            
-            if not path == None:
-                out = open(path, 'w')
-                has_errors = True
-                if self.action_ids[id].dy==None or self.action_ids[id].dy==[]:
-                    has_errors = False
-                    
-                # Sanity check
-                if has_errors:
-                    try:
-                        if len(self.action_ids[id].y) != len(self.action_ids[id].dy):
-                            print "Y and dY have different lengths"
-                            has_errors = False
-                    except:
-                        has_errors = False
-                
-                if has_errors:
-                    out.write("<X>   <Y>   <dY>\n")
-                else:
-                    out.write("<X>   <Y>\n")
-                    
-                for i in range(len(self.action_ids[id].x)):
-                    if has_errors:
-                        out.write("%g  %g  %g\n" % (self.action_ids[id].x[i], 
-                                                    self.action_ids[id].y[i],
-                                                    self.action_ids[id].dy[i]))
-                    else:
-                        out.write("%g  %g\n" % (self.action_ids[id].x[i], 
-                                                self.action_ids[id].y[i]))
-                        
-                out.close()
-    
-    
-    def _onToggleScale(self, event):
-        if self.get_yscale() == 'log':
-            self.set_yscale('linear')
-        else:
-            self.set_yscale('log')
-        self.subplot.figure.canvas.draw_idle()    
-        
-class View1DPanel2D( View1DPanel1D):
-    """
-        Plot panel for use with the GUI manager
-    """
-    
-    ## Internal name for the AUI manager
-    window_name = "plotpanel"
-    ## Title to appear on top of the window
-    window_caption = "Plot Panel"
-    ## Flag to tell the GUI manager that this panel is not
-    #  tied to any perspective
-    ALWAYS_ON = True
-    ## Group ID
-    group_id = None
-    
-    def __init__(self, parent, id = -1, color = None,\
-        dpi = None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
-        """
-            Initialize the panel
-        """
-        View1DPanel1D.__init__(self, parent, id = id, style = style, **kwargs)
-        
-        ## Reference to the parent window
-        self.parent = parent
-        ## Plottables
-        self.plots = {}
-        
-        ## Unique ID (from gui_manager)
-        self.uid = None
-        
-        ## Action IDs for internal call-backs
-        self.action_ids = {}
-        
-        ## Graph        
-        self.graph = Graph()
-        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
-        self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
-        self.graph.render(self)
-  
-    def _onEVT_1DREPLOT(self, event):
-        """
-            Data is ready to be displayed
-            @param event: data event
-        """
-        #TODO: Check for existence of plot attribute
-        # Check whether this is a replot. If we ask for a replot
-        # and the plottable no longer exists, ignore the event.
-        if hasattr(event, "update") and event.update==True \
-            and event.plot.name not in self.plots.keys():
-            return
-        if hasattr(event, "reset"):
-            self._reset()
-        is_new = True
-        if event.plot.name in self.plots.keys():
-            # Check whether the class of plottable changed
-            if not event.plot.__class__==self.plots[event.plot.name].__class__:
-                self.graph.delete(self.plots[event.plot.name])
-            else:
-                is_new = False
-        self.plots[event.plot.name] = event.plot
-        #if is_new:
-        self.graph.add(self.plots[event.plot.name])
-        
-
-        # Check axis labels
-        #TODO: Should re-factor this
-        #if event.plot._xunit != self.graph.prop["xunit"]:
-       
-        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
-        #if event.plot._yunit != self.graph.prop["yunit"]:
-        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
-        self.graph.render(self)
-        self.subplot.figure.canvas.draw_idle()
-
-
-    def onContextMenu(self, event):
-        """
-            2D plot context menu
-            @param event: wx context event
-        """
-        
-        #slicerpop = wx.Menu()
-        slicerpop = PanelMenu()
-        slicerpop.set_plots(self.plots)
-        slicerpop.set_graph(self.graph)
-                
-        # Option to save the data displayed
-    
-        # Various plot options
-        id = wx.NewId()
-        slicerpop.Append(id,'&Save image', 'Save image as PNG')
-        wx.EVT_MENU(self, id, self.onSaveImage)
-        
-        
-        item_list = self.parent.get_context_menu(self.graph)
-        if (not item_list==None) and (not len(item_list)==0):
-                slicerpop.AppendSeparator()
-                for item in item_list:
-                    try:
-                        id = wx.NewId()
-                        slicerpop.Append(id, item[0], item[1])
-                        wx.EVT_MENU(self, id, item[2])
-                    except:
-                        print sys.exc_value
-                        print RuntimeError, "View1DPanel2D.onContextMenu: bad menu item"
-        
-        slicerpop.AppendSeparator()
-      
-        id = wx.NewId()
-        slicerpop.Append(id, '&Toggle Linear/Log scale')
-        wx.EVT_MENU(self, id, self._onToggleScale) 
-
-        pos = event.GetPosition()
-        pos = self.ScreenToClient(pos)
-        self.PopupMenu(slicerpop, pos)
-    
-    def _onToggleScale(self, event):
-        """
-            toggle axis and replot image
-        """
-        if self.scale == 'log':
-            self.scale = 'linear'
-        else:
-            self.scale = 'log'
-        self.image(self.data,self.xmin_2D,self.xmax_2D,self.ymin_2D,
-                   self.ymax_2D,self.zmin_2D ,self.zmax_2D )
-        wx.PostEvent(self.parent, StatusEvent(status="Image is in %s scale"%self.scale))
-      
 class Plugin:
     """
@@ -545,7 +117,9 @@
         if not is_available:
             if not hasattr(event.plot,'data'):
+                from DataPanel import View1DPanel1D
                 new_panel = View1DPanel1D(self.parent, -1, style=wx.RAISED_BORDER)
             else:
-                new_panel = View1DPanel2D(self.parent, -1, style=wx.RAISED_BORDER)
+                from DataPanel import View1DPanel2D
+                new_panel = View1DPanel2D(self.parent, -1, data2d=event.plot,style=wx.RAISED_BORDER)
             # Set group ID if available
             group_id_str = ''
