Python;graph permanent grid print; Canvas

Thread Starter

Vindhyachal Takniki

Joined Nov 3, 2014
594
1. I have two made continuous graph of data like in CRO in python, for 2 or 4 channels depending on need.
2. When graph reaches right corner it again starts from left again. Right now random values are printed which I will replace with voltage values.
3. Didn't use matplotlib since I didn't find how to do that.
4. Code for this is below. ( special thx to @strantor for that, I had some issues)
5. What I do when i reach horizontal pixel, first I delete it completely, then I print the value on it.
Now how to print grid on it. It gets deleted also.
6. I also tried to put grid image on canvas & then tried to put graph on it, but then that images also gets deleted pixel by pixel.
7. Any way to do that or workaround.
(In actual I have to print graph right to left like in CRO, bur currently i am doing left to right as it is easy).

Python:
import Tkinter
from Tkinter import *
from random import randint

class pixelTron():

    def __init__(self):
        self.hor_pixel = 5                      #starting point of horizontal pixel
        self.old_ver_pixel_1 = 0                #last pixel value of graph 1
        self.old_ver_pixel_2 = 302              #last pixel value of graph 2
        self.new_ver_pixel_1 = 0                #new pixel value of graph 1
        self.new_ver_pixel_2 = 0                #new pixel value of graph 2
        self.y0_1 = 0                           # y cordinate of graph 1 & 2
        self.y1_1 = 0
        self.y0_2 = 0
        self.y1_2 = 0
        self.screen = Tkinter.Tk()
        self.running = 1
        self.screen.protocol("WM_DELETE_WINDOW", self.suicide)

    def pixel(self, C):
        nP1 = self.new_ver_pixel_1
        oP1 = self.old_ver_pixel_1
        nP2 = self.new_ver_pixel_2
        oP2 = self.old_ver_pixel_2

             
        hP = self.hor_pixel
        if(nP1 == oP1):
            nP1 = nP1 + 1
        if(nP2 == oP2):
            nP2 = nP2 + 1

        if(nP1 > oP1):
            self.y0_1 = oP1
            self.y1_1 = nP1
        else:
            self.y0_1 = nP1
            self.y1_1 = oP1
         
        if(nP2 > oP2):
            self.y0_2 = oP2
            self.y1_2 = nP2
        else:
            self.y0_2 = nP2
            self.y1_2 = oP2
         
        coord = hP, self.y0_1, hP, self.y1_1
        coord2 = hP, self.y0_2, hP, self.y1_2
        print coord2
     
        hP = hP + 1
        if(hP > 400):
            hP = 5;
        self.old_ver_pixel_1 = nP1
        self.old_ver_pixel_2 = nP2

        C.create_line(hP , 0 , hP , 300, fill = 'black')
        C.create_line(hP , 302 , hP , 602, fill = 'black')      
        C.create_line(coord, fill = 'red')
        C.create_line(coord2, fill = 'yellow')
        C.pack()
        self.hor_pixel = hP

    def graph(self):
        screen = self.screen

        graph_index(screen)
     
        screen.title("Analog Channel")
        screen.geometry("800x800")
        C = Tkinter.Canvas(screen , bg = "black", width = 600, height = 602)
        C.create_line(5 , 301 , 400 , 301, fill = 'white')

        #photo = PhotoImage(file="../untitled.gif")
        #C.create_image(0,0 ,image = photo)
     
        C.pack()
        pixel = self.pixel
        while self.running:
            self.new_ver_pixel_1 = randint(0,300);
            self.new_ver_pixel_2 = randint(0,300) + 302;
            screen.after(100,pixel(C))
            screen.update()
        screen.mainloop()

    def suicide(self):
        self.running = 0
        self.screen.destroy()

def my_graph():
    PT = pixelTron()
    PTG = PT.graph
    PTG()

def graph_index(screen):
    label = Label(screen , text = "-20")
    label.pack()
    label.place(x = 70, y = 270)

    label = Label(screen , text = "-10")
    label.pack()
    label.place(x = 70, y = 215)

    label = Label(screen , text = "0")
    label.pack()
    label.place(x = 80, y = 140)      

    label = Label(screen , text = "+10")
    label.pack()
    label.place(x = 70, y = 65)

    label = Label(screen , text = "+20")
    label.pack()
    label.place(x = 70, y = 0)    

    label = Label(screen , text = "+20")
    label.pack()
    label.place(x = 70, y = 302)

    label = Label(screen , text = "+10")
    label.pack()
    label.place(x = 70, y = 375)

    label = Label(screen , text = "0")
    label.pack()
    label.place(x = 80, y = 450)      

    label = Label(screen , text = "-10")
    label.pack()
    label.place(x = 70, y = 525)

    label = Label(screen , text = "-20")
    label.pack()
    label.place(x = 70, y = 600)    

