source: sasview/guitools/PlotPanel.py @ 52b1f77

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

plotpanel modified…trouble to load dx…dy

  • Property mode set to 100644
File size: 14.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
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 =""
61        self.yscales =""
62        # keep track if the previous transformation
63        self.prevXtrans ="x"
64        self.prevYtrans ="Log(y)"
65       
66    def onFitting(self, event): 
67        list =[]
68        list = self.graph.returnPlottable()
69        from fitDialog import LinearFit
70       
71        print len(list)
72        if len(list.keys())>0:
73            first_item = list.keys()[0]
74            print first_item, list[first_item].__class__.__name__
75            dlg = LinearFit( None, first_item, self.onFitDisplay, -1, 'Fitting')
76            dlg.ShowModal() 
77
78    def _onProperties(self, event):
79       
80        from PropertyDialog import Properties
81        dial = Properties(self, -1, 'Properties')
82        if dial.ShowModal() == wx.ID_OK:
83            self.xscales, self.yscales = dial.getValues()
84            self._onEVT_FUNC_PROPERTY()
85        dial.Destroy()
86           
87    def toX(self,x):
88        return x
89   
90    def toX2(self,x):
91        """
92            Calculate x^(2)
93            @param x: float value
94        """
95        return math.pow(x,2)
96    def fromX2(self,x):
97         """
98            Calculate square root of x
99            @param x: float value
100         """
101         if x >=0 :
102             return math.sqrt(x)
103         else:
104            return 0
105    def toLogXY(self,x):
106        """
107            calculate log x
108            @param x: float value
109        """
110        if x > 0:
111            return math.log(x)
112        else:
113            return 0
114    def fromLogXY(self,x):
115        """
116            Calculate e^(x)
117            @param x: float value
118        """
119        if x.__class__.__name__ == 'list':
120            temp =[]
121            for x_i in x:
122                temp.append(math.exp(x_i))
123            return temp
124        else:
125            return math.exp(x)
126   
127
128    def set_yscale(self, scale='linear'):
129        self.subplot.set_yscale(scale)
130        self.yscale = scale
131       
132    def get_yscale(self):
133        return self.yscale
134   
135    def set_xscale(self, scale='linear'):
136        self.subplot.set_xscale(scale)
137        self.xscale = scale
138       
139    def get_xscale(self):
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    def onSaveImage(self, evt):
178        #figure.savefig
179        print "Save image not implemented"
180        path = None
181        dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.png", wx.SAVE)
182        if dlg.ShowModal() == wx.ID_OK:
183            path = dlg.GetPath()
184            mypath = os.path.basename(path)
185            print path
186        dlg.Destroy()
187        if not path == None:
188            self.subplot.figure.savefig(path,dpi=300, facecolor='w', edgecolor='w',
189                                        orentation='portrait', papertype=None, format='png')
190       
191    def onContextMenu(self, event):
192        """
193            Default context menu for a plot panel
194        """
195        # Slicer plot popup menu
196        slicerpop = wx.Menu()
197        slicerpop.Append(313,'&Save image', 'Save image as PNG')
198        wx.EVT_MENU(self, 313, self.onSaveImage)
199        slicerpop.Append(316, '&Load 1D data file')
200       
201        wx.EVT_MENU(self, 314, self.onSave1DData)
202        wx.EVT_MENU(self, 316, self._onLoad1DData)
203        slicerpop.AppendSeparator()
204        slicerpop.Append(315, '&Properties')
205       
206        slicerpop.AppendSeparator()
207        slicerpop.Append(317, '&Linear Fit')
208       
209        wx.EVT_MENU(self, 314, self.onSave1DData)
210        wx.EVT_MENU(self, 316, self._onLoad1DData)
211        wx.EVT_MENU(self, 315, self._onProperties)
212        wx.EVT_MENU(self, 317, self.onFitting)
213       
214        pos = event.GetPosition()
215        pos = self.ScreenToClient(pos)
216        self.PopupMenu(slicerpop, pos)
217       
218   
219    ## The following is plottable functionality
220
221
222    def properties(self,prop):
223        """Set some properties of the graph.
224       
225        The set of properties is not yet determined.
226        """
227        # The particulars of how they are stored and manipulated (e.g., do
228        # we want an inventory internally) is not settled.  I've used a
229        # property dictionary for now.
230        #
231        # How these properties interact with a user defined style file is
232        # even less clear.
233
234        # Properties defined by plot
235        self.subplot.set_xlabel(r"$%s$" % prop["xlabel"])
236        self.subplot.set_ylabel(r"$%s$" % prop["ylabel"])
237        self.subplot.set_title(prop["title"])
238
239        # Properties defined by user
240        #self.axes.grid(True)
241
242    def clear(self):
243        """Reset the plot"""
244       
245        # TODO: Redraw is brutal.  Render to a backing store and swap in
246        # TODO: rather than redrawing on the fly.
247        self.subplot.clear()
248        self.subplot.hold(True)
249
250
251    def render(self):
252        """Commit the plot after all objects are drawn"""
253        # TODO: this is when the backing store should be swapped in.
254        from matplotlib.font_manager import FontProperties
255        self.subplot.legend(prop=FontProperties(size=10))
256        #self.subplot.legend()
257        pass
258
259    def xaxis(self,label,units):
260        """xaxis label and units.
261       
262        Axis labels know about units.
263       
264        We need to do this so that we can detect when axes are not
265        commesurate.  Currently this is ignored other than for formatting
266        purposes.
267        """
268        if units != "": label = label + " (" + units + ")"
269        self.subplot.set_xlabel(label)
270        pass
271   
272    def yaxis(self,label,units):
273        """yaxis label and units."""
274        if units != "": label = label + " (" + units + ")"
275        self.subplot.set_ylabel(label)
276        pass
277
278    def _connect_to_xlim(self,callback):
279        """Bind the xlim change notification to the callback"""
280        def process_xlim(axes):
281            lo,hi = subplot.get_xlim()
282            callback(lo,hi)
283        self.subplot.callbacks.connect('xlim_changed',process_xlim)
284   
285    #def connect(self,trigger,callback):
286    #    print "PlotPanel.connect???"
287    #    if trigger == 'xlim': self._connect_to_xlim(callback)
288
289    def points(self,x,y,dx=None,dy=None,color=0,symbol=0,label=None):
290        """Draw markers with error bars"""
291        self.subplot.set_yscale('linear')
292        self.subplot.set_xscale('linear')
293        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
294        if dx != None and type(dx) == type(()):
295            dx = nx.vstack((x-dx[0],dx[1]-x)).transpose()
296        if dy != None and type(dy) == type(()):
297            dy = nx.vstack((y-dy[0],dy[1]-y)).transpose()
298
299        if dx==None and dy==None:
300            h = self.subplot.plot(x,y,color=self._color(color),
301                                   marker=self._symbol(symbol),linestyle='',label=label)
302        else:
303            self.subplot.errorbar(x, y, yerr=dy, xerr=None,
304             ecolor=self._color(color), capsize=2,linestyle='', barsabove=False,
305             marker=self._symbol(symbol),
306             lolims=False, uplims=False,
307             xlolims=False, xuplims=False,label=label)
308           
309        self.subplot.set_yscale(self.yscale)
310        self.subplot.set_xscale(self.xscale)
311
312    def curve(self,x,y,dy=None,color=0,symbol=0,label=None):
313        """Draw a line on a graph, possibly with confidence intervals."""
314        c = self._color(color)
315        self.subplot.set_yscale('linear')
316        self.subplot.set_xscale('linear')
317       
318        hlist = self.subplot.plot(x,y,color=c,marker='',linestyle='-',label=label)
319       
320        self.subplot.set_yscale(self.yscale)
321        self.subplot.set_xscale(self.xscale)
322
323    def _color(self,c):
324        """Return a particular colour"""
325        return self.colorlist[c%len(self.colorlist)]
326
327    def _symbol(self,s):
328        """Return a particular symbol"""
329        return self.symbollist[s%len(self.symbollist)]
330   
331    def _onEVT_FUNC_PROPERTY(self):
332        """
333             Receive the scale from myDialog and set the scale
334        """ 
335        list =[]
336        list = self.graph.returnPlottable()
337        for item in list:
338            if ( self.xscales=="x" ):
339                if self.prevXtrans == "x^(2)":
340                    item.transform_x(  self.fromX2, self.errFunc )
341                #elif self.prevXtrans == "Log(x)"
342                    #item.transform_x(  self.fromLogXY,self.errFunc )
343                print "Values of  x",item.x[0:5]
344                print "Values of view x",item.view.x[0:5]
345                self.set_xscale("linear")
346                self.graph.xaxis('\\rm{q} ', 'A^{-1}')
347               
348            if ( self.xscales=="x^(2)" ):
349                #if self.prevXtrans == "Log(x)":
350                    #item.transform_x(  self.fromLogXY, self.errFunc )
351                if  self.prevXtrans != "x^(2)":
352                    item.transform_x(  self.toX2, self.errFunc )
353                    print "Values of  x",item.x[0:5]
354                    print "Values of view x^(2)",item.view.x[0:5]
355                self.set_xscale('linear')
356                self.graph.xaxis('\\rm{q^{2}} ', 'A^{-2}')
357               
358            if (self.xscales=="Log(x)" ):
359                if self.prevXtrans == "x^(2)":
360                    item.transform_x(  self.fromX2, self.errFunc )
361                #elif self.prevXtrans == "Log(x)":
362                    #item.transform_x(  self.toLogXY, self.errFunc )
363                #self.set_xscale("linear")
364                self.set_xscale('log')
365                self.graph.xaxis('\\rm{log(q)} ', 'A^{-1}')
366               
367            if ( self.yscales=="y" ):
368                if self.prevYtrans == "y^(2)":
369                    item.transform_y(  self.toX2, self.errFunc )
370                #elif self.prevXtrans == "Log(y)"
371                    #item.transform_y(  self.fromLogXY.errFunc )   
372                self.set_yscale("linear")
373                self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
374               
375            if ( self.yscales=="Log(y)" ):
376                if self.prevYtrans == "y^(2)":
377                     item.transform_y(  self.fromX2, self.errFunc )
378                #elif self.prevYtrans != "Log(y)":
379                    #item.transform_y(  self.toLogXY, self.errFunc )
380                #self.set_yscale("linear") 
381                self.set_yscale("log") 
382                self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
383               
384            if ( self.yscales=="y^(2)" ):
385                 #if self.prevYtrans == "Log(y)":
386                       #item.transform_y(  self.fromLogXY, self.errFunc )
387                if self.prevYtrans != "y^(2)":
388                     item.transform_y(  self.toX2, self.errFunc )   
389                self.set_yscale("linear")
390                self.graph.yaxis("\\rm{Intensity^{2}} ","cm^{-2}")
391            item.set_View(item.x,item.y) 
392             
393        self.prevXtrans = self.xscales
394        self.prevYtrans = self.yscales 
395       
396        self.graph.render(self)
397        self.subplot.figure.canvas.draw_idle()
398       
399    def errFunc(self,x):
400        """
401            calculate log x
402            @param x: float value
403        """
404        if x >=0:
405            return math.sqrt(x)
406        else:
407            return 0
408   
409               
410    def onFitDisplay(self, plottable):
411        self.graph.add(plottable)
412        self.graph.render(self)
413        self.subplot.figure.canvas.draw_idle()
414     
415   
416class NoRepaintCanvas(FigureCanvasWxAgg):
417    """We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
418    the draw method is only called for the first two paint events. After that,
419    the canvas will only be redrawn when it is resized.
420    """
421    def __init__(self, *args, **kwargs):
422        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
423        self._drawn = 0
424
425    def _onPaint(self, evt):
426        """
427        Called when wxPaintEvt is generated
428        """
429        if not self._isRealized:
430            self.realize()
431        if self._drawn < 2:
432            self.draw(repaint = False)
433            self._drawn += 1
434        self.gui_repaint(drawDC=wx.PaintDC(self))
435           
Note: See TracBrowser for help on using the repository browser.