source: sasview/guitools/PlotPanel.py @ 35891ce

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

changed labels

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