my_graph()
 
Last edited by a moderator:

strantor

Joined Oct 3, 2010
6,798
This works:
Python:
import Tkinter
from Tkinter import *
from random import randint

class pixelTron():

    def __init__(self):
        self.hor_pixel = 5                      #starting point of horizontal pixel
        self.old_ver_pixel_1 = 0                #last pixel value of graph 1
        self.old_ver_pixel_2 = 302              #last pixel value of graph 2
        self.new_ver_pixel_1 = 0                #new pixel value of graph 1
        self.new_ver_pixel_2 = 0                #new pixel value of graph 2
        self.y0_1 = 0                           # y cordinate of graph 1 & 2
        self.y1_1 = 0
        self.y0_2 = 0
        self.y1_2 = 0
        self.screen = Tkinter.Tk()
        self.running = 1
        self.screen.protocol("WM_DELETE_WINDOW", self.suicide)

    def pixel(self, C):
        nP1 = self.new_ver_pixel_1
        oP1 = self.old_ver_pixel_1
        nP2 = self.new_ver_pixel_2
        oP2 = self.old_ver_pixel_2


        hP = self.hor_pixel
        if(nP1 == oP1):
            nP1 = nP1 + 1
        if(nP2 == oP2):
            nP2 = nP2 + 1

        if(nP1 > oP1):
            self.y0_1 = oP1
            self.y1_1 = nP1
        else:
            self.y0_1 = nP1
            self.y1_1 = oP1

        if(nP2 > oP2):
            self.y0_2 = oP2
            self.y1_2 = nP2
        else:
            self.y0_2 = nP2
            self.y1_2 = oP2

        coord = hP, self.y0_1, hP, self.y1_1
        coord2 = hP, self.y0_2, hP, self.y1_2
        print coord2

        hP = hP + 1
        if(hP > 400):
            hP = 5;
        self.old_ver_pixel_1 = nP1
        self.old_ver_pixel_2 = nP2

        #hP is the cycle number (movement along x-axis)
        #this makes vertical black lines every cycle (every x-increment) to create a black background
        C.create_line(hP , 0 , hP , 300, fill = 'black')
        C.create_line(hP , 302 , hP , 602, fill = 'black')
      
        hwls = 50 #Horizontal white line spacing
        vwls = 50 #Vertical white line spacing
      
        #every nTH vertical line, make it white (overwrites previous black line)
        if hP in range (0, 600, vwls):
            C.create_line(hP , 0 , hP , 602, fill = 'white')

        #every nTH horizonal pixel of every vertical line, make it white (overwrites previous black pixels)
        for i in range (0, 602, hwls):
            C.create_line(hP, i, hP, i+1, fill = 'white')

        C.create_line(coord, fill = 'red')
        C.create_line(coord2, fill = 'yellow')
        C.pack()
        self.hor_pixel = hP

    def graph(self):
        screen = self.screen

        graph_index(screen)

        screen.title("Analog Channel")
        screen.geometry("800x800")
        C = Tkinter.Canvas(screen , bg = "black", width = 600, height = 602)
        C.create_line(5 , 301 , 400 , 301, fill = 'white')

        #photo = PhotoImage(file="../untitled.gif")
        #C.create_image(0,0 ,image = photo)

        C.pack()
        pixel = self.pixel
        while self.running:
            self.new_ver_pixel_1 = randint(0,300);
            self.new_ver_pixel_2 = randint(0,300) + 302;
            screen.after(10,pixel(C))
            screen.update()
        screen.mainloop()

    def suicide(self):
        self.running = 0
        self.screen.destroy()

def my_graph():
    PT = pixelTron()
    PTG = PT.graph
    PTG()

