Realtime Graphing in Gnuplot.py with variable number of Gnuplot.Data() objects

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • ejether
    New Member
    • Jun 2010
    • 7

    Realtime Graphing in Gnuplot.py with variable number of Gnuplot.Data() objects

    Hi all,
    I'm building a tool that interfaces with a serial port that spits out data. I want to graph the data real time and have chose Gnuplot.py

    The number of data 'files' to graph will be flexible but known before execution of the program (up to 6 currently but this could change). I want to write it so that the number a Data objects plotted is flexible without a cascade of if + elif statments as I have already written.

    This function is called each time the program grabs another line from the serial data stream and appends values list.

    Code:
    def rtGraph(g):
        """ workhouse graphing function """
        for file in activefiles:
            activeindex = activefiles.index(file)
            #two arrays must be the same for Gnuplot to be happy
            if len(x) != 0 and len(x) == len(values[activeindex]):
                d[activeindex] = Gnuplot.Data(x, values[activeindex], title = file)
    
        # TODO, fix this crap
        if len(d) == 1:
            g.plot(d[0])
        elif len(d) == 2:
            g.plot(d[0],d[1])
        elif len(d) == 3:
            g.plot(d[0],d[1],d[2])
        elif len(d) == 4:
            g.plot(d[0],d[1],d[2],d[3])
        elif len(d) == 5:
            g.plot(d[0],d[1],d[2],d[3],d[4])
        elif len9d0 == 6:
            g.plot(d[0],d[1],d[2],d[3],d[4],d[5])
        else:
            print "Something is wrong with you data: There is none or too much."
    I'm aware that there is probably a way to format the Gnuplot.Data() call to accomdate this but I couldn't figure it out from the documentation.



    I'm pretty new to python in general and am doing the best I can. Any thoughts are greatly appreciated.
    Thanks!
  • ejether
    New Member
    • Jun 2010
    • 7

    #2
    I think I've figured out that Gnuplot.py does not support what I want.

    Any thoughts on how I can get around it?

    Does Python support indirect reference notation like PHPs $$var that might work?

    Comment

    • Glenton
      Recognized Expert Contributor
      • Nov 2008
      • 391

      #3
      Hi

      Here's a class I wrote some time back for doing almost exactly this. The main() has some examples of usage, but it's pretty straightforward .

      Code:
      #!usr/bin/env python
      #This has the myGraph class, which allows graphing on the fly.
      #See the main() function at the bottom of this file for examples on usage
      
      
      import Gnuplot
      from numpy import inf
      
      class myGraph(object):
          """class for graphing experimental data on the fly"""
          
          def __init__(self,xname,yname,title=None,logx=False,logy=False,
          xlabel=None,ylabel=None):
              """xname and yname are the dictionary label items to be plotted.
              Title is the graph title.
              logx and logy are by default set to False.  Changing to True will
              plot semi-log or log-log graph.
              xlabel and ylabel can be set.  If not the xname and yname will
              be used.
              maxDataPoints is the maximum number of data points to be plotted.
              When this number is reached, early points are removed.  This only
              applies for appendplot."""
              self.xname=xname
              self.yname=yname
              if title==None:
                  self.title = yname+" vs "+xname
              else:
                  self.title=title    
              
              if xlabel:
                  self.xlabel=xlabel
              else:
                  self.xlabel=xname
              if ylabel:
                  self.ylabel=ylabel
              else:
                  self.ylabel=yname
              print self.xlabel, self.ylabel
              self.initgraph()
              if logx: self.G('set logscale x')
              if logy: self.G('set logscale y')
              self.x=[]
              self.y=[]
          
          def initgraph(self):
              """initialise gnuplot graph"""
              self.G = Gnuplot.Gnuplot()  #persist=1)  #for linux
              self.G.title(self.title)
              self.G.xlabel(self.xlabel)
              self.G.ylabel(self.ylabel)
          
          
          def plot(self,datadict):
              """Plots from a dictionary of lists of the whole data set"""
              D=Gnuplot.Data(datadict[self.xname],datadict[self.yname])
              self.G.plot(D)
          
          def listplot(self,xlist,ylist):
              """Plots xlist against ylist"""
              D=Gnuplot.Data(xlist,ylist)
              self.G.plot(D)
              
          def appendplot(self,newdict):
              """Plots from a dictionary of most recent data set"""
              self.x.append(newdict[self.xname])
              self.y.append(newdict[self.yname])
              self.listplot(self.x,self.y)
          
          def appendplot2(self,newdict,maxpoints):
              """Plots from a dictionary of most recent data set.  Deletes
              earliest points when number of points is more than maxpoints"""
              self.x.append(newdict[self.xname])
              self.y.append(newdict[self.yname])
              if len(self.x)>maxpoints:
                  self.x.pop(0)
                  self.y.pop(0)
              self.listplot(self.x,self.y)
                  
          
          
      
      def f(x):
          return 2*(x/10.0)**2
      
      
      def main():
          import time
          
          mydict={}
          mydict["temp"]=[]
          mydict["time"]=[]
          mydict["other"]=[]
          G1=myGraph("time","temp",logx=True, logy=True)
          
          #OPTION 1:  USING A DICTIONARY CONTAINING WHOLE DATA LIST
          for i in range(1,11):
              mydict["time"].append(i)
              mydict["temp"].append(f(i))
              mydict["other"].append("blahblah")
              G1.plot(mydict)
              time.sleep(0.3)
          
          
          #OPTION 2:  USING A DICTIONARY CONTAINING JUST THE MOST RECENT DATA
          for i in range(1,11):
              mydict["temp"]=f(i)
              mydict["time"]=i
              mydict["other"]="blah"
              G1.appendplot(mydict)
              time.sleep(0.3)
          
          #OPTION 3:  USING A DICTIONARY CONTAINING JUST THE MOST RECENT DATA.
          #ONLY PLOTTING A CERTAIN NUMBER AT A TIME
          for i in range(1,100):
              mydict["temp"]=f(i)
              mydict["time"]=i
              mydict["other"]="blah"
              G1.appendplot2(mydict,maxpoints=10)
              time.sleep(0.05)
          
      
      if __name__=="__main__":main()
      I wrote a similar one for saving to a file, if you're interested.

      Comment

      • ejether
        New Member
        • Jun 2010
        • 7

        #4
        Hi Glenton
        Your class does look very straight forward and useful!
        Its process is fairly similar to what I'm doing though yours a lot more polished.

        Unfortunately, unless I'm missing something, I don't think it will do what I want it to. Unless plot() can handle multidimensiona l dictionaries or lists.

        I need to be able to simultaneously plot up to 6 lists of data that will all have the same [x] value so a multidimensiona l list would looks like:

        [[x,y1],[x,y2],[x,y3],....,[x,y6]] where each [x,y] pair is a list of the data points. The number of data lists is variable and feeding gnuplot, an multidimensiona l list like this, doesn't seem to work. It appears that your class is built for one [x,y] dictionary or list, right?

        I've tried plotting each in a separate plot object but its too slow. I have data coming in 100ms intervals, I re-plot every 500ms it seems to be able to keep up fairly well. Using and append method may be faster but I'll tackle that later.

        In short its really that I need to be able to gracefully plot n data traces on the plot in "realtime" where n is unknown.

        I have to admit, your max points cuttoff makes your proggie look like a gnuplot worm game though :-) neat!

        Comment

        • Glenton
          Recognized Expert Contributor
          • Nov 2008
          • 391

          #5
          Hi

          So if it has to be that quick, you might want to consider using python arrays (not to be confused with numpy arrays).

          But anyway, to plot it all on one curve, you'd need to edit the class so that yname is a list of names (I'd call it ynames so it's less confusing!).

          Then you'd set up several different data sets, one for each yname in ynames, and eventually plot(D1,D2,D3,. ..), or
          plot(D1), and then replot(D2), replot(D3), ...

          Let me know if you're struggling, and I'll see if I can help. It shouldn't be too different from the code I gave you.

          If you succeed, then post back with the code anyway!

          Good luck

          G

          Comment

          • ejether
            New Member
            • Jun 2010
            • 7

            #6
            Editing the class is what I was thinking too. I was kind of hoping to avoid that but I'll tackle that when I get the chance and post some code back

            Thanks for your help Glenton!

            Comment

            • Glenton
              Recognized Expert Contributor
              • Nov 2008
              • 391

              #7
              oh, by the way, the original post lines 12 on could be done by creating the relevant string and then executing it with the exec function.

              Glenton

              Comment

              Working...