Skip to content
This repository was archived by the owner on Mar 31, 2020. It is now read-only.

Commit 5e4149c

Browse files
committed
Merge branch 'animation' into dev
2 parents 6172803 + 14ee5dd commit 5e4149c

File tree

17 files changed

+444
-308
lines changed

17 files changed

+444
-308
lines changed

Pipfile.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

res/api.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"image": "https://api.thecatapi.com/v1/images/search",
3+
"info": "https://www.pawclub.com.au/assets/js/namesTemp.json",
4+
"hobbies": "https://gist.githubusercontent.com/mbejda/453fdb77ef8d4d3b3a67/raw/e8334f09109dc212892406e25fdee03efdc23f56/hobbies.txt"
5+
}

res/docs/intro.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Note:
2+
3+
Through this quiz you will achieve new levels of spiritual insight, of not only yourself, but the very fabric of reality.
4+
5+
Please answer these very important questions with the utmost honesty, they are crucial to achieving true enlightenment.

res/docs/questions.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"What is your least favorite color?": ["#0801d5", "#ee018d", "#03a130", "#db0013", "#fd9501"]
3+
}

res/images/checkbox.png

5.4 KB
Loading

res/images/loading.gif

442 KB
Loading

res/settings.ini

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,3 @@
1-
[DEFAULT]
2-
main.title = App
3-
main.geometry = 400x500
4-
main.background = black
5-
cachesize = 10
6-
catapi = https://api.thecatapi.com/v1/images/search
7-
namefile = https://www.pawclub.com.au/assets/js/namesTemp.json
8-
hobbiesfile = https://gist.githubusercontent.com/mbejda/453fdb77ef8d4d3b3a67/raw/e8334f09109dc212892406e25fdee03efdc23f56/hobbies.txt
9-
like.text = Like
10-
like.background = red
11-
dislike.text = Dislike
12-
dislike.background = red
13-
back.text = Back to Photo
14-
back.background = blue
15-
bio.text = Bio
16-
bio.background = blue
17-
18-
191
[APP]
20-
title = App
2+
title = The Always (One-Hundred Percent of the Time) Correct and Never Wrong (In Any Way), Completely Accurate Personality Quiz Adventure
3+
geometry = 400x500

src/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@
88
THEME: Path = RES / 'widgets.ini'
99
IMAGES: Path = RES / 'images'
1010
SOUNDS: Path = RES / 'sounds'
11+
API: Path = RES / 'api.json'
12+
DOCS: Path = RES / 'docs'

src/__main__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from .main import App
22

33
if __name__ == "__main__":
4-
App().mainloop()
4+
root = App()
5+
root.protocol('WM_DELETE_WINDOW', root.cleanup)
6+
root.mainloop()

src/animate.py

Lines changed: 116 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,120 @@
33
import tkinter as tk
44
import operator
55
import time
6-
from typing import NamedTuple, Callable, TypeVar, Generator, Tuple
6+
import math
7+
from typing import NamedTuple, Callable, TypeVar, Generator
78
from enum import Enum
89
from dataclasses import dataclass
9-
from functools import partialmethod, partial
10+
from functools import partialmethod
11+
12+
13+
class Animater:
14+
"""
15+
Manager for executing animations.
16+
17+
example::
18+
19+
```
20+
motion = Motion(...)
21+
window = Animater(...)
22+
window.add_motion(motion)
23+
```
24+
"""
25+
_motions = set()
26+
27+
def __init__(self, canvas: tk.Canvas):
28+
self.canvas = canvas
29+
30+
def start(self):
31+
while self._motions:
32+
self.run()
33+
34+
def run(self):
35+
for motion in self._motions.copy():
36+
try:
37+
move = next(motion)
38+
move()
39+
except StopIteration:
40+
self._motions.remove(motion)
41+
42+
def add(self, motion: Motion):
43+
self._motions.add(motion.start())
44+
45+
def add_motion(self, id: int, end: Coord, **kwargs):
46+
motion = Motion(self.canvas, id, end, **kwargs)
47+
self.add(motion)
48+
49+
def clear(self):
50+
self._motions.clear()
51+
52+
@property
53+
def running(self):
54+
return bool(self._motions)
55+
56+
57+
@dataclass
58+
class Motion:
59+
"""
60+
61+
"""
62+
canvas: tk.Canvas
63+
id: int
64+
end: Coord
65+
66+
speed: int = 1
67+
68+
def __iter__(self):
69+
return self.start()
70+
71+
def __key(self):
72+
return self.id
73+
74+
def __hash__(self):
75+
return hash(self.__key())
76+
77+
def __eq__(self):
78+
return isinstance(self, type(other)) and self.__key() == other.__key()
79+
80+
def start(self) -> Generator[Callable]:
81+
"""
82+
The entry point for generating move commands.
83+
"""
84+
self.time = time.time()
85+
self.start = self.current
86+
self.distance = self.start.distance(self.end)
87+
self.speed = self.speed ** 3
88+
while self.current != self.end:
89+
yield self.move
90+
91+
def move(self):
92+
self.canvas.move(self.id, *self.increment)
93+
self.canvas.update_idletasks()
94+
95+
@property
96+
def time(self):
97+
return time.time() - self._time
98+
99+
@time.setter
100+
def time(self, val):
101+
self._time = val
102+
103+
@property
104+
def increment(self):
105+
mult = (self.time * self.speed) / self.distance
106+
point = (self.end - self.start) * mult + self.start
107+
108+
if point.distance(self.end) > self.journey:
109+
return self.end - self.current
110+
else:
111+
return point - self.current
112+
113+
@property
114+
def current(self):
115+
return Coord(*self.canvas.coords(self.id))
116+
117+
@property
118+
def journey(self):
119+
return self.current.distance(self.end)
10120

