source: sasview/guitools/PlotPanel.py @ 70d975c

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 70d975c was 3755d77, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

fixed bug with transforms

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