This module was inspired by http://processing.org/ . The code is meant to mimic an extremely small subset of this high-performance library. After seeing how easy it was to accomplish certain task in the Java framework, a desire was created to have a simple starting point to create little graphical experiments that would be easy to program without haveing to know a lot of details regarding setup, timing, et cetera. This is the result of that desire.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | import itertools
import time
import tkinter
import vector
################################################################################
class Polygon:
"Polygon(*vertices) -> Polygon"
__slots__ = 'vertices'
def __init__(self, *vertices):
"Initializes polygon with a list of vertices."
self.vertices = vertices
def translate(self, offset):
"Moves the polygon by the specified offset."
for vertex in self.vertices:
vertex += offset
def rotate(self, direction):
"Rotates the polygon by a vector's direction."
for vertex in self.vertices:
vertex.direction += direction
def scale(self, factor):
"Increases or decreases size of the polygon."
for vertex in self.vertices:
vertex *= factor
def copy(self):
"Copies the polygon by copying its vertices."
return Polygon(*(vertex.copy() for vertex in self.vertices))
################################################################################
class Graphics:
"Graphics(canvas) -> Graphics"
__slots__ = 'canvas'
def __init__(self, canvas):
"Initializes graphics by wrapping a canvas."
self.canvas = canvas
def draw(self, polygon, fill, outline):
"Draws a polygon on the underlying canvas."
self.canvas.create_polygon(*itertools.chain(*polygon.vertices),
fill=fill, outline=outline)
def write(self, x, y, text, fill):
"Writes the text to bottom-left of location."
self.canvas.create_text(x, y, text=text, fill=fill, anchor=tkinter.NW)
def clear(self):
"Clears canvas of all objects shown on it."
self.canvas.delete(tkinter.ALL)
def fill(self, background):
"Fills in the canvas with the given color."
self.canvas.configure(background=background)
################################################################################
class Process(tkinter.Frame):
"Process(master, width, height) -> Process"
FRAMESKIP = 5 # How many frames should skip before issuing speed warning?
RENDER_EACH_SECOND = 15 # How often should the DISPLAY be updated / second?
UPDATE_EACH_SECOND = 30 # How often should the PHYSICS be updated / second?
@classmethod
def main(cls, width, height):
"Creates a process in a window and executes it."
tkinter.NoDefaultRoot()
root = tkinter.Tk()
root.title('Processing 1.1')
root.resizable(False, False)
view = cls(root, width, height)
view.grid()
root.mainloop()
########################################################################
def __init__(self, master, width, height):
"Initializes process and starts simulation loops."
super().__init__(master)
canvas = tkinter.Canvas(self, width=width, height=height,
background='white', highlightthickness=0)
canvas.bind('<1>', self.mouse_pressed)
canvas.grid()
graphics = Graphics(canvas)
self.setup(graphics.fill)
render = 1 / self.RENDER_EACH_SECOND
update = 1 / self.UPDATE_EACH_SECOND
self.__loop(lambda: self.render(graphics), render, time.clock(), 0)
self.__loop(lambda: self.update(update), update, time.clock(), 0)
def __loop(self, method, interval, target, errors):
"Runs the method after each interval has passed."
method()
target += interval
ms = int((target - time.clock()) * 1000)
if ms >= 0:
self.after(ms, self.__loop, method, interval, target, 0)
elif errors == self.FRAMESKIP:
self.speed_warning()
self.after_idle(self.__loop, method, interval, target, 0)
else:
self.after_idle(self.__loop, method, interval, target, errors + 1)
########################################################################
def setup(self, background):
"This method is called before render and update."
raise NotImplementedError()
def render(self, graphics):
"This method is called when screen should render."
raise NotImplementedError()
def update(self, interval):
"This method is called when physics should update."
raise NotImplementedError()
def mouse_pressed(self, event):
"This method is called when left-clicking mouse."
raise NotImplementedError()
def speed_warning(self):
"This method is called when code is running slow."
raise NotImplementedError()
################################################################################
import recipe576904; recipe576904.bind_all(globals())
|