source: sasview/guitools/plottables.py @ 2e014bf

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

making change on fitdialog ..still some bugs

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