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

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 f50330b7 was 4ac8556, checked in by Gervaise Alina <gervyh@…>, 15 years ago

change on data_loader

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