User avatar
RogerW
Posts: 296
Joined: Sat Dec 20, 2014 12:15 pm
Location: London UK

Scrolling graph using tkinter

Fri Jul 24, 2015 11:05 am

I have been playing with a scrolling graph object. So far it works but lacks axes and legends. As new data points are added the trace scrolls left to right. More than one trace can be shown. Performance depends on the speed of updates, the width of the graph and the number of traces. The test program works well on a pi2 but a bit slower on a pi B+

This file contains the graph object (graph.py)

Code: Select all

# graph.py
# class to show a moving graph
# written by Roger Woollett

from sys import version_info
if version_info[0] < 3:
    import Tkinter as tk
else:
    import tkinter as tk

class Trace():
    # class to represent a single trace in a ScrollGraph
    def __init__(self,master,max_x,min_x,colour,size):
        
        self.master = master
        self.size = size
        self.scale = master.height/(max_x - min_x)
        self.offset = -self.scale*min_x
        
        # create a list of line ids
        self.lines=[]
        i = 0
        while i < master.width:
            self.lines.append(master.create_line(i,0,i,0,fill = colour,width = size))
            i += 1
            
    def scroll(self,value):
        # scroll to the right and add new value
        value = self.scale*value + self.offset
        
        # we want positive upwards
        value = self.master.height - value
        
        # do scroll
        i = self.master.width - 1
        while i > 0:
            x = self.master.coords(self.lines[i - 1])
            self.master.coords(self.lines[i],i,x[1],i,x[3])
            i -= 1
            
        # add new value
        self.master.coords(self.lines[0],0,value,0,value + self.size)
        
class ScrollGraph(tk.Canvas):
    # class to show a scrolling graph
    def __init__(self,master,*args,**kwargs):
        tk.Canvas.__init__(self,master,*args,**kwargs)
        
        self.width = int(self['width'])
        self.height = int(self['height'])
        
        self.traces = {}
         
    def add_trace(self,name,max_x,min_x,colour = 'black',size = 1):
        # call to add a trace to the graph
        self.traces[name] = Trace(self,max_x,min_x,colour=colour,size = size)
    
    def scroll(self,name,value):
        # call to add a new value to a trace
        self.traces[name].scroll(value)
This file contains the test program

Code: Select all

# trygraph.py
# test program to try out the graph class
# written by Roger Woollett

from sys import version_info
if version_info[0] < 3:
    import Tkinter as tk
else:
    import tkinter as tk
    
import graph as g
from math import sin,pi

DELAY = 20  # time period for generating dada points 

class Mainframe(tk.Frame):
    def __init__(self,master,*args,**kwargs):
        tk.Frame.__init__(self,master,*args,**kwargs)
        
        # create the scroll graph 
        self.graph = g.ScrollGraph(self,width = 300,height = 100)
        self.graph.grid(row = 0,column = 0)
                
        # add a thick red sine wave trace
        self.graph.add_trace('sin',1,-1,'red',size = 3)
        self.angle = 0 
          
        # add a thin green saw tooth trace
        self.graph.add_trace('saw',100,0,'green')
        self.saw = 0
        self.inc = 1
               
        # add a quit button
        tk.Button(self,text = 'Quit',width = 15,command = master.destroy) \
        .grid(row = 1,column = 0)

        # start the process of adding data to traces
        self.dodata()
        
    def dodata(self):
        # add data to both traces
        self.graph.scroll('sin',sin(self.angle))
        self.angle += 0.05
        if self.angle >= 2*pi:
            self.angle = 0
            
        self.graph.scroll('saw',self.saw)
        self.saw += self.inc
        if self.saw == 100:
            self.inc = -1
        if self.saw == 0:
            self.inc = 1
        
        # call this function again after DELAY milliseconds
        self.after(DELAY,self.dodata)

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        
        self.title('Try Graph')
    
        Mainframe(self).pack()
                     
App().mainloop()
Anybody interested? Constructive comments welcome.
[EDIT] I should have said to put both files in the same directory.

tonywaite
Posts: 30
Joined: Sat Sep 15, 2012 10:13 am

Re: Scrolling graph using tkinter

Fri Jul 24, 2015 9:56 pm

Thank you for sharing this.

I can't test it for a fortnight, but look forward to using it as a template.

