source: sasview/guiframe/local_perspectives/plotting/AnnulusSlicer.py @ 65a8b34

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 65a8b34 was 83f4445, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on documentation

  • Property mode set to 100644
File size: 18.4 KB
RevLine 
[ef0c170]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
[0d9dae8]8import math
9import wx
10from copy import deepcopy
[ef0c170]11# Debug printout
[0d9dae8]12from sans.guicomm.events import NewPlotEvent, StatusEvent,SlicerParameterEvent,EVT_SLICER_PARS
[ef0c170]13from BaseInteractor import _BaseInteractor
[4ac8556]14from sans.guiframe.dataFitting import Data1D
[ef0c170]15
16class AnnulusInteractor(_BaseInteractor):
17    """
[83f4445]18    Select an annulus through a 2D plot.
19    This interactor is used to average 2D data  with the region
20    defined by 2 radius.
21    this class is defined by 2 Ringinterators.
[ef0c170]22    """
23    def __init__(self,base,axes,color='black', zorder=3):
24       
25        _BaseInteractor.__init__(self, base, axes, color=color)
26        self.markers = []
27        self.axes = axes
[a2c38de]28        self.base= base
29        self.qmax = min(math.fabs(self.base.data2D.xmax),math.fabs(self.base.data2D.xmin))  #must be positive
[ef0c170]30        self.connect = self.base.connect
[eba08f1a]31   
[ef0c170]32        ## Number of points on the plot
33        self.nbins = 20
[a2c38de]34       
35        #Cursor position of Rings (Left(-1) or Right(1))
36        self.xmaxd=self.base.data2D.xmax
37        self.xmind=self.base.data2D.xmin
[ef0c170]38
[a2c38de]39        if (self.xmaxd+self.xmind)>0:
40            self.sign=1
41        else:
42            self.sign=-1
43                 
[ef0c170]44        # Inner circle
[a2c38de]45        self.inner_circle = RingInteractor(self, self.base.subplot, zorder=zorder, r=self.qmax/2.0,sign=self.sign)
[bd1d9d9]46        self.inner_circle.qmax = self.qmax
[a2c38de]47        self.outer_circle = RingInteractor(self, self.base.subplot, zorder=zorder+1, r=self.qmax/1.8,sign=self.sign)
[bd1d9d9]48        self.outer_circle.qmax = self.qmax*1.2
[ca88b2e]49       
[ef0c170]50        self.update()
[7ab9241]51        self._post_data()
[ef0c170]52       
53        # Bind to slice parameter events
[1ce365f8]54        self.base.Bind(EVT_SLICER_PARS, self._onEVT_SLICER_PARS)
[a2c38de]55       
[ef0c170]56    def _onEVT_SLICER_PARS(self, event):
[eba08f1a]57        """
[83f4445]58        receive an event containing parameters values to reset the slicer
59       
60        :param event: event of type SlicerParameterEvent with params as
[eba08f1a]61            attribute
[83f4445]62           
[eba08f1a]63        """
[1ce365f8]64        wx.PostEvent(self.base, StatusEvent(status="AnnulusSlicer._onEVT_SLICER_PARS"))
[ef0c170]65        event.Skip()
66        if event.type == self.__class__.__name__:
67            self.set_params(event.params)
68            self.base.update()
69
70    def set_layer(self, n):
[eba08f1a]71        """
[83f4445]72        Allow adding plot to the same panel
73       
74        :param n: the number of layer
75       
[eba08f1a]76        """
[ef0c170]77        self.layernum = n
78        self.update()
79       
80    def clear(self):
[eba08f1a]81        """
[83f4445]82        Clear the slicer and all connected events related to this slicer
[eba08f1a]83        """
[ef0c170]84        self.clear_markers()
85        self.outer_circle.clear()
86        self.inner_circle.clear()
[18eba35]87        self.base.connect.clearall()
[d468daa]88        self.base.Unbind(EVT_SLICER_PARS)
[ef0c170]89       
90    def update(self):
91        """
[83f4445]92        Respond to changes in the model by recalculating the profiles and
93        resetting the widgets.
[ef0c170]94        """
95        # Update locations       
96        self.inner_circle.update()
97        self.outer_circle.update()
98       
99    def save(self, ev):
100        """
[83f4445]101        Remember the roughness for this layer and the next so that we
102        can restore on Esc.
[ef0c170]103        """
104        self.base.freeze_axes()
105        self.inner_circle.save(ev)
106        self.outer_circle.save(ev)
107
[5554566]108    def _post_data(self,nbins=None):
[eba08f1a]109        """
[83f4445]110        Uses annulus parameters to plot averaged data into 1D data.
111       
112        :param nbins: the number of points to plot
113       
[eba08f1a]114        """
115        #Data to average
[ef0c170]116        data = self.base.data2D
117        # If we have no data, just return
118        if data == None:
119            return
120       
[78cae5a]121        from DataLoader.manipulations import Ring
[eba08f1a]122   
[3b909b7]123        rmin= min(math.fabs(self.inner_circle.get_radius()),
124                  math.fabs(self.outer_circle.get_radius()))
125        rmax = max(math.fabs(self.inner_circle.get_radius()),
126                   math.fabs(self.outer_circle.get_radius()))
[eba08f1a]127        #if the user does not specify the numbers of points to plot
128        # the default number will be nbins= 20
[1ce365f8]129        if nbins==None:
[eba08f1a]130            self.nbins= 20
131        else:
132            self.nbins = nbins
133        ## create the data1D Q average of data2D   
[78cae5a]134        sect = Ring(r_min=rmin , r_max= rmax, nbins=self.nbins)
[ef0c170]135        sector = sect(self.base.data2D)
136       
[4ac8556]137       
[ef0c170]138        if hasattr(sector,"dxl"):
139            dxl= sector.dxl
140        else:
141            dxl= None
142        if hasattr(sector,"dxw"):
143            dxw= sector.dxw
144        else:
145            dxw= None
146       
[78cae5a]147        new_plot = Data1D(x=(sector.x-math.pi)*180/math.pi,y=sector.y,dy=sector.dy)
[4ac8556]148        new_plot.dxl = dxl
149        new_plot.dxw = dxw
[78cae5a]150        new_plot.name = "AnnulusPhi" +"("+ self.base.data2D.name+")"
[ef0c170]151       
152        new_plot.source=self.base.data2D.source
[ac9a5f6]153        #new_plot.info=self.base.data2D.info
[eba08f1a]154       
[ef0c170]155        new_plot.interactive = True
156        new_plot.detector =self.base.data2D.detector
157        # If the data file does not tell us what the axes are, just assume...
[14d3495]158        new_plot.xaxis("\\rm{\phi}", 'degrees')
[ef0c170]159        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
[78cae5a]160        new_plot.group_id = "AnnulusPhi"+self.base.data2D.name
161        new_plot.id= "AnnulusPhi"+self.base.data2D.name
[70cf5d3]162        #new_plot.is_data= True
163       
[ffd23b5]164        new_plot.xtransform="x"
165        new_plot.ytransform="y"
[ef0c170]166        wx.PostEvent(self.base.parent, NewPlotEvent(plot=new_plot,
[78cae5a]167                                                 title="AnnulusPhi" ))
[ef0c170]168       
169    def moveend(self, ev):
[eba08f1a]170        """
[83f4445]171        Called when any dragging motion ends.
172        Post an event (type =SlicerParameterEvent)
173        to plotter 2D with a copy  slicer parameters
174        Call  _post_data method
[eba08f1a]175        """
[ef0c170]176        self.base.thaw_axes()
[eba08f1a]177        # Post parameters to plotter 2D
[0d9dae8]178        event = SlicerParameterEvent()
[ef0c170]179        event.type = self.__class__.__name__
180        event.params = self.get_params()
[d468daa]181        wx.PostEvent(self.base, event)
[eba08f1a]182        # create a 1D data plot
[78cae5a]183        #self._post_data()
[ef0c170]184           
185    def restore(self):
186        """
187        Restore the roughness for this layer.
188        """
189        self.inner_circle.restore()
190        self.outer_circle.restore()
191
192    def move(self, x, y, ev):
193        """
194        Process move to a new position, making sure that the move is allowed.
195        """
196        pass
197       
198    def set_cursor(self, x, y):
199        pass
200       
201    def get_params(self):
[eba08f1a]202        """
[83f4445]203        Store a copy of values of parameters of the slicer into a dictionary.
204       
205        :return params: the dictionary created
206       
[eba08f1a]207        """
[ef0c170]208        params = {}
[0bd125d]209        params["inner_radius"] = math.fabs(self.inner_circle._inner_mouse_x)
210        params["outer_radius"] = math.fabs(self.outer_circle._inner_mouse_x)
[ef0c170]211        params["nbins"] = self.nbins
212        return params
213   
214    def set_params(self, params):
[eba08f1a]215        """
[83f4445]216        Receive a dictionary and reset the slicer with values contained
217        in the values of the dictionary.
218       
219        :param params: a dictionary containing name of slicer parameters and
[eba08f1a]220            values the user assigned to the slicer.
[83f4445]221           
[eba08f1a]222        """
[0bd125d]223        inner = math.fabs(params["inner_radius"] )
224        outer = math.fabs(params["outer_radius"] )
[ef0c170]225        self.nbins = int(params["nbins"])
[eba08f1a]226        ## Update the picture
[ef0c170]227        self.inner_circle.set_cursor(inner, self.inner_circle._inner_mouse_y)
228        self.outer_circle.set_cursor(outer, self.outer_circle._inner_mouse_y)
[eba08f1a]229        ## Post the data given the nbins entered by the user
[5554566]230        self._post_data(self.nbins)
[ef0c170]231       
232    def freeze_axes(self):
[83f4445]233        """
234        """
[ef0c170]235        self.base.freeze_axes()
236       
237    def thaw_axes(self):
[83f4445]238        """
239        """
[ef0c170]240        self.base.thaw_axes()
241
242    def draw(self):
[83f4445]243        """
244        """
[ef0c170]245        self.base.draw()
246
247       
248class RingInteractor(_BaseInteractor):
249    """
[83f4445]250     Draw a ring Given a radius
[ef0c170]251    """
[a2c38de]252    def __init__(self,base,axes,color='black', zorder=5, r=1.0,sign=1):
[83f4445]253        """
254        :param: the color of the line that defined the ring
255        :param r: the radius of the ring
256        :param sign: the direction of motion the the marker
[ef0c170]257       
[83f4445]258        """
[ef0c170]259        _BaseInteractor.__init__(self, base, axes, color=color)
260        self.markers = []
261        self.axes = axes
[eba08f1a]262        # Current radius of the ring
[ef0c170]263        self._inner_mouse_x = r
[eba08f1a]264        #Value of the center of the ring
[ef0c170]265        self._inner_mouse_y = 0
[eba08f1a]266        # previous value of that radius
[ef0c170]267        self._inner_save_x  = r
[eba08f1a]268        #Save value of the center of the ring
[ef0c170]269        self._inner_save_y  = 0
[eba08f1a]270        #Class instantiating RingIterator class
[a2c38de]271        self.base= base
[eba08f1a]272        #the direction of the motion of the marker
[a2c38de]273        self.sign=sign
[eba08f1a]274        ## Create a marker
[ef0c170]275        try:
276            # Inner circle marker
[a2c38de]277            self.inner_marker = self.axes.plot([self.sign*math.fabs(self._inner_mouse_x)],[0], linestyle='',
[ef0c170]278                                          marker='s', markersize=10,
279                                          color=self.color, alpha=0.6,
280                                          pickradius=5, label="pick", 
281                                          zorder=zorder, # Prefer this to other lines
282                                          visible=True)[0]
283        except:
[a2c38de]284            self.inner_marker = self.axes.plot([self.sign*math.fabs(self._inner_mouse_x)],[0], linestyle='',
[ef0c170]285                                          marker='s', markersize=10,
286                                          color=self.color, alpha=0.6,
287                                          label="pick", 
288                                          visible=True)[0]
289            message  = "\nTHIS PROTOTYPE NEEDS THE LATEST VERSION OF MATPLOTLIB\n"
290            message += "Get the SVN version that is at least as recent as June 1, 2007"
291           
[eba08f1a]292            owner=self.base.base.parent
293            wx.PostEvent(owner, StatusEvent(status="AnnulusSlicer: %s"%message))
[ef0c170]294           
[eba08f1a]295        # Draw a circle
[ef0c170]296        [self.inner_circle] = self.axes.plot([],[],
297                                      linestyle='-', marker='',
298                                      color=self.color)
[eba08f1a]299        # the number of points that make the ring line
[e4032d64]300        self.npts = 40
[ef0c170]301           
302        self.connect_markers([self.inner_marker])
303        self.update()
304
305    def set_layer(self, n):
[eba08f1a]306        """
[83f4445]307        Allow adding plot to the same panel
308         
309        :param n: the number of layer
310       
[eba08f1a]311        """
[ef0c170]312        self.layernum = n
313        self.update()
314       
315    def clear(self):
[eba08f1a]316        """
[83f4445]317        Clear the slicer and all connected events related to this slicer
[eba08f1a]318        """
[ef0c170]319        self.clear_markers()
320        try:
321            self.inner_marker.remove()
322            self.inner_circle.remove()
323        except:
324            # Old version of matplotlib
325            for item in range(len(self.axes.lines)):
326                del self.axes.lines[0]
327       
328    def get_radius(self):
[eba08f1a]329        """
[83f4445]330        :return self._inner_mouse_x: the current radius of the ring
[eba08f1a]331        """
[ef0c170]332        return self._inner_mouse_x
333       
334    def update(self):
335        """
[83f4445]336        Draw the new roughness on the graph.
[ef0c170]337        """
338        # Plot inner circle
339        x = []
340        y = []
341        for i in range(self.npts):
342            phi = 2.0*math.pi/(self.npts-1)*i
343           
344            xval = 1.0*self._inner_mouse_x*math.cos(phi) 
345            yval = 1.0*self._inner_mouse_x*math.sin(phi) 
346           
347            x.append(xval)
348            y.append(yval)
[a2c38de]349           
350        self.inner_marker.set(xdata=[self.sign*math.fabs(self._inner_mouse_x)],ydata=[0])
[ef0c170]351        self.inner_circle.set_data(x, y)       
352
353    def save(self, ev):
354        """
355        Remember the roughness for this layer and the next so that we
356        can restore on Esc.
357        """
358        self._inner_save_x = self._inner_mouse_x
359        self._inner_save_y = self._inner_mouse_y
360        self.base.freeze_axes()
361
362    def moveend(self, ev):
[eba08f1a]363        """
[83f4445]364        Called after a dragging motion
[eba08f1a]365        """
[ef0c170]366        self.base.moveend(ev)
367           
368    def restore(self):
369        """
370        Restore the roughness for this layer.
371        """
372        self._inner_mouse_x = self._inner_save_x
373        self._inner_mouse_y = self._inner_save_y
374
375    def move(self, x, y, ev):
376        """
377        Process move to a new position, making sure that the move is allowed.
378        """
379        self._inner_mouse_x = x
380        self._inner_mouse_y = y
381        self.base.base.update()
382       
383    def set_cursor(self, x, y):
[eba08f1a]384        """
[83f4445]385        draw the ring given x, y value
[eba08f1a]386        """
[ef0c170]387        self.move(x, y, None)
388        self.update()
389       
390       
391    def get_params(self):
[eba08f1a]392        """
[83f4445]393        Store a copy of values of parameters of the slicer into a dictionary.
394       
395        :return params: the dictionary created
396       
[eba08f1a]397        """
[ef0c170]398        params = {}
[a2c38de]399        params["radius"] = math.fabs(self._inner_mouse_x)
[ef0c170]400        return params
401   
402    def set_params(self, params):
[eba08f1a]403        """
[83f4445]404        Receive a dictionary and reset the slicer with values contained
405        in the values of the dictionary.
406       
407        :param params: a dictionary containing name of slicer parameters and
[eba08f1a]408            values the user assigned to the slicer.
[83f4445]409           
[eba08f1a]410        """
[ef0c170]411        x = params["radius"] 
412        self.set_cursor(x, self._inner_mouse_y)
413       
[c5874f2]414class CircularMask(_BaseInteractor):
415    """
[83f4445]416     Draw a ring Given a radius
[c5874f2]417    """
[aef2cf2]418    def __init__(self,base,axes,color='grey', zorder=3, side=None):
[83f4445]419        """
[c5874f2]420       
[83f4445]421        :param: the color of the line that defined the ring
422        :param r: the radius of the ring
423        :param sign: the direction of motion the the marker
424       
425        """
[c5874f2]426        _BaseInteractor.__init__(self, base, axes, color=color)
427        self.markers = []
428        self.axes = axes
429        self.base= base
430        self.is_inside = side
431        self.qmax = min(math.fabs(self.base.data.xmax),math.fabs(self.base.data.xmin))  #must be positive
432        self.connect = self.base.connect
433       
434        #Cursor position of Rings (Left(-1) or Right(1))
435        self.xmaxd=self.base.data.xmax
436        self.xmind=self.base.data.xmin
437
438        if (self.xmaxd+self.xmind)>0:
439            self.sign=1
440        else:
441            self.sign=-1
442                 
443        # Inner circle
[aef2cf2]444        self.outer_circle = RingInteractor(self, self.base.subplot, 'blue', zorder=zorder+1, r=self.qmax/1.8,sign=self.sign)
[c5874f2]445        self.outer_circle.qmax = self.qmax*1.2
446       
447        self.update()
448        self._post_data()
449       
450        # Bind to slice parameter events
451        #self.base.Bind(EVT_SLICER_PARS, self._onEVT_SLICER_PARS)
452       
453    def _onEVT_SLICER_PARS(self, event):
454        """
[83f4445]455        receive an event containing parameters values to reset the slicer
456       
457        :param event: event of type SlicerParameterEvent with params as
[c5874f2]458            attribute
459        """
460        wx.PostEvent(self.base, StatusEvent(status="AnnulusSlicer._onEVT_SLICER_PARS"))
461        event.Skip()
462        if event.type == self.__class__.__name__:
463            self.set_params(event.params)
464            self.base.update()
465
466    def set_layer(self, n):
467        """
[83f4445]468        Allow adding plot to the same panel
469         
470        :param n: the number of layer
471       
[c5874f2]472        """
473        self.layernum = n
474        self.update()
475       
476    def clear(self):
477        """
[83f4445]478        Clear the slicer and all connected events related to this slicer
[c5874f2]479        """
480        self.clear_markers()
481        self.outer_circle.clear()
482        self.base.connect.clearall()
483        #self.base.Unbind(EVT_SLICER_PARS)
484       
485    def update(self):
486        """
[83f4445]487        Respond to changes in the model by recalculating the profiles and
488        resetting the widgets.
[c5874f2]489        """
490        # Update locations       
491        self.outer_circle.update()
492        #if self.is_inside != None:
493        out = self._post_data()
494        return out
495
496    def save(self, ev):
497        """
[83f4445]498        Remember the roughness for this layer and the next so that we
499        can restore on Esc.
[c5874f2]500        """
501        self.base.freeze_axes()
502        self.outer_circle.save(ev)
503
504    def _post_data(self):
505        """
[83f4445]506        Uses annulus parameters to plot averaged data into 1D data.
507       
508        :param nbins: the number of points to plot
509       
[c5874f2]510        """
511        #Data to average
512        data = self.base.data
513             
514        # If we have no data, just return
515        if data == None:
516            return
517        mask = data.mask 
518        from DataLoader.manipulations import Ringcut
519   
520        rmin= 0
521        rmax = math.fabs(self.outer_circle.get_radius())
522
523        ## create the data1D Q average of data2D   
524        mask = Ringcut(r_min=rmin , r_max= rmax)
525
526        if self.is_inside:
527            out = (mask(data)==False)
528        else:
529            out = (mask(data))
530        #self.base.data.mask=out
531        return out                   
532
533         
534    def moveend(self, ev):
535        """
[83f4445]536        Called when any dragging motion ends.
537        Post an event (type =SlicerParameterEvent)
538        to plotter 2D with a copy  slicer parameters
539        Call  _post_data method
[c5874f2]540        """
541        self.base.thaw_axes()
542        # create a 1D data plot
543        self._post_data()
544           
545    def restore(self):
546        """
547        Restore the roughness for this layer.
548        """
549        self.outer_circle.restore()
550
551    def move(self, x, y, ev):
552        """
553        Process move to a new position, making sure that the move is allowed.
554        """
555        pass
556       
557    def set_cursor(self, x, y):
558        pass
559       
560    def get_params(self):
561        """
[83f4445]562        Store a copy of values of parameters of the slicer into a dictionary.
563       
564        :return params: the dictionary created
565       
[c5874f2]566        """
567        params = {}
568        params["outer_radius"] = math.fabs(self.outer_circle._inner_mouse_x)
569        return params
570   
571    def set_params(self, params):
572        """
[83f4445]573        Receive a dictionary and reset the slicer with values contained
574        in the values of the dictionary.
575       
576        :param params: a dictionary containing name of slicer parameters and
[c5874f2]577            values the user assigned to the slicer.
578        """
579        outer = math.fabs(params["outer_radius"] )
580        ## Update the picture
581        self.outer_circle.set_cursor(outer, self.outer_circle._inner_mouse_y)
582        ## Post the data given the nbins entered by the user
583        self._post_data()
584       
585    def freeze_axes(self):
586        self.base.freeze_axes()
587       
588    def thaw_axes(self):
589        self.base.thaw_axes()
590
591    def draw(self):
592        self.base.update()
593             
Note: See TracBrowser for help on using the repository browser.