Last active
November 15, 2016 16:32
-
-
Save sourceperl/af93d3c64286bb1b62f8d95732873279 to your computer and use it in GitHub Desktop.
Python/Tk graph plotter test
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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