source: sasview/guitools/plottables.py @ 46693050

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 46693050 was 46693050, checked in by Gervaise Alina <gervyh@…>, 16 years ago

working on fit linear

  • Property mode set to 100644
File size: 30.1 KB
Line 
1"""Prototype plottable object support.
2
3The main point of this prototype is to provide a clean separation between
4the style (plotter details: color, grids, widgets, etc.) and substance
5(application details: which information to plot).  Programmers should not be
6dictating line colours and plotting symbols.
7
8Unlike the problem of style in CSS or Word, where most paragraphs look
9the same, each line on a graph has to be distinguishable from its neighbours.
10Our solution is to provide parametric styles, in which a number of
11different classes of object (e.g., reflectometry data, reflectometry
12theory) representing multiple graph primitives cycle through a colour
13palette provided by the underlying plotter.
14
15A full treatment would provide perceptual dimensions of prominence and
16distinctiveness rather than a simple colour number.
17"""
18
19# Design question: who owns the color?
20# Is it a property of the plottable?
21# Or of the plottable as it exists on the graph?
22# Or if the graph?
23# If a plottable can appear on multiple graphs, in some case the
24# color should be the same on each graph in which it appears, and
25# in other cases (where multiple plottables from different graphs
26# coexist), the color should be assigned by the graph.  In any case
27# once a plottable is placed on the graph its color should not
28# depend on the other plottables on the graph.  Furthermore, if
29# a plottable is added and removed from a graph and added again,
30# it may be nice, but not necessary, to have the color persist.
31#
32# The safest approach seems to be to give ownership of color
33# to the graph, which will allocate the colors along with the
34# plottable.  The plottable will need to return the number of
35# colors that are needed.
36#
37# The situation is less clear for symbols.  It is less clear
38# how much the application requires that symbols be unique across
39# all plots on the graph.
40
41# Support for ancient python versions
42import copy
43import numpy
44
45if 'any' not in dir(__builtins__):
46    def any(L):
47        for cond in L:
48            if cond: return True
49        return False
50    def all(L):
51        for cond in L:
52            if not cond: return False
53        return True
54
55# Graph structure for holding multiple plottables
56class Graph:
57    """
58    Generic plottables graph structure.
59   
60    Plot styles are based on color/symbol lists.  The user gets to select
61    the list of colors/symbols/sizes to choose from, not the application
62    developer.  The programmer only gets to add/remove lines from the
63    plot and move to the next symbol/color.
64
65    Another dimension is prominence, which refers to line sizes/point sizes.
66
67    Axis transformations allow the user to select the coordinate view
68    which provides clarity to the data.  There is no way we can provide
69    every possible transformation for every application generically, so
70    the plottable objects themselves will need to provide the transformations.
71    Here are some examples from reflectometry:
72       independent: x -> f(x)
73          monitor scaling: y -> M*y
74          log:  y -> log(y if y > min else min)
75          cos:  y -> cos(y*pi/180)
76       dependent:   x -> f(x,y)
77          Q4:      y -> y*x^4
78          fresnel: y -> y*fresnel(x)
79       coordinated: x,y = f(x,y)
80          Q:    x -> 2*pi/L (cos(x*pi/180) - cos(y*pi/180))
81                y -> 2*pi/L (sin(x*pi/180) + sin(y*pi/180))
82       reducing: x,y = f(x1,x2,y1,y2)
83          spin asymmetry: x -> x1, y -> (y1 - y2)/(y1 + y2)
84          vector net: x -> x1, y -> y1*cos(y2*pi/180)
85    Multiple transformations are possible, such as Q4 spin asymmetry
86
87    Axes have further complications in that the units of what are being
88    plotted should correspond to the units on the axes.  Plotting multiple
89    types on the same graph should be handled gracefully, e.g., by creating
90    a separate tab for each available axis type, breaking into subplots,
91    showing multiple axes on the same plot, or generating inset plots.
92    Ultimately the decision should be left to the user.
93
94    Graph properties such as grids/crosshairs should be under user control,
95    as should the sizes of items such as axis fonts, etc.  No direct
96    access will be provided to the application.
97
98    Axis limits are mostly under user control.  If the user has zoomed or
99    panned then those limits are preserved even if new data is plotted.
100    The exception is when, e.g., scanning through a set of related lines
101    in which the user may want to fix the limits so that user can compare
102    the values directly.  Another exception is when creating multiple
103    graphs sharing the same limits, though this case may be important
104    enough that it is handled by the graph widget itself.  Axis limits
105    will of course have to understand the effects of axis transformations.
106
107    High level plottable objects may be composed of low level primitives.
108    Operations such as legend/hide/show copy/paste, etc. need to operate
109    on these primitives as a group.  E.g., allowing the user to have a
110    working canvas where they can drag lines they want to save and annotate
111    them.
112
113    Graphs need to be printable.  A page layout program for entire plots
114    would be nice.
115    """
116    def xaxis(self,name,units):
117        """Properties of the x axis.
118        """
119        if self.prop["xunit"] and units != self.prop["xunit"]:
120            pass
121            #print "Plottable: how do we handle non-commensurate units"
122        self.prop["xlabel"] = "%s (%s)"%(name,units)
123        self.prop["xunit"] = units
124
125    def yaxis(self,name,units):
126        """Properties of the y axis.
127        """
128        if self.prop["yunit"] and units != self.prop["yunit"]:
129            pass
130            #print "Plottable: how do we handle non-commensurate units"
131        self.prop["ylabel"] = "%s (%s)"%(name,units)
132        self.prop["yunit"] = units
133       
134    def title(self,name):
135        """Graph title
136        """
137        self.prop["title"] = name
138       
139    def get(self,key):
140        """Get the graph properties"""
141        if key=="color":
142            return self.color
143        elif key == "symbol":
144            return self.symbol
145        else:
146            return self.prop[key]
147
148    def set(self,**kw):
149        """Set the graph properties"""
150        for key in kw:
151            if key == "color":
152                self.color = kw[key]%len(self.colorlist)
153            elif key == "symbol":
154                self.symbol = kw[key]%len(self.symbollist)
155            else:
156                self.prop[key] = kw[key]
157
158    def isPlotted(self, plottable):
159        """Return True is the plottable is already on the graph"""
160        if plottable in self.plottables:
161            return True
162        return False 
163       
164    def add(self,plottable):
165        """Add a new plottable to the graph"""
166        # record the colour associated with the plottable
167        if not plottable in self.plottables:         
168            self.plottables[plottable]=self.color
169            self.color += plottable.colors()
170       
171    def changed(self):
172        """Detect if any graphed plottables have changed"""
173        return any([p.changed() for p in self.plottables])
174
175    def delete(self,plottable):
176        """Remove an existing plottable from the graph"""
177        if plottable in self.plottables:
178            del self.plottables[plottable]
179        if self.color > 0:
180            self.color =  self.color -1
181        else:
182            self.color =0 
183
184    def reset(self):
185        """Reset the graph."""
186        self.color = 0
187        self.symbol = 0
188        self.prop = {"xlabel":"", "xunit":None,
189                     "ylabel":"","yunit":None,
190                     "title":""}
191        self.plottables = {}
192
193    def _make_labels(self):
194        # Find groups of related plottables
195        sets = {}
196        for p in self.plottables:
197            if p.__class__ in sets:
198                sets[p.__class__].append(p)
199            else:
200                sets[p.__class__] = [p]
201               
202        # Ask each plottable class for a set of unique labels
203        labels = {}
204        for c in sets:
205            labels.update(c.labels(sets[c]))
206       
207        return labels
208   
209    def returnPlottable(self):
210        return self.plottables
211
212    def render(self,plot):
213        """Redraw the graph"""
214        plot.clear()
215        plot.properties(self.prop)
216        labels = self._make_labels()
217        for p in self.plottables:
218            p.render(plot,color=self.plottables[p],symbol=0,label=labels[p])
219        plot.render()
220   
221    def clear(self,plot): 
222        plot.clear()
223
224    def __init__(self,**kw):
225        self.reset()
226        self.set(**kw)
227
228
229# Transform interface definition
230# No need to inherit from this class, just need to provide
231# the same methods.
232class Transform:
233    """Define a transform plugin to the plottable architecture.
234   
235    Transforms operate on axes.  The plottable defines the
236    set of transforms available for it, and the axes on which
237    they operate.  These transforms can operate on the x axis
238    only, the y axis only or on the x and y axes together.
239   
240    This infrastructure is not able to support transformations
241    such as log and polar plots as these require full control
242    over the drawing of axes and grids.
243   
244    A transform has a number of attributes.
245   
246    name: user visible name for the transform.  This will
247        appear in the context menu for the axis and the transform
248        menu for the graph.
249    type: operational axis.  This determines whether the
250        transform should appear on x,y or z axis context
251        menus, or if it should appear in the context menu for
252        the graph.
253    inventory: (not implemented)
254        a dictionary of user settable parameter names and
255        their associated types.  These should appear as keyword
256        arguments to the transform call.  For example, Fresnel
257        reflectivity requires the substrate density:
258             { 'rho': type.Value(10e-6/units.angstrom**2) }
259        Supply reasonable defaults in the callback so that
260        limited plotting clients work even though they cannot
261        set the inventory.
262    """
263       
264    def __call__(self,plottable,**kwargs):
265        """Transform the data.  Whenever a plottable is added
266        to the axes, the infrastructure will apply all required
267        transforms.  When the user selects a different representation
268        for the axes (via menu, script, or context menu), all
269        plottables on the axes will be transformed.  The
270        plottable should store the underlying data but set
271        the standard x,dx,y,dy,z,dz attributes appropriately.
272       
273        If the call raises a NotImplemented error the dataline
274        will not be plotted.  The associated string will usually
275        be 'Not a valid transform', though other strings are possible.
276        The application may or may not display the message to the
277        user, along with an indication of which plottable was at fault.
278        """
279        raise NotImplemented,"Not a valid transform"
280
281    # Related issues
282    # ==============
283    #
284    # log scale:
285    #    All axes have implicit log/linear scaling options.
286    #
287    # normalization:
288    #    Want to display raw counts vs detector efficiency correction
289    #    Want to normalize by time/monitor/proton current/intensity.
290    #    Want to display by eg. counts per 3 sec or counts per 10000 monitor.
291    #    Want to divide by footprint (ab initio, fitted or measured).
292    #    Want to scale by attenuator values.
293    #
294    # compare/contrast:
295    #    Want to average all visible lines with the same tag, and
296    #    display difference from one particular line.  Not a transform
297    #    issue?
298    #
299    # multiline graph:
300    #    How do we show/hide data parts.  E.g., data or theory, or
301    #    different polarization cross sections?  One way is with
302    #    tags: each plottable has a set of tags and the tags are
303    #    listed as check boxes above the plotting area.  Click a
304    #    tag and all plottables with that tag are hidden on the
305    #    plot and on the legend.
306    #
307    # nonconformant y-axes:
308    #    What do we do with temperature vs. Q and reflectivity vs. Q
309    #    on the same graph?
310    #
311    # 2D -> 1D:
312    #    Want various slices through the data.  Do transforms apply
313    #    to the sliced data as well?
314
315
316class Plottable:
317    def xaxis(self, name, units):
318        """
319            Set the name and unit of x_axis
320            @param name: the name of x-axis
321            @param units : the units of x_axis
322        """
323        self._xaxis = name
324        self._xunit = units
325
326    def yaxis(self, name, units):
327        """
328            Set the name and unit of y_axis
329            @param name: the name of y-axis
330            @param units : the units of y_axis
331        """
332        self._yaxis = name
333        self._yunit = units
334       
335    def get_xaxis(self):
336        """ Return the units and name of x-axis"""
337        return self._xaxis, self._xunit
338   
339    def get_yaxis(self):
340        """ Return the units and name of y- axis"""
341        return self._yaxis, self._yunit
342
343    @classmethod
344    def labels(cls,collection):
345        """
346        Construct a set of unique labels for a collection of plottables of
347        the same type.
348       
349        Returns a map from plottable to name.
350        """
351        n = len(collection)
352        map = {}
353        if n > 0:
354            basename = str(cls).split('.')[-1]
355            if n == 1:
356                map[collection[0]] = basename
357            else:
358                for i in xrange(len(collection)):
359                    map[collection[i]] = "%s %d"%(basename,i)
360        return map
361    ##Use the following if @classmethod doesn't work
362    # labels = classmethod(labels)
363
364    def __init__(self):
365        self.view = View()
366        self._xaxis = ""
367        self._xunit = ""
368        self._yaxis = ""
369        self._yunit = "" 
370       
371    def set_View(self,x,y):
372        """ Load View  """
373        self.x= x
374        self.y = y
375        self.reset_view()
376       
377    def reset_view(self):
378        """ Reload view with new value to plot"""
379        self.view = self.View(self.x, self.y, self.dx, self.dy)
380        #save the initial value
381        self.view.Xreel = self.view.x
382        self.view.Yreel = self.view.y
383        self.view.DXreel = self.view.dx
384        self.view.DYreel = self.view.dy
385       
386   
387    def render(self,plot):
388        """The base class makes sure the correct units are being used for
389        subsequent plottable. 
390       
391        For now it is assumed that the graphs are commensurate, and if you
392        put a Qx object on a Temperature graph then you had better hope
393        that it makes sense.
394        """
395       
396        plot.xaxis(self._xaxis, self._xunit)
397        plot.yaxis(self._yaxis, self._yunit)
398       
399    def colors(self):
400        """Return the number of colors need to render the object"""
401        return 1
402   
403    def transform_x(self, func, errfunc):
404        """
405            @param func: reference to x transformation function
406           
407        """
408        self.view.transform_x(func, errfunc, x=self.x, y=self.y, dx=self.dx, dy=self.dy)
409   
410    def transform_y(self, func, errfunc):
411        """
412            @param func: reference to y transformation function
413           
414        """
415        self.view.transform_y(func, errfunc, self.y, self.x, self.dx,  self.dy)
416       
417       
418    def returnValuesOfView(self):
419       
420        return self.view.returnXview()
421    def check_data_PlottableX(self): 
422        self.view.check_data_logX()
423    def check_data_PlottableY(self): 
424        self.view.check_data_logY() 
425    def originalXrange(self):
426        self.view.reelXrange()
427    def reducedXrange(self,min,max):
428        self.view.reduceXrange(min, max)
429    class View:
430        """
431            Representation of the data that might include a transformation
432        """
433        x = None
434        y = None
435        dx = None
436        dy = None
437       
438       
439        def __init__(self, x=None, y=None, dx=None, dy=None):
440            self.x = x
441            self.y = y
442            self.dx = dx
443            self.dy = dy
444            #to change x range to the reel range
445            self.Xreel = self.x
446            self.Yreel = self.y
447            self.DXreel = self.dx
448            self.DYreel = self.dy
449           
450        def transform_x(self, func, errfunc, x,y=None,dx=None, dy=None):
451            """
452                Transforms the x and dx vectors and stores the output.
453               
454                @param func: function to apply to the data
455                @param x: array of x values
456                @param dx: array of error values
457                @param errfunc: function to apply to errors
458            """
459           
460           
461            # Sanity check
462            has_y = False
463            if dx and not len(x)==len(dx):
464                    raise ValueError, "Plottable.View: Given x and dx are not of the same length" 
465            # Check length of y array
466            if not y==None:
467                if not len(y)==len(x):
468                    raise ValueError, "Plottable.View: Given y and x are not of the same length"
469                else:
470                    has_y = True
471                if dy and not len(y)==len(dy):
472                    raise ValueError, "Plottable.View: Given y and dy are not of the same length"
473           
474            self.x = []
475            self.dx = []
476           
477            for i in range(len(x)):
478                if has_y:
479                     try:
480                         xtemp = func(x[i],y[i])
481                         
482                         
483                         if (dx!=None) and (dy !=None):
484                             dxtemp = errfunc(x[i], y[i], dx[i], dy[i])
485                         elif (dx != None):
486                             dxtemp = errfunc(x[i], y[i], dx[i],0)
487                         elif (dy != None):
488                             dxtemp = errfunc(x[i], y[i],0,dy[i])
489                         else:
490                             dxtemp = errfunc(x[i],y[i],0, 0)
491                         
492                         self.x.append(xtemp)
493                         self.dx.append(dxtemp)
494                         self.Xreel = []
495                         self.DXreel=[]
496                         self.Xreel = self.x
497                         self.DXreel = self.dx
498                         
499                     except:
500                         print "View.transform_x: skipping point %g" % x[i]
501                         print sys.exc_value
502                         
503                         
504                else:
505                    try:
506                        xtemp = func(x[i])
507                        if (dx != None):
508                            dxtemp = errfunc(x[i], dx[i])
509                        else:
510                            dxtemp = errfunc(x[i],None) 
511                        self.x.append(xtemp)
512                        self.dx.append(dxtemp)
513                        self.Xreel = []
514                        self.DXreel=[]
515                        self.Xreel = self.x
516                        self.DXreel = self.dx
517                         
518                    except:
519                         print "View.transform_x: skipping point %g" % x[i]
520                         print sys.exc_value
521                   
522                         
523        def transform_y(self, func, errfunc, y, x=None,dx=None,dy=None):
524            """
525                Transforms the y and dy vectors and stores the output.
526               
527                @param func: function to apply to the data y
528                @param x: array of x values
529                @param dx: array of error values
530                @param y: array of y values
531                @param dy: array of error values
532                @param errfunc: function to apply to errors dy
533            """
534            # Sanity check
535            has_x = False
536            if dy and not len(y)==len(dy):
537                raise ValueError, "Plottable.View: Given y and dy are not of the same length"
538            # Check length of x array
539            if not x==None:
540                if not len(y)==len(x):
541                    raise ValueError, "Plottable.View: Given y and x are not of the same length"
542                else:
543                    has_x = True
544                if dx and not len(x)==len(dx):
545                    raise ValueError, "Plottable.View: Given x and dx are not of the same length"
546           
547            self.y = []
548            self.dy = []
549           
550            for i in range(len(y)):
551               
552                 if has_x:
553                     try:
554                         tempy = func(y[i],x[i])
555                         if (dx!=None) and (dy !=None):
556                             tempdy = errfunc(y[i], x[i], dy[i], dx[i])
557                         elif (dx != None):
558                             tempdy = errfunc(y[i], x[i], 0, dx[i])
559                         elif (dy != None):
560                             tempdy = errfunc(y[i], x[i], dy[i], 0)
561                         else:
562                             tempdy = errfunc(y[i], None)
563                         self.y.append(tempy)
564                         self.dy.append(tempdy)
565                         self.Yreel = []
566                         self.DYreel=[]
567                         self.Yreel = self.y
568                         self.DYreel = self.dy
569                     except:
570                         print "View.transform_y: skipping point %g" % y[i]
571                         print sys.exc_value
572                           
573                 else:
574                     try:
575                         tempy = func(y[i])
576                         if (dy != None):
577                             tempdy = errfunc( y[i],dy[i])
578                         else:
579                             tempdy = errfunc( y[i],None)
580                         self.y.append(tempy)
581                         self.dy.append(tempdy)
582                         self.Yreel = []
583                         self.DYreel=[]
584                         self.Yreel = self.y
585                         self.DYreel = self.dy
586                     except:
587                          print "View.transform_y: skipping point %g" % y[i]
588                          print sys.exc_value
589                         
590
591           
592        def returnXview(self):
593            return self.x,self.y,self.dx,self.dy
594       
595        def checkMin(self,x,min,pos=None):
596            if pos==None:
597                pos=0
598                for i in range(len(self.x)):
599                    if not min in x:# The user enters a value not in x
600                        if x[i] > min:
601                            index= i-1
602                            print "this is index",index
603                            return index1
604                    else:
605                         index=i
606                         print"the user enter a value inside x",index
607                         return index
608            else:
609                index= pos
610                return index
611           
612        def checkMax(self,x,max,pos=None):
613            if pos==None:
614                pos=0
615                for i in range(len(self.x)):
616                    if not max in x:# The user enters a value not in x
617                        if x[i] >max:
618                           
619                            index= i-1
620                            return index
621                    else:
622                         index=i
623                         return index
624            else:
625                index= pos
626                return index
627             
628        def reduceXrange(self,min,max):
629           
630            # to change the x range to the user range
631            self.Xscale = []
632            self.Yscale = []
633            self.DXscale = []
634            self.DYscale = []
635            indexmin =self.checkMin(self.x,min,None)
636            indexmax =self.checkMin(self.x,max,None)
637            for i in range(len(self.x)):
638                if( self.x[i] >=self.x[indexmin])and( self.x[i] <= max):
639                    self.Xscale.append(self.x[i])
640                    self.Yscale.append(self.y[i])
641                    self.DXscale.append(self.dx[i])
642                    self.DYscale.append(self.dy[i])
643            print self.Xscale
644            self.x= self.Xscale 
645            self.y= self.Yscale
646            self.dx= self.DXscale
647            self.dy= self.DYscale
648       
649           
650        def reelXrange(self):
651            self.x= self.Xreel
652            self.y= self.Yreel
653            self.dx= self.DXreel
654            self.dy= self.DYreel
655           
656       
657        def check_data_logX(self): 
658            tempx=[]
659            tempdx=[]
660            tempy=[]
661            tempdy=[]
662       
663            for i in range(len(self.x)):
664                try:
665                    if (self.x[i]> 0):
666                       
667                        tempx.append(self.x[i])
668                        tempdx.append(self.dx[i])
669                        tempy.append(self.y[i])
670                        tempdy.append(self.dy[i])
671                except:
672                    #print "View.transform_x: skipping point %g" %self.x[i]
673                    print sys.exc_value 
674                    pass     
675               
676            self.x=[]
677            self.dx=[]
678            self.y=[]
679            self.dy=[]
680            self.x=tempx
681            self.y=tempy
682            self.dx=tempdx
683            self.dy=tempdy
684        def check_data_logY(self): 
685            tempx=[]
686            tempdx=[]
687            tempy=[]
688            tempdy=[]
689       
690            for i in range(len(self.x)):
691                try:
692                    if (self.y[i]> 0):
693                       
694                        tempx.append(self.x[i])
695                        tempdx.append(self.dx[i])
696                        tempy.append(self.y[i])
697                        tempdy.append(self.dy[i])
698                except:
699                    #print "View.transform_x: skipping point %g" %self.x[i]
700                    print sys.exc_value 
701                    pass     
702               
703            self.x=[]
704            self.dx=[]
705            self.y=[]
706            self.dy=[]
707            self.x=tempx
708            self.y=tempy
709            self.dx=tempdx
710            self.dy=tempdy
711               
712           
713
714class Data1D(Plottable):
715    """Data plottable: scatter plot of x,y with errors in x and y.
716    """
717   
718    def __init__(self,x,y,dx=None,dy=None):
719        """Draw points specified by x[i],y[i] in the current color/symbol.
720        Uncertainty in x is given by dx[i], or by (xlo[i],xhi[i]) if the
721        uncertainty is asymmetric.  Similarly for y uncertainty.
722
723        The title appears on the legend.
724        The label, if it is different, appears on the status bar.
725        """
726        self.name = "data"
727        self.x = x
728        self.y = y
729        self.dx = dx
730        self.dy = dy
731        self.xaxis( 'q', 'A')
732        self.yaxis( 'intensity', 'cm')
733        self.view = self.View(self.x, self.y, self.dx, self.dy)
734       
735    def render(self,plot,**kw):
736        plot.points(self.view.x,self.view.y,dx=self.view.dx,dy=self.view.dy,**kw)
737        #plot.points(self.x,self.y,dx=self.dx,dy=self.dy,**kw)
738   
739    def changed(self):
740        return False
741
742    @classmethod
743    def labels(cls, collection):
744        """Build a label mostly unique within a collection"""
745        map = {}
746        for item in collection:
747            #map[item] = label(item, collection)
748            map[item] = r"$\rm{%s}$" % item.name
749        return map
750   
751class Theory1D(Plottable):
752    """Theory plottable: line plot of x,y with confidence interval y.
753    """
754    def __init__(self,x,y,dy=None):
755        """Draw lines specified in x[i],y[i] in the current color/symbol.
756        Confidence intervals in x are given by dx[i] or by (xlo[i],xhi[i])
757        if the limits are asymmetric.
758       
759        The title is the name that will show up on the legend.
760        """
761        self.name= "theo"
762        self.x = x
763        self.y = y
764        self.dy = dy
765       
766        self.view = self.View(self.x, self.y, None, self.dy)
767    def render(self,plot,**kw):
768        #plot.curve(self.x,self.y,dy=self.dy,**kw)
769        plot.curve(self.view.x,self.view.y,dy=self.view.dy,**kw)
770
771    def changed(self):
772        return False
773   
774    @classmethod
775    def labels(cls, collection):
776        """Build a label mostly unique within a collection"""
777        map = {}
778        for item in collection:
779            #map[item] = label(item, collection)
780            map[item] = r"$\rm{%s}$" % item.name
781        return map
782   
783   
784class Fit1D(Plottable):
785    """Fit plottable: composed of a data line plus a theory line.  This
786    is treated like a single object from the perspective of the graph,
787    except that it will have two legend entries, one for the data and
788    one for the theory.
789
790    The color of the data and theory will be shared."""
791
792    def __init__(self,data=None,theory=None):
793        self.data=data
794        self.theory=theory
795
796    def render(self,plot,**kw):
797        self.data.render(plot,**kw)
798        self.theory.render(plot,**kw)
799
800    def changed(self):
801        return self.data.changed() or self.theory.changed()
802
803######################################################
804
805def sample_graph():
806    import numpy as nx
807   
808    # Construct a simple graph
809    if False:
810        x = nx.array([1,2,3,4,5,6],'d')
811        y = nx.array([4,5,6,5,4,5],'d')
812        dy = nx.array([0.2, 0.3, 0.1, 0.2, 0.9, 0.3])
813    else:
814        x = nx.linspace(0,1.,10000)
815        y = nx.sin(2*nx.pi*x*2.8)
816        dy = nx.sqrt(100*nx.abs(y))/100
817    data = Data1D(x,y,dy=dy)
818    data.xaxis('distance', 'm')
819    data.yaxis('time', 's')
820    graph = Graph()
821    graph.title('Walking Results')
822    graph.add(data)
823    graph.add(Theory1D(x,y,dy=dy))
824
825    return graph
826
827def demo_plotter(graph):
828    import wx
829    #from pylab_plottables import Plotter
830    from mplplotter import Plotter
831
832    # Make a frame to show it
833    app = wx.PySimpleApp()
834    frame = wx.Frame(None,-1,'Plottables')
835    plotter = Plotter(frame)
836    frame.Show()
837
838    # render the graph to the pylab plotter
839    graph.render(plotter)
840   
841    class GraphUpdate:
842        callnum=0
843        def __init__(self,graph,plotter):
844            self.graph,self.plotter = graph,plotter
845        def __call__(self):
846            if self.graph.changed(): 
847                self.graph.render(self.plotter)
848                return True
849            return False
850        def onIdle(self,event):
851            #print "On Idle checker %d"%(self.callnum)
852            self.callnum = self.callnum+1
853            if self.__call__(): 
854                pass # event.RequestMore()
855    update = GraphUpdate(graph,plotter)
856    frame.Bind(wx.EVT_IDLE,update.onIdle)
857    app.MainLoop()
858
859import sys; print sys.version
860if __name__ == "__main__":
861    demo_plotter(sample_graph())
862   
Note: See TracBrowser for help on using the repository browser.