11121

12122
class Coord(NamedTuple):
@@ -60,18 +170,18 @@ def midpoint(self, other: Coord) -> Coord:
60170
"""
61171
return (self + other) / 2
62172

63-
def distance(self, other: Coord) -> int:
173+
def distance(self, other: Coord) -> float:
64174
"""
65-
The Manhattan distance between `self` and `other`.
175+
The distance between `self` and `other`.
66176
67177
param:
68178
other: Coord -- THe point to consider.
69179
70180
return:
71181
int -- A numeric representation of the distance between two points.
72182
"""
73-
dist = map(abs, other - self)
74-
return sum(dist)
183+
diff = other - self
184+
return math.hypot(*diff)
75185

76186
__add__ = partialmethod(__apply, operator.add)
77187
__sub__ = partialmethod(__apply, operator.sub)
@@ -111,127 +221,3 @@ def __add__(self, other: Direction) -> Coord:
111221

112222
def flip(self):
113223
return Direction(Coord(0, 0) - self.value)
114-
115-
116-
class Animater:
117-
"""
118-
Manager for executing animations.
119-
120-
example::
121-
122-
```
123-
motion = Motion(...)
124-
window = Animater(...)
125-
window.add_motion(motion)
126-
```
127-
"""
128-
_motions = set()
129-
fps = 60
130-
131-
def __init__(self, canvas: tk.Canvas):
132-
self.canvas = canvas
133-
134-
def start(self):
135-
while self._motions:
136-
time.sleep(1/self.fps)
137-
self.run()
138-
139-
def run(self):
140-
for motion in self._motions:
141-
try:
142-
next(motion)()
143-
self.canvas.update()
144-
except StopIteration:
145-
self._motions.remove(motion)
146-
break
147-
148-
def add(self, motion: Motion):
149-
self._motions.add(iter(motion))
150-
151-
def add_motion(self, id: int, end: Coord, **kwargs):
152-
motion = Motion(self.canvas, id, (end,), **kwargs)
153-
self.add(motion)
154-
155-
def clear(self):
156-
self._motions.clear()
157-
158-
159-
@dataclass
160-
class Motion:
161-
"""
162-
Defines the movements derived from a generated vector.
163-
The result is a two dimensional generator: the first dimension yields
164-
a "frame" generator, which in turn yields move commands. This structure allows
165-
for different `speed`s of motion, as the length of the second
166-
dimension is determined by `speed`. In other words, the `speed` determines
167-
how many movements occur in one frame.
168-
169-
param:
170-
canvas: tk.Canvas -- The parent canvas to issue the move command with.
171-
id: int -- The id of the widget to be animated.
172-
endpoints: Tuple[Coord] -- The final position(s) of the widget. Multiple positions allow for
173-
more intricate pathing.
174-
speed: int (optional) -- The multipler for move commands per frame.
175-
Defaults to 1.
176-
177-
example::
178-
179-
```
180-
root = tk.Tk()
181-
182-
window = Animater(root)
183-
window.pack()
184-
185-
c1 = Coord(50, 55)
186-
c2 = Coord(60, 65)
187-
rect = window.create_rectangle(c1, c2)
188-
189-
end = c1 + Direction.RIGHT * 50
190-
end2 = end + Direction.DOWN * 50
191-
end3 = end2 + (Direction.UP + Direction.LEFT) * 50
192-
193-
animation = Motion(window, rect, (end, end2, end3), speed=1)
194-
195-
window.add_motion(animation)
196-
window.add_event('<B1-Motion>')
197-
198-
root.mainloop()
199-
```
200-
"""
201-
canvas: tk.Canvas
202-
id: int
203-
endpoints: Tuple[Coord]
204-
205-
speed: int = 1
206-
207-
def start(self) -> Generator[Callable]:
208-
"""
209-
The entry point for generating move commands.
210-
"""
211-
move = partial(self.canvas.move, self.id)
212-
213-
def frame(increment: Coord, count: int):
214-
for _ in range(count):
215-
move(*increment)
216-
self.canvas.master.update_idletasks()
217-
218-
for end in self.endpoints:
219-
start = Coord(*self.canvas.coords(self.id)[:2])
220-
steps = round(start.distance(end) / self.speed)
221-
frames = round(steps / self.speed)
222-
increment = (end - start) / steps
223-
224-
for _ in range(frames):
225-
yield partial(frame, increment, round(steps / frames))
226-
227-
def __iter__(self):
228-
return self.start()
229-
230-
def __key(self):
231-
return self.id
232-
233-
def __hash__(self):
234-
return hash(self.__key())
235-
236-
def __eq__(self):
237-
return isinstance(self, type(other)) and self.__key() == other.__key()

0 commit comments

Comments
 (0)