source: sasview/guiframe/local_perspectives/plotting/AnnulusSlicer.py @ 445f949

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 445f949 was eba08f1a, checked in by Gervaise Alina <gervyh@…>, 16 years ago

add comments

  • Property mode set to 100644
File size: 13.1 KB
Line 
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
8import math
9import wx
10from copy import deepcopy
11# Debug printout
12from sans.guicomm.events import NewPlotEvent, StatusEvent,SlicerParameterEvent,EVT_SLICER_PARS
13from BaseInteractor import _BaseInteractor
14
15
16class AnnulusInteractor(_BaseInteractor):
17    """
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.
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
28        self.base= base
29        self.qmax = min(math.fabs(self.base.data2D.xmax),math.fabs(self.base.data2D.xmin))  #must be positive
30        self.connect = self.base.connect
31   
32        ## Number of points on the plot
33        self.nbins = 20
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
38
39        if (self.xmaxd+self.xmind)>0:
40            self.sign=1
41        else:
42            self.sign=-1
43                 
44        # Inner circle
45        self.inner_circle = RingInteractor(self, self.base.subplot, zorder=zorder, r=self.qmax/2.0,sign=self.sign)
46        self.inner_circle.qmax = self.qmax
47        self.outer_circle = RingInteractor(self, self.base.subplot, zorder=zorder+1, r=self.qmax/1.8,sign=self.sign)
48        self.outer_circle.qmax = self.qmax*1.2
49       
50        self.update()
51        self._post_data()
52       
53        # Bind to slice parameter events
54        self.base.Bind(EVT_SLICER_PARS, self._onEVT_SLICER_PARS)
55       
56       
57
58    def _onEVT_SLICER_PARS(self, event):
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        """
64        wx.PostEvent(self.base, StatusEvent(status="AnnulusSlicer._onEVT_SLICER_PARS"))
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):
71        """
72             Allow adding plot to the same panel
73             @param n: the number of layer
74        """
75        self.layernum = n
76        self.update()
77       
78    def clear(self):
79        """
80            Clear the slicer and all connected events related to this slicer
81        """
82        self.clear_markers()
83        self.outer_circle.clear()
84        self.inner_circle.clear()
85        self.base.connect.clearall()
86        self.base.Unbind(EVT_SLICER_PARS)
87       
88       
89    def update(self):
90        """
91            Respond to changes in the model by recalculating the profiles and
92            resetting the widgets.
93        """
94        # Update locations       
95        self.inner_circle.update()
96        self.outer_circle.update()
97       
98
99    def save(self, ev):
100        """
101            Remember the roughness for this layer and the next so that we
102            can restore on Esc.
103        """
104        self.base.freeze_axes()
105        self.inner_circle.save(ev)
106        self.outer_circle.save(ev)
107
108    def _post_data(self,nbins=None):
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
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
120   
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()))
125        #if the user does not specify the numbers of points to plot
126        # the default number will be nbins= 20
127        if nbins==None:
128            self.nbins= 20
129        else:
130            self.nbins = nbins
131        ## create the data1D Q average of data2D   
132        sect = SectorPhi(r_min=rmin , r_max= rmax,
133                          phi_min=0, phi_max=2*math.pi , nbins=self.nbins)
134        sector = sect(self.base.data2D)
135       
136        from sans.guiframe.dataFitting import Data1D
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       
146        new_plot = Data1D(x=sector.x,y=sector.y,dy=sector.dy,dxl=dxl,dxw=dxw)
147        new_plot.name = "SectorPhi" +"("+ self.base.data2D.name+")"
148       
149        new_plot.source=self.base.data2D.source
150        #new_plot.info=self.base.data2D.info
151       
152        new_plot.interactive = True
153        new_plot.detector =self.base.data2D.detector
154        # If the data file does not tell us what the axes are, just assume...
155        new_plot.xaxis("\\rm{\phi}", 'degrees')
156        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
157        new_plot.group_id = "SectorPhi"+self.base.data2D.name
158        new_plot.id= "SectorPhi"+self.base.data2D.name
159        new_plot.xtransform="x"
160        new_plot.ytransform="y"
161        wx.PostEvent(self.base.parent, NewPlotEvent(plot=new_plot,
162                                                 title="SectorPhi" ))
163       
164         
165    def moveend(self, ev):
166        """
167            Called when any dragging motion ends.
168            Post an event (type =SlicerParameterEvent)
169            to plotter 2D with a copy  slicer parameters
170            Call  _post_data method
171        """
172        self.base.thaw_axes()
173        # Post parameters to plotter 2D
174        event = SlicerParameterEvent()
175        event.type = self.__class__.__name__
176        event.params = self.get_params()
177        wx.PostEvent(self.base, event)
178        # create a 1D data plot
179        self._post_data()
180           
181    def restore(self):
182        """
183        Restore the roughness for this layer.
184        """
185        self.inner_circle.restore()
186        self.outer_circle.restore()
187
188    def move(self, x, y, ev):
189        """
190        Process move to a new position, making sure that the move is allowed.
191        """
192        pass
193       
194    def set_cursor(self, x, y):
195        pass
196       
197    def get_params(self):
198        """
199            Store a copy of values of parameters of the slicer into a dictionary.
200            @return params: the dictionary created
201        """
202        params = {}
203        params["inner_radius"] = math.fabs(self.inner_circle._inner_mouse_x)
204        params["outer_radius"] = math.fabs(self.outer_circle._inner_mouse_x)
205        params["nbins"] = self.nbins
206        return params
207   
208    def set_params(self, params):
209        """
210            Receive a dictionary and reset the slicer with values contained
211            in the values of the dictionary.
212            @param params: a dictionary containing name of slicer parameters and
213            values the user assigned to the slicer.
214        """
215        inner = math.fabs(params["inner_radius"] )
216        outer = math.fabs(params["outer_radius"] )
217        self.nbins = int(params["nbins"])
218        ## Update the picture
219        self.inner_circle.set_cursor(inner, self.inner_circle._inner_mouse_y)
220        self.outer_circle.set_cursor(outer, self.outer_circle._inner_mouse_y)
221        ## Post the data given the nbins entered by the user
222        self._post_data(self.nbins)
223       
224    def freeze_axes(self):
225        self.base.freeze_axes()
226       
227    def thaw_axes(self):
228        self.base.thaw_axes()
229
230    def draw(self):
231        self.base.draw()
232
233       
234class RingInteractor(_BaseInteractor):
235    """
236         Draw a ring Given a radius
237         @param: the color of the line that defined the ring
238         @param r: the radius of the ring
239         @param sign: the direction of motion the the marker
240    """
241    def __init__(self,base,axes,color='black', zorder=5, r=1.0,sign=1):
242       
243        _BaseInteractor.__init__(self, base, axes, color=color)
244        self.markers = []
245        self.axes = axes
246        # Current radius of the ring
247        self._inner_mouse_x = r
248        #Value of the center of the ring
249        self._inner_mouse_y = 0
250        # previous value of that radius
251        self._inner_save_x  = r
252        #Save value of the center of the ring
253        self._inner_save_y  = 0
254        #Class instantiating RingIterator class
255        self.base= base
256        #the direction of the motion of the marker
257        self.sign=sign
258        ## Create a marker
259        try:
260            # Inner circle marker
261            self.inner_marker = self.axes.plot([self.sign*math.fabs(self._inner_mouse_x)],[0], linestyle='',
262                                          marker='s', markersize=10,
263                                          color=self.color, alpha=0.6,
264                                          pickradius=5, label="pick", 
265                                          zorder=zorder, # Prefer this to other lines
266                                          visible=True)[0]
267        except:
268            self.inner_marker = self.axes.plot([self.sign*math.fabs(self._inner_mouse_x)],[0], linestyle='',
269                                          marker='s', markersize=10,
270                                          color=self.color, alpha=0.6,
271                                          label="pick", 
272                                          visible=True)[0]
273            message  = "\nTHIS PROTOTYPE NEEDS THE LATEST VERSION OF MATPLOTLIB\n"
274            message += "Get the SVN version that is at least as recent as June 1, 2007"
275           
276            owner=self.base.base.parent
277            wx.PostEvent(owner, StatusEvent(status="AnnulusSlicer: %s"%message))
278           
279        # Draw a circle
280        [self.inner_circle] = self.axes.plot([],[],
281                                      linestyle='-', marker='',
282                                      color=self.color)
283        # the number of points that make the ring line
284        self.npts = 40
285           
286        self.connect_markers([self.inner_marker])
287        self.update()
288
289    def set_layer(self, n):
290        """
291             Allow adding plot to the same panel
292             @param n: the number of layer
293        """
294        self.layernum = n
295        self.update()
296       
297    def clear(self):
298        """
299            Clear the slicer and all connected events related to this slicer
300        """
301        self.clear_markers()
302        try:
303            self.inner_marker.remove()
304            self.inner_circle.remove()
305        except:
306            # Old version of matplotlib
307            for item in range(len(self.axes.lines)):
308                del self.axes.lines[0]
309       
310       
311       
312    def get_radius(self):
313        """
314            @return self._inner_mouse_x: the current radius of the ring
315        """
316        return self._inner_mouse_x
317       
318    def update(self):
319        """
320            Draw the new roughness on the graph.
321        """
322        # Plot inner circle
323        x = []
324        y = []
325        for i in range(self.npts):
326            phi = 2.0*math.pi/(self.npts-1)*i
327           
328            xval = 1.0*self._inner_mouse_x*math.cos(phi) 
329            yval = 1.0*self._inner_mouse_x*math.sin(phi) 
330           
331            x.append(xval)
332            y.append(yval)
333           
334        self.inner_marker.set(xdata=[self.sign*math.fabs(self._inner_mouse_x)],ydata=[0])
335        self.inner_circle.set_data(x, y)       
336
337    def save(self, ev):
338        """
339        Remember the roughness for this layer and the next so that we
340        can restore on Esc.
341        """
342        self._inner_save_x = self._inner_mouse_x
343        self._inner_save_y = self._inner_mouse_y
344        self.base.freeze_axes()
345
346    def moveend(self, ev):
347        """
348            Called after a dragging motion
349        """
350        self.base.moveend(ev)
351           
352    def restore(self):
353        """
354        Restore the roughness for this layer.
355        """
356        self._inner_mouse_x = self._inner_save_x
357        self._inner_mouse_y = self._inner_save_y
358
359    def move(self, x, y, ev):
360        """
361        Process move to a new position, making sure that the move is allowed.
362        """
363        self._inner_mouse_x = x
364        self._inner_mouse_y = y
365        self.base.base.update()
366       
367    def set_cursor(self, x, y):
368        """
369            draw the ring given x, y value
370        """
371        self.move(x, y, None)
372        self.update()
373       
374       
375    def get_params(self):
376        """
377            Store a copy of values of parameters of the slicer into a dictionary.
378            @return params: the dictionary created
379        """
380        params = {}
381        params["radius"] = math.fabs(self._inner_mouse_x)
382        return params
383   
384    def set_params(self, params):
385        """
386            Receive a dictionary and reset the slicer with values contained
387            in the values of the dictionary.
388            @param params: a dictionary containing name of slicer parameters and
389            values the user assigned to the slicer.
390        """
391        x = params["radius"] 
392        self.set_cursor(x, self._inner_mouse_y)
393       
394       
Note: See TracBrowser for help on using the repository browser.