User avatar
RogerW
Posts: 296
Joined: Sat Dec 20, 2014 12:15 pm
Location: London UK

Re: Scrolling graph using tkinter

Sat Jul 25, 2015 8:18 am

tonywaite wrote: I can't test it for a fortnight, but look forward to using it as a template.
Do let us know how you get on.

racpi
Posts: 33
Joined: Mon Dec 30, 2013 11:54 am
Location: australia

Re: Scrolling graph using tkinter

Sat Jul 25, 2015 1:03 pm

thanks for sharing this ,it was the next thing to do on my pi learning curve .
works just fine python 2.7 on win7 machine too

User avatar
davef21370
Posts: 897
Joined: Fri Sep 21, 2012 4:13 pm
Location: Earth But Not Grounded

Re: Scrolling graph using tkinter

Sat Jul 25, 2015 3:10 pm

Just tried on my laptop and works fine, not got a use for this yet but will definitely keep hold.

Dave.
Attachments
Untitled-1.jpg
Untitled-1.jpg (53.67 KiB) Viewed 2931 times
Apple say... Monkey do !!

racpi
Posts: 33
Joined: Mon Dec 30, 2013 11:54 am
Location: australia

Re: Scrolling graph using tkinter

Wed Nov 25, 2015 2:19 am

hi roger
just got around to using your code for a scrolling barometer display , works great.
did you get anywhere with legends/axis info, ticks etc ?

Code: Select all

from sys import version_info
if version_info[0] < 3:
    import Tkinter as tk
else:
    import tkinter as tk
import os
import datetime
import sqlite3   
import graph as g


DELAY = 600000  # time period for generating dada points 

class Mainframe(tk.Frame):
    def __init__(self,master,*args,**kwargs):
        tk.Frame.__init__(self,master,*args,**kwargs)
        pd = str(datetime.datetime.now() - datetime.timedelta(hours=34))
        
        conn=sqlite3.connect('/media/CORSAIR/wh3.db') 
        curs=conn.cursor()
      

            
        # create the scroll graph 
        self.graph = g.ScrollGraph(self,width = 200,height = 200)
        self.graph.grid(row = 0,column = 0)
        self.graph.add_trace('b',1040,900,'green',size = 3)        
        # add a thick red sine wave trace
        self.graph.add_trace('baro',1032,990,'red')
        #curs.execute("        SELECT data,ts from wh where ts >= '"+base+"' order by ts asc  ") 
        for row in curs.execute("SELECT mslp,ts FROM house WHERE    ts >='"+pd+"' order by ts asc   "):
            d=row[0]
            self.graph.scroll('baro',d)
            self.graph.scroll('b',1008)
##            print row
           
        self.lastread=row[1]
        conn.close()       
        # add a quit button
        tk.Button(self,text = 'Quit',width = 15,command = master.destroy) \
        .grid(row = 1,column = 0)

        # start the process of adding data to traces
        self.dodata()
        
    def dodata(self):
        # add data to  traces
        
       
        conn=sqlite3.connect('/media/CORSAIR/wh3.db') 
        curs=conn.cursor()
        pd=self.lastread
        for row in curs.execute("SELECT mslp,ts FROM house WHERE    ts >'"+pd+"' order by ts asc   "):
            if row != None:
                d=row[0]
                self.graph.scroll('baro',d)
                print row
                self.lastread=row[1]    
        conn.close()
        
       
        
            
       
        # call this function again after DELAY milliseconds
        self.after(DELAY,self.dodata)

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        
        self.title('mslp Graph')
    
        Mainframe(self).pack()
                     
App().mainloop()

User avatar
RogerW
Posts: 296
Joined: Sat Dec 20, 2014 12:15 pm
Location: London UK

Re: Scrolling graph using tkinter

Wed Nov 25, 2015 9:52 am

I am afraid I did not get much further. I did add an X axis with ticks but that is all.

Code: Select all

# trygraph.py
# test program to try out the graph class
# written by Roger Woollett

from sys import version_info
if version_info[0] < 3:
    import Tkinter as tk
else:
    import tkinter as tk
    
from graph import ScrollGraph,XAxis
from math import sin,pi

DELAY = 20  # time period for generating dada points 

