source: sasview/guiframe/local_perspectives/plotting/boxSlicer.py @ d37ba31

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since d37ba31 was 70cf5d3, checked in by Gervaise Alina <gervyh@…>, 16 years ago

allow selecting for fit for average data1D

  • Property mode set to 100644
File size: 18.9 KB
RevLine 
[2d107b8]1#TODO: the line slicer should listen to all 2DREFRESH events, get the data and slice it
2#      before pushing a new 1D data update.
3
4#
5#TODO: NEED MAJOR REFACTOR
6#
7
8
9# Debug printout
[38224f10]10#from config import printEVT
[2d107b8]11from BaseInteractor import _BaseInteractor
12from copy import deepcopy
[18eba35]13import math, numpy
[2d107b8]14
[0d9dae8]15from sans.guicomm.events import NewPlotEvent, StatusEvent,SlicerParameterEvent,EVT_SLICER_PARS
[2d107b8]16import SlicerParameters
17import wx
18
[0f6d05f8]19
[38224f10]20class BoxInteractor(_BaseInteractor):
[2d107b8]21    """
[eba08f1a]22         BoxInteractor define a rectangle that return data1D average of Data2D
23         in a rectangle area defined by -x, x ,y, -y
[2d107b8]24    """
[18eba35]25    def __init__(self,base,axes,color='black', zorder=3):
[2d107b8]26        _BaseInteractor.__init__(self, base, axes, color=color)
[6c0568b]27        ## Class initialization
[2d107b8]28        self.markers = []
29        self.axes = axes
[6c0568b]30        ##connecting artist
[2d107b8]31        self.connect = self.base.connect
[6c0568b]32        ## determine x y  values
[3b909b7]33        self.x= 0.5*min(math.fabs(self.base.data2D.xmax),math.fabs( self.base.data2D.xmin))
34        self.y= 0.5*min(math.fabs(self.base.data2D.xmax),math.fabs( self.base.data2D.xmin))       
[6c0568b]35        ## when reach qmax reset the graph
[18eba35]36        self.qmax = max(self.base.data2D.xmax,self.base.data2D.xmin,
37                        self.base.data2D.ymax,self.base.data2D.ymin )   
[2d107b8]38        ## Number of points on the plot
[ffd23b5]39        self.nbins = 30
[6c0568b]40        ## reference of the current  Slab averaging
[ffd23b5]41        self.averager=None
[6c0568b]42        ## Create vertical and horizaontal lines for the rectangle
[18eba35]43        self.vertical_lines = VerticalLines(self, self.base.subplot,color='blue', 
[dd40217]44                                      zorder=zorder,
[18eba35]45                                        y= self.y ,
46                                        x= self.x)
47        self.vertical_lines.qmax = self.qmax
48       
49        self.horizontal_lines= HorizontalLines(self, self.base.subplot,color='green', 
[dd40217]50                                      zorder=zorder,
[18eba35]51                                      x= self.x,
52                                      y= self.y)
53        self.horizontal_lines.qmax= self.qmax
[6c0568b]54        ## draw the rectangle and plost the data 1D resulting
55        ## of averaging data2D
[38224f10]56        self.update()
[0f6d05f8]57        self._post_data()
[6c0568b]58        ## Bind to slice parameter events
[18eba35]59        self.base.Bind(EVT_SLICER_PARS, self._onEVT_SLICER_PARS)
[2d107b8]60
61
62    def _onEVT_SLICER_PARS(self, event):
[6c0568b]63        """
64            receive an event containing parameters values to reset the slicer
65            @param event: event of type SlicerParameterEvent with params as
66            attribute
67        """
[18eba35]68        wx.PostEvent(self.base.parent, StatusEvent(status="BoxSlicer._onEVT_SLICER_PARS"))
[2d107b8]69        event.Skip()
70        if event.type == self.__class__.__name__:
[0f6d05f8]71            self.set_params(event.params)
[2d107b8]72            self.base.update()
73
[6c0568b]74
[2d107b8]75    def update_and_post(self):
[6c0568b]76        """
77            Update the slicer and plot the resulting data
78        """
[2d107b8]79        self.update()
80        self._post_data()
81
82
83    def set_layer(self, n):
[6c0568b]84        """
85             Allow adding plot to the same panel
86             @param n: the number of layer
87        """
[2d107b8]88        self.layernum = n
89        self.update()
90       
[6c0568b]91       
[2d107b8]92    def clear(self):
[6c0568b]93        """
94            Clear the slicer and all connected events related to this slicer
95        """
[ffd23b5]96        self.averager=None
[2d107b8]97        self.clear_markers()
[18eba35]98        self.horizontal_lines.clear()
99        self.vertical_lines.clear()
100        self.base.connect.clearall()
101        self.base.Unbind(EVT_SLICER_PARS)
[2d107b8]102       
[6c0568b]103       
[2d107b8]104    def update(self):
105        """
106        Respond to changes in the model by recalculating the profiles and
107        resetting the widgets.
108        """
[6c0568b]109        ##Update the slicer if an horizontal line is dragged   
[18eba35]110        if self.horizontal_lines.has_move:
111            self.horizontal_lines.update()
112            self.vertical_lines.update(y=self.horizontal_lines.y)
[6c0568b]113       
114        ##Update the slicer if a vertical line is dragged   
[18eba35]115        if self.vertical_lines.has_move:
116            self.vertical_lines.update()
117            self.horizontal_lines.update(x=self.vertical_lines.x)
[dd40217]118               
[0f6d05f8]119           
[2d107b8]120    def save(self, ev):
121        """
122        Remember the roughness for this layer and the next so that we
123        can restore on Esc.
124        """
125        self.base.freeze_axes()
[18eba35]126        self.vertical_lines.save(ev)
127        self.horizontal_lines.save(ev)
128   
[ffd23b5]129    def _post_data(self):
130        pass
131       
132   
133    def post_data(self,new_slab=None , nbins=None):
[6c0568b]134        """
135             post data averaging in Qx or Qy given new_slab type
136             @param new_slab: slicer that determine with direction to average
137             @param nbins: the number of points plotted when averaging
138        """
[18eba35]139        x_min= -1*math.fabs(self.vertical_lines.x)
140        x_max= math.fabs(self.vertical_lines.x)
[ffd23b5]141       
[18eba35]142        y_min= -1*math.fabs(self.horizontal_lines.y)
143        y_max= math.fabs(self.horizontal_lines.y)
[ffd23b5]144       
145        if nbins !=None:
146            self.nbins
147        if self.averager==None:
148            if new_slab ==None:
149                raise ValueError,"post data:cannot average , averager is empty"
150            self.averager= new_slab
151        bin_width= (x_max + math.fabs(x_min))/self.nbins
[6c0568b]152        ## Average data2D given Qx or Qy
[ffd23b5]153        box = self.averager( x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max,
154                         bin_width=bin_width)
155       
156        boxavg = box(self.base.data2D)
[6c0568b]157        #3 Create Data1D to plot
[ffd23b5]158        from sans.guiframe.dataFitting import Data1D
159        if hasattr(boxavg,"dxl"):
160            dxl= boxavg.dxl
161        else:
162            dxl= None
163        if hasattr(boxavg,"dxw"):
164            dxw=boxavg.dxw
165        else:
166            dxw= None
167       
168        new_plot = Data1D(x=boxavg.x,y=boxavg.y,dy=boxavg.dy,dxl=dxl,dxw=dxw)
169        new_plot.name = str(self.averager.__name__) +"("+ self.base.data2D.name+")"
170       
171        new_plot.source=self.base.data2D.source
172        new_plot.interactive = True
173        new_plot.detector =self.base.data2D.detector
174        # If the data file does not tell us what the axes are, just assume...
175        new_plot.xaxis("\\rm{Q}", 'rad')
176        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
177        new_plot.group_id = str(self.averager.__name__)+self.base.data2D.name
178        new_plot.id = str(self.averager.__name__)
[70cf5d3]179        #new_plot.is_data= True
180       
[ffd23b5]181        wx.PostEvent(self.base.parent, NewPlotEvent(plot=new_plot,
182                                                 title=str(self.averager.__name__) ))
[18eba35]183   
[b319def8]184             
[38224f10]185                                       
[2d107b8]186    def moveend(self, ev):
[6c0568b]187        """
188            Called after a dragging event.
189            Post the slicer new parameters and creates a new Data1D
190            corresponding to the new average
191        """
[2d107b8]192        self.base.thaw_axes()
193        # Post paramters
[0d9dae8]194        event = SlicerParameterEvent()
[2d107b8]195        event.type = self.__class__.__name__
[0f6d05f8]196        event.params = self.get_params()
[2d107b8]197        wx.PostEvent(self.base.parent, event)
[6c0568b]198        # create the new data1D
[2d107b8]199        self._post_data()
200           
[6c0568b]201           
[2d107b8]202    def restore(self):
203        """
204        Restore the roughness for this layer.
205        """
[18eba35]206        self.horizontal_lines.restore()
207        self.vertical_lines.restore()
208       
[2d107b8]209
210    def move(self, x, y, ev):
211        """
212        Process move to a new position, making sure that the move is allowed.
213        """
214        pass
215       
[6c0568b]216       
[2d107b8]217    def set_cursor(self, x, y):
218        pass
219       
[6c0568b]220       
[2d107b8]221    def get_params(self):
[6c0568b]222        """
223            Store a copy of values of parameters of the slicer into a dictionary.
224            @return params: the dictionary created
225        """
[2d107b8]226        params = {}
[18eba35]227        params["x_max"]= math.fabs(self.vertical_lines.x)
228        params["y_max"]= math.fabs(self.horizontal_lines.y)
[ffd23b5]229        params["nbins"]= self.nbins
[2d107b8]230        return params
231   
232    def set_params(self, params):
[6c0568b]233        """
234            Receive a dictionary and reset the slicer with values contained
235            in the values of the dictionary.
236            @param params: a dictionary containing name of slicer parameters and
237            values the user assigned to the slicer.
238        """
[0f6d05f8]239        self.x = float(math.fabs(params["x_max"]))
240        self.y = float(math.fabs(params["y_max"] ))
[ffd23b5]241        self.nbins=params["nbins"]
[18eba35]242       
243        self.horizontal_lines.update(x= self.x, y=  self.y)
244        self.vertical_lines.update(x= self.x, y=  self.y)
[ffd23b5]245        self.post_data( nbins=None)
[18eba35]246       
247       
[2d107b8]248    def freeze_axes(self):
249        self.base.freeze_axes()
250       
[6c0568b]251       
[2d107b8]252    def thaw_axes(self):
253        self.base.thaw_axes()
254
[6c0568b]255
[2d107b8]256    def draw(self):
257        self.base.draw()
258
[6c0568b]259
[18eba35]260class HorizontalLines(_BaseInteractor):
[2d107b8]261    """
[6c0568b]262         Draw 2 Horizontal lines centered on (0,0) that can move
263         on the x- direction and in opposite direction
[2d107b8]264    """
[18eba35]265    def __init__(self,base,axes,color='black', zorder=5,x=0.5, y=0.5):
[2d107b8]266       
267        _BaseInteractor.__init__(self, base, axes, color=color)
[6c0568b]268        ##Class initialization
[2d107b8]269        self.markers = []
270        self.axes = axes
[6c0568b]271        ## Saving the end points of two lines
[18eba35]272        self.x= x
273        self.save_x= x
[3554b99a]274       
[18eba35]275        self.y= y
276        self.save_y= y
[6c0568b]277        ## Creating a marker
[18eba35]278        try:
279            # Inner circle marker
280            self.inner_marker = self.axes.plot([0],[self.y], linestyle='',
281                                          marker='s', markersize=10,
282                                          color=self.color, alpha=0.6,
283                                          pickradius=5, label="pick", 
284                                          zorder=zorder, # Prefer this to other lines
285                                          visible=True)[0]
286        except:
287            self.inner_marker = self.axes.plot([0],[self.y], linestyle='',
288                                          marker='s', markersize=10,
289                                          color=self.color, alpha=0.6,
290                                          label="pick", 
291                                          visible=True)[0]
292            message  = "\nTHIS PROTOTYPE NEEDS THE LATEST VERSION OF MATPLOTLIB\n"
293            message += "Get the SVN version that is at least as recent as June 1, 2007"
[6c0568b]294            owner=self.base.base.parent
295            wx.PostEvent(owner, StatusEvent(status="AnnulusSlicer: %s"%message))
[18eba35]296           
[6c0568b]297        ## Define 2 horizontal lines
[18eba35]298        self.top_line = self.axes.plot([self.x,-self.x],
299                                   [self.y,self.y],
300                                      linestyle='-', marker='',
301                                      color=self.color,
302                                      visible=True)[0]
303        self.bottom_line = self.axes.plot([self.x,-self.x],
304                                   [-self.y,-self.y],
[2d107b8]305                                      linestyle='-', marker='',
306                                      color=self.color,
307                                      visible=True)[0]
[6c0568b]308        ## Flag to check the motion of the lines
[2d107b8]309        self.has_move=False
[6c0568b]310        ## Connecting markers to mouse events and draw
[18eba35]311        self.connect_markers([self.top_line, self.inner_marker])
[3554b99a]312        self.update()
[2d107b8]313
[6c0568b]314
[2d107b8]315    def set_layer(self, n):
[6c0568b]316        """
317            Allow adding plot to the same panel
318            @param n: the number of layer
319        """
[2d107b8]320        self.layernum = n
321        self.update()
322       
[6c0568b]323       
[2d107b8]324    def clear(self):
[6c0568b]325        """
326            Clear this slicer  and its markers
327        """
[2d107b8]328        self.clear_markers()
329        try:
[18eba35]330            self.inner_marker.remove()
331            self.top_line.remove() 
332            self.bottom_line.remove()
[2d107b8]333        except:
334            # Old version of matplotlib
335            for item in range(len(self.axes.lines)):
336                del self.axes.lines[0]
[030873e]337   
[8ff3ec1]338   
[18eba35]339    def update(self,x=None,y=None):
[2d107b8]340        """
[6c0568b]341            Draw the new roughness on the graph.
342            @param x: x-coordinates to reset current class x
343            @param y: y-coordinates to reset current class y
[2d107b8]344        """
[6c0568b]345        ## Reset x, y- coordinates if send as parameters
[18eba35]346        if x!=None:
347            self.x = numpy.sign(self.x)*math.fabs(x)
348        if y !=None:
349            self.y = numpy.sign(self.y)*math.fabs(y)
[6c0568b]350        ## Draw lines and markers
[18eba35]351        self.inner_marker.set(xdata=[0],ydata=[self.y])
352        self.top_line.set(xdata=[self.x,-self.x],
353                       ydata=[self.y,self.y])
354        self.bottom_line.set(xdata=[self.x,-self.x],
355                       ydata=[-self.y, -self.y])
[2d107b8]356       
357       
358    def save(self, ev):
359        """
360        Remember the roughness for this layer and the next so that we
361        can restore on Esc.
362        """
[18eba35]363        self.save_x= self.x
364        self.save_y= self.y
[2d107b8]365        self.base.freeze_axes()
366
[6c0568b]367
[2d107b8]368    def moveend(self, ev):
[6c0568b]369        """
370            Called after a dragging this edge and set self.has_move to False
371            to specify the end of dragging motion
372        """
[2d107b8]373        self.has_move=False
374        self.base.moveend(ev)
[6c0568b]375           
[2d107b8]376           
377    def restore(self):
378        """
379        Restore the roughness for this layer.
380        """
[18eba35]381        self.x = self.save_x
382        self.y = self.save_y
[0f6d05f8]383       
[2d107b8]384
385    def move(self, x, y, ev):
386        """
387        Process move to a new position, making sure that the move is allowed.
388        """
[18eba35]389        self.y= y
[2d107b8]390        self.has_move=True
391        self.base.base.update()
392       
[18eba35]393 
394class VerticalLines(_BaseInteractor):
[2d107b8]395    """
396         Select an annulus through a 2D plot
397    """
[18eba35]398    def __init__(self,base,axes,color='black',zorder=5,x=0.5, y=0.5):
[2d107b8]399       
400        _BaseInteractor.__init__(self, base, axes, color=color)
401        self.markers = []
402        self.axes = axes
[030873e]403       
[18eba35]404        self.x= math.fabs(x)
405        self.save_x= self.x
406        self.y= math.fabs(y)
407        self.save_y= y
408       
409        try:
410            # Inner circle marker
411            self.inner_marker = self.axes.plot([self.x],[0], linestyle='',
412                                          marker='s', markersize=10,
413                                          color=self.color, alpha=0.6,
414                                          pickradius=5, label="pick", 
415                                          zorder=zorder, # Prefer this to other lines
416                                          visible=True)[0]
417        except:
418            self.inner_marker = self.axes.plot([self.x],[0], linestyle='',
419                                          marker='s', markersize=10,
420                                          color=self.color, alpha=0.6,
421                                          label="pick", 
422                                          visible=True)[0]
423            message  = "\nTHIS PROTOTYPE NEEDS THE LATEST VERSION OF MATPLOTLIB\n"
424            message += "Get the SVN version that is at least as recent as June 1, 2007"
425           
426        self.right_line = self.axes.plot([self.x,self.x],[self.y,-self.y],
427                                      linestyle='-', marker='',
428                                      color=self.color,
429                                      visible=True)[0]
430        self.left_line = self.axes.plot([-self.x,-self.x],[self.y,-self.y],
[2d107b8]431                                      linestyle='-', marker='',
432                                      color=self.color,
433                                      visible=True)[0]
434     
435        self.has_move=False
[18eba35]436        self.connect_markers([self.right_line, self.inner_marker])
[2d107b8]437        self.update()
438
[6c0568b]439
[2d107b8]440    def set_layer(self, n):
[6c0568b]441        """
442            Allow adding plot to the same panel
443            @param n: the number of layer
444        """
[2d107b8]445        self.layernum = n
446        self.update()
447       
[6c0568b]448       
[2d107b8]449    def clear(self):
[6c0568b]450        """
451            Clear this slicer  and its markers
452        """
[2d107b8]453        self.clear_markers()
454        try:
[18eba35]455            self.inner_marker.remove()
456            self.left_line.remove()
457            self.right_line.remove()
[2d107b8]458        except:
459            # Old version of matplotlib
460            for item in range(len(self.axes.lines)):
461                del self.axes.lines[0]
[18eba35]462
[6c0568b]463
[18eba35]464    def update(self,x=None,y=None):
[2d107b8]465        """
[6c0568b]466            Draw the new roughness on the graph.
467            @param x: x-coordinates to reset current class x
468            @param y: y-coordinates to reset current class y
[2d107b8]469        """
[6c0568b]470        ## reset x, y -coordinates if given as parameters
[18eba35]471        if x!=None:
472            self.x = numpy.sign(self.x)*math.fabs(x)
473        if y !=None:
474            self.y = numpy.sign(self.y)*math.fabs(y)
[6c0568b]475        ## draw lines and markers 
[18eba35]476        self.inner_marker.set(xdata=[self.x],ydata=[0]) 
477        self.left_line.set(xdata=[-self.x,-self.x],
478                       ydata=[self.y,-self.y]) 
479        self.right_line.set(xdata=[self.x,self.x],
480                       ydata=[self.y,-self.y]) 
[0f6d05f8]481   
[3554b99a]482       
[2d107b8]483    def save(self, ev):
484        """
485        Remember the roughness for this layer and the next so that we
486        can restore on Esc.
487        """
[18eba35]488        self.save_x= self.x
489        self.save_y= self.y
[8ff3ec1]490       
[2d107b8]491        self.base.freeze_axes()
492
[6c0568b]493
[2d107b8]494    def moveend(self, ev):
[6c0568b]495        """
496            Called after a dragging this edge and set self.has_move to False
497            to specify the end of dragging motion
498        """
[2d107b8]499        self.has_move=False
500        self.base.moveend(ev)
501           
[6c0568b]502           
[2d107b8]503    def restore(self):
504        """
505        Restore the roughness for this layer.
506        """
[18eba35]507        self.x = self.save_x
508        self.y = self.save_y
[3554b99a]509     
[78ed1ad]510       
[2d107b8]511    def move(self, x, y, ev):
512        """
513        Process move to a new position, making sure that the move is allowed.
514        """
[8ff3ec1]515        self.has_move=True
[18eba35]516        self.x= x
[8ff3ec1]517        self.base.base.update()
518       
[18eba35]519   
[2d107b8]520       
[8ff3ec1]521
[ffd23b5]522class BoxInteractorX(BoxInteractor):
[eba08f1a]523    """
524        Average in Qx direction
525    """
[ffd23b5]526    def __init__(self,base,axes,color='black', zorder=3):
527        BoxInteractor.__init__(self, base, axes, color=color)
528        self.base=base
529        self._post_data()
[6c0568b]530       
531       
[ffd23b5]532    def _post_data(self):
[6c0568b]533        """
534             Post data creating by averaging in Qx direction
535        """
[ffd23b5]536        from DataLoader.manipulations import SlabX
537        self.post_data(SlabX )   
538       
539
540class BoxInteractorY(BoxInteractor):
[eba08f1a]541    """
542         Average in Qy direction
543    """
[ffd23b5]544    def __init__(self,base,axes,color='black', zorder=3):
545        BoxInteractor.__init__(self, base, axes, color=color)
546        self.base=base
547        self._post_data()
[6c0568b]548       
549       
[ffd23b5]550    def _post_data(self):
[6c0568b]551        """
552             Post data creating by averaging in Qy direction
553        """
[ffd23b5]554        from DataLoader.manipulations import SlabY
555        self.post_data(SlabY )   
556       
[2d107b8]557       
Note: See TracBrowser for help on using the repository browser.