source: sasview/guitools/PlotPanel.py @ 863607a

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

fixing the fit

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