source: sasview/guitools/PlotPanel.py @ 1c94a9f1

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

Fixed all sorts of bugs: replotting problems, bad logic in rescaling function, removed buggy field in dialog box, improved usability for linear fit.

  • Property mode set to 100644
File size: 32.7 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        self.leftdown=True
192        ax = event.inaxes
193        if ax != None:
194            self.xInit,self.yInit=event.xdata,event.ydata
195           
196           
197    def onLeftUp(self,event): 
198        """ Dragging is done """
199        self.leftdown=False
200        self.mousemotion=False 
201        self.leftup=True
202     
203    def onMouseMotion(self,event): 
204        """
205            check if the left button is press and the mouse in moving.
206            computer delta for x and y coordinates and then calls draghelper
207            to perform the drag
208        """
209        self.mousemotion=True 
210        if self.leftdown==True and self.mousemotion==True:
211           
212            ax = event.inaxes
213            if ax !=None:#the dragging is perform inside the figure
214                self.xFinal,self.yFinal=event.xdata,event.ydata
215               
216                # Check whether this is the first point
217                if self.xInit==None:
218                    self.xInit = self.xFinal
219                    self.yInit = self.yFinal
220                   
221                xdelta = self.xFinal -self.xInit
222                ydelta = self.yFinal -self.yInit
223               
224                if self.xscale=='log':
225                    xdelta = math.log10(self.xFinal) -math.log10(self.xInit)
226                if self.yscale=='log':
227                    ydelta = math.log10(self.yFinal) -math.log10(self.yInit)
228                self.dragHelper(xdelta,ydelta)
229             
230            else:# no dragging is perform elsewhere
231                self.dragHelper(0,0)
232               
233    def _offset_graph(self):
234        """
235             Zoom and offset the graph to the last known
236             settings
237        """
238
239        for ax in self.axes:
240            if self._scale_xhi is not None and self._scale_xlo is not None:
241                ax.set_xlim(self._scale_xlo, self._scale_xhi)
242            if self._scale_yhi is not None and self._scale_ylo is not None:
243                ax.set_ylim(self._scale_ylo, self._scale_yhi)
244           
245           
246    def dragHelper(self,xdelta,ydelta):
247        """ dragging occurs here"""
248       
249        # Event occurred inside a plotting area
250        for ax in self.axes:
251            lo,hi= ax.get_xlim()
252            #print "x lo %f and x hi %f"%(lo,hi)
253            newlo,newhi= lo- xdelta, hi- xdelta
254            if self.xscale=='log':
255                if lo > 0:
256                    newlo= math.log10(lo)-xdelta
257                if hi > 0:
258                    newhi= math.log10(hi)-xdelta
259            if self.xscale=='log':
260                self._scale_xlo = math.pow(10,newlo)
261                self._scale_xhi = math.pow(10,newhi)
262                ax.set_xlim(math.pow(10,newlo),math.pow(10,newhi))
263            else:
264                self._scale_xlo = newlo
265                self._scale_xhi = newhi
266                ax.set_xlim(newlo,newhi)
267            #print "new lo %f and new hi %f"%(newlo,newhi)
268           
269            lo,hi= ax.get_ylim()
270            #print "y lo %f and y hi %f"%(lo,hi)
271            newlo,newhi= lo- ydelta, hi- ydelta
272            if self.yscale=='log':
273                if lo > 0:
274                    newlo= math.log10(lo)-ydelta
275                if hi > 0:
276                    newhi= math.log10(hi)-ydelta
277                #print "new lo %f and new hi %f"%(newlo,newhi)
278            if  self.yscale=='log':
279                self._scale_ylo = math.pow(10,newlo)
280                self._scale_yhi = math.pow(10,newhi)
281                ax.set_ylim(math.pow(10,newlo),math.pow(10,newhi))
282            else:
283                self._scale_ylo = newlo
284                self._scale_yhi = newhi
285                ax.set_ylim(newlo,newhi)
286        self.canvas.draw_idle()
287       
288       
289   
290    def resetFitView(self):
291        """
292             For fit Dialog initial display
293        """
294        self.xmin=0.0
295        self.xmax=0.0
296        self.xminView=0.0
297        self.xmaxView=0.0
298        self._scale_xlo = None
299        self._scale_xhi = None
300        self._scale_ylo = None
301        self._scale_yhi = None
302        self.Avalue=None
303        self.Bvalue=None
304        self.ErrAvalue=None
305        self.ErrBvalue=None
306        self.Chivalue=None
307   
308    def onWheel(self, event):
309        """
310            Process mouse wheel as zoom events
311            @param event: Wheel event
312        """
313        ax = event.inaxes
314        step = event.step
315
316        if ax != None:
317            # Event occurred inside a plotting area
318            lo,hi = ax.get_xlim()
319            lo,hi = _rescale(lo,hi,step,pt=event.xdata,scale=ax.get_xscale())
320            if not self.xscale=='log' or lo>0:
321                self._scale_xlo = lo
322                self._scale_xhi = hi
323                ax.set_xlim((lo,hi))
324
325            lo,hi = ax.get_ylim()
326            lo,hi = _rescale(lo,hi,step,pt=event.ydata,scale=ax.get_yscale())
327            if not self.yscale=='log' or lo>0:
328                self._scale_ylo = lo
329                self._scale_yhi = hi
330                ax.set_ylim((lo,hi))
331        else:
332             # Check if zoom happens in the axes
333            xdata,ydata = None,None
334            x,y = event.x,event.y
335           
336            for ax in self.axes:
337                insidex,_ = ax.xaxis.contains(event)
338                if insidex:
339                    xdata,_ = ax.transAxes.inverse_xy_tup((x,y))
340                insidey,_ = ax.yaxis.contains(event)
341                if insidey:
342                    _,ydata = ax.transAxes.inverse_xy_tup((x,y))
343            if xdata is not None:
344                lo,hi = ax.get_xlim()
345                lo,hi = _rescale(lo,hi,step,bal=xdata,scale=ax.get_xscale())
346                if not self.xscale=='log' or lo>0:
347                    self._scale_xlo = lo
348                    self._scale_xhi = hi
349                    ax.set_xlim((lo,hi))
350            if ydata is not None:
351                lo,hi = ax.get_ylim()
352                lo,hi = _rescale(lo,hi,step,bal=ydata,scale=ax.get_yscale())
353                if not self.yscale=='log' or lo>0:
354                    self._scale_ylo = lo
355                    self._scale_yhi = hi
356                    ax.set_ylim((lo,hi))
357               
358        self.canvas.draw_idle()
359
360
361    def returnTrans(self):
362        """
363            Return values and labels used by Fit Dialog
364        """
365        return self.xLabel,self.yLabel, self.Avalue, self.Bvalue,\
366                self.ErrAvalue,self.ErrBvalue,self.Chivalue
367   
368    def setTrans(self,xtrans,ytrans): 
369        """
370            @param xtrans: set x transformation on Property dialog
371            @param ytrans: set y transformation on Property dialog
372        """
373        self.prevXtrans =xtrans
374        self.prevYtrans =ytrans
375   
376    def onFitting(self, event): 
377        """
378            when clicking on linear Fit on context menu , display Fitting Dialog
379        """
380        list =[]
381        list = self.graph.returnPlottable()
382        from fitDialog import LinearFit
383       
384        if len(list.keys())>0:
385            first_item = list.keys()[0]
386            dlg = LinearFit( None, first_item, self.onFitDisplay,self.returnTrans, -1, 'Linear Fit')
387           
388            if (self.xmin !=0.0 )and ( self.xmax !=0.0)\
389                and(self.xminView !=0.0 )and ( self.xmaxView !=0.0):
390                dlg.setFitRange(self.xminView,self.xmaxView,self.xmin,self.xmax)
391            dlg.ShowModal() 
392
393    def linear_plottable_fit(self, plot): 
394        """
395            when clicking on linear Fit on context menu , display Fitting Dialog
396            @param plot: PlotPanel owning the graph
397        """
398        from fitDialog import LinearFit
399       
400        if self._fit_dialog is not None:
401            return
402       
403        self._fit_dialog = LinearFit( None, plot, self.onFitDisplay,self.returnTrans, -1, 'Linear Fit')
404       
405        # Set the zoom area
406        if self._scale_xhi is not None and self._scale_xlo is not None:
407            self._fit_dialog.set_fit_region(self._scale_xlo, self._scale_xhi)
408       
409        # Register the close event
410        self._fit_dialog.register_close(self._linear_fit_close)
411       
412        # Show a non-model dialog
413        self._fit_dialog.Show() 
414
415    def _linear_fit_close(self):
416        """
417            A fit dialog was closed
418        """
419        self._fit_dialog = None
420       
421
422    def _onProperties(self, event):
423        """
424            when clicking on Properties on context menu ,The Property dialog is displayed
425            The user selects a transformation for x or y value and a new plot is displayed
426        """
427        if self._fit_dialog is not None:
428            self._fit_dialog.Destroy()
429            self._fit_dialog = None
430           
431        list =[]
432        list = self.graph.returnPlottable()
433        if len(list.keys())>0:
434            first_item = list.keys()[0]
435            if first_item.x !=[]:
436                from PropertyDialog import Properties
437                dial = Properties(self, -1, 'Properties')
438                dial.setValues( self.prevXtrans, self.prevYtrans,self.viewModel )
439                if dial.ShowModal() == wx.ID_OK:
440                    self.xLabel, self.yLabel,self.viewModel = dial.getValues()
441                    if self.viewModel =="Guinier lny vs x^(2)":
442                        self.xLabel="x^(2)"
443                        self.yLabel="ln(y)"
444                        self.viewModel = "--"
445                        dial.setValues( self.xLabel, self.yLabel,self.viewModel )
446                    self._onEVT_FUNC_PROPERTY()
447                dial.Destroy()
448           
449 
450    def set_yscale(self, scale='linear'):
451        """
452            Set the scale on Y-axis
453            @param scale: the scale of y-axis
454        """
455        self.subplot.set_yscale(scale)
456        self.yscale = scale
457       
458    def get_yscale(self):
459        """
460             @return: Y-axis scale
461        """
462        return self.yscale
463   
464    def set_xscale(self, scale='linear'):
465        """
466            Set the scale on x-axis
467            @param scale: the scale of x-axis
468        """
469        self.subplot.set_xscale(scale)
470        self.xscale = scale
471       
472    def get_xscale(self):
473        """
474             @return: x-axis scale
475        """
476        return self.xscale
477
478    def SetColor(self, rgbtuple):
479        """Set figure and canvas colours to be the same"""
480        if not rgbtuple:
481            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
482        col = [c/255.0 for c in rgbtuple]
483        self.figure.set_facecolor(col)
484        self.figure.set_edgecolor(col)
485        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))
486
487    def _onSize(self, event):
488        self._resizeflag = True
489
490    def _onIdle(self, evt):
491        if self._resizeflag:
492            self._resizeflag = False
493            self._SetSize()
494            self.draw()
495
496    def _SetSize(self, pixels = None):
497        """
498        This method can be called to force the Plot to be a desired size, which defaults to
499        the ClientSize of the panel
500        """
501        if not pixels:
502            pixels = self.GetClientSize()
503        self.canvas.SetSize(pixels)
504        self.figure.set_size_inches(pixels[0]/self.figure.get_dpi(),
505        pixels[1]/self.figure.get_dpi())
506
507    def draw(self):
508        """Where the actual drawing happens"""
509        self.figure.canvas.draw_idle()
510       
511       
512    def onSaveImage(self, evt):
513        #figure.savefig
514        #print "Save image not implemented"
515        path = None
516        dlg = wx.FileDialog(self, "Choose a file", self._default_save_location, "", "*.png", wx.SAVE)
517        if dlg.ShowModal() == wx.ID_OK:
518            path = dlg.GetPath()
519            self._default_save_location = os.path.dirname(path)
520        dlg.Destroy()
521        if not path == None:
522            self.subplot.figure.savefig(path,dpi=300, facecolor='w', edgecolor='w',
523                                        orentation='portrait', papertype=None, format='png')
524       
525    def onContextMenu(self, event):
526        """
527            Default context menu for a plot panel
528        """
529        # Slicer plot popup menu
530        id = wx.NewId()
531        slicerpop = wx.Menu()
532        slicerpop.Append(id,'&Save image', 'Save image as PNG')
533        wx.EVT_MENU(self, id, self.onSaveImage)
534       
535        #id = wx.NewId()
536        #slicerpop.Append(id, '&Load 1D data file')
537        #wx.EVT_MENU(self, id, self._onLoad1DData)
538       
539        id = wx.NewId()
540        slicerpop.AppendSeparator()
541        slicerpop.Append(id, '&Properties')
542        wx.EVT_MENU(self, id, self._onProperties)
543       
544        id = wx.NewId()
545        slicerpop.AppendSeparator()
546        slicerpop.Append(id, '&Linear Fit')
547        wx.EVT_MENU(self, id, self.onFitting)
548       
549        id = wx.NewId()
550        slicerpop.AppendSeparator()
551        slicerpop.Append(id, '&Reset Graph')
552        wx.EVT_MENU(self, id, self.onResetGraph)
553       
554        pos = event.GetPosition()
555        pos = self.ScreenToClient(pos)
556        self.PopupMenu(slicerpop, pos)
557   
558    ## The following is plottable functionality
559
560
561    def properties(self,prop):
562        """Set some properties of the graph.
563       
564        The set of properties is not yet determined.
565        """
566        # The particulars of how they are stored and manipulated (e.g., do
567        # we want an inventory internally) is not settled.  I've used a
568        # property dictionary for now.
569        #
570        # How these properties interact with a user defined style file is
571        # even less clear.
572
573        # Properties defined by plot
574        self.subplot.set_xlabel(r"$%s$" % prop["xlabel"])
575        self.subplot.set_ylabel(r"$%s$" % prop["ylabel"])
576        self.subplot.set_title(prop["title"])
577
578        # Properties defined by user
579        #self.axes.grid(True)
580
581    def clear(self):
582        """Reset the plot"""
583       
584        # TODO: Redraw is brutal.  Render to a backing store and swap in
585        # TODO: rather than redrawing on the fly.
586        self.subplot.clear()
587        self.subplot.hold(True)
588   
589    def render(self):
590        """Commit the plot after all objects are drawn"""
591        # TODO: this is when the backing store should be swapped in.
592        from matplotlib.font_manager import FontProperties
593        self.subplot.legend(prop=FontProperties(size=10))
594        #self.subplot.legend()
595        pass
596
597    def xaxis(self,label,units):
598        """xaxis label and units.
599       
600        Axis labels know about units.
601       
602        We need to do this so that we can detect when axes are not
603        commesurate.  Currently this is ignored other than for formatting
604        purposes.
605        """
606        if units != "": label = label + " (" + units + ")"
607        self.subplot.set_xlabel(label)
608        pass
609   
610    def yaxis(self,label,units):
611        """yaxis label and units."""
612        if units != "": label = label + " (" + units + ")"
613        self.subplot.set_ylabel(label)
614        pass
615
616    def _connect_to_xlim(self,callback):
617        """Bind the xlim change notification to the callback"""
618        def process_xlim(axes):
619            lo,hi = subplot.get_xlim()
620            callback(lo,hi)
621        self.subplot.callbacks.connect('xlim_changed',process_xlim)
622   
623    #def connect(self,trigger,callback):
624    #    print "PlotPanel.connect???"
625    #    if trigger == 'xlim': self._connect_to_xlim(callback)
626
627    def interactive_points(self,x,y,dx=None,dy=None,name='', color=0,symbol=0,label=None):
628        """Draw markers with error bars"""
629        self.subplot.set_yscale('linear')
630        self.subplot.set_xscale('linear')
631       
632        from plottable_interactor import PointInteractor
633        p = PointInteractor(self, self.subplot, zorder=3, id=name)
634        p.points(x, y, dx=dx, dy=dy, color=color, symbol=symbol, label=label)
635       
636        self.subplot.set_yscale(self.yscale)
637        self.subplot.set_xscale(self.xscale)
638
639    def interactive_curve(self,x,y,dy=None,name='',color=0,symbol=0,label=None):
640        """Draw markers with error bars"""
641        self.subplot.set_yscale('linear')
642        self.subplot.set_xscale('linear')
643       
644        from plottable_interactor import PointInteractor
645        p = PointInteractor(self, self.subplot, zorder=4, id=name)
646        p.curve(x, y, dy=dy, color=color, symbol=symbol, label=label)
647       
648        self.subplot.set_yscale(self.yscale)
649        self.subplot.set_xscale(self.xscale)
650
651    def plottable_selected(self, id):
652        """
653            Called to register a plottable as selected
654        """
655        #TODO: check that it really is in the list of plottables
656        self.graph.selected_plottable = id
657
658    def points(self,x,y,dx=None,dy=None,color=0,symbol=0,label=None):
659        """Draw markers with error bars"""
660        self.subplot.set_yscale('linear')
661        self.subplot.set_xscale('linear')
662       
663        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
664        if dx != None and type(dx) == type(()):
665            dx = nx.vstack((x-dx[0],dx[1]-x)).transpose()
666        if dy != None and type(dy) == type(()):
667            dy = nx.vstack((y-dy[0],dy[1]-y)).transpose()
668
669        if dx==None and dy==None:
670            h = self.subplot.plot(x,y,color=self._color(color),
671                                   marker=self._symbol(symbol),linestyle='',label=label)
672        else:
673            col = self._color(color)
674            self.subplot.errorbar(x, y, yerr=dy, xerr=None,
675             ecolor=col, capsize=2,linestyle='', barsabove=False,
676             mec=col, mfc=col,
677             marker=self._symbol(symbol),
678             lolims=False, uplims=False,
679             xlolims=False, xuplims=False,label=label)
680           
681        self.subplot.set_yscale(self.yscale)
682        self.subplot.set_xscale(self.xscale)
683
684    def curve(self,x,y,dy=None,color=0,symbol=0,label=None):
685        """Draw a line on a graph, possibly with confidence intervals."""
686        c = self._color(color)
687        self.subplot.set_yscale('linear')
688        self.subplot.set_xscale('linear')
689       
690        hlist = self.subplot.plot(x,y,color=c,marker='',linestyle='-',label=label)
691       
692        self.subplot.set_yscale(self.yscale)
693        self.subplot.set_xscale(self.xscale)
694
695    def _color(self,c):
696        """Return a particular colour"""
697        return self.colorlist[c%len(self.colorlist)]
698
699    def _symbol(self,s):
700        """Return a particular symbol"""
701        return self.symbollist[s%len(self.symbollist)]
702   
703    def _replot(self, remove_fit=False):
704        """
705            Rescale the plottables according to the latest
706            user selection and update the plot
707           
708            @param remove_fit: Fit line will be removed if True
709        """
710        self.graph.reset_scale()
711        self._onEVT_FUNC_PROPERTY(remove_fit=remove_fit)
712       
713        #TODO: Why do we have to have the following line?
714        self.fit_result.reset_view()
715       
716        self.graph.render(self)
717        self.subplot.figure.canvas.draw_idle()
718   
719    def _onEVT_FUNC_PROPERTY(self, remove_fit=True):
720        """
721             Receive the x and y transformation from myDialog,Transforms x and y in View
722              and set the scale   
723        """ 
724        # The logic should be in the right order
725        # Delete first, and then get the whole list...
726        if remove_fit:
727            self.graph.delete(self.fit_result)
728           
729        list =[]
730        list = self.graph.returnPlottable()
731       
732        # Changing the scale might be incompatible with
733        # currently displayed data (for instance, going
734        # from ln to log when all plotted values have
735        # negative natural logs).
736        # Go linear and only change the scale at the end.
737        self.set_xscale("linear")
738        self.set_yscale("linear")
739        _xscale = 'linear'
740        _yscale = 'linear'
741       
742       
743        for item in list:
744            print item.name
745            item.setLabel(self.xLabel,self.yLabel)
746            if ( self.xLabel=="x" ):
747                item.transformX(transform.toX,transform.errToX)
748                name, units = item.get_xaxis()
749                self.graph.xaxis("%s" % name,  "%s" % units)
750               
751               
752            if ( self.xLabel=="x^(2)" ):
753                item.transformX(transform.toX2,transform.errToX2)
754                name, units = item.get_xaxis()
755                units=convertUnit(2,units) 
756                self.graph.xaxis("%s^{2}" % name,  "%s" % units)
757               
758               
759            if (self.xLabel=="log10(x)" ):
760                item.transformX(transform.toX_pos,transform.errToX_pos)
761                _xscale = 'log'
762                name, units = item.get_xaxis() 
763                self.graph.xaxis("\log_{10}\ \  (%s)" % name,  "%s" % units)
764               
765               
766            if ( self.yLabel=="ln(y)" ):
767                item.transformY(transform.toLogX,transform.errToLogX)
768                name, units = item.get_yaxis()
769                self.graph.yaxis("\log\ \ %s" % name,  "%s" % units)
770               
771               
772            if ( self.yLabel=="y" ):
773                item.transformY(transform.toX,transform.errToX)
774                name, units = item.get_yaxis()
775                self.graph.yaxis("%s" % name,  "%s" % units)
776               
777               
778            if ( self.yLabel=="log10(y)" ): 
779                item.transformY(transform.toX_pos,transform.errToX_pos)
780                _yscale = 'log' 
781                name, units = item.get_yaxis()
782                self.graph.yaxis("\log_{10}\ \ (%s)" % name,  "%s" % units)
783               
784               
785            if ( self.yLabel=="y^(2)" ):
786                item.transformY( transform.toX2,transform.errToX2 )   
787                name, units = item.get_yaxis()
788                units=convertUnit(2,units) 
789                self.graph.yaxis("%s^{2}" % name,  "%s" % units)
790               
791               
792            if ( self.yLabel =="1/y"):
793                item.transformY(transform.toOneOverX,transform.errOneOverX )
794                name, units = item.get_yaxis()
795                units=convertUnit(-1,units)
796                self.graph.yaxis("1/%s" % name,  "%s" % units)
797               
798            if ( self.yLabel =="1/sqrt(y)" ):
799                item.transformY(transform.toOneOverSqrtX,transform.errOneOverSqrtX )
800                name, units = item.get_yaxis()
801                units=convertUnit(-0.5,units)
802                self.graph.yaxis("1/\sqrt{%s}" %name,  "%s" % units)
803               
804            if ( self.yLabel =="ln(y*x)"):
805                item.transformY( transform.toLogXY,transform.errToLogXY)
806                yname, yunits = item.get_yaxis()
807                xname, xunits = item.get_xaxis()
808                self.graph.yaxis("\log\ (%s \ \ %s)" % (yname,xname),  "%s%s" % (yunits,xunits))
809               
810               
811            if ( self.yLabel =="ln(y*x^(2))"):
812                item.transformY( transform.toLogYX2,transform.errToLogYX2)
813                yname, yunits = item.get_yaxis()
814                xname, xunits = item.get_xaxis() 
815                xunits = convertUnit(2,xunits) 
816                self.graph.yaxis("\log (%s \ \ %s^{2})" % (yname,xname),  "%s%s" % (yunits,xunits))
817               
818           
819            if ( self.yLabel =="ln(y*x^(4))"):
820                item.transformY(transform.toLogYX4,transform.errToLogYX4)
821                yname, yunits = item.get_yaxis()
822                xname, xunits = item.get_xaxis()
823                xunits = convertUnit(4,xunits) 
824                self.graph.yaxis("\log (%s \ \ %s^{4})" % (yname,xname),  "%s%s" % (yunits,xunits))
825               
826            if ( self.viewModel == "Guinier lny vs x^(2)"):
827               
828                item.transformX(transform.toX2,transform.errToX2)
829                name, units = item.get_xaxis()
830                units = convertUnit(2,units) 
831                self.graph.xaxis("%s^{2}" % name,  "%s" % units)
832               
833               
834                item.transformY(transform.toLogX,transform.errToLogX )
835                name, units = item.get_yaxis()
836                self.graph.yaxis("\log\ \ %s" % name,  "%s" % units)
837               
838               
839            item.transformView()
840           
841        self.resetFitView()   
842        self.prevXtrans = self.xLabel
843        self.prevYtrans = self.yLabel 
844        self.graph.render(self)
845       
846        self.set_xscale(_xscale)
847        self.set_yscale(_yscale)
848         
849        self.subplot.figure.canvas.draw_idle()
850       
851       
852   
853    def onFitDisplay(self, tempx,tempy,xminView,xmaxView,xmin,xmax,func):
854        """
855            Add a new plottable into the graph .In this case this plottable will be used
856            to fit some data
857            @param tempx: The x data of fit line
858            @param tempy: The y data of fit line
859            @param xminView: the lower bound of fitting range
860            @param xminView: the upper bound of  fitting range
861            @param xmin: the lowest value of data to fit to the line
862            @param xmax: the highest value of data to fit to the line
863        """
864        # Saving value to redisplay in Fit Dialog when it is opened again
865        self.Avalue,self.Bvalue,self.ErrAvalue,self.ErrBvalue,self.Chivalue=func
866        self.xminView=xminView
867        self.xmaxView=xmaxView
868        self.xmin= xmin
869        self.xmax= xmax
870        #In case need to change the range of data plotted
871        list =[]
872        list = self.graph.returnPlottable()
873        for item in list:
874            #item.onFitRange(xminView,xmaxView)
875            item.onFitRange(None,None)
876       
877        # Create new data plottable with result
878        self.fit_result.x =[] 
879        self.fit_result.y =[]
880        self.fit_result.x =tempx 
881        self.fit_result.y =tempy     
882        self.fit_result.dx=None
883        self.fit_result.dy=None
884        #Load the view with the new values
885        self.fit_result.reset_view()
886        # Add the new plottable to the graph
887        self.graph.add(self.fit_result) 
888        self.graph.render(self)
889        self._offset_graph()
890        self.subplot.figure.canvas.draw_idle()
891       
892   
893    def onResetGraph(self,event):
894        """
895            Reset the graph by plotting the full range of data
896        """
897        list =[]
898        list = self.graph.returnPlottable()
899        for item in list:
900            item.onReset()
901        self.graph.render(self)
902        self.subplot.figure.canvas.draw_idle()
903       
904class NoRepaintCanvas(FigureCanvasWxAgg):
905    """We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
906    the draw method is only called for the first two paint events. After that,
907    the canvas will only be redrawn when it is resized.
908    """
909    def __init__(self, *args, **kwargs):
910        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
911        self._drawn = 0
912
913    def _onPaint(self, evt):
914        """
915        Called when wxPaintEvt is generated
916        """
917        if not self._isRealized:
918            self.realize()
919        if self._drawn < 2:
920            self.draw(repaint = False)
921            self._drawn += 1
922        self.gui_repaint(drawDC=wx.PaintDC(self))
923           
Note: See TracBrowser for help on using the repository browser.