source: sasview/guiframe/local_perspectives/plotting/AnnulusSlicer.py @ 76c1727

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

change on data_loader

  • Property mode set to 100644
File size: 13.2 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    """
[eba08f1a]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       
[d468daa]56       
[ef0c170]57
58    def _onEVT_SLICER_PARS(self, event):
[eba08f1a]59        """
60            receive an event containing parameters values to reset the slicer
61            @param event: event of type SlicerParameterEvent with params as
62            attribute
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        """
72             Allow adding plot to the same panel
73             @param n: the number of layer
74        """
[ef0c170]75        self.layernum = n
76        self.update()
77       
78    def clear(self):
[eba08f1a]79        """
80            Clear the slicer and all connected events related to this slicer
81        """
[ef0c170]82        self.clear_markers()
83        self.outer_circle.clear()
84        self.inner_circle.clear()
[18eba35]85        self.base.connect.clearall()
[d468daa]86        self.base.Unbind(EVT_SLICER_PARS)
[ef0c170]87       
[eba08f1a]88       
[ef0c170]89    def update(self):
90        """
[eba08f1a]91            Respond to changes in the model by recalculating the profiles and
92            resetting the widgets.
[ef0c170]93        """
94        # Update locations       
95        self.inner_circle.update()
96        self.outer_circle.update()
97       
98
99    def save(self, ev):
100        """
[eba08f1a]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        """
110            Uses annulus parameters to plot averaged data into 1D data.
111            @param nbins: the number of points to plot
112        """
113        #Data to average
[ef0c170]114        data = self.base.data2D
115        # If we have no data, just return
116        if data == None:
117            return
118       
119        from DataLoader.manipulations import SectorPhi
[eba08f1a]120   
[3b909b7]121        rmin= min(math.fabs(self.inner_circle.get_radius()),
122                  math.fabs(self.outer_circle.get_radius()))
123        rmax = max(math.fabs(self.inner_circle.get_radius()),
124                   math.fabs(self.outer_circle.get_radius()))
[eba08f1a]125        #if the user does not specify the numbers of points to plot
126        # the default number will be nbins= 20
[1ce365f8]127        if nbins==None:
[eba08f1a]128            self.nbins= 20
129        else:
130            self.nbins = nbins
131        ## create the data1D Q average of data2D   
[1ce365f8]132        sect = SectorPhi(r_min=rmin , r_max= rmax,
[eba08f1a]133                          phi_min=0, phi_max=2*math.pi , nbins=self.nbins)
[ef0c170]134        sector = sect(self.base.data2D)
135       
[4ac8556]136       
[ef0c170]137        if hasattr(sector,"dxl"):
138            dxl= sector.dxl
139        else:
140            dxl= None
141        if hasattr(sector,"dxw"):
142            dxw= sector.dxw
143        else:
144            dxw= None
145       
[4ac8556]146        new_plot = Data1D(x=sector.x,y=sector.y,dy=sector.dy)
147        new_plot.dxl = dxl
148        new_plot.dxw = dxw
[ef0c170]149        new_plot.name = "SectorPhi" +"("+ self.base.data2D.name+")"
150       
151        new_plot.source=self.base.data2D.source
[ac9a5f6]152        #new_plot.info=self.base.data2D.info
[eba08f1a]153       
[ef0c170]154        new_plot.interactive = True
155        new_plot.detector =self.base.data2D.detector
156        # If the data file does not tell us what the axes are, just assume...
[14d3495]157        new_plot.xaxis("\\rm{\phi}", 'degrees')
[ef0c170]158        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
159        new_plot.group_id = "SectorPhi"+self.base.data2D.name
[8b30e62]160        new_plot.id= "SectorPhi"+self.base.data2D.name
[70cf5d3]161        #new_plot.is_data= True
162       
[ffd23b5]163        new_plot.xtransform="x"
164        new_plot.ytransform="y"
[ef0c170]165        wx.PostEvent(self.base.parent, NewPlotEvent(plot=new_plot,
166                                                 title="SectorPhi" ))
167       
168         
169    def moveend(self, ev):
[eba08f1a]170        """
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
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
[ef0c170]183        self._post_data()
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        """
203            Store a copy of values of parameters of the slicer into a dictionary.
204            @return params: the dictionary created
205        """
[ef0c170]206        params = {}
[0bd125d]207        params["inner_radius"] = math.fabs(self.inner_circle._inner_mouse_x)
208        params["outer_radius"] = math.fabs(self.outer_circle._inner_mouse_x)
[ef0c170]209        params["nbins"] = self.nbins
210        return params
211   
212    def set_params(self, params):
[eba08f1a]213        """
214            Receive a dictionary and reset the slicer with values contained
215            in the values of the dictionary.
216            @param params: a dictionary containing name of slicer parameters and
217            values the user assigned to the slicer.
218        """
[0bd125d]219        inner = math.fabs(params["inner_radius"] )
220        outer = math.fabs(params["outer_radius"] )
[ef0c170]221        self.nbins = int(params["nbins"])
[eba08f1a]222        ## Update the picture
[ef0c170]223        self.inner_circle.set_cursor(inner, self.inner_circle._inner_mouse_y)
224        self.outer_circle.set_cursor(outer, self.outer_circle._inner_mouse_y)
[eba08f1a]225        ## Post the data given the nbins entered by the user
[5554566]226        self._post_data(self.nbins)
[ef0c170]227       
228    def freeze_axes(self):
229        self.base.freeze_axes()
230       
231    def thaw_axes(self):
232        self.base.thaw_axes()
233
234    def draw(self):
235        self.base.draw()
236
237       
238class RingInteractor(_BaseInteractor):
239    """
[eba08f1a]240         Draw a ring Given a radius
241         @param: the color of the line that defined the ring
242         @param r: the radius of the ring
243         @param sign: the direction of motion the the marker
[ef0c170]244    """
[a2c38de]245    def __init__(self,base,axes,color='black', zorder=5, r=1.0,sign=1):
[ef0c170]246       
247        _BaseInteractor.__init__(self, base, axes, color=color)
248        self.markers = []
249        self.axes = axes
[eba08f1a]250        # Current radius of the ring
[ef0c170]251        self._inner_mouse_x = r
[eba08f1a]252        #Value of the center of the ring
[ef0c170]253        self._inner_mouse_y = 0
[eba08f1a]254        # previous value of that radius
[ef0c170]255        self._inner_save_x  = r
[eba08f1a]256        #Save value of the center of the ring
[ef0c170]257        self._inner_save_y  = 0
[eba08f1a]258        #Class instantiating RingIterator class
[a2c38de]259        self.base= base
[eba08f1a]260        #the direction of the motion of the marker
[a2c38de]261        self.sign=sign
[eba08f1a]262        ## Create a marker
[ef0c170]263        try:
264            # Inner circle marker
[a2c38de]265            self.inner_marker = self.axes.plot([self.sign*math.fabs(self._inner_mouse_x)],[0], linestyle='',
[ef0c170]266                                          marker='s', markersize=10,
267                                          color=self.color, alpha=0.6,
268                                          pickradius=5, label="pick", 
269                                          zorder=zorder, # Prefer this to other lines
270                                          visible=True)[0]
271        except:
[a2c38de]272            self.inner_marker = self.axes.plot([self.sign*math.fabs(self._inner_mouse_x)],[0], linestyle='',
[ef0c170]273                                          marker='s', markersize=10,
274                                          color=self.color, alpha=0.6,
275                                          label="pick", 
276                                          visible=True)[0]
277            message  = "\nTHIS PROTOTYPE NEEDS THE LATEST VERSION OF MATPLOTLIB\n"
278            message += "Get the SVN version that is at least as recent as June 1, 2007"
279           
[eba08f1a]280            owner=self.base.base.parent
281            wx.PostEvent(owner, StatusEvent(status="AnnulusSlicer: %s"%message))
[ef0c170]282           
[eba08f1a]283        # Draw a circle
[ef0c170]284        [self.inner_circle] = self.axes.plot([],[],
285                                      linestyle='-', marker='',
286                                      color=self.color)
[eba08f1a]287        # the number of points that make the ring line
[e4032d64]288        self.npts = 40
[ef0c170]289           
290        self.connect_markers([self.inner_marker])
291        self.update()
292
293    def set_layer(self, n):
[eba08f1a]294        """
295             Allow adding plot to the same panel
296             @param n: the number of layer
297        """
[ef0c170]298        self.layernum = n
299        self.update()
300       
301    def clear(self):
[eba08f1a]302        """
303            Clear the slicer and all connected events related to this slicer
304        """
[ef0c170]305        self.clear_markers()
306        try:
307            self.inner_marker.remove()
308            self.inner_circle.remove()
309        except:
310            # Old version of matplotlib
311            for item in range(len(self.axes.lines)):
312                del self.axes.lines[0]
313       
314       
315       
316    def get_radius(self):
[eba08f1a]317        """
318            @return self._inner_mouse_x: the current radius of the ring
319        """
[ef0c170]320        return self._inner_mouse_x
321       
322    def update(self):
323        """
[eba08f1a]324            Draw the new roughness on the graph.
[ef0c170]325        """
326        # Plot inner circle
327        x = []
328        y = []
329        for i in range(self.npts):
330            phi = 2.0*math.pi/(self.npts-1)*i
331           
332            xval = 1.0*self._inner_mouse_x*math.cos(phi) 
333            yval = 1.0*self._inner_mouse_x*math.sin(phi) 
334           
335            x.append(xval)
336            y.append(yval)
[a2c38de]337           
338        self.inner_marker.set(xdata=[self.sign*math.fabs(self._inner_mouse_x)],ydata=[0])
[ef0c170]339        self.inner_circle.set_data(x, y)       
340
341    def save(self, ev):
342        """
343        Remember the roughness for this layer and the next so that we
344        can restore on Esc.
345        """
346        self._inner_save_x = self._inner_mouse_x
347        self._inner_save_y = self._inner_mouse_y
348        self.base.freeze_axes()
349
350    def moveend(self, ev):
[eba08f1a]351        """
352            Called after a dragging motion
353        """
[ef0c170]354        self.base.moveend(ev)
355           
356    def restore(self):
357        """
358        Restore the roughness for this layer.
359        """
360        self._inner_mouse_x = self._inner_save_x
361        self._inner_mouse_y = self._inner_save_y
362
363    def move(self, x, y, ev):
364        """
365        Process move to a new position, making sure that the move is allowed.
366        """
367        self._inner_mouse_x = x
368        self._inner_mouse_y = y
369        self.base.base.update()
370       
371    def set_cursor(self, x, y):
[eba08f1a]372        """
373            draw the ring given x, y value
374        """
[ef0c170]375        self.move(x, y, None)
376        self.update()
377       
378       
379    def get_params(self):
[eba08f1a]380        """
381            Store a copy of values of parameters of the slicer into a dictionary.
382            @return params: the dictionary created
383        """
[ef0c170]384        params = {}
[a2c38de]385        params["radius"] = math.fabs(self._inner_mouse_x)
[ef0c170]386        return params
387   
388    def set_params(self, params):
[eba08f1a]389        """
390            Receive a dictionary and reset the slicer with values contained
391            in the values of the dictionary.
392            @param params: a dictionary containing name of slicer parameters and
393            values the user assigned to the slicer.
394        """
[ef0c170]395        x = params["radius"] 
396        self.set_cursor(x, self._inner_mouse_y)
397       
398       
Note: See TracBrowser for help on using the repository browser.