source: sasview/guitools/PlotPanel.py @ 08b9e586

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

Updated plot legend behavior

  • Property mode set to 100644
File size: 33.0 KB
Line 
1import wx.lib.newevent
2import matplotlib
3matplotlib.interactive(False)
4#Use the WxAgg back end. The Wx one takes too long to render
5matplotlib.use('WXAgg')
6from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
7from matplotlib.figure import Figure
8import os
9import fittings
10import transform
11from canvas import FigureCanvas
12from matplotlib.widgets import RectangleSelector
13from pylab import  gca, gcf
14from plottables import Theory1D
15#TODO: make the plottables interactive
16from binder import BindArtist
17
18DEBUG = False
19
20from plottables import Graph
21#(FuncFitEvent, EVT_FUNC_FIT) = wx.lib.newevent.NewEvent()
22import math,pylab,re
23
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)
29     
30from unitConverter import UnitConvertion as convertUnit   
31def _convertUnit(pow,unit):
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    """ 
37    return unit
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
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
62        loprev = lo
63        hiprev = hi
64        ptprev = pt
65        if scale=='log':
66            assert lo >0
67            if lo > 0 :
68                lo = math.log10(lo)
69            if hi > 0 :
70                hi = math.log10(hi)
71            if pt is not None: pt = math.log10(pt)
72       
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':
88            if (lo <= -250) or (hi >= 250):
89                lo=loprev
90                hi=hiprev
91            else:
92                lo,hi = math.pow(10.,lo),math.pow(10.,hi)
93        return (lo,hi)
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)
105        self.parent = parent
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'
118        self.xscale = 'linear'
119        sizer = wx.BoxSizer(wx.VERTICAL)
120        sizer.Add(self.canvas,1,wx.EXPAND)
121        self.SetSizer(sizer)
122       
123        # Graph object to manage the plottables
124        self.graph = Graph()
125        #self.Bind(EVT_FUNC_FIT, self.onFitRange)
126        self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
127       
128        #self.Bind(EVT_PROPERTY, self._onEVT_FUNC_PROPERTY)
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']
132        #User scale
133        self.xLabel ="x"
134        self.yLabel ="y"
135        self.viewModel ="--"
136        # keep track if the previous transformation of x and y in Property dialog
137        self.prevXtrans ="x"
138        self.prevYtrans ="y"
139        self.canvas.mpl_connect('scroll_event',self.onWheel)
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)
144       
145       
146        self.leftdown=False
147        self.leftup=False
148        self.mousemotion=False
149       
150        self.axes = [self.subplot]
151       
152        ## Fit dialog
153        self._fit_dialog = None
154       
155        # Interactor
156        self.connect = BindArtist(self.subplot.figure)
157        #self.selected_plottable = None
158
159        # new data for the fit
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"
163        # For fit Dialog initial display
164        self.xmin=0.0
165        self.xmax=0.0
166        self.xminView=0.0
167        self.xmaxView=0.0
168        self._scale_xlo = None
169        self._scale_xhi = None
170        self._scale_ylo = None
171        self._scale_yhi = None
172        self.Avalue=None
173        self.Bvalue=None
174        self.ErrAvalue=None
175        self.ErrBvalue=None
176        self.Chivalue=None
177       
178        # Dragging info
179        self.begDrag=False
180        self.xInit=None
181        self.yInit=None
182        self.xFinal=None
183        self.yFinal=None
184       
185        # Default locations
186        self._default_save_location = os.getcwd()       
187       
188       
189    def onLeftDown(self,event): 
190        """ left button down and ready to drag"""
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
197           
198           
199    def onLeftUp(self,event): 
200        """ Dragging is done """
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
206     
207    def onMouseMotion(self,event): 
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        """
213        self.mousemotion=True 
214        if self.leftdown==True and self.mousemotion==True:
215           
216            ax = event.inaxes
217            if ax !=None:#the dragging is perform inside the figure
218                self.xFinal,self.yFinal=event.xdata,event.ydata
219               
220                # Check whether this is the first point
221                if self.xInit==None:
222                    self.xInit = self.xFinal
223                    self.yInit = self.yFinal
224                   
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)
233             
234            else:# no dragging is perform elsewhere
235                self.dragHelper(0,0)
236               
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           
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':
259                if lo > 0:
260                    newlo= math.log10(lo)-xdelta
261                if hi > 0:
262                    newhi= math.log10(hi)-xdelta
263            if self.xscale=='log':
264                self._scale_xlo = math.pow(10,newlo)
265                self._scale_xhi = math.pow(10,newhi)
266                ax.set_xlim(math.pow(10,newlo),math.pow(10,newhi))
267            else:
268                self._scale_xlo = newlo
269                self._scale_xhi = newhi
270                ax.set_xlim(newlo,newhi)
271            #print "new lo %f and new hi %f"%(newlo,newhi)
272           
273            lo,hi= ax.get_ylim()
274            #print "y lo %f and y hi %f"%(lo,hi)
275            newlo,newhi= lo- ydelta, hi- ydelta
276            if self.yscale=='log':
277                if lo > 0:
278                    newlo= math.log10(lo)-ydelta
279                if hi > 0:
280                    newhi= math.log10(hi)-ydelta
281                #print "new lo %f and new hi %f"%(newlo,newhi)
282            if  self.yscale=='log':
283                self._scale_ylo = math.pow(10,newlo)
284                self._scale_yhi = math.pow(10,newhi)
285                ax.set_ylim(math.pow(10,newlo),math.pow(10,newhi))
286            else:
287                self._scale_ylo = newlo
288                self._scale_yhi = newhi
289                ax.set_ylim(newlo,newhi)
290        self.canvas.draw_idle()
291       
292       
293   
294    def resetFitView(self):
295        """
296             For fit Dialog initial display
297        """
298        self.xmin=0.0
299        self.xmax=0.0
300        self.xminView=0.0
301        self.xmaxView=0.0
302        self._scale_xlo = None
303        self._scale_xhi = None
304        self._scale_ylo = None
305        self._scale_yhi = None
306        self.Avalue=None
307        self.Bvalue=None
308        self.ErrAvalue=None
309        self.ErrBvalue=None
310        self.Chivalue=None
311   
312    def onWheel(self, event):
313        """
314            Process mouse wheel as zoom events
315            @param event: Wheel event
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()
323            lo,hi = _rescale(lo,hi,step,pt=event.xdata,scale=ax.get_xscale())
324            if not self.xscale=='log' or lo>0:
325                self._scale_xlo = lo
326                self._scale_xhi = hi
327                ax.set_xlim((lo,hi))
328
329            lo,hi = ax.get_ylim()
330            lo,hi = _rescale(lo,hi,step,pt=event.ydata,scale=ax.get_yscale())
331            if not self.yscale=='log' or lo>0:
332                self._scale_ylo = lo
333                self._scale_yhi = hi
334                ax.set_ylim((lo,hi))
335        else:
336             # Check if zoom happens in the axes
337            xdata,ydata = None,None
338            x,y = event.x,event.y
339           
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()
349                lo,hi = _rescale(lo,hi,step,bal=xdata,scale=ax.get_xscale())
350                if not self.xscale=='log' or lo>0:
351                    self._scale_xlo = lo
352                    self._scale_xhi = hi
353                    ax.set_xlim((lo,hi))
354            if ydata is not None:
355                lo,hi = ax.get_ylim()
356                lo,hi = _rescale(lo,hi,step,bal=ydata,scale=ax.get_yscale())
357                if not self.yscale=='log' or lo>0:
358                    self._scale_ylo = lo
359                    self._scale_yhi = hi
360                    ax.set_ylim((lo,hi))
361               
362        self.canvas.draw_idle()
363
364
365    def returnTrans(self):
366        """
367            Return values and labels used by Fit Dialog
368        """
369        return self.xLabel,self.yLabel, self.Avalue, self.Bvalue,\
370                self.ErrAvalue,self.ErrBvalue,self.Chivalue
371   
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
379   
380    def onFitting(self, event): 
381        """
382            when clicking on linear Fit on context menu , display Fitting Dialog
383        """
384        list =[]
385        list = self.graph.returnPlottable()
386        from fitDialog import LinearFit
387       
388        if len(list.keys())>0:
389            first_item = list.keys()[0]
390            dlg = LinearFit( None, first_item, self.onFitDisplay,self.returnTrans, -1, 'Linear Fit')
391           
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)
395            dlg.ShowModal() 
396
397    def linear_plottable_fit(self, plot): 
398        """
399            when clicking on linear Fit on context menu , display Fitting Dialog
400            @param plot: PlotPanel owning the graph
401        """
402        from fitDialog import LinearFit
403       
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       
425
426    def _onProperties(self, event):
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        """
431        if self._fit_dialog is not None:
432            self._fit_dialog.Destroy()
433            self._fit_dialog = None
434           
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:
444                    self.xLabel, self.yLabel,self.viewModel = dial.getValues()
445                    if self.viewModel =="Guinier lny vs x^(2)":
446                        self.xLabel="x^(2)"
447                        self.yLabel="ln(y)"
448                        self.viewModel = "--"
449                        dial.setValues( self.xLabel, self.yLabel,self.viewModel )
450                    self._onEVT_FUNC_PROPERTY()
451                dial.Destroy()
452           
453 
454    def set_yscale(self, scale='linear'):
455        """
456            Set the scale on Y-axis
457            @param scale: the scale of y-axis
458        """
459        self.subplot.set_yscale(scale)
460        self.yscale = scale
461       
462    def get_yscale(self):
463        """
464             @return: Y-axis scale
465        """
466        return self.yscale
467   
468    def set_xscale(self, scale='linear'):
469        """
470            Set the scale on x-axis
471            @param scale: the scale of x-axis
472        """
473        self.subplot.set_xscale(scale)
474        self.xscale = scale
475       
476    def get_xscale(self):
477        """
478             @return: x-axis scale
479        """
480        return self.xscale
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       
515       
516    def onSaveImage(self, evt):
517        #figure.savefig
518        #print "Save image not implemented"
519        path = None
520        dlg = wx.FileDialog(self, "Choose a file", self._default_save_location, "", "*.png", wx.SAVE)
521        if dlg.ShowModal() == wx.ID_OK:
522            path = dlg.GetPath()
523            self._default_save_location = os.path.dirname(path)
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
534        id = wx.NewId()
535        slicerpop = wx.Menu()
536        slicerpop.Append(id,'&Save image', 'Save image as PNG')
537        wx.EVT_MENU(self, id, self.onSaveImage)
538       
539        #id = wx.NewId()
540        #slicerpop.Append(id, '&Load 1D data file')
541        #wx.EVT_MENU(self, id, self._onLoad1DData)
542       
543        id = wx.NewId()
544        slicerpop.AppendSeparator()
545        slicerpop.Append(id, '&Properties')
546        wx.EVT_MENU(self, id, self._onProperties)
547       
548        id = wx.NewId()
549        slicerpop.AppendSeparator()
550        slicerpop.Append(id, '&Linear Fit')
551        wx.EVT_MENU(self, id, self.onFitting)
552       
553        id = wx.NewId()
554        slicerpop.AppendSeparator()
555        slicerpop.Append(id, '&Reset Graph')
556        wx.EVT_MENU(self, id, self.onResetGraph)
557       
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)
592   
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), handletextsep=.05)
598
599    def xaxis(self,label,units):
600        """xaxis label and units.
601       
602        Axis labels know about units.
603       
604        We need to do this so that we can detect when axes are not
605        commesurate.  Currently this is ignored other than for formatting
606        purposes.
607        """
608        if units != "": label = label + " (" + units + ")"
609        self.subplot.set_xlabel(label)
610        pass
611   
612    def yaxis(self,label,units):
613        """yaxis label and units."""
614        if units != "": label = label + " (" + units + ")"
615        self.subplot.set_ylabel(label)
616        pass
617
618    def _connect_to_xlim(self,callback):
619        """Bind the xlim change notification to the callback"""
620        def process_xlim(axes):
621            lo,hi = subplot.get_xlim()
622            callback(lo,hi)
623        self.subplot.callbacks.connect('xlim_changed',process_xlim)
624   
625    #def connect(self,trigger,callback):
626    #    print "PlotPanel.connect???"
627    #    if trigger == 'xlim': self._connect_to_xlim(callback)
628
629    def interactive_points(self,x,y,dx=None,dy=None,name='', color=0,symbol=0,label=None):
630        """Draw markers with error bars"""
631        self.subplot.set_yscale('linear')
632        self.subplot.set_xscale('linear')
633       
634        from plottable_interactor import PointInteractor
635        p = PointInteractor(self, self.subplot, zorder=3, id=name)
636        p.points(x, y, dx=dx, dy=dy, color=color, symbol=symbol, label=label)
637       
638        self.subplot.set_yscale(self.yscale)
639        self.subplot.set_xscale(self.xscale)
640
641    def interactive_curve(self,x,y,dy=None,name='',color=0,symbol=0,label=None):
642        """Draw markers with error bars"""
643        self.subplot.set_yscale('linear')
644        self.subplot.set_xscale('linear')
645       
646        from plottable_interactor import PointInteractor
647        p = PointInteractor(self, self.subplot, zorder=4, id=name)
648        p.curve(x, y, dy=dy, color=color, symbol=symbol, label=label)
649       
650        self.subplot.set_yscale(self.yscale)
651        self.subplot.set_xscale(self.xscale)
652
653    def plottable_selected(self, id):
654        """
655            Called to register a plottable as selected
656        """
657        #TODO: check that it really is in the list of plottables
658        self.graph.selected_plottable = id
659
660    def points(self,x,y,dx=None,dy=None,color=0,symbol=0,label=None):
661        """Draw markers with error bars"""
662        self.subplot.set_yscale('linear')
663        self.subplot.set_xscale('linear')
664       
665        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
666        if dx != None and type(dx) == type(()):
667            dx = nx.vstack((x-dx[0],dx[1]-x)).transpose()
668        if dy != None and type(dy) == type(()):
669            dy = nx.vstack((y-dy[0],dy[1]-y)).transpose()
670
671        if dx==None and dy==None:
672            h = self.subplot.plot(x,y,color=self._color(color),
673                                   marker=self._symbol(symbol),linestyle='',label=label)
674        else:
675            col = self._color(color)
676            self.subplot.errorbar(x, y, yerr=dy, xerr=None,
677             ecolor=col, capsize=2,linestyle='', barsabove=False,
678             mec=col, mfc=col,
679             marker=self._symbol(symbol),
680             lolims=False, uplims=False,
681             xlolims=False, xuplims=False,label=label)
682           
683        self.subplot.set_yscale(self.yscale)
684        self.subplot.set_xscale(self.xscale)
685
686    def curve(self,x,y,dy=None,color=0,symbol=0,label=None):
687        """Draw a line on a graph, possibly with confidence intervals."""
688        c = self._color(color)
689        self.subplot.set_yscale('linear')
690        self.subplot.set_xscale('linear')
691       
692        hlist = self.subplot.plot(x,y,color=c,marker='',linestyle='-',label=label)
693       
694        self.subplot.set_yscale(self.yscale)
695        self.subplot.set_xscale(self.xscale)
696
697    def _color(self,c):
698        """Return a particular colour"""
699        return self.colorlist[c%len(self.colorlist)]
700
701    def _symbol(self,s):
702        """Return a particular symbol"""
703        return self.symbollist[s%len(self.symbollist)]
704   
705    def _replot(self, remove_fit=False):
706        """
707            Rescale the plottables according to the latest
708            user selection and update the plot
709           
710            @param remove_fit: Fit line will be removed if True
711        """
712        self.graph.reset_scale()
713        self._onEVT_FUNC_PROPERTY(remove_fit=remove_fit)
714       
715        #TODO: Why do we have to have the following line?
716        self.fit_result.reset_view()
717       
718        self.graph.render(self)
719        self.subplot.figure.canvas.draw_idle()
720   
721    def _onEVT_FUNC_PROPERTY(self, remove_fit=True):
722        """
723             Receive the x and y transformation from myDialog,Transforms x and y in View
724              and set the scale   
725        """ 
726        # The logic should be in the right order
727        # Delete first, and then get the whole list...
728        if remove_fit:
729            self.graph.delete(self.fit_result)
730           
731        list =[]
732        list = self.graph.returnPlottable()
733       
734        # Changing the scale might be incompatible with
735        # currently displayed data (for instance, going
736        # from ln to log when all plotted values have
737        # negative natural logs).
738        # Go linear and only change the scale at the end.
739        self.set_xscale("linear")
740        self.set_yscale("linear")
741        _xscale = 'linear'
742        _yscale = 'linear'
743       
744       
745        for item in list:
746            item.setLabel(self.xLabel,self.yLabel)
747            if ( self.xLabel=="x" ):
748                item.transformX(transform.toX,transform.errToX)
749                name, units = item.get_xaxis()
750                self.graph._xaxis_transformed("%s" % name,  "%s" % units)
751               
752               
753            if ( self.xLabel=="x^(2)" ):
754                item.transformX(transform.toX2,transform.errToX2)
755                name, units = item.get_xaxis()
756                units=convertUnit(2,units) 
757                self.graph._xaxis_transformed("%s^{2}" % name,  "%s" % units)
758               
759               
760            if (self.xLabel=="log10(x)" ):
761                item.transformX(transform.toX_pos,transform.errToX_pos)
762                _xscale = 'log'
763                name, units = item.get_xaxis() 
764                self.graph._xaxis_transformed("\log_{10}\ \  (%s)" % name,  "%s" % units)
765               
766               
767            if ( self.yLabel=="ln(y)" ):
768                item.transformY(transform.toLogX,transform.errToLogX)
769                name, units = item.get_yaxis()
770                self.graph._yaxis_transformed("\log\ \ %s" % name,  "%s" % units)
771               
772               
773            if ( self.yLabel=="y" ):
774                item.transformY(transform.toX,transform.errToX)
775                name, units = item.get_yaxis()
776                self.graph._yaxis_transformed("%s" % name,  "%s" % units)
777               
778               
779            if ( self.yLabel=="log10(y)" ): 
780                item.transformY(transform.toX_pos,transform.errToX_pos)
781                _yscale = 'log' 
782                name, units = item.get_yaxis()
783                self.graph._yaxis_transformed("\log_{10}\ \ (%s)" % name,  "%s" % units)
784               
785               
786            if ( self.yLabel=="y^(2)" ):
787                item.transformY( transform.toX2,transform.errToX2 )   
788                name, units = item.get_yaxis()
789                units=convertUnit(2,units) 
790                self.graph._yaxis_transformed("%s^{2}" % name,  "%s" % units)
791               
792               
793            if ( self.yLabel =="1/y"):
794                item.transformY(transform.toOneOverX,transform.errOneOverX )
795                name, units = item.get_yaxis()
796                units=convertUnit(-1,units)
797                self.graph._yaxis_transformed("1/%s" % name,  "%s" % units)
798               
799            if ( self.yLabel =="1/sqrt(y)" ):
800                item.transformY(transform.toOneOverSqrtX,transform.errOneOverSqrtX )
801                name, units = item.get_yaxis()
802                units=convertUnit(-0.5,units)
803                self.graph._yaxis_transformed("1/\sqrt{%s}" %name,  "%s" % units)
804               
805            if ( self.yLabel =="ln(y*x)"):
806                item.transformY( transform.toLogXY,transform.errToLogXY)
807                yname, yunits = item.get_yaxis()
808                xname, xunits = item.get_xaxis()
809                self.graph._yaxis_transformed("\log\ (%s \ \ %s)" % (yname,xname),  "%s%s" % (yunits,xunits))
810               
811               
812            if ( self.yLabel =="ln(y*x^(2))"):
813                item.transformY( transform.toLogYX2,transform.errToLogYX2)
814                yname, yunits = item.get_yaxis()
815                xname, xunits = item.get_xaxis() 
816                xunits = convertUnit(2,xunits) 
817                self.graph._yaxis_transformed("\log (%s \ \ %s^{2})" % (yname,xname),  "%s%s" % (yunits,xunits))
818               
819           
820            if ( self.yLabel =="ln(y*x^(4))"):
821                item.transformY(transform.toLogYX4,transform.errToLogYX4)
822                yname, yunits = item.get_yaxis()
823                xname, xunits = item.get_xaxis()
824                xunits = convertUnit(4,xunits) 
825                self.graph._yaxis_transformed("\log (%s \ \ %s^{4})" % (yname,xname),  "%s%s" % (yunits,xunits))
826               
827            if ( self.viewModel == "Guinier lny vs x^(2)"):
828               
829                item.transformX(transform.toX2,transform.errToX2)
830                name, units = item.get_xaxis()
831                units = convertUnit(2,units) 
832                self.graph._xaxis_transformed("%s^{2}" % name,  "%s" % units)
833               
834               
835                item.transformY(transform.toLogX,transform.errToLogX )
836                name, units = item.get_yaxis()
837                self.graph._yaxis_transformed("\log\ \ %s" % name,  "%s" % units)
838               
839               
840            item.transformView()
841           
842        self.resetFitView()   
843        self.prevXtrans = self.xLabel
844        self.prevYtrans = self.yLabel 
845        self.graph.render(self)
846       
847        self.set_xscale(_xscale)
848        self.set_yscale(_yscale)
849         
850        self.subplot.figure.canvas.draw_idle()
851       
852       
853   
854    def onFitDisplay(self, tempx,tempy,xminView,xmaxView,xmin,xmax,func):
855        """
856            Add a new plottable into the graph .In this case this plottable will be used
857            to fit some data
858            @param tempx: The x data of fit line
859            @param tempy: The y data of fit line
860            @param xminView: the lower bound of fitting range
861            @param xminView: the upper bound of  fitting range
862            @param xmin: the lowest value of data to fit to the line
863            @param xmax: the highest value of data to fit to the line
864        """
865        # Saving value to redisplay in Fit Dialog when it is opened again
866        self.Avalue,self.Bvalue,self.ErrAvalue,self.ErrBvalue,self.Chivalue=func
867        self.xminView=xminView
868        self.xmaxView=xmaxView
869        self.xmin= xmin
870        self.xmax= xmax
871        #In case need to change the range of data plotted
872        list =[]
873        list = self.graph.returnPlottable()
874        for item in list:
875            #item.onFitRange(xminView,xmaxView)
876            item.onFitRange(None,None)
877       
878        # Create new data plottable with result
879        self.fit_result.x =[] 
880        self.fit_result.y =[]
881        self.fit_result.x =tempx 
882        self.fit_result.y =tempy     
883        self.fit_result.dx=None
884        self.fit_result.dy=None
885        #Load the view with the new values
886        self.fit_result.reset_view()
887        # Add the new plottable to the graph
888        self.graph.add(self.fit_result) 
889        self.graph.render(self)
890        self._offset_graph()
891        self.subplot.figure.canvas.draw_idle()
892       
893   
894    def onResetGraph(self,event):
895        """
896            Reset the graph by plotting the full range of data
897        """
898        list =[]
899        list = self.graph.returnPlottable()
900        for item in list:
901            item.onReset()
902        self.graph.render(self)
903        self.subplot.figure.canvas.draw_idle()
904       
905class NoRepaintCanvas(FigureCanvasWxAgg):
906    """We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
907    the draw method is only called for the first two paint events. After that,
908    the canvas will only be redrawn when it is resized.
909    """
910    def __init__(self, *args, **kwargs):
911        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
912        self._drawn = 0
913
914    def _onPaint(self, evt):
915        """
916        Called when wxPaintEvt is generated
917        """
918        if not self._isRealized:
919            self.realize()
920        if self._drawn < 2:
921            self.draw(repaint = False)
922            self._drawn += 1
923        self.gui_repaint(drawDC=wx.PaintDC(self))
924           
Note: See TracBrowser for help on using the repository browser.