source: sasview/guitools/PlotPanel.py @ f52bea1

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

Plotpanel plottable fitdialog req modified

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