source: sasview/guitools/plottables.py @ f193585

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

some changes

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