class Mainframe(tk.Frame):
    def __init__(self,master,*args,**kwargs):
        tk.Frame.__init__(self,master,*args,**kwargs)
        
        # create the scroll graph 
        self.graph = ScrollGraph(self,width = 300,height = 100)
        self.graph.grid(row = 0,column = 0)
                
        # add a thick red sine wave trace
        self.graph.add_trace('sin',1,-1,'red',size = 3)
        self.angle = 0 
          
        # add a thin green saw tooth trace
        self.graph.add_trace('saw',100,0,'green')
        self.saw = 0
        self.inc = 1
        
        # add x axis
        XAxis(self,30,10,height = 10,width = self.graph.width,size = 2) \
        .grid(row = 1,column = 0)
              
        # add a quit button
        tk.Button(self,text = 'Quit',width = 15,command = master.destroy) \
        .grid(row = 2,column = 0)

        # start the process of adding data to traces
        self.dodata()
        
    def dodata(self):
        # add data to both traces
        self.graph.scroll('sin',sin(self.angle))
        self.angle += 0.05
        if self.angle >= 2*pi:
            self.angle = 0
            
        self.graph.scroll('saw',self.saw)
        self.saw += self.inc
        if self.saw == 100:
            self.inc = -1
        if self.saw == 0:
            self.inc = 1
        
        # call this function again after DELAY milliseconds
        self.after(DELAY,self.dodata)

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        
        self.title('Try Graph')
    
        Mainframe(self).pack()
                     
App().mainloop()

Code: Select all

# graph.py
# class to show a moving graph
# written by Roger Woollett

from sys import version_info
if version_info[0] < 3:
    import Tkinter as tk
else:
    import tkinter as tk

class Trace():
    # class to represent a single trace in a ScrollGraph
    def __init__(self,master,max_x,min_x,colour,size):
        
        self.master = master
        self.size = size
        self.scale = master.height/(max_x - min_x)
        self.offset = -self.scale*min_x
        
        # create a list of line ids
        self.lines=list()
        i = 0
        while i < master.width:
            self.lines.append(master.create_line(i,0,i,0,fill = colour,width = size))
            i += 1
            
    def scroll(self,value):
        # scroll to the right and add new value
        value = self.scale*value + self.offset
        
        # we want positive upwards
        value = self.master.height - value
        
        # do scroll
        coords=self.master.coords
        i = self.master.width - 1
        while i > 0:
            x = coords(self.lines[i - 1])
            coords(self.lines[i],i,x[1],i,x[3])
            i -= 1
            
        # add new value
        coords(self.lines[0],0,value,0,value + self.size)
        
class ScrollGraph(tk.Canvas):
    # class to show a scrolling graph
    def __init__(self,master,*args,**kwargs):
        tk.Canvas.__init__(self,master,*args,**kwargs)
        
        self.width = int(self['width'])
        self.height = int(self['height'])
        
        self.traces = dict()
         
    def add_trace(self,name,max_x,min_x,colour = 'black',size = 1):
        # call to add a trace to the graph
        self.traces[name] = Trace(self,max_x,min_x,colour=colour,size = size)
    
    def scroll(self,name,value):
        # call to add a new value to a trace
        self.traces[name].scroll(value)
        
class XAxis(tk.Canvas):
    # class to show horizantal x axis with ticks
    def __init__(self,master,big,small,size = 1,*args,**kwargs):
        tk.Canvas.__init__(self,master,*args,**kwargs)
        
        width = int(self['width'])
        height = int(self['height'])
        
        self.create_line(0,1,width-1,1,width = size)
        
        
        # do small ticks
        i = small
        while i < width:
            self.create_line(i,1,i,height/2,width = size)
            i += small
            
        # do big ticks
        i = 0
        while i < width:
            self.create_line(i,1,i,height,width = size)
            i += big

User avatar
RogerW
Posts: 296
Joined: Sat Dec 20, 2014 12:15 pm
Location: London UK

Re: Scrolling graph using tkinter

Wed Nov 25, 2015 10:08 am

Sorry - I just found out that the code in my reply is out of date.
If you look at
https://github.com/RogerWoollett/PyView

the graph.py there has a Y axis as well.

You might find PyView of interest. It has scrolling views for memory and cpu usage.

Return to “Python”