123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- import time, math
- import tkinter as tk
- from PIL import Image, ImageDraw, ImageTk, ImageFont
- class Graph(tk.Canvas):
- def __init__(self, root, scale=(-50, 450), **kwargs):
- self.root = root
- tk.Canvas.__init__(self, root, **kwargs)
- self.image = self.create_image(0, 0, image=None, anchor='nw')
- self.height = self.winfo_reqheight()
- self.width = self.winfo_reqwidth()
- # store last point of plot to connect the lines
- self.lastPoints = [(0, 0)] * 3
- # scale contains the (min, max) values of both axes
- self.scale = scale
- # appearance of the plots
- self.colors = [(100, 255, 100, 255), (255, 100, 100, 255) ,(100, 100, 255, 255)]
- self.font = ImageFont.truetype("gui/SourceSansPro-Semibold.otf", 12)
- self.lineWidth = 1
-
- # the background contains all static elements
- self.drawBackground()
- # the plots will be drawn on a separate canvas
- self.canvas = self.bg.copy()
- self.bind("<Configure>", self.on_resize)
- def drawBackground(self):
- self.bg = Image.new('RGB', (self.width, self.height), (0,10,0))
- draw = ImageDraw.Draw(self.bg)
- # draw x and y axis
- axes = self.pointToCoord((0, 0))
- draw.line([(0, axes[1]), (self.width, axes[1])], (60,127,127), self.lineWidth)
- draw.line([(axes[0], 0), (axes[0], self.height)], (60,127,127), self.lineWidth)
- step = 10**int(math.log10(self.scale[1]-self.scale[0]))
- begin = math.floor(self.scale[0] / step) * step
- end = math.ceil(self.scale[1] / step) * step
- halfStep = int(step / 2)
- # label x and y axis
- draw.text(self.pointToCoord(((end+begin)/2+25, -30)), "x in mm", font=self.font, align='center')
- draw.text(self.pointToCoord((-45, (end+begin)/2+25)), "y in mm", font=self.font, align='left')
- for p in range(begin, end, halfStep):
- tickPosX = self.pointToCoord((p, 0))
- tickPosY = self.pointToCoord((0, p))
- #draw grid
- draw.line([(tickPosX[0], self.height), (tickPosX[0], 0)], (60,127,60), 1)
- draw.line([(self.width, tickPosY[1]), (0, tickPosY[1])], (60,127,60), 1)
- for p in range(begin, end, step):
- tickPosX = self.pointToCoord((p, 0))
- tickPosY = self.pointToCoord((0, p))
- # draw ticks
- draw.line([(tickPosX[0], tickPosX[1]+self.lineWidth*3), (tickPosX[0], tickPosX[1]-self.lineWidth*3)], (60,127,100), int(self.lineWidth/2))
- draw.line([(tickPosY[0]+self.lineWidth*3, tickPosY[1]), (tickPosY[0]-self.lineWidth*3, tickPosY[1])], (60,127,100), int(self.lineWidth/2))
- # draw tick labels
- draw.text((tickPosX[0]+3, tickPosX[1]+self.lineWidth*1), str(p), font=self.font)
- if p != 0:
- draw.text((tickPosY[0]+self.lineWidth*2, tickPosY[1]), str(p), font=self.font, align='right')
- def on_resize(self,event):
- self.width = max(100, event.width-4)
- self.height = max(100, event.height-4)
- self.lineWidth = int(max(min(self.width,self.height) / 100, 1))
- # resize the canvas
- self.canvas = self.canvas.resize((self.width, self.height))
- self.drawBackground()
- self.canvas = Image.blend(self.canvas, self.bg, 1)
- # convert physical space to screen space
- def pointToCoord(self, point):
- return ((point[0] - self.scale[0]) / (self.scale[1]-self.scale[0]) * self.width,
- self.height - (point[1] - self.scale[0]) / (self.scale[1]-self.scale[0]) * self.height - 1)
- def update(self, data):
- # load first point of line
- coord = [[self.pointToCoord(p)] for p in self.lastPoints if p]
- newPoints = False
- # append new points
- for i in range(len(data)):
- for point in data[i]:
- coord[i].append(self.pointToCoord(point))
- self.lastPoints[i] = point
- newPoints = True
- self.canvas = Image.blend(self.canvas, self.bg, 1/100)
- if newPoints:
- #fade out old lines by mixing with background
- draw = ImageDraw.Draw(self.canvas)
- for i in range(len(coord)):
- draw.line(coord[i], fill=self.colors[i], width=self.lineWidth+2, joint='curve')
- # draw to tk.Canvas
- self.photo = ImageTk.PhotoImage(self.canvas)
- self.itemconfig(self.image, image=self.photo)
- def clear(self):
- self.canvas = self.bg.copy()
|