source: sasview/guitools/PlotPanel.py @ f63f5ff

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

is working better

  • Property mode set to 100644
File size: 16.6 KB
Line 
1import wx.lib.newevent
2import matplotlib
3matplotlib.interactive(False)
4#Use the WxAgg back end. The Wx one takes too long to render
5matplotlib.use('WXAgg')
6from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
7from matplotlib.figure import Figure
8import os
9import fittings
10from canvas import FigureCanvas
11#TODO: make the plottables interactive
12
13from plottables import Graph
14#(FuncFitEvent, EVT_FUNC_FIT) = wx.lib.newevent.NewEvent()
15import math,pylab
16def show_tree(obj,d=0):
17    """Handy function for displaying a tree of graph objects"""
18    print "%s%s" % ("-"*d,obj.__class__.__name__)
19    if 'get_children' in dir(obj):
20        for a in obj.get_children(): show_tree(a,d+1)
21
22
23
24class PlotPanel(wx.Panel):
25    """
26    The PlotPanel has a Figure and a Canvas. OnSize events simply set a
27    flag, and the actually redrawing of the
28    figure is triggered by an Idle event.
29    """
30    def __init__(self, parent, id = -1, color = None,\
31        dpi = None, **kwargs):
32        wx.Panel.__init__(self, parent, id = id, **kwargs)
33        self.parent = parent
34        self.figure = Figure(None, dpi)
35        #self.figure = pylab.Figure(None, dpi)
36        #self.canvas = NoRepaintCanvas(self, -1, self.figure)
37        self.canvas = FigureCanvas(self, -1, self.figure)
38        self.SetColor(color)
39        #self.Bind(wx.EVT_IDLE, self._onIdle)
40        #self.Bind(wx.EVT_SIZE, self._onSize)
41        self._resizeflag = True
42        self._SetSize()
43        self.subplot = self.figure.add_subplot(111)
44        self.figure.subplots_adjust(left=.2, bottom=.2)
45        self.yscale = 'linear'
46        self.xscale = 'linear'
47        sizer = wx.BoxSizer(wx.VERTICAL)
48        sizer.Add(self.canvas,1,wx.EXPAND)
49        self.SetSizer(sizer)
50
51        # Graph object to manage the plottables
52        self.graph = Graph()
53        #self.Bind(EVT_FUNC_FIT, self.onFitRange)
54        self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
55        #self.Bind(EVT_PROPERTY, self._onEVT_FUNC_PROPERTY)
56        # Define some constants
57        self.colorlist = ['b','g','r','c','m','y']
58        self.symbollist = ['o','x','^','v','<','>','+','s','d','D','h','H','p']
59        #User scale
60        self.xscales =""
61        self.yscales =""
62        # keep track if the previous transformation of x and y in Property dialog
63        self.prevXtrans =" "
64        self.prevYtrans =" "
65       
66    def setTrans(self,xtrans,ytrans): 
67        """
68            @param xtrans: set x transformation on Property dialog
69            @param ytrans: set y transformation on Property dialog
70        """
71        self.prevXtrans =xtrans
72        self.prevYtrans =ytrans
73       
74    def onFitting(self, event): 
75        """
76            when clicking on linear Fit on context menu , display Fitting Dialog
77        """
78        list =[]
79        list = self.graph.returnPlottable()
80        from fitDialog import LinearFit
81        print len(list)
82        if len(list.keys())>0:
83            first_item = list.keys()[0]
84            #print first_item, list[first_item].__class__.__name__
85            dlg = LinearFit( None, first_item, self.onFitDisplay, -1, 'Fitting')
86            dlg.ShowModal() 
87
88    def _onProperties(self, event):
89        """
90            when clicking on Properties on context menu ,The Property dialog is displayed
91            The user selects a transformation for x or y value and a new plot is displayed
92        """
93        from PropertyDialog import Properties
94        dial = Properties(self, -1, 'Properties')
95        dial.setValues( self.prevXtrans, self.prevYtrans )
96        if dial.ShowModal() == wx.ID_OK:
97            self.xscales, self.yscales = dial.getValues()
98            self._onEVT_FUNC_PROPERTY()
99        dial.Destroy()
100           
101    def toX(self,x):
102        """
103            This function is used to load value on Plottable.View
104            @param x: Float value
105            @return x,
106        """
107        return x
108   
109    def toX2(self,x):
110        """
111            This function is used to load value on Plottable.View
112            Calculate x^(2)
113            @param x: float value
114        """
115        return x*x
116   
117    def fromX2(self,x):
118         """
119             This function is used to load value on Plottable.View
120            Calculate square root of x
121            @param x: float value
122         """
123         if not x >=0 :
124             raise ValueError, "square root of a negative value "
125         else:
126             return math.sqrt(x)
127         
128    def toLogXY(self,x,y):
129        """
130            This function is used to load value on Plottable.View
131            calculate log x
132            @param x: float value
133        """
134        if not x*y > 0:
135            raise ValueError, "Log(X*Y)of a negative value "
136        else:
137            return math.log(x*y)
138       
139    def fromLogXY(self,x):
140        """
141            This function is used to load value on Plottable.View
142            Calculate e^(x)
143            @param x: float value
144        """
145        return math.exp(x*y)
146   
147    def set_yscale(self, scale='linear'):
148        """
149            Set the scale on Y-axis
150            @param scale: the scale of y-axis
151        """
152        self.subplot.set_yscale(scale)
153        self.yscale = scale
154       
155    def get_yscale(self):
156        """
157             @return: Y-axis scale
158        """
159        return self.yscale
160   
161    def set_xscale(self, scale='linear'):
162        """
163            Set the scale on x-axis
164            @param scale: the scale of x-axis
165        """
166        self.subplot.set_xscale(scale)
167        self.xscale = scale
168       
169    def get_xscale(self):
170        """
171             @return: x-axis scale
172        """
173        return self.xscale
174
175    def SetColor(self, rgbtuple):
176        """Set figure and canvas colours to be the same"""
177        if not rgbtuple:
178            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
179        col = [c/255.0 for c in rgbtuple]
180        self.figure.set_facecolor(col)
181        self.figure.set_edgecolor(col)
182        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))
183
184    def _onSize(self, event):
185        self._resizeflag = True
186
187    def _onIdle(self, evt):
188        if self._resizeflag:
189            self._resizeflag = False
190            self._SetSize()
191            self.draw()
192
193    def _SetSize(self, pixels = None):
194        """
195        This method can be called to force the Plot to be a desired size, which defaults to
196        the ClientSize of the panel
197        """
198        if not pixels:
199            pixels = self.GetClientSize()
200        self.canvas.SetSize(pixels)
201        self.figure.set_size_inches(pixels[0]/self.figure.get_dpi(),
202        pixels[1]/self.figure.get_dpi())
203
204    def draw(self):
205        """Where the actual drawing happens"""
206        self.figure.canvas.draw_idle()
207       
208
209
210    def onSaveImage(self, evt):
211        #figure.savefig
212        print "Save image not implemented"
213        path = None
214        dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.png", wx.SAVE)
215        if dlg.ShowModal() == wx.ID_OK:
216            path = dlg.GetPath()
217            mypath = os.path.basename(path)
218            print path
219        dlg.Destroy()
220        if not path == None:
221            self.subplot.figure.savefig(path,dpi=300, facecolor='w', edgecolor='w',
222                                        orentation='portrait', papertype=None, format='png')
223       
224    def onContextMenu(self, event):
225        """
226            Default context menu for a plot panel
227        """
228        # Slicer plot popup menu
229        slicerpop = wx.Menu()
230        slicerpop.Append(313,'&Save image', 'Save image as PNG')
231        wx.EVT_MENU(self, 313, self.onSaveImage)
232        slicerpop.Append(316, '&Load 1D data file')
233       
234        wx.EVT_MENU(self, 314, self.onSave1DData)
235        wx.EVT_MENU(self, 316, self._onLoad1DData)
236        slicerpop.AppendSeparator()
237        slicerpop.Append(315, '&Properties')
238       
239        slicerpop.AppendSeparator()
240        slicerpop.Append(317, '&Linear Fit')
241       
242        wx.EVT_MENU(self, 314, self.onSave1DData)
243        wx.EVT_MENU(self, 316, self._onLoad1DData)
244        wx.EVT_MENU(self, 315, self._onProperties)
245        wx.EVT_MENU(self, 317, self.onFitting)
246       
247        pos = event.GetPosition()
248        pos = self.ScreenToClient(pos)
249        self.PopupMenu(slicerpop, pos)
250       
251   
252    ## The following is plottable functionality
253
254
255    def properties(self,prop):
256        """Set some properties of the graph.
257       
258        The set of properties is not yet determined.
259        """
260        # The particulars of how they are stored and manipulated (e.g., do
261        # we want an inventory internally) is not settled.  I've used a
262        # property dictionary for now.
263        #
264        # How these properties interact with a user defined style file is
265        # even less clear.
266
267        # Properties defined by plot
268        self.subplot.set_xlabel(r"$%s$" % prop["xlabel"])
269        self.subplot.set_ylabel(r"$%s$" % prop["ylabel"])
270        self.subplot.set_title(prop["title"])
271
272        # Properties defined by user
273        #self.axes.grid(True)
274
275    def clear(self):
276        """Reset the plot"""
277       
278        # TODO: Redraw is brutal.  Render to a backing store and swap in
279        # TODO: rather than redrawing on the fly.
280        self.subplot.clear()
281        self.subplot.hold(True)
282
283
284    def render(self):
285        """Commit the plot after all objects are drawn"""
286        # TODO: this is when the backing store should be swapped in.
287        from matplotlib.font_manager import FontProperties
288        self.subplot.legend(prop=FontProperties(size=10))
289        #self.subplot.legend()
290        pass
291
292    def xaxis(self,label,units):
293        """xaxis label and units.
294       
295        Axis labels know about units.
296       
297        We need to do this so that we can detect when axes are not
298        commesurate.  Currently this is ignored other than for formatting
299        purposes.
300        """
301        if units != "": label = label + " (" + units + ")"
302        self.subplot.set_xlabel(label)
303        pass
304   
305    def yaxis(self,label,units):
306        """yaxis label and units."""
307        if units != "": label = label + " (" + units + ")"
308        self.subplot.set_ylabel(label)
309        pass
310
311    def _connect_to_xlim(self,callback):
312        """Bind the xlim change notification to the callback"""
313        def process_xlim(axes):
314            lo,hi = subplot.get_xlim()
315            callback(lo,hi)
316        self.subplot.callbacks.connect('xlim_changed',process_xlim)
317   
318    #def connect(self,trigger,callback):
319    #    print "PlotPanel.connect???"
320    #    if trigger == 'xlim': self._connect_to_xlim(callback)
321
322    def points(self,x,y,dx=None,dy=None,color=0,symbol=0,label=None):
323        """Draw markers with error bars"""
324        self.subplot.set_yscale('linear')
325        self.subplot.set_xscale('linear')
326        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
327        if dx != None and type(dx) == type(()):
328            dx = nx.vstack((x-dx[0],dx[1]-x)).transpose()
329        if dy != None and type(dy) == type(()):
330            dy = nx.vstack((y-dy[0],dy[1]-y)).transpose()
331
332        if dx==None and dy==None:
333            h = self.subplot.plot(x,y,color=self._color(color),
334                                   marker=self._symbol(symbol),linestyle='',label=label)
335        else:
336            self.subplot.errorbar(x, y, yerr=dy, xerr=None,
337             ecolor=self._color(color), capsize=2,linestyle='', barsabove=False,
338             marker=self._symbol(symbol),
339             lolims=False, uplims=False,
340             xlolims=False, xuplims=False,label=label)
341           
342        self.subplot.set_yscale(self.yscale)
343        self.subplot.set_xscale(self.xscale)
344
345    def curve(self,x,y,dy=None,color=0,symbol=0,label=None):
346        """Draw a line on a graph, possibly with confidence intervals."""
347        c = self._color(color)
348        self.subplot.set_yscale('linear')
349        self.subplot.set_xscale('linear')
350       
351        hlist = self.subplot.plot(x,y,color=c,marker='',linestyle='-',label=label)
352       
353        self.subplot.set_yscale(self.yscale)
354        self.subplot.set_xscale(self.xscale)
355
356    def _color(self,c):
357        """Return a particular colour"""
358        return self.colorlist[c%len(self.colorlist)]
359
360    def _symbol(self,s):
361        """Return a particular symbol"""
362        return self.symbollist[s%len(self.symbollist)]
363   
364    def _onEVT_FUNC_PROPERTY(self):
365        """
366             Receive the x and y transformation from myDialog,Transforms x and y in View
367              and set the scale   
368        """ 
369        list =[]
370        list = self.graph.returnPlottable()
371        for item in list:
372            if ( self.xscales=="x" ):
373                item.transform_x(  self.toX, self.errFunctoX )
374                self.set_xscale("linear")
375                self.graph.xaxis('\\rm{q} ', 'A^{-1}')
376               
377            if ( self.xscales=="x^(2)" ):
378                item.transform_x(  self.toX2, self.errFuncToX2 )
379                self.set_xscale('linear')
380                self.graph.xaxis('\\rm{q^{2}} ', 'A^{-2}')
381               
382            if (self.xscales=="Log(x)" ):
383                item.transform_x(  self.toX, self.errFuncToLogX )
384                self.set_xscale("log")
385                self.graph.xaxis('\\rm{q} ', 'A^{-1}')
386               
387            if ( self.yscales=="y" ):
388                item.transform_y(  self.toX, self.errFunctoX )
389                self.set_yscale("linear")
390                self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
391               
392            if ( self.yscales=="Log(y)" ): 
393                item.transform_y(  self.toX, self.errFuncToLogX)
394                self.set_yscale("log") 
395                self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
396               
397            if ( self.yscales=="y^(2)" ):
398                item.transform_y(  self.toX2, self.errFuncToX2 )   
399                self.set_yscale("linear")
400                self.graph.yaxis("\\rm{Intensity^{2}} ","cm^{-2}")
401   
402        self.prevXtrans = self.xscales
403        self.prevYtrans = self.yscales 
404       
405        self.graph.render(self)
406        self.subplot.figure.canvas.draw_idle()
407    def errFunctoX(self,x,dx=None):
408        """
409            calculate error of x**2
410            @param x: float value
411            @param dx: float value
412        """
413        if dx >=x:
414            return math.fabs(0.9*x)
415        if dx==None:
416             return 0
417        return math.fabs(dx)
418    def errFuncToX2(self,x,dx=None):
419        """
420            calculate error of x**2
421            @param x: float value
422            @param dx: float value
423        """
424        if  dx != None:
425            err = 2*x*dx
426            if err >= x:
427                err = 0.9*x
428            return math.fabs(err)
429        else:
430            return 0.0
431    def errFuncfromX2(self,x,dx=None):
432        """
433            calculate error of sqrt(x)
434            @param x: float value
435            @param dx: float value
436        """
437        if (x > 0):
438            if(dx != None):
439                err = dx/(2*math.sqrt(x))
440            else:
441                err = 0
442            if err >= x:
443                err = 0.9*x   
444        else:
445            err = 0.9*x
446           
447            return math.fabs(err)
448       
449    def errFuncToLogX(self,x,dx=None):
450        """
451            calculate error of Log(xy)
452            @param x: float value
453            @param dx: float value
454        """
455        if x!=0:
456            if dx==None:
457                #err = 1/x
458                err = 0
459            else:
460                err = dx/x
461            if err >= x:
462                err = 0.9*x
463        else:
464            err = 0.9*x
465       
466        return math.fabs(err)
467       
468    def errFuncfromLogXY(self,x,dx=None):
469        """
470            calculate error of Log(xy)
471            @param x: float value
472            @param dx: float value
473        """
474        return 
475       
476                   
477    def onFitDisplay(self, plottable):
478        """
479            Add a new plottable into the graph .In this case this plottable will be used
480            to fit some data
481            @param plottable: the plottable to plot
482        """
483        plottable.reset_view()
484        self.graph.add(plottable)
485        self.graph.render(self)
486        self.subplot.figure.canvas.draw_idle()
487       
488   
489class NoRepaintCanvas(FigureCanvasWxAgg):
490    """We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
491    the draw method is only called for the first two paint events. After that,
492    the canvas will only be redrawn when it is resized.
493    """
494    def __init__(self, *args, **kwargs):
495        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
496        self._drawn = 0
497
498    def _onPaint(self, evt):
499        """
500        Called when wxPaintEvt is generated
501        """
502        if not self._isRealized:
503            self.realize()
504        if self._drawn < 2:
505            self.draw(repaint = False)
506            self._drawn += 1
507        self.gui_repaint(drawDC=wx.PaintDC(self))
508           
Note: See TracBrowser for help on using the repository browser.