source: sasview/guiframe/local_perspectives/plotting/boxSlicer.py @ 04349fe

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 04349fe was a4d4b35, checked in by Jae Cho <jhjcho@…>, 15 years ago

fixed slabY bug: binning x-direction

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