source: sasview/guitools/PlotPanel.py @ dfca3de

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

prpoerty dialog modified

  • Property mode set to 100644
File size: 16.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        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("%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("%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("%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            if ( self.yscales =="1/y"):
374                item.transform_y( transform.toOneOverX , transform.errOneOverX )
375                self.set_yscale("linear")
376                name, units = item.get_yaxis()
377                self.graph.yaxis("%s" % name,  "%s" % units)
378            if ( self.yscales =="1/sqrt(y)" ):
379                item.transform_y( transform.toOneOverSqrtX ,transform.errOneOverSqrtX )
380                self.set_yscale("linear")
381                name, units = item.get_yaxis()
382                self.graph.yaxis("%s" %name,  "%s" % units)
383               
384            if ( self.yscales =="ln(y*x)"):
385                item.transform_y( transform.toLogXY ,transform.errToLogXY )
386                self.set_yscale("linear")
387                yname, yunits = item.get_yaxis()
388                xname, xunits = item.get_xaxis()
389                self.graph.yaxis("%s%s" % (yname,xname),  "%s^{-1}%s^{-1}" % (yunits,xunits))
390               
391            if ( self.yscales =="ln(y*x^(2)"):
392                item.transform_y( transform.toYX2 ,transform.ToYX2 )
393                self.set_yscale("linear")
394                yname, yunits = item.get_yaxis()
395                xname, xunits = item.get_xaxis()
396                self.graph.yaxis("%s%s^{2}" % (yname,xname),  "%s^{-1}%s^{-2}" % (yunits,xunits))
397           
398            if ( self.yscales =="ln(y*x^(4))"):
399                item.transform_y( transform.toLogYX4 ,transform.errToLogYX4 )
400                self.set_yscale("linear")
401                yname, yunits = item.get_yaxis()
402                xname, xunits = item.get_xaxis()
403                self.graph.yaxis("%s%s^{4}" % (yname,xname),  "%s^{-1}%s^{-4}" % (yunits,xunits))
404           
405            if ( self.viewModel == "Guinier lny vs x^(2)"):
406               
407                item.transform_x( transform.toX2,transform.errToX2 )
408                self.set_xscale('linear')
409                name, units = item.get_xaxis()
410                self.graph.xaxis("%s^{2}" % name,  "%s^{-2}" % units)
411               
412                item.transform_y( transform.toLogX, transform.errToLogX )
413                self.set_yscale("linear")
414                name, units = item.get_yaxis()
415                self.graph.yaxis("%s" % name,  "%s^{-1}" % units)
416                 
417        self.prevXtrans = self.xscales
418        self.prevYtrans = self.yscales 
419       
420        self.graph.render(self)
421        self.subplot.figure.canvas.draw_idle()
422    def onFitDisplay(self, plottable):
423        """
424            Add a new plottable into the graph .In this case this plottable will be used
425            to fit some data
426            @param plottable: the plottable to plot
427        """
428        plottable.reset_view()
429        self.graph.add(plottable)
430        self.graph.render(self)
431       
432        self.subplot.figure.canvas.draw_idle()
433        self.graph.delete(plottable)
434     
435   
436   
437class NoRepaintCanvas(FigureCanvasWxAgg):
438    """We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
439    the draw method is only called for the first two paint events. After that,
440    the canvas will only be redrawn when it is resized.
441    """
442    def __init__(self, *args, **kwargs):
443        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
444        self._drawn = 0
445
446    def _onPaint(self, evt):
447        """
448        Called when wxPaintEvt is generated
449        """
450        if not self._isRealized:
451            self.realize()
452        if self._drawn < 2:
453            self.draw(repaint = False)
454            self._drawn += 1
455        self.gui_repaint(drawDC=wx.PaintDC(self))
456           
Note: See TracBrowser for help on using the repository browser.