source: sasview/guitools/PlotPanel.py @ 55fd102

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 55fd102 was c87d55a, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

updated test_panel as a "test case" for refactoring

  • Property mode set to 100644
File size: 33.2 KB
RevLine 
[52b1f77]1import wx.lib.newevent
[2bf92f2]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
[52b1f77]9import fittings
[831149e]10import transform
[2bf92f2]11from canvas import FigureCanvas
[f193585]12from matplotlib.widgets import RectangleSelector
13from pylab import  gca, gcf
[bbec827]14from plottables import Theory1D
[2bf92f2]15#TODO: make the plottables interactive
[4972de2]16from binder import BindArtist
[2bf92f2]17
[05da1f89]18DEBUG = False
19
[52b1f77]20from plottables import Graph
21#(FuncFitEvent, EVT_FUNC_FIT) = wx.lib.newevent.NewEvent()
[b6972a0f]22import math,pylab,re
23
[2bf92f2]24def show_tree(obj,d=0):
25    """Handy function for displaying a tree of graph objects"""
26    print "%s%s" % ("-"*d,obj.__class__.__name__)
27    if 'get_children' in dir(obj):
28        for a in obj.get_children(): show_tree(a,d+1)
[43bf807]29     
30from unitConverter import UnitConvertion as convertUnit   
31def _convertUnit(pow,unit):
[b6972a0f]32    """
33        Displays the unit with the proper convertion
34        @param pow: the user set the power of the unit
35        @param unit: the unit of the data
36    """ 
[052a66bc]37    return unit
[b6972a0f]38    toks=re.match("^", unit)
39    if not toks==None:
40        unitValue= re.split("{",unit)
41        unitPower= re.split("}",unitValue[1])
42        power= int(unitPower[0])*pow
43        word= unitValue[0]+"{"+str(power)+"}"
44        if power==1:
45            tempUnit=re.split("\^",unitValue[0])
46            unit=tempUnit[0]
47        else:
48            unit = word
49    #print"this is unit",unit
50    return unit
[47f695c9]51def _rescale(lo,hi,step,pt=None,bal=None,scale='linear'):
52        """
53        Rescale (lo,hi) by step, returning the new (lo,hi)
54        The scaling is centered on pt, with positive values of step
55        driving lo/hi away from pt and negative values pulling them in.
56        If bal is given instead of point, it is already in [0,1] coordinates.
57   
58        This is a helper function for step-based zooming.
59        """
60        # Convert values into the correct scale for a linear transformation
61        # TODO: use proper scale transformers
[150c04a]62        loprev = lo
63        hiprev = hi
64        ptprev = pt
[47f695c9]65        if scale=='log':
[34ab06d]66            assert lo >0
[150c04a]67            if lo > 0 :
68                lo = math.log10(lo)
69            if hi > 0 :
70                hi = math.log10(hi)
[34ae302]71            if pt is not None: pt = math.log10(pt)
[150c04a]72       
[47f695c9]73        # Compute delta from axis range * %, or 1-% if persent is negative
74        if step > 0:
75            delta = float(hi-lo)*step/100
76        else:
77            delta = float(hi-lo)*step/(100-step)
78   
79        # Add scale factor proportionally to the lo and hi values, preserving the
80        # point under the mouse
81        if bal is None:
82            bal = float(pt-lo)/(hi-lo)
83        lo = lo - bal*delta
84        hi = hi + (1-bal)*delta
85   
86        # Convert transformed values back to the original scale
87        if scale=='log':
[34ab06d]88            if (lo <= -250) or (hi >= 250):
[150c04a]89                lo=loprev
90                hi=hiprev
91            else:
92                lo,hi = math.pow(10.,lo),math.pow(10.,hi)
[47f695c9]93        return (lo,hi)
[2bf92f2]94
95
96class PlotPanel(wx.Panel):
97    """
98    The PlotPanel has a Figure and a Canvas. OnSize events simply set a
99    flag, and the actually redrawing of the
100    figure is triggered by an Idle event.
101    """
102    def __init__(self, parent, id = -1, color = None,\
103        dpi = None, **kwargs):
104        wx.Panel.__init__(self, parent, id = id, **kwargs)
[52b1f77]105        self.parent = parent
[2bf92f2]106        self.figure = Figure(None, dpi)
107        #self.figure = pylab.Figure(None, dpi)
108        #self.canvas = NoRepaintCanvas(self, -1, self.figure)
109        self.canvas = FigureCanvas(self, -1, self.figure)
110        self.SetColor(color)
111        #self.Bind(wx.EVT_IDLE, self._onIdle)
112        #self.Bind(wx.EVT_SIZE, self._onSize)
113        self._resizeflag = True
114        self._SetSize()
115        self.subplot = self.figure.add_subplot(111)
116        self.figure.subplots_adjust(left=.2, bottom=.2)
117        self.yscale = 'linear'
[52b1f77]118        self.xscale = 'linear'
[2bf92f2]119        sizer = wx.BoxSizer(wx.VERTICAL)
120        sizer.Add(self.canvas,1,wx.EXPAND)
121        self.SetSizer(sizer)
[f193585]122       
[057210c]123        # Graph object to manage the plottables
124        self.graph = Graph()
[52b1f77]125        #self.Bind(EVT_FUNC_FIT, self.onFitRange)
[2bf92f2]126        self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
[df25521]127       
[52b1f77]128        #self.Bind(EVT_PROPERTY, self._onEVT_FUNC_PROPERTY)
[2bf92f2]129        # Define some constants
130        self.colorlist = ['b','g','r','c','m','y']
131        self.symbollist = ['o','x','^','v','<','>','+','s','d','D','h','H','p']
[52b1f77]132        #User scale
[b43a009]133        self.xLabel ="x"
[052a66bc]134        self.yLabel ="y"
[dfca3de]135        self.viewModel ="--"
[e2914b1]136        # keep track if the previous transformation of x and y in Property dialog
[052a66bc]137        self.prevXtrans ="x"
138        self.prevYtrans ="y"
[6ed101a]139        self.canvas.mpl_connect('scroll_event',self.onWheel)
[df25521]140        #taking care of dragging
141        self.canvas.mpl_connect('motion_notify_event',self.onMouseMotion)
142        self.canvas.mpl_connect('button_press_event',self.onLeftDown)
143        self.canvas.mpl_connect('button_release_event',self.onLeftUp)
[38fc601]144       
[df25521]145       
146        self.leftdown=False
147        self.leftup=False
148        self.mousemotion=False
149       
[47f695c9]150        self.axes = [self.subplot]
[4972de2]151       
[1c94a9f1]152        ## Fit dialog
153        self._fit_dialog = None
154       
[4972de2]155        # Interactor
156        self.connect = BindArtist(self.subplot.figure)
157        #self.selected_plottable = None
158
[ad8bcd6]159        # new data for the fit
[bbec827]160        self.fit_result = Theory1D(x=[], y=[], dy=None)
161        #self.fit_result = Data1D(x=[], y=[],dx=None, dy=None)
162        self.fit_result.name = "Fit"
[b43a009]163        # For fit Dialog initial display
[ddff053]164        self.xmin=0.0
165        self.xmax=0.0
166        self.xminView=0.0
167        self.xmaxView=0.0
[1c94a9f1]168        self._scale_xlo = None
169        self._scale_xhi = None
170        self._scale_ylo = None
171        self._scale_yhi = None
[b43a009]172        self.Avalue=None
173        self.Bvalue=None
174        self.ErrAvalue=None
175        self.ErrBvalue=None
176        self.Chivalue=None
[ad8bcd6]177       
178        # Dragging info
[df25521]179        self.begDrag=False
180        self.xInit=None
181        self.yInit=None
182        self.xFinal=None
183        self.yFinal=None
[ad8bcd6]184       
185        # Default locations
186        self._default_save_location = os.getcwd()       
187       
188       
[df25521]189    def onLeftDown(self,event): 
190        """ left button down and ready to drag"""
[8800ff6]191        # Check that the LEFT button was pressed
192        if event.button == 1:
193            self.leftdown=True
194            ax = event.inaxes
195            if ax != None:
196                self.xInit,self.yInit=event.xdata,event.ydata
[ac9c465]197           
198           
[df25521]199    def onLeftUp(self,event): 
200        """ Dragging is done """
[8800ff6]201        # Check that the LEFT button was released
202        if event.button == 1:
203            self.leftdown=False
204            self.mousemotion=False 
205            self.leftup=True
[ac9c465]206     
[df25521]207    def onMouseMotion(self,event): 
[ac9c465]208        """
209            check if the left button is press and the mouse in moving.
210            computer delta for x and y coordinates and then calls draghelper
211            to perform the drag
212        """
[38fc601]213        self.mousemotion=True 
214        if self.leftdown==True and self.mousemotion==True:
[df25521]215           
[38fc601]216            ax = event.inaxes
[ac9c465]217            if ax !=None:#the dragging is perform inside the figure
[4101eea]218                self.xFinal,self.yFinal=event.xdata,event.ydata
[38fc601]219               
[ad8bcd6]220                # Check whether this is the first point
221                if self.xInit==None:
222                    self.xInit = self.xFinal
223                    self.yInit = self.yFinal
224                   
[38fc601]225                xdelta = self.xFinal -self.xInit
226                ydelta = self.yFinal -self.yInit
227               
228                if self.xscale=='log':
229                    xdelta = math.log10(self.xFinal) -math.log10(self.xInit)
230                if self.yscale=='log':
231                    ydelta = math.log10(self.yFinal) -math.log10(self.yInit)
232                self.dragHelper(xdelta,ydelta)
[ac9c465]233             
234            else:# no dragging is perform elsewhere
[38fc601]235                self.dragHelper(0,0)
[ac9c465]236               
[1c94a9f1]237    def _offset_graph(self):
238        """
239             Zoom and offset the graph to the last known
240             settings
241        """
242
243        for ax in self.axes:
244            if self._scale_xhi is not None and self._scale_xlo is not None:
245                ax.set_xlim(self._scale_xlo, self._scale_xhi)
246            if self._scale_yhi is not None and self._scale_ylo is not None:
247                ax.set_ylim(self._scale_ylo, self._scale_yhi)
248           
249           
[df25521]250    def dragHelper(self,xdelta,ydelta):
251        """ dragging occurs here"""
252       
253        # Event occurred inside a plotting area
254        for ax in self.axes:
255            lo,hi= ax.get_xlim()
256            #print "x lo %f and x hi %f"%(lo,hi)
257            newlo,newhi= lo- xdelta, hi- xdelta
258            if self.xscale=='log':
[06290c8]259                if lo > 0:
260                    newlo= math.log10(lo)-xdelta
261                if hi > 0:
262                    newhi= math.log10(hi)-xdelta
[df25521]263            if self.xscale=='log':
[1c94a9f1]264                self._scale_xlo = math.pow(10,newlo)
265                self._scale_xhi = math.pow(10,newhi)
[df25521]266                ax.set_xlim(math.pow(10,newlo),math.pow(10,newhi))
267            else:
[1c94a9f1]268                self._scale_xlo = newlo
269                self._scale_xhi = newhi
[df25521]270                ax.set_xlim(newlo,newhi)
[38fc601]271            #print "new lo %f and new hi %f"%(newlo,newhi)
[df25521]272           
273            lo,hi= ax.get_ylim()
[38fc601]274            #print "y lo %f and y hi %f"%(lo,hi)
[df25521]275            newlo,newhi= lo- ydelta, hi- ydelta
276            if self.yscale=='log':
[06290c8]277                if lo > 0:
278                    newlo= math.log10(lo)-ydelta
279                if hi > 0:
280                    newhi= math.log10(hi)-ydelta
[38fc601]281                #print "new lo %f and new hi %f"%(newlo,newhi)
[df25521]282            if  self.yscale=='log':
[1c94a9f1]283                self._scale_ylo = math.pow(10,newlo)
284                self._scale_yhi = math.pow(10,newhi)
[df25521]285                ax.set_ylim(math.pow(10,newlo),math.pow(10,newhi))
286            else:
[1c94a9f1]287                self._scale_ylo = newlo
288                self._scale_yhi = newhi
[df25521]289                ax.set_ylim(newlo,newhi)
290        self.canvas.draw_idle()
291       
[052a66bc]292       
[df25521]293   
[b43a009]294    def resetFitView(self):
[edce46e]295        """
296             For fit Dialog initial display
297        """
[b43a009]298        self.xmin=0.0
299        self.xmax=0.0
300        self.xminView=0.0
301        self.xmaxView=0.0
[1c94a9f1]302        self._scale_xlo = None
303        self._scale_xhi = None
304        self._scale_ylo = None
305        self._scale_yhi = None
[b43a009]306        self.Avalue=None
307        self.Bvalue=None
308        self.ErrAvalue=None
309        self.ErrBvalue=None
310        self.Chivalue=None
[df25521]311   
[6ed101a]312    def onWheel(self, event):
313        """
[05da1f89]314            Process mouse wheel as zoom events
315            @param event: Wheel event
[6ed101a]316        """
317        ax = event.inaxes
318        step = event.step
319
320        if ax != None:
321            # Event occurred inside a plotting area
322            lo,hi = ax.get_xlim()
[05da1f89]323            lo,hi = _rescale(lo,hi,step,pt=event.xdata,scale=ax.get_xscale())
324            if not self.xscale=='log' or lo>0:
[1c94a9f1]325                self._scale_xlo = lo
326                self._scale_xhi = hi
[05da1f89]327                ax.set_xlim((lo,hi))
[6ed101a]328
329            lo,hi = ax.get_ylim()
[05da1f89]330            lo,hi = _rescale(lo,hi,step,pt=event.ydata,scale=ax.get_yscale())
331            if not self.yscale=='log' or lo>0:
[1c94a9f1]332                self._scale_ylo = lo
333                self._scale_yhi = hi
[05da1f89]334                ax.set_ylim((lo,hi))
[6ed101a]335        else:
[47f695c9]336             # Check if zoom happens in the axes
[6ed101a]337            xdata,ydata = None,None
338            x,y = event.x,event.y
[34ae302]339           
[6ed101a]340            for ax in self.axes:
341                insidex,_ = ax.xaxis.contains(event)
342                if insidex:
343                    xdata,_ = ax.transAxes.inverse_xy_tup((x,y))
344                insidey,_ = ax.yaxis.contains(event)
345                if insidey:
346                    _,ydata = ax.transAxes.inverse_xy_tup((x,y))
347            if xdata is not None:
348                lo,hi = ax.get_xlim()
[34ae302]349                lo,hi = _rescale(lo,hi,step,bal=xdata,scale=ax.get_xscale())
[05da1f89]350                if not self.xscale=='log' or lo>0:
[1c94a9f1]351                    self._scale_xlo = lo
352                    self._scale_xhi = hi
[05da1f89]353                    ax.set_xlim((lo,hi))
[6ed101a]354            if ydata is not None:
355                lo,hi = ax.get_ylim()
[34ae302]356                lo,hi = _rescale(lo,hi,step,bal=ydata,scale=ax.get_yscale())
[05da1f89]357                if not self.yscale=='log' or lo>0:
[1c94a9f1]358                    self._scale_ylo = lo
359                    self._scale_yhi = hi
[05da1f89]360                    ax.set_ylim((lo,hi))
[150c04a]361               
[6ed101a]362        self.canvas.draw_idle()
363
364
[f52bea1]365    def returnTrans(self):
[edce46e]366        """
367            Return values and labels used by Fit Dialog
368        """
[b43a009]369        return self.xLabel,self.yLabel, self.Avalue, self.Bvalue,\
[edce46e]370                self.ErrAvalue,self.ErrBvalue,self.Chivalue
[dfca3de]371   
[e2914b1]372    def setTrans(self,xtrans,ytrans): 
373        """
374            @param xtrans: set x transformation on Property dialog
375            @param ytrans: set y transformation on Property dialog
376        """
377        self.prevXtrans =xtrans
378        self.prevYtrans =ytrans
[dfca3de]379   
[52b1f77]380    def onFitting(self, event): 
[e2914b1]381        """
382            when clicking on linear Fit on context menu , display Fitting Dialog
383        """
[c87d55a]384        list = {}
385        plotlist = self.graph.returnPlottable()
386        if self.graph.selected_plottable is not None:
387            for item in plotlist:
388                if item.name==self.graph.selected_plottable:
389                    list[item] = plotlist[item]
390        else:
391            list = plotlist
[52b1f77]392        from fitDialog import LinearFit
[7a03e65]393       
[52b1f77]394        if len(list.keys())>0:
395            first_item = list.keys()[0]
[05da1f89]396            dlg = LinearFit( None, first_item, self.onFitDisplay,self.returnTrans, -1, 'Linear Fit')
[b43a009]397           
[ddff053]398            if (self.xmin !=0.0 )and ( self.xmax !=0.0)\
399                and(self.xminView !=0.0 )and ( self.xmaxView !=0.0):
400                dlg.setFitRange(self.xminView,self.xmaxView,self.xmin,self.xmax)
[52b1f77]401            dlg.ShowModal() 
402
[052a66bc]403    def linear_plottable_fit(self, plot): 
404        """
405            when clicking on linear Fit on context menu , display Fitting Dialog
[1c94a9f1]406            @param plot: PlotPanel owning the graph
[052a66bc]407        """
408        from fitDialog import LinearFit
409       
[1c94a9f1]410        if self._fit_dialog is not None:
411            return
412       
413        self._fit_dialog = LinearFit( None, plot, self.onFitDisplay,self.returnTrans, -1, 'Linear Fit')
414       
415        # Set the zoom area
416        if self._scale_xhi is not None and self._scale_xlo is not None:
417            self._fit_dialog.set_fit_region(self._scale_xlo, self._scale_xhi)
418       
419        # Register the close event
420        self._fit_dialog.register_close(self._linear_fit_close)
421       
422        # Show a non-model dialog
423        self._fit_dialog.Show() 
424
425    def _linear_fit_close(self):
426        """
427            A fit dialog was closed
428        """
429        self._fit_dialog = None
430       
[052a66bc]431
[52b1f77]432    def _onProperties(self, event):
[e2914b1]433        """
434            when clicking on Properties on context menu ,The Property dialog is displayed
435            The user selects a transformation for x or y value and a new plot is displayed
436        """
[1c94a9f1]437        if self._fit_dialog is not None:
438            self._fit_dialog.Destroy()
439            self._fit_dialog = None
440           
[34ae302]441        list =[]
442        list = self.graph.returnPlottable()
443        if len(list.keys())>0:
444            first_item = list.keys()[0]
445            if first_item.x !=[]:
446                from PropertyDialog import Properties
447                dial = Properties(self, -1, 'Properties')
448                dial.setValues( self.prevXtrans, self.prevYtrans,self.viewModel )
449                if dial.ShowModal() == wx.ID_OK:
[b43a009]450                    self.xLabel, self.yLabel,self.viewModel = dial.getValues()
[34ae302]451                    if self.viewModel =="Guinier lny vs x^(2)":
[b43a009]452                        self.xLabel="x^(2)"
453                        self.yLabel="ln(y)"
[34ae302]454                        self.viewModel = "--"
[b43a009]455                        dial.setValues( self.xLabel, self.yLabel,self.viewModel )
[34ae302]456                    self._onEVT_FUNC_PROPERTY()
457                dial.Destroy()
458           
[831149e]459 
[2bf92f2]460    def set_yscale(self, scale='linear'):
[e2914b1]461        """
462            Set the scale on Y-axis
463            @param scale: the scale of y-axis
464        """
[2bf92f2]465        self.subplot.set_yscale(scale)
466        self.yscale = scale
467       
468    def get_yscale(self):
[e2914b1]469        """
470             @return: Y-axis scale
471        """
[2bf92f2]472        return self.yscale
[52b1f77]473   
474    def set_xscale(self, scale='linear'):
[e2914b1]475        """
476            Set the scale on x-axis
477            @param scale: the scale of x-axis
478        """
[52b1f77]479        self.subplot.set_xscale(scale)
480        self.xscale = scale
[e2914b1]481       
[52b1f77]482    def get_xscale(self):
[e2914b1]483        """
484             @return: x-axis scale
485        """
[52b1f77]486        return self.xscale
[2bf92f2]487
488    def SetColor(self, rgbtuple):
489        """Set figure and canvas colours to be the same"""
490        if not rgbtuple:
491            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
492        col = [c/255.0 for c in rgbtuple]
493        self.figure.set_facecolor(col)
494        self.figure.set_edgecolor(col)
495        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))
496
497    def _onSize(self, event):
498        self._resizeflag = True
499
500    def _onIdle(self, evt):
501        if self._resizeflag:
502            self._resizeflag = False
503            self._SetSize()
504            self.draw()
505
506    def _SetSize(self, pixels = None):
507        """
508        This method can be called to force the Plot to be a desired size, which defaults to
509        the ClientSize of the panel
510        """
511        if not pixels:
512            pixels = self.GetClientSize()
513        self.canvas.SetSize(pixels)
514        self.figure.set_size_inches(pixels[0]/self.figure.get_dpi(),
515        pixels[1]/self.figure.get_dpi())
516
517    def draw(self):
518        """Where the actual drawing happens"""
519        self.figure.canvas.draw_idle()
520       
[f193585]521       
[2bf92f2]522    def onSaveImage(self, evt):
523        #figure.savefig
[7a03e65]524        #print "Save image not implemented"
[2bf92f2]525        path = None
[ad8bcd6]526        dlg = wx.FileDialog(self, "Choose a file", self._default_save_location, "", "*.png", wx.SAVE)
[2bf92f2]527        if dlg.ShowModal() == wx.ID_OK:
528            path = dlg.GetPath()
[ad8bcd6]529            self._default_save_location = os.path.dirname(path)
[2bf92f2]530        dlg.Destroy()
531        if not path == None:
532            self.subplot.figure.savefig(path,dpi=300, facecolor='w', edgecolor='w',
533                                        orentation='portrait', papertype=None, format='png')
534       
535    def onContextMenu(self, event):
536        """
537            Default context menu for a plot panel
538        """
539        # Slicer plot popup menu
[052a66bc]540        id = wx.NewId()
[2bf92f2]541        slicerpop = wx.Menu()
[052a66bc]542        slicerpop.Append(id,'&Save image', 'Save image as PNG')
543        wx.EVT_MENU(self, id, self.onSaveImage)
[bceddd6]544       
[105614c]545        #id = wx.NewId()
546        #slicerpop.Append(id, '&Load 1D data file')
547        #wx.EVT_MENU(self, id, self._onLoad1DData)
[bceddd6]548       
[052a66bc]549        id = wx.NewId()
[52b1f77]550        slicerpop.AppendSeparator()
[052a66bc]551        slicerpop.Append(id, '&Properties')
552        wx.EVT_MENU(self, id, self._onProperties)
[52b1f77]553       
[052a66bc]554        id = wx.NewId()
[52b1f77]555        slicerpop.AppendSeparator()
[052a66bc]556        slicerpop.Append(id, '&Linear Fit')
557        wx.EVT_MENU(self, id, self.onFitting)
[34ae302]558       
[052a66bc]559        id = wx.NewId()
[34ae302]560        slicerpop.AppendSeparator()
[052a66bc]561        slicerpop.Append(id, '&Reset Graph')
562        wx.EVT_MENU(self, id, self.onResetGraph)
[52b1f77]563       
[2bf92f2]564        pos = event.GetPosition()
565        pos = self.ScreenToClient(pos)
566        self.PopupMenu(slicerpop, pos)
567   
568    ## The following is plottable functionality
569
570
571    def properties(self,prop):
572        """Set some properties of the graph.
573       
574        The set of properties is not yet determined.
575        """
576        # The particulars of how they are stored and manipulated (e.g., do
577        # we want an inventory internally) is not settled.  I've used a
578        # property dictionary for now.
579        #
580        # How these properties interact with a user defined style file is
581        # even less clear.
582
583        # Properties defined by plot
584        self.subplot.set_xlabel(r"$%s$" % prop["xlabel"])
585        self.subplot.set_ylabel(r"$%s$" % prop["ylabel"])
586        self.subplot.set_title(prop["title"])
587
588        # Properties defined by user
589        #self.axes.grid(True)
590
591    def clear(self):
592        """Reset the plot"""
593       
594        # TODO: Redraw is brutal.  Render to a backing store and swap in
595        # TODO: rather than redrawing on the fly.
596        self.subplot.clear()
597        self.subplot.hold(True)
[6ed101a]598   
[2bf92f2]599    def render(self):
600        """Commit the plot after all objects are drawn"""
601        # TODO: this is when the backing store should be swapped in.
602        from matplotlib.font_manager import FontProperties
[08b9e586]603        self.subplot.legend(prop=FontProperties(size=10), handletextsep=.05)
[2bf92f2]604
605    def xaxis(self,label,units):
606        """xaxis label and units.
607       
608        Axis labels know about units.
609       
610        We need to do this so that we can detect when axes are not
611        commesurate.  Currently this is ignored other than for formatting
612        purposes.
613        """
614        if units != "": label = label + " (" + units + ")"
615        self.subplot.set_xlabel(label)
616        pass
617   
618    def yaxis(self,label,units):
619        """yaxis label and units."""
620        if units != "": label = label + " (" + units + ")"
621        self.subplot.set_ylabel(label)
622        pass
623
624    def _connect_to_xlim(self,callback):
625        """Bind the xlim change notification to the callback"""
626        def process_xlim(axes):
627            lo,hi = subplot.get_xlim()
628            callback(lo,hi)
629        self.subplot.callbacks.connect('xlim_changed',process_xlim)
630   
631    #def connect(self,trigger,callback):
632    #    print "PlotPanel.connect???"
633    #    if trigger == 'xlim': self._connect_to_xlim(callback)
634
[4972de2]635    def interactive_points(self,x,y,dx=None,dy=None,name='', color=0,symbol=0,label=None):
636        """Draw markers with error bars"""
637        self.subplot.set_yscale('linear')
638        self.subplot.set_xscale('linear')
639       
640        from plottable_interactor import PointInteractor
641        p = PointInteractor(self, self.subplot, zorder=3, id=name)
[a17ffdf]642        p.points(x, y, dx=dx, dy=dy, color=color, symbol=symbol, label=label)
[4972de2]643       
644        self.subplot.set_yscale(self.yscale)
645        self.subplot.set_xscale(self.xscale)
646
647    def interactive_curve(self,x,y,dy=None,name='',color=0,symbol=0,label=None):
648        """Draw markers with error bars"""
649        self.subplot.set_yscale('linear')
650        self.subplot.set_xscale('linear')
651       
652        from plottable_interactor import PointInteractor
653        p = PointInteractor(self, self.subplot, zorder=4, id=name)
[a17ffdf]654        p.curve(x, y, dy=dy, color=color, symbol=symbol, label=label)
[4972de2]655       
656        self.subplot.set_yscale(self.yscale)
657        self.subplot.set_xscale(self.xscale)
658
659    def plottable_selected(self, id):
660        """
661            Called to register a plottable as selected
662        """
663        #TODO: check that it really is in the list of plottables
664        self.graph.selected_plottable = id
665
[2bf92f2]666    def points(self,x,y,dx=None,dy=None,color=0,symbol=0,label=None):
667        """Draw markers with error bars"""
668        self.subplot.set_yscale('linear')
[52b1f77]669        self.subplot.set_xscale('linear')
[052a66bc]670       
[2bf92f2]671        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
672        if dx != None and type(dx) == type(()):
673            dx = nx.vstack((x-dx[0],dx[1]-x)).transpose()
674        if dy != None and type(dy) == type(()):
675            dy = nx.vstack((y-dy[0],dy[1]-y)).transpose()
676
677        if dx==None and dy==None:
678            h = self.subplot.plot(x,y,color=self._color(color),
679                                   marker=self._symbol(symbol),linestyle='',label=label)
680        else:
[052a66bc]681            col = self._color(color)
[2bf92f2]682            self.subplot.errorbar(x, y, yerr=dy, xerr=None,
[052a66bc]683             ecolor=col, capsize=2,linestyle='', barsabove=False,
684             mec=col, mfc=col,
[2bf92f2]685             marker=self._symbol(symbol),
686             lolims=False, uplims=False,
[52b1f77]687             xlolims=False, xuplims=False,label=label)
[2bf92f2]688           
689        self.subplot.set_yscale(self.yscale)
[52b1f77]690        self.subplot.set_xscale(self.xscale)
[2bf92f2]691
692    def curve(self,x,y,dy=None,color=0,symbol=0,label=None):
693        """Draw a line on a graph, possibly with confidence intervals."""
694        c = self._color(color)
695        self.subplot.set_yscale('linear')
[52b1f77]696        self.subplot.set_xscale('linear')
[2bf92f2]697       
698        hlist = self.subplot.plot(x,y,color=c,marker='',linestyle='-',label=label)
699       
700        self.subplot.set_yscale(self.yscale)
[52b1f77]701        self.subplot.set_xscale(self.xscale)
[2bf92f2]702
703    def _color(self,c):
704        """Return a particular colour"""
705        return self.colorlist[c%len(self.colorlist)]
706
707    def _symbol(self,s):
708        """Return a particular symbol"""
709        return self.symbollist[s%len(self.symbollist)]
[52b1f77]710   
[1c94a9f1]711    def _replot(self, remove_fit=False):
[052a66bc]712        """
713            Rescale the plottables according to the latest
714            user selection and update the plot
[1c94a9f1]715           
716            @param remove_fit: Fit line will be removed if True
[052a66bc]717        """
718        self.graph.reset_scale()
[1c94a9f1]719        self._onEVT_FUNC_PROPERTY(remove_fit=remove_fit)
[052a66bc]720       
721        #TODO: Why do we have to have the following line?
722        self.fit_result.reset_view()
723       
724        self.graph.render(self)
725        self.subplot.figure.canvas.draw_idle()
726   
727    def _onEVT_FUNC_PROPERTY(self, remove_fit=True):
[52b1f77]728        """
[8cebf9b]729             Receive the x and y transformation from myDialog,Transforms x and y in View
730              and set the scale   
[52b1f77]731        """ 
[1c94a9f1]732        # The logic should be in the right order
733        # Delete first, and then get the whole list...
734        if remove_fit:
735            self.graph.delete(self.fit_result)
736           
[52b1f77]737        list =[]
738        list = self.graph.returnPlottable()
[052a66bc]739       
[1c94a9f1]740        # Changing the scale might be incompatible with
741        # currently displayed data (for instance, going
742        # from ln to log when all plotted values have
743        # negative natural logs).
744        # Go linear and only change the scale at the end.
745        self.set_xscale("linear")
746        self.set_yscale("linear")
747        _xscale = 'linear'
748        _yscale = 'linear'
749       
[f193585]750       
[52b1f77]751        for item in list:
[b43a009]752            item.setLabel(self.xLabel,self.yLabel)
753            if ( self.xLabel=="x" ):
754                item.transformX(transform.toX,transform.errToX)
[f52bea1]755                name, units = item.get_xaxis()
[d6d411d]756                self.graph._xaxis_transformed("%s" % name,  "%s" % units)
[b6972a0f]757               
[52b1f77]758               
[b43a009]759            if ( self.xLabel=="x^(2)" ):
760                item.transformX(transform.toX2,transform.errToX2)
[f52bea1]761                name, units = item.get_xaxis()
[b6972a0f]762                units=convertUnit(2,units) 
[d6d411d]763                self.graph._xaxis_transformed("%s^{2}" % name,  "%s" % units)
[b6972a0f]764               
[52b1f77]765               
[b43a009]766            if (self.xLabel=="log10(x)" ):
[ad8bcd6]767                item.transformX(transform.toX_pos,transform.errToX_pos)
[1c94a9f1]768                _xscale = 'log'
[f193585]769                name, units = item.get_xaxis() 
[d6d411d]770                self.graph._xaxis_transformed("\log_{10}\ \  (%s)" % name,  "%s" % units)
[b6972a0f]771               
[52b1f77]772               
[b43a009]773            if ( self.yLabel=="ln(y)" ):
774                item.transformY(transform.toLogX,transform.errToLogX)
[831149e]775                name, units = item.get_yaxis()
[d6d411d]776                self.graph._yaxis_transformed("\log\ \ %s" % name,  "%s" % units)
[b6972a0f]777               
[831149e]778               
[b43a009]779            if ( self.yLabel=="y" ):
780                item.transformY(transform.toX,transform.errToX)
[f52bea1]781                name, units = item.get_yaxis()
[d6d411d]782                self.graph._yaxis_transformed("%s" % name,  "%s" % units)
[b6972a0f]783               
[52b1f77]784               
[b43a009]785            if ( self.yLabel=="log10(y)" ): 
[ad8bcd6]786                item.transformY(transform.toX_pos,transform.errToX_pos)
[1c94a9f1]787                _yscale = 'log' 
[f52bea1]788                name, units = item.get_yaxis()
[d6d411d]789                self.graph._yaxis_transformed("\log_{10}\ \ (%s)" % name,  "%s" % units)
[b6972a0f]790               
[52b1f77]791               
[b43a009]792            if ( self.yLabel=="y^(2)" ):
793                item.transformY( transform.toX2,transform.errToX2 )   
[f52bea1]794                name, units = item.get_yaxis()
[b6972a0f]795                units=convertUnit(2,units) 
[d6d411d]796                self.graph._yaxis_transformed("%s^{2}" % name,  "%s" % units)
[b6972a0f]797               
[35891ce]798               
[b43a009]799            if ( self.yLabel =="1/y"):
800                item.transformY(transform.toOneOverX,transform.errOneOverX )
[3d3a0e5]801                name, units = item.get_yaxis()
[b6972a0f]802                units=convertUnit(-1,units)
[d6d411d]803                self.graph._yaxis_transformed("1/%s" % name,  "%s" % units)
[35891ce]804               
[b43a009]805            if ( self.yLabel =="1/sqrt(y)" ):
806                item.transformY(transform.toOneOverSqrtX,transform.errOneOverSqrtX )
[3d3a0e5]807                name, units = item.get_yaxis()
[43bf807]808                units=convertUnit(-0.5,units)
[d6d411d]809                self.graph._yaxis_transformed("1/\sqrt{%s}" %name,  "%s" % units)
[7a03e65]810               
[b43a009]811            if ( self.yLabel =="ln(y*x)"):
812                item.transformY( transform.toLogXY,transform.errToLogXY)
[3d3a0e5]813                yname, yunits = item.get_yaxis()
814                xname, xunits = item.get_xaxis()
[d6d411d]815                self.graph._yaxis_transformed("\log\ (%s \ \ %s)" % (yname,xname),  "%s%s" % (yunits,xunits))
[b6972a0f]816               
[831149e]817               
[b43a009]818            if ( self.yLabel =="ln(y*x^(2))"):
819                item.transformY( transform.toLogYX2,transform.errToLogYX2)
[3d3a0e5]820                yname, yunits = item.get_yaxis()
[47f695c9]821                xname, xunits = item.get_xaxis() 
[b6972a0f]822                xunits = convertUnit(2,xunits) 
[d6d411d]823                self.graph._yaxis_transformed("\log (%s \ \ %s^{2})" % (yname,xname),  "%s%s" % (yunits,xunits))
[b6972a0f]824               
[831149e]825           
[b43a009]826            if ( self.yLabel =="ln(y*x^(4))"):
827                item.transformY(transform.toLogYX4,transform.errToLogYX4)
[831149e]828                yname, yunits = item.get_yaxis()
829                xname, xunits = item.get_xaxis()
[b6972a0f]830                xunits = convertUnit(4,xunits) 
[d6d411d]831                self.graph._yaxis_transformed("\log (%s \ \ %s^{4})" % (yname,xname),  "%s%s" % (yunits,xunits))
[b6972a0f]832               
[dfca3de]833            if ( self.viewModel == "Guinier lny vs x^(2)"):
[b43a009]834               
835                item.transformX(transform.toX2,transform.errToX2)
[dfca3de]836                name, units = item.get_xaxis()
[b6972a0f]837                units = convertUnit(2,units) 
[d6d411d]838                self.graph._xaxis_transformed("%s^{2}" % name,  "%s" % units)
[b6972a0f]839               
[b43a009]840               
841                item.transformY(transform.toLogX,transform.errToLogX )
[dfca3de]842                name, units = item.get_yaxis()
[d6d411d]843                self.graph._yaxis_transformed("\log\ \ %s" % name,  "%s" % units)
[b6972a0f]844               
[b43a009]845               
[bbec827]846            item.transformView()
[b43a009]847           
[71c64d6]848        self.resetFitView()   
[b43a009]849        self.prevXtrans = self.xLabel
850        self.prevYtrans = self.yLabel 
[52b1f77]851        self.graph.render(self)
[1c94a9f1]852       
853        self.set_xscale(_xscale)
854        self.set_yscale(_yscale)
855         
[52b1f77]856        self.subplot.figure.canvas.draw_idle()
[46693050]857       
[b6972a0f]858       
859   
[b43a009]860    def onFitDisplay(self, tempx,tempy,xminView,xmaxView,xmin,xmax,func):
[e2914b1]861        """
862            Add a new plottable into the graph .In this case this plottable will be used
863            to fit some data
[edce46e]864            @param tempx: The x data of fit line
865            @param tempy: The y data of fit line
866            @param xminView: the lower bound of fitting range
867            @param xminView: the upper bound of  fitting range
868            @param xmin: the lowest value of data to fit to the line
869            @param xmax: the highest value of data to fit to the line
[e2914b1]870        """
[edce46e]871        # Saving value to redisplay in Fit Dialog when it is opened again
[b43a009]872        self.Avalue,self.Bvalue,self.ErrAvalue,self.ErrBvalue,self.Chivalue=func
[edce46e]873        self.xminView=xminView
874        self.xmaxView=xmaxView
875        self.xmin= xmin
876        self.xmax= xmax
877        #In case need to change the range of data plotted
[34ae302]878        list =[]
879        list = self.graph.returnPlottable()
880        for item in list:
[150c04a]881            #item.onFitRange(xminView,xmaxView)
882            item.onFitRange(None,None)
[edce46e]883       
[bbec827]884        # Create new data plottable with result
885        self.fit_result.x =[] 
886        self.fit_result.y =[]
887        self.fit_result.x =tempx 
888        self.fit_result.y =tempy     
889        self.fit_result.dx=None
890        self.fit_result.dy=None
891        #Load the view with the new values
[edce46e]892        self.fit_result.reset_view()
893        # Add the new plottable to the graph
[bbec827]894        self.graph.add(self.fit_result) 
895        self.graph.render(self)
[1c94a9f1]896        self._offset_graph()
[52b1f77]897        self.subplot.figure.canvas.draw_idle()
[edce46e]898       
[f193585]899   
[34ae302]900    def onResetGraph(self,event):
[edce46e]901        """
902            Reset the graph by plotting the full range of data
903        """
[34ae302]904        list =[]
905        list = self.graph.returnPlottable()
906        for item in list:
907            item.onReset()
908        self.graph.render(self)
909        self.subplot.figure.canvas.draw_idle()
[f193585]910       
[2bf92f2]911class NoRepaintCanvas(FigureCanvasWxAgg):
912    """We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
913    the draw method is only called for the first two paint events. After that,
914    the canvas will only be redrawn when it is resized.
915    """
916    def __init__(self, *args, **kwargs):
917        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
918        self._drawn = 0
919
920    def _onPaint(self, evt):
921        """
922        Called when wxPaintEvt is generated
923        """
924        if not self._isRealized:
925            self.realize()
926        if self._drawn < 2:
927            self.draw(repaint = False)
928            self._drawn += 1
929        self.gui_repaint(drawDC=wx.PaintDC(self))
930           
Note: See TracBrowser for help on using the repository browser.