source: sasview/guiframe/local_perspectives/plotting/boxSlicer.py @ 4e9583c

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 4e9583c was d955bf19, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on documentation

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