Skip to content

Instantly share code, notes, and snippets.

@sourceperl
Last active November 15, 2016 16:32
Show Gist options
  • Save sourceperl/af93d3c64286bb1b62f8d95732873279 to your computer and use it in GitHub Desktop.
Save sourceperl/af93d3c64286bb1b62f8d95732873279 to your computer and use it in GitHub Desktop.
Python/Tk graph plotter test
#!/usr/bin/env python3
from tkinter import *
from random import randint
from collections import deque
from itertools import chain
class CanGraph(Canvas):
def __init__(self, master, **options):
super(CanGraph, self).__init__(master, **options)
# Canvas.__init__(self, master, **options)
self['background'] = 'black'
self.line_color = '#476042'
self.x_line_color = '#FF0000'
self.x_labels = []
self.x_margin = 20
self.y_labels = []
self.y_margin = 20
self.y_max = None
self.y_min = None
self._plots = {}
def get_height(self):
return int(self['height'])
def get_width(self):
return int(self['width'])
def can_xy(self, *args):
# set args as list for normalize cv_xy(x, y) = cv_xy([x, y])...
r_list = []
if isinstance(args[0], (list, tuple)):
r_list = list(chain.from_iterable(args))
elif isinstance(args[0], int):
r_list = list(args)
# in (x1 ,y1, x2, y2) normalize xy coord
for index, v in enumerate(r_list):
if index % 2:
# y
r_list[index] = self.get_height() - r_list[index] - self.y_margin
else:
# x
r_list[index] += self.x_margin
return r_list
def add_line(self, *args, color=None, tags='all'):
# check params
if not color:
color = self.line_color
# draw
self.create_line(self.can_xy(args), fill=color, tags=tags)
def add_point(self, *args, color=None, tags='all'):
# check params
if isinstance(args[0], (list, tuple)):
(x, y) = list(args[0])
elif isinstance(args[0], int):
(x, y) = list(args)
if not color:
color = self.line_color
# draw
self.create_oval(self.can_xy((x + 3, y + 3), (x - 3, y - 3)), fill=color, outline=color, tags=tags)
def add_plot(self, name, data):
self._plots[name] = {'data': data}
def toggle_plot(self, name, data):
if self.have_plot(name):
self.rm_plot(name)
else:
self.add_plot(name, data)
def have_plot(self, name):
return name in self._plots
def rm_plot(self, name):
if self.have_plot(name):
del self._plots[name]
def rm_all_plots(self):
self._plots = {}
def render(self):
# wipe
self.clear()
# auto step/auto scale
v_max = -10000
v_min = 10000
len_max = 2
for name in self._plots:
data = self._plots[name]['data']
if data:
v_max = max(max(data), v_max)
v_min = min(min(data), v_min)
len_max = max(len(data), len_max)
if self.y_max is not None:
v_max = self.y_max
if self.y_min is not None:
v_min = self.y_min
x_step = int(round((self.get_width()-self.x_margin) / (len_max - 1), 0))
# draw x labels
if self.x_labels is not None:
# x axe
self.create_line(self.can_xy((0, 0), (self.get_width(), 0)), fill=self.x_line_color, width=2)
# x ticks
for x_ticks in range(0, self.get_width(), x_step):
self.create_line(self.can_xy((x_ticks, 0), (x_ticks, 5)), fill=self.x_line_color, width=2)
# x labels
for (i, x_label) in enumerate(self.x_labels):
self.create_text(self.can_xy(x_step * i, -self.y_margin/2), text=x_label, fill=self.x_line_color)
# draw y labels
if self.y_labels is not None:
# y axe
self.create_line(self.can_xy((0, 0), (0, self.get_height())), fill=self.x_line_color, width=2)
# draw loop
for name in self._plots:
data = self._plots[name]['data']
last_pt = None
x = 0
for value in data:
# scale value to map y pos
y = ((value - v_min)/(v_max - v_min)) * (self.get_height() - self.y_margin)
# draw line and point
if last_pt:
self.add_line(last_pt[0], last_pt[1], x, y, tags=name)
self.add_point(x, y, tags=name)
last_pt = (x, y)
x += x_step
def clear(self):
self.delete('all')
# Tk app
master = Tk()
# graph
can = CanGraph(master, width=800, height=400)
can.pack()
can.y_max = 1000
can.y_min = 0
# build X labels
for i in range(100):
can.x_labels.append(str(i) if not i % 2 else '')
t = deque([randint(150, 250) for i in range(60)])
def plot1():
can.toggle_plot('l1', (0, 100, 200, 250, 300, 321, 322, 355, 366))
can.render()
def plot2():
can.toggle_plot('l2', (50, 0, 10, 25, 30, 55))
can.render()
def plot3():
can.toggle_plot('l3', [randint(0, 400) for i in range(60)])
can.render()
def toggle_anim():
can.toggle_plot('l4', ())
can.render()
def animation():
if can.have_plot('l4'):
avg = int(sum(t)/len(t))
t.append(int((randint(100, 300) + 4*avg) / 5))
if len(t) > 60:
t.popleft()
can.add_plot('l4', t)
can.render()
can.after(300, animation)
# buttons
b_plot1 = Button(master, text='Plot 1', command=plot1)
b_plot1.pack(side=LEFT, padx=5, pady=5)
b_plot2 = Button(master, text='Plot 2', command=plot2)
b_plot2.pack(side=LEFT, padx=5, pady=5)
b_plot3 = Button(master, text='Plot 3', command=plot3)
b_plot3.pack(side=LEFT, padx=5, pady=5)
b_plot4 = Button(master, text='On/off anim', command=toggle_anim)
b_plot4.pack(side=LEFT, padx=5, pady=5)
b_quit = Button(master, text='Quit', command=lambda: exit(0))
b_quit.pack(side=RIGHT, padx=5, pady=5)
animation()
mainloop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment