source: sasview/guiframe/local_perspectives/plotting/boxSlicer.py @ 89ef796

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 89ef796 was 6c0568b, checked in by Gervaise Alina <gervyh@…>, 16 years ago

comment class

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