def graph_index(screen):
    label = Label(screen , text = "-20")
    label.pack()
    label.place(x = 70, y = 270)

    label = Label(screen , text = "-10")
    label.pack()
    label.place(x = 70, y = 215)

    label = Label(screen , text = "0")
    label.pack()
    label.place(x = 80, y = 140)

    label = Label(screen , text = "+10")
    label.pack()
    label.place(x = 70, y = 65)

    label = Label(screen , text = "+20")
    label.pack()
    label.place(x = 70, y = 0)

    label = Label(screen , text = "+20")
    label.pack()
    label.place(x = 70, y = 302)

    label = Label(screen , text = "+10")
    label.pack()
    label.place(x = 70, y = 375)

    label = Label(screen , text = "0")
    label.pack()
    label.place(x = 80, y = 450)

    label = Label(screen , text = "-10")
    label.pack()
    label.place(x = 70, y = 525)

    label = Label(screen , text = "-20")
    label.pack()
    label.place(x = 70, y = 600)

my_graph()
 

strantor

Joined Oct 3, 2010
6,798
I am also working on a stripchart project. You may be more pleased with this one. It is more efficient and looks very professional. This code requires no package installations so runs right out of the box, but it is for Python 3; you could modify it to work with Python 2 if you aren't keen on downloading Python 3



The original code I got from stackoverflow, but that code does not work; it has a problem with the way threads are managed, and makes python.exe freeze up when you try to stop or reset the chart. I have corrected that error in the code posted below. The code posted below is a work-in-progress; it's the last "working" version of something that has now evolved quite a bit. I cannot post the most current version as it doesn't work yet and it's really messy. If you're interested, I can post future versions here.

NOTE there is one known issue with this code; You MUST stop or reset the chart before clicking the "X" close button or else the running thread isn't properly terminated.

Python:
#http://stackoverflow.com/questions/457246/what-is-the-best-real-time-plotting-widget-for-wxpython
from tkinter import *
import math, random, threading, time

class StripChart:

    def __init__(self, root):
        self.chartThreadName = "_gen0_"
        self.x=0
        self.data = 0
        self.bg = '#345'
        self.gf = self.makeGraph(root)
        self.cf = self.makeControls(root)
        self.gf.pack()
        self.cf.pack()
        self.Reset()
        self.runcount = 0


    def makeGraph(self, frame):
        self.sw = 1000
        self.h = 200
        self.top = 2
        gf = Canvas(frame, width=self.sw, height=self.h+10,
                    bg="#002", bd=0, highlightthickness=0)
        gf.p = PhotoImage(width=2*self.sw, height=self.h)
        self.item = gf.create_image(0, self.top, image=gf.p, anchor=NW)
        return(gf)

    def makeControls(self, frame):
        cf = Frame(frame, borderwidth=1, relief="raised")
        Button(cf, text="Run", command=self.Run).grid(column=2, row=2)
        Button(cf, text="Stop", command=self.Stop).grid(column=4, row=2)
        Button(cf, text="Reset", command=self.Reset).grid(column=6, row=2)
        self.fps = Label(cf, text="0 fps")
        self.fps.grid(column=2, row=4, columnspan=5)
        return(cf)

    def Run(self):
        self.go = 1
        for t in threading.enumerate():
            if t.name == self.chartThreadName:# "_gen_":
            #if t.name == "_gen0_":
                print("already running")
                return
        #self.chartThreadName = "_gen" + str(self.runcount) + "_"
        self.chartThread = threading.Thread(target=self.do_start, name=self.chartThreadName)
        self.chartThread.start()
        #self.runcount +=1
        print(self.chartThreadName)

    def Stop(self):
        self.go = 0

    def Reset(self):
        self.Stop()
        self.clearstrip(self.gf.p, '#345')

    def do_start(self):
        t = 0
        y2 = 0
        tx = time.time()
        if not self.go:
            self.chartThread.join()
        while self.go:
            y1 = 0.2*math.sin(0.02*math.pi*t)
            y2 = 0.9*y2 + 0.1*(random.random()-0.5)
            self.scrollstrip(self.gf.p,
               (0.25+y1,   0.25, 0.7+y2,   0.6,     0.7,   0.8),
               ( '#ff4', '#f40', '#4af', '#080', '#0f0', '#080'),
                 "" if t % 65 else "#088")

            t += 1
            if not t % 100:
                tx2 = time.time()
                self.fps.config(text='%d fps' % int(100/(tx2 - tx)))
                tx = tx2
            #time.sleep(0.001)

    def clearstrip(self, p, color):  # Fill strip with background color
        self.bg = color              # save background color for scroll
        self.data = None             # clear previous data
        self.x = 0
        p.tk.call(p, 'put', color, '-to', 0, 0, p['width'], p['height'])

    def scrollstrip(self, p, data, colors, bar=""):   # Scroll the strip, add new data
        self.x = (self.x + 1) % self.sw               # x = double buffer position
        bg = bar if bar else self.bg
        p.tk.call(p, 'put', bg, '-to', self.x, 0,
                  self.x+1, self.h)
        p.tk.call(p, 'put', bg, '-to', self.x+self.sw, 0,
                  self.x+self.sw+1, self.h)
        self.gf.coords(self.item, -1-self.x, self.top)  # scroll to just-written column
        if not self.data:
            self.data = data
        for d in range(len(data)):
            y0 = int((self.h-1) * (1.0-self.data[d]))   # plot all the data points
            y1 = int((self.h-1) * (1.0-data[d]))
            ya, yb = sorted((y0, y1))
            for y in range(ya, yb+1):                   # connect the dots
                p.put(colors[d], (self.x,y))
                p.put(colors[d], (self.x+self.sw,y))
        self.data = data            # save for next call

