source: sasview/guitools/PlotPanel.py @ f193585

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

some changes

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