source: sasview/guitools/PlotPanel.py @ 7a2feab

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 7a2feab was 8800ff6, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

Added button ID check

  • Property mode set to 100644
File size: 32.8 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        """
[52b1f77]384        list =[]
385        list = self.graph.returnPlottable()
386        from fitDialog import LinearFit
[7a03e65]387       
[52b1f77]388        if len(list.keys())>0:
389            first_item = list.keys()[0]
[05da1f89]390            dlg = LinearFit( None, first_item, self.onFitDisplay,self.returnTrans, -1, 'Linear Fit')
[b43a009]391           
[ddff053]392            if (self.xmin !=0.0 )and ( self.xmax !=0.0)\
393                and(self.xminView !=0.0 )and ( self.xmaxView !=0.0):
394                dlg.setFitRange(self.xminView,self.xmaxView,self.xmin,self.xmax)
[52b1f77]395            dlg.ShowModal() 
396
[052a66bc]397    def linear_plottable_fit(self, plot): 
398        """
399            when clicking on linear Fit on context menu , display Fitting Dialog
[1c94a9f1]400            @param plot: PlotPanel owning the graph
[052a66bc]401        """
402        from fitDialog import LinearFit
403       
[1c94a9f1]404        if self._fit_dialog is not None:
405            return
406       
407        self._fit_dialog = LinearFit( None, plot, self.onFitDisplay,self.returnTrans, -1, 'Linear Fit')
408       
409        # Set the zoom area
410        if self._scale_xhi is not None and self._scale_xlo is not None:
411            self._fit_dialog.set_fit_region(self._scale_xlo, self._scale_xhi)
412       
413        # Register the close event
414        self._fit_dialog.register_close(self._linear_fit_close)
415       
416        # Show a non-model dialog
417        self._fit_dialog.Show() 
418
419    def _linear_fit_close(self):
420        """
421            A fit dialog was closed
422        """
423        self._fit_dialog = None
424       
[052a66bc]425
[52b1f77]426    def _onProperties(self, event):
[e2914b1]427        """
428            when clicking on Properties on context menu ,The Property dialog is displayed
429            The user selects a transformation for x or y value and a new plot is displayed
430        """
[1c94a9f1]431        if self._fit_dialog is not None:
432            self._fit_dialog.Destroy()
433            self._fit_dialog = None
434           
[34ae302]435        list =[]
436        list = self.graph.returnPlottable()
437        if len(list.keys())>0:
438            first_item = list.keys()[0]
439            if first_item.x !=[]:
440                from PropertyDialog import Properties
441                dial = Properties(self, -1, 'Properties')
442                dial.setValues( self.prevXtrans, self.prevYtrans,self.viewModel )
443                if dial.ShowModal() == wx.ID_OK:
[b43a009]444                    self.xLabel, self.yLabel,self.viewModel = dial.getValues()
[34ae302]445                    if self.viewModel =="Guinier lny vs x^(2)":
[b43a009]446                        self.xLabel="x^(2)"
447                        self.yLabel="ln(y)"
[34ae302]448                        self.viewModel = "--"
[b43a009]449                        dial.setValues( self.xLabel, self.yLabel,self.viewModel )
[34ae302]450                    self._onEVT_FUNC_PROPERTY()
451                dial.Destroy()
452           
[831149e]453 
[2bf92f2]454    def set_yscale(self, scale='linear'):
[e2914b1]455        """
456            Set the scale on Y-axis
457            @param scale: the scale of y-axis
458        """
[2bf92f2]459        self.subplot.set_yscale(scale)
460        self.yscale = scale
461       
462    def get_yscale(self):
[e2914b1]463        """
464             @return: Y-axis scale
465        """
[2bf92f2]466        return self.yscale
[52b1f77]467   
468    def set_xscale(self, scale='linear'):
[e2914b1]469        """
470            Set the scale on x-axis
471            @param scale: the scale of x-axis
472        """
[52b1f77]473        self.subplot.set_xscale(scale)
474        self.xscale = scale
[e2914b1]475       
[52b1f77]476    def get_xscale(self):
[e2914b1]477        """
478             @return: x-axis scale
479        """
[52b1f77]480        return self.xscale
[2bf92f2]481
482    def SetColor(self, rgbtuple):
483        """Set figure and canvas colours to be the same"""
484        if not rgbtuple:
485            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
486        col = [c/255.0 for c in rgbtuple]
487        self.figure.set_facecolor(col)
488        self.figure.set_edgecolor(col)
489        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))
490
491    def _onSize(self, event):
492        self._resizeflag = True
493
494    def _onIdle(self, evt):
495        if self._resizeflag:
496            self._resizeflag = False
497            self._SetSize()
498            self.draw()
499
500    def _SetSize(self, pixels = None):
501        """
502        This method can be called to force the Plot to be a desired size, which defaults to
503        the ClientSize of the panel
504        """
505        if not pixels:
506            pixels = self.GetClientSize()
507        self.canvas.SetSize(pixels)
508        self.figure.set_size_inches(pixels[0]/self.figure.get_dpi(),
509        pixels[1]/self.figure.get_dpi())
510
511    def draw(self):
512        """Where the actual drawing happens"""
513        self.figure.canvas.draw_idle()
514       
[f193585]515       
[2bf92f2]516    def onSaveImage(self, evt):
517        #figure.savefig
[7a03e65]518        #print "Save image not implemented"
[2bf92f2]519        path = None
[ad8bcd6]520        dlg = wx.FileDialog(self, "Choose a file", self._default_save_location, "", "*.png", wx.SAVE)
[2bf92f2]521        if dlg.ShowModal() == wx.ID_OK:
522            path = dlg.GetPath()
[ad8bcd6]523            self._default_save_location = os.path.dirname(path)
[2bf92f2]524        dlg.Destroy()
525        if not path == None:
526            self.subplot.figure.savefig(path,dpi=300, facecolor='w', edgecolor='w',
527                                        orentation='portrait', papertype=None, format='png')
528       
529    def onContextMenu(self, event):
530        """
531            Default context menu for a plot panel
532        """
533        # Slicer plot popup menu
[052a66bc]534        id = wx.NewId()
[2bf92f2]535        slicerpop = wx.Menu()
[052a66bc]536        slicerpop.Append(id,'&Save image', 'Save image as PNG')
537        wx.EVT_MENU(self, id, self.onSaveImage)
[bceddd6]538       
[105614c]539        #id = wx.NewId()
540        #slicerpop.Append(id, '&Load 1D data file')
541        #wx.EVT_MENU(self, id, self._onLoad1DData)
[bceddd6]542       
[052a66bc]543        id = wx.NewId()
[52b1f77]544        slicerpop.AppendSeparator()
[052a66bc]545        slicerpop.Append(id, '&Properties')
546        wx.EVT_MENU(self, id, self._onProperties)
[52b1f77]547       
[052a66bc]548        id = wx.NewId()
[52b1f77]549        slicerpop.AppendSeparator()
[052a66bc]550        slicerpop.Append(id, '&Linear Fit')
551        wx.EVT_MENU(self, id, self.onFitting)
[34ae302]552       
[052a66bc]553        id = wx.NewId()
[34ae302]554        slicerpop.AppendSeparator()
[052a66bc]555        slicerpop.Append(id, '&Reset Graph')
556        wx.EVT_MENU(self, id, self.onResetGraph)
[52b1f77]557       
[2bf92f2]558        pos = event.GetPosition()
559        pos = self.ScreenToClient(pos)
560        self.PopupMenu(slicerpop, pos)
561   
562    ## The following is plottable functionality
563
564
565    def properties(self,prop):
566        """Set some properties of the graph.
567       
568        The set of properties is not yet determined.
569        """
570        # The particulars of how they are stored and manipulated (e.g., do
571        # we want an inventory internally) is not settled.  I've used a
572        # property dictionary for now.
573        #
574        # How these properties interact with a user defined style file is
575        # even less clear.
576
577        # Properties defined by plot
578        self.subplot.set_xlabel(r"$%s$" % prop["xlabel"])
579        self.subplot.set_ylabel(r"$%s$" % prop["ylabel"])
580        self.subplot.set_title(prop["title"])
581
582        # Properties defined by user
583        #self.axes.grid(True)
584
585    def clear(self):
586        """Reset the plot"""
587       
588        # TODO: Redraw is brutal.  Render to a backing store and swap in
589        # TODO: rather than redrawing on the fly.
590        self.subplot.clear()
591        self.subplot.hold(True)
[6ed101a]592   
[2bf92f2]593    def render(self):
594        """Commit the plot after all objects are drawn"""
595        # TODO: this is when the backing store should be swapped in.
596        from matplotlib.font_manager import FontProperties
597        self.subplot.legend(prop=FontProperties(size=10))
598        #self.subplot.legend()
599        pass
600
601    def xaxis(self,label,units):
602        """xaxis label and units.
603       
604        Axis labels know about units.
605       
606        We need to do this so that we can detect when axes are not
607        commesurate.  Currently this is ignored other than for formatting
608        purposes.
609        """
610        if units != "": label = label + " (" + units + ")"
611        self.subplot.set_xlabel(label)
612        pass
613   
614    def yaxis(self,label,units):
615        """yaxis label and units."""
616        if units != "": label = label + " (" + units + ")"
617        self.subplot.set_ylabel(label)
618        pass
619
620    def _connect_to_xlim(self,callback):
621        """Bind the xlim change notification to the callback"""
622        def process_xlim(axes):
623            lo,hi = subplot.get_xlim()
624            callback(lo,hi)
625        self.subplot.callbacks.connect('xlim_changed',process_xlim)
626   
627    #def connect(self,trigger,callback):
628    #    print "PlotPanel.connect???"
629    #    if trigger == 'xlim': self._connect_to_xlim(callback)
630
[4972de2]631    def interactive_points(self,x,y,dx=None,dy=None,name='', color=0,symbol=0,label=None):
632        """Draw markers with error bars"""
633        self.subplot.set_yscale('linear')
634        self.subplot.set_xscale('linear')
635       
636        from plottable_interactor import PointInteractor
637        p = PointInteractor(self, self.subplot, zorder=3, id=name)
[a17ffdf]638        p.points(x, y, dx=dx, dy=dy, color=color, symbol=symbol, label=label)
[4972de2]639       
640        self.subplot.set_yscale(self.yscale)
641        self.subplot.set_xscale(self.xscale)
642
643    def interactive_curve(self,x,y,dy=None,name='',color=0,symbol=0,label=None):
644        """Draw markers with error bars"""
645        self.subplot.set_yscale('linear')
646        self.subplot.set_xscale('linear')
647       
648        from plottable_interactor import PointInteractor
649        p = PointInteractor(self, self.subplot, zorder=4, id=name)
[a17ffdf]650        p.curve(x, y, dy=dy, color=color, symbol=symbol, label=label)
[4972de2]651       
652        self.subplot.set_yscale(self.yscale)
653        self.subplot.set_xscale(self.xscale)
654
655    def plottable_selected(self, id):
656        """
657            Called to register a plottable as selected
658        """
659        #TODO: check that it really is in the list of plottables
660        self.graph.selected_plottable = id
661
[2bf92f2]662    def points(self,x,y,dx=None,dy=None,color=0,symbol=0,label=None):
663        """Draw markers with error bars"""
664        self.subplot.set_yscale('linear')
[52b1f77]665        self.subplot.set_xscale('linear')
[052a66bc]666       
[2bf92f2]667        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
668        if dx != None and type(dx) == type(()):
669            dx = nx.vstack((x-dx[0],dx[1]-x)).transpose()
670        if dy != None and type(dy) == type(()):
671            dy = nx.vstack((y-dy[0],dy[1]-y)).transpose()
672
673        if dx==None and dy==None:
674            h = self.subplot.plot(x,y,color=self._color(color),
675                                   marker=self._symbol(symbol),linestyle='',label=label)
676        else:
[052a66bc]677            col = self._color(color)
[2bf92f2]678            self.subplot.errorbar(x, y, yerr=dy, xerr=None,
[052a66bc]679             ecolor=col, capsize=2,linestyle='', barsabove=False,
680             mec=col, mfc=col,
[2bf92f2]681             marker=self._symbol(symbol),
682             lolims=False, uplims=False,
[52b1f77]683             xlolims=False, xuplims=False,label=label)
[2bf92f2]684           
685        self.subplot.set_yscale(self.yscale)
[52b1f77]686        self.subplot.set_xscale(self.xscale)
[2bf92f2]687
688    def curve(self,x,y,dy=None,color=0,symbol=0,label=None):
689        """Draw a line on a graph, possibly with confidence intervals."""
690        c = self._color(color)
691        self.subplot.set_yscale('linear')
[52b1f77]692        self.subplot.set_xscale('linear')
[2bf92f2]693       
694        hlist = self.subplot.plot(x,y,color=c,marker='',linestyle='-',label=label)
695       
696        self.subplot.set_yscale(self.yscale)
[52b1f77]697        self.subplot.set_xscale(self.xscale)
[2bf92f2]698
699    def _color(self,c):
700        """Return a particular colour"""
701        return self.colorlist[c%len(self.colorlist)]
702
703    def _symbol(self,s):
704        """Return a particular symbol"""
705        return self.symbollist[s%len(self.symbollist)]
[52b1f77]706   
[1c94a9f1]707    def _replot(self, remove_fit=False):
[052a66bc]708        """
709            Rescale the plottables according to the latest
710            user selection and update the plot
[1c94a9f1]711           
712            @param remove_fit: Fit line will be removed if True
[052a66bc]713        """
714        self.graph.reset_scale()
[1c94a9f1]715        self._onEVT_FUNC_PROPERTY(remove_fit=remove_fit)
[052a66bc]716       
717        #TODO: Why do we have to have the following line?
718        self.fit_result.reset_view()
719       
720        self.graph.render(self)
721        self.subplot.figure.canvas.draw_idle()
722   
723    def _onEVT_FUNC_PROPERTY(self, remove_fit=True):
[52b1f77]724        """
[8cebf9b]725             Receive the x and y transformation from myDialog,Transforms x and y in View
726              and set the scale   
[52b1f77]727        """ 
[1c94a9f1]728        # The logic should be in the right order
729        # Delete first, and then get the whole list...
730        if remove_fit:
731            self.graph.delete(self.fit_result)
732           
[52b1f77]733        list =[]
734        list = self.graph.returnPlottable()
[052a66bc]735       
[1c94a9f1]736        # Changing the scale might be incompatible with
737        # currently displayed data (for instance, going
738        # from ln to log when all plotted values have
739        # negative natural logs).
740        # Go linear and only change the scale at the end.
741        self.set_xscale("linear")
742        self.set_yscale("linear")
743        _xscale = 'linear'
744        _yscale = 'linear'
745       
[f193585]746       
[52b1f77]747        for item in list:
[1c94a9f1]748            print item.name
[b43a009]749            item.setLabel(self.xLabel,self.yLabel)
750            if ( self.xLabel=="x" ):
751                item.transformX(transform.toX,transform.errToX)
[f52bea1]752                name, units = item.get_xaxis()
[b6972a0f]753                self.graph.xaxis("%s" % name,  "%s" % units)
754               
[52b1f77]755               
[b43a009]756            if ( self.xLabel=="x^(2)" ):
757                item.transformX(transform.toX2,transform.errToX2)
[f52bea1]758                name, units = item.get_xaxis()
[b6972a0f]759                units=convertUnit(2,units) 
760                self.graph.xaxis("%s^{2}" % name,  "%s" % units)
761               
[52b1f77]762               
[b43a009]763            if (self.xLabel=="log10(x)" ):
[ad8bcd6]764                item.transformX(transform.toX_pos,transform.errToX_pos)
[1c94a9f1]765                _xscale = 'log'
[f193585]766                name, units = item.get_xaxis() 
[b6972a0f]767                self.graph.xaxis("\log_{10}\ \  (%s)" % name,  "%s" % units)
768               
[52b1f77]769               
[b43a009]770            if ( self.yLabel=="ln(y)" ):
771                item.transformY(transform.toLogX,transform.errToLogX)
[831149e]772                name, units = item.get_yaxis()
[ad8bcd6]773                self.graph.yaxis("\log\ \ %s" % name,  "%s" % units)
[b6972a0f]774               
[831149e]775               
[b43a009]776            if ( self.yLabel=="y" ):
777                item.transformY(transform.toX,transform.errToX)
[f52bea1]778                name, units = item.get_yaxis()
[b6972a0f]779                self.graph.yaxis("%s" % name,  "%s" % units)
780               
[52b1f77]781               
[b43a009]782            if ( self.yLabel=="log10(y)" ): 
[ad8bcd6]783                item.transformY(transform.toX_pos,transform.errToX_pos)
[1c94a9f1]784                _yscale = 'log' 
[f52bea1]785                name, units = item.get_yaxis()
[b6972a0f]786                self.graph.yaxis("\log_{10}\ \ (%s)" % name,  "%s" % units)
787               
[52b1f77]788               
[b43a009]789            if ( self.yLabel=="y^(2)" ):
790                item.transformY( transform.toX2,transform.errToX2 )   
[f52bea1]791                name, units = item.get_yaxis()
[b6972a0f]792                units=convertUnit(2,units) 
793                self.graph.yaxis("%s^{2}" % name,  "%s" % units)
794               
[35891ce]795               
[b43a009]796            if ( self.yLabel =="1/y"):
797                item.transformY(transform.toOneOverX,transform.errOneOverX )
[3d3a0e5]798                name, units = item.get_yaxis()
[b6972a0f]799                units=convertUnit(-1,units)
800                self.graph.yaxis("1/%s" % name,  "%s" % units)
[35891ce]801               
[b43a009]802            if ( self.yLabel =="1/sqrt(y)" ):
803                item.transformY(transform.toOneOverSqrtX,transform.errOneOverSqrtX )
[3d3a0e5]804                name, units = item.get_yaxis()
[43bf807]805                units=convertUnit(-0.5,units)
[05da1f89]806                self.graph.yaxis("1/\sqrt{%s}" %name,  "%s" % units)
[7a03e65]807               
[b43a009]808            if ( self.yLabel =="ln(y*x)"):
809                item.transformY( transform.toLogXY,transform.errToLogXY)
[3d3a0e5]810                yname, yunits = item.get_yaxis()
811                xname, xunits = item.get_xaxis()
[ad8bcd6]812                self.graph.yaxis("\log\ (%s \ \ %s)" % (yname,xname),  "%s%s" % (yunits,xunits))
[b6972a0f]813               
[831149e]814               
[b43a009]815            if ( self.yLabel =="ln(y*x^(2))"):
816                item.transformY( transform.toLogYX2,transform.errToLogYX2)
[3d3a0e5]817                yname, yunits = item.get_yaxis()
[47f695c9]818                xname, xunits = item.get_xaxis() 
[b6972a0f]819                xunits = convertUnit(2,xunits) 
[ad8bcd6]820                self.graph.yaxis("\log (%s \ \ %s^{2})" % (yname,xname),  "%s%s" % (yunits,xunits))
[b6972a0f]821               
[831149e]822           
[b43a009]823            if ( self.yLabel =="ln(y*x^(4))"):
824                item.transformY(transform.toLogYX4,transform.errToLogYX4)
[831149e]825                yname, yunits = item.get_yaxis()
826                xname, xunits = item.get_xaxis()
[b6972a0f]827                xunits = convertUnit(4,xunits) 
[ad8bcd6]828                self.graph.yaxis("\log (%s \ \ %s^{4})" % (yname,xname),  "%s%s" % (yunits,xunits))
[b6972a0f]829               
[dfca3de]830            if ( self.viewModel == "Guinier lny vs x^(2)"):
[b43a009]831               
832                item.transformX(transform.toX2,transform.errToX2)
[dfca3de]833                name, units = item.get_xaxis()
[b6972a0f]834                units = convertUnit(2,units) 
835                self.graph.xaxis("%s^{2}" % name,  "%s" % units)
836               
[b43a009]837               
838                item.transformY(transform.toLogX,transform.errToLogX )
[dfca3de]839                name, units = item.get_yaxis()
[ad8bcd6]840                self.graph.yaxis("\log\ \ %s" % name,  "%s" % units)
[b6972a0f]841               
[b43a009]842               
[bbec827]843            item.transformView()
[b43a009]844           
[71c64d6]845        self.resetFitView()   
[b43a009]846        self.prevXtrans = self.xLabel
847        self.prevYtrans = self.yLabel 
[52b1f77]848        self.graph.render(self)
[1c94a9f1]849       
850        self.set_xscale(_xscale)
851        self.set_yscale(_yscale)
852         
[52b1f77]853        self.subplot.figure.canvas.draw_idle()
[46693050]854       
[b6972a0f]855       
856   
[b43a009]857    def onFitDisplay(self, tempx,tempy,xminView,xmaxView,xmin,xmax,func):
[e2914b1]858        """
859            Add a new plottable into the graph .In this case this plottable will be used
860            to fit some data
[edce46e]861            @param tempx: The x data of fit line
862            @param tempy: The y data of fit line
863            @param xminView: the lower bound of fitting range
864            @param xminView: the upper bound of  fitting range
865            @param xmin: the lowest value of data to fit to the line
866            @param xmax: the highest value of data to fit to the line
[e2914b1]867        """
[edce46e]868        # Saving value to redisplay in Fit Dialog when it is opened again
[b43a009]869        self.Avalue,self.Bvalue,self.ErrAvalue,self.ErrBvalue,self.Chivalue=func
[edce46e]870        self.xminView=xminView
871        self.xmaxView=xmaxView
872        self.xmin= xmin
873        self.xmax= xmax
874        #In case need to change the range of data plotted
[34ae302]875        list =[]
876        list = self.graph.returnPlottable()
877        for item in list:
[150c04a]878            #item.onFitRange(xminView,xmaxView)
879            item.onFitRange(None,None)
[edce46e]880       
[bbec827]881        # Create new data plottable with result
882        self.fit_result.x =[] 
883        self.fit_result.y =[]
884        self.fit_result.x =tempx 
885        self.fit_result.y =tempy     
886        self.fit_result.dx=None
887        self.fit_result.dy=None
888        #Load the view with the new values
[edce46e]889        self.fit_result.reset_view()
890        # Add the new plottable to the graph
[bbec827]891        self.graph.add(self.fit_result) 
892        self.graph.render(self)
[1c94a9f1]893        self._offset_graph()
[52b1f77]894        self.subplot.figure.canvas.draw_idle()
[edce46e]895       
[f193585]896   
[34ae302]897    def onResetGraph(self,event):
[edce46e]898        """
899            Reset the graph by plotting the full range of data
900        """
[34ae302]901        list =[]
902        list = self.graph.returnPlottable()
903        for item in list:
904            item.onReset()
905        self.graph.render(self)
906        self.subplot.figure.canvas.draw_idle()
[f193585]907       
[2bf92f2]908class NoRepaintCanvas(FigureCanvasWxAgg):
909    """We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
910    the draw method is only called for the first two paint events. After that,
911    the canvas will only be redrawn when it is resized.
912    """
913    def __init__(self, *args, **kwargs):
914        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
915        self._drawn = 0
916
917    def _onPaint(self, evt):
918        """
919        Called when wxPaintEvt is generated
920        """
921        if not self._isRealized:
922            self.realize()
923        if self._drawn < 2:
924            self.draw(repaint = False)
925            self._drawn += 1
926        self.gui_repaint(drawDC=wx.PaintDC(self))
927           
Note: See TracBrowser for help on using the repository browser.