def main():
    root = Tk()
    root.title("StripChart")
    app = StripChart(root)
    root.mainloop()

main()
 

Thread Starter

Vindhyachal Takniki

Joined Nov 3, 2014
594
Thx @strantor for this help.
I was thinking of same. But I thought it would take huge time to print grids again & again on RPI2 board
I am trying to measure what time it would take to sample adc & print it on graph. So I have changed the code as below. Now will see how much time it.
On windows code had taken max 1.5 msec.

Python:
    def pixel(self, C):
[B]        time1 = time.clock()
        var1 = sample_adc_1()
        var2 = sample_adc_2()[/B]
        self.new_ver_pixel_1 = get_pixel(var1  , 0)
        self.new_ver_pixel_2 = get_pixel(var2  , 302)
      
        nP1 = self.new_ver_pixel_1
        oP1 = self.old_ver_pixel_1
        nP2 = self.new_ver_pixel_2
        oP2 = self.old_ver_pixel_2


        hP = self.hor_pixel
        if(nP1 == oP1):
            nP1 = nP1 + 1
        if(nP2 == oP2):
            nP2 = nP2 + 1

        if(nP1 > oP1):
            self.y0_1 = oP1
            self.y1_1 = nP1
        else:
            self.y0_1 = nP1
            self.y1_1 = oP1

        if(nP2 > oP2):
            self.y0_2 = oP2
            self.y1_2 = nP2
        else:
            self.y0_2 = nP2
            self.y1_2 = oP2

        coord = hP, self.y0_1, hP, self.y1_1
        coord2 = hP, self.y0_2, hP, self.y1_2

        hP = hP + 1
        if(hP > 400):
            hP = 5;
        self.old_ver_pixel_1 = nP1
        self.old_ver_pixel_2 = nP2

        #hP is the cycle number (movement along x-axis)
        #this makes vertical black lines every cycle (every x-increment) to create a black background
        C.create_line(hP , 0 , hP , 300, fill = 'black')
        C.create_line(hP , 302 , hP , 602, fill = 'black')
    
        hwls = 75 #Horizontal white line spacing
        vwls = 50 #Vertical white line spacing
    
        #every nTH vertical line, make it white (overwrites previous black line)
        if hP in range (0, 600, vwls):
            C.create_line(hP , 0 , hP , 602, fill = 'white')

        #every nTH horizonal pixel of every vertical line, make it white (overwrites previous black pixels)
        for i in range (0, 602, hwls):
            C.create_line(hP, i, hP, i+1, fill = 'white')

        C.create_line(coord, fill = 'red')
        C.create_line(coord2, fill = 'yellow')
        C.pack()
        self.hor_pixel = hP
        [B]print time.clock() - time1[/B]
      
    def graph(self):
        screen = self.screen

        graph_index(screen)

        screen.title("Analog Channel")
        screen.geometry("800x800")
        C = Tkinter.Canvas(screen , bg = "black", width = 600, height = 602)

        C.pack()
        pixel = self.pixel
        while self.running:
            [B]screen.after(100,pixel(C))[/B]
            screen.update()
        screen.mainloop()
Moderators note: set code to python
 
Last edited by a moderator:

popad

Joined Sep 7, 2016
1
I am also working on a stripchart project. You may be more pleased with this one. It is more efficient and looks very professional. This code requires no package installations so runs right out of the box, but it is for Python 3; you could modify it to work with Python 2 if you aren't keen on downloading Python 3



The original code I got from stackoverflow, but that code does not work; it has a problem with the way threads are managed, and makes python.exe freeze up when you try to stop or reset the chart. I have corrected that error in the code posted below. The code posted below is a work-in-progress; it's the last "working" version of something that has now evolved quite a bit. I cannot post the most current version as it doesn't work yet and it's really messy. If you're interested, I can post future versions here.

NOTE there is one known issue with this code; You MUST stop or reset the chart before clicking the "X" close button or else the running thread isn't properly terminated.

Python:
#http://stackoverflow.com/questions/457246/what-is-the-best-real-time-plotting-widget-for-wxpython
from tkinter import *
import math, random, threading, time

class StripChart:

    def __init__(self, root):
        self.chartThreadName = "_gen0_"
        self.x=0
        self.data = 0
        self.bg = '#345'
        self.gf = self.makeGraph(root)
        self.cf = self.makeControls(root)
        self.gf.pack()
        self.cf.pack()
        self.Reset()
        self.runcount = 0


    def makeGraph(self, frame):
        self.sw = 1000
        self.h = 200
        self.top = 2
        gf = Canvas(frame, width=self.sw, height=self.h+10,
                    bg="#002", bd=0, highlightthickness=0)
        gf.p = PhotoImage(width=2*self.sw, height=self.h)
        self.item = gf.create_image(0, self.top, image=gf.p, anchor=NW)
        return(gf)

    def makeControls(self, frame):
        cf = Frame(frame, borderwidth=1, relief="raised")
        Button(cf, text="Run", command=self.Run).grid(column=2, row=2)
        Button(cf, text="Stop", command=self.Stop).grid(column=4, row=2)
        Button(cf, text="Reset", command=self.Reset).grid(column=6, row=2)
        self.fps = Label(cf, text="0 fps")
        self.fps.grid(column=2, row=4, columnspan=5)
        return(cf)

    def Run(self):
        self.go = 1
        for t in threading.enumerate():
            if t.name == self.chartThreadName:# "_gen_":
            #if t.name == "_gen0_":
                print("already running")
                return
        #self.chartThreadName = "_gen" + str(self.runcount) + "_"
        self.chartThread = threading.Thread(target=self.do_start, name=self.chartThreadName)
        self.chartThread.start()
        #self.runcount +=1
        print(self.chartThreadName)

    def Stop(self):
        self.go = 0

    def Reset(self):
        self.Stop()
        self.clearstrip(self.gf.p, '#345')

    def do_start(self):
        t = 0
        y2 = 0
        tx = time.time()
        if not self.go:
            self.chartThread.join()
        while self.go:
            y1 = 0.2*math.sin(0.02*math.pi*t)
            y2 = 0.9*y2 + 0.1*(random.random()-0.5)
            self.scrollstrip(self.gf.p,
               (0.25+y1,   0.25, 0.7+y2,   0.6,     0.7,   0.8),
               ( '#ff4', '#f40', '#4af', '#080', '#0f0', '#080'),
                 "" if t % 65 else "#088")

            t += 1
            if not t % 100:
                tx2 = time.time()
                self.fps.config(text='%d fps' % int(100/(tx2 - tx)))
                tx = tx2
            #time.sleep(0.001)

    def clearstrip(self, p, color):  # Fill strip with background color
        self.bg = color              # save background color for scroll
        self.data = None             # clear previous data
        self.x = 0
        p.tk.call(p, 'put', color, '-to', 0, 0, p['width'], p['height'])

    def scrollstrip(self, p, data, colors, bar=""):   # Scroll the strip, add new data
        self.x = (self.x + 1) % self.sw               # x = double buffer position
        bg = bar if bar else self.bg
        p.tk.call(p, 'put', bg, '-to', self.x, 0,
                  self.x+1, self.h)
        p.tk.call(p, 'put', bg, '-to', self.x+self.sw, 0,
                  self.x+self.sw+1, self.h)
        self.gf.coords(self.item, -1-self.x, self.top)  # scroll to just-written column
        if not self.data:
            self.data = data
        for d in range(len(data)):
            y0 = int((self.h-1) * (1.0-self.data[d]))   # plot all the data points
            y1 = int((self.h-1) * (1.0-data[d]))
            ya, yb = sorted((y0, y1))
            for y in range(ya, yb+1):                   # connect the dots
                p.put(colors[d], (self.x,y))
                p.put(colors[d], (self.x+self.sw,y))
        self.data = data            # save for next call

def main():
    root = Tk()
    root.title("StripChart")
    app = StripChart(root)
    root.mainloop()

main()
Wonderful code. Thank you to share.

If I want to write annotations with some values on this stripchart, how can I do that ?

Best regards,

PopaD
 
Top