turtleshell 1.1.0__tar.gz → 1.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {turtleshell-1.1.0 → turtleshell-1.3.0}/PKG-INFO +1 -1
- {turtleshell-1.1.0 → turtleshell-1.3.0}/README.md +0 -0
- {turtleshell-1.1.0 → turtleshell-1.3.0}/pyproject.toml +2 -2
- {turtleshell-1.1.0 → turtleshell-1.3.0}/setup.cfg +0 -0
- {turtleshell-1.1.0 → turtleshell-1.3.0}/src/turtleshell/__init__.py +1 -1
- turtleshell-1.3.0/src/turtleshell/core.py +291 -0
- {turtleshell-1.1.0 → turtleshell-1.3.0}/src/turtleshell.egg-info/PKG-INFO +1 -1
- {turtleshell-1.1.0 → turtleshell-1.3.0}/src/turtleshell.egg-info/SOURCES.txt +0 -0
- {turtleshell-1.1.0 → turtleshell-1.3.0}/src/turtleshell.egg-info/dependency_links.txt +0 -0
- {turtleshell-1.1.0 → turtleshell-1.3.0}/src/turtleshell.egg-info/requires.txt +0 -0
- {turtleshell-1.1.0 → turtleshell-1.3.0}/src/turtleshell.egg-info/top_level.txt +0 -0
- {turtleshell-1.1.0 → turtleshell-1.3.0}/tests/test_turtle.py +4 -2
- turtleshell-1.1.0/src/turtleshell/core.py +0 -174
File without changes
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "turtleshell"
|
3
|
-
version = "1.
|
3
|
+
version = "1.3.0"
|
4
4
|
authors = [
|
5
5
|
{ name="Sam Mangan", email="sam.mangan2@gmail.com" },
|
6
6
|
]
|
@@ -27,7 +27,7 @@ addopts = [
|
|
27
27
|
]
|
28
28
|
|
29
29
|
[tool.bumpver]
|
30
|
-
current_version = "1.
|
30
|
+
current_version = "1.3.0"
|
31
31
|
version_pattern = "MAJOR.MINOR.PATCH"
|
32
32
|
commit_message = "Bump version {old_version} -> {new_version}"
|
33
33
|
commit = true
|
File without changes
|
@@ -0,0 +1,291 @@
|
|
1
|
+
"""Convenience wrappers around turtle.Turtle and turtle.Screen
|
2
|
+
See https://github.com/python/cpython/blob/main/Lib/turtle.py
|
3
|
+
"""
|
4
|
+
#TODO handle floats, randcoords, default screensize, default shape
|
5
|
+
#TODO # def _polytrafo(self, poly):
|
6
|
+
|
7
|
+
import colorsys, math, turtle
|
8
|
+
from dataclasses import dataclass
|
9
|
+
|
10
|
+
sign = lambda x: round(math.copysign(1, x))
|
11
|
+
|
12
|
+
@dataclass
|
13
|
+
class HSV:
|
14
|
+
hue: float
|
15
|
+
sat: float
|
16
|
+
val: float
|
17
|
+
def __iter__(self):
|
18
|
+
return iter(self.__dict__.values())
|
19
|
+
|
20
|
+
def Screen():
|
21
|
+
"""Return the singleton screen object."""
|
22
|
+
if Turtle._screen is None:
|
23
|
+
Turtle._screen = _Screen()
|
24
|
+
return Turtle._screen
|
25
|
+
|
26
|
+
class _Screen(turtle._Screen):
|
27
|
+
def __init__(self):
|
28
|
+
super().__init__()
|
29
|
+
turtle.TurtleScreen.__init__(self, _Screen._canvas)
|
30
|
+
if turtle.Turtle._screen is None:
|
31
|
+
turtle.Turtle._screen = self
|
32
|
+
self.colormode(255)
|
33
|
+
self.timers = {}
|
34
|
+
|
35
|
+
@property
|
36
|
+
def width(self):
|
37
|
+
return self.window_width()
|
38
|
+
|
39
|
+
@property
|
40
|
+
def height(self):
|
41
|
+
return self.window_height()
|
42
|
+
|
43
|
+
def setup(self, width=turtle._CFG["width"], height=turtle._CFG["height"],
|
44
|
+
startx=turtle._CFG["leftright"], starty=turtle._CFG["topbottom"]):
|
45
|
+
super().setup(width, height, startx, starty)
|
46
|
+
self.screensize(self.width-20, self.height-20)
|
47
|
+
|
48
|
+
def _onkeypress(self, fun, key=None):
|
49
|
+
if fun is None:
|
50
|
+
if key is None:
|
51
|
+
for key in self._keys:
|
52
|
+
self._keys.remove(key)
|
53
|
+
self.cv.unbind("<KeyPress-%s>" % key, None)
|
54
|
+
else:
|
55
|
+
self.cv.unbind("<KeyPress-%s>" % key, None)
|
56
|
+
else:
|
57
|
+
def eventfun(event):
|
58
|
+
fun()
|
59
|
+
if key is None:
|
60
|
+
self.cv.bind("<KeyPress>", eventfun)
|
61
|
+
else:
|
62
|
+
self.cv.bind("<KeyPress-%s>" % key, eventfun)
|
63
|
+
|
64
|
+
def onmove(self, fun):
|
65
|
+
def eventfun(event):
|
66
|
+
x, y = (self.cv.canvasx(event.x)/self.xscale,
|
67
|
+
-self.cv.canvasy(event.y)/self.yscale)
|
68
|
+
fun(x, y)
|
69
|
+
self.cv.bind("<Motion>", eventfun)
|
70
|
+
|
71
|
+
def cancel_timer(self, func):
|
72
|
+
if not func in self.timers:
|
73
|
+
return
|
74
|
+
for id in self.timers.pop(func):
|
75
|
+
self.getcanvas().after_cancel(id)
|
76
|
+
|
77
|
+
def set_timer(self, func, ms, add=False):
|
78
|
+
if not add and func in self.timers:
|
79
|
+
self.cancel_timer(func)
|
80
|
+
id = self.getcanvas().after(ms, func)
|
81
|
+
if func in self.timers:
|
82
|
+
self.timers[func].append(id)
|
83
|
+
else:
|
84
|
+
self.timers[func] = [id]
|
85
|
+
|
86
|
+
def _colorstr(self, color):
|
87
|
+
isnumber = lambda x: isinstance(x, (int, float))
|
88
|
+
if isinstance(color, tuple) and len(color) == 1:
|
89
|
+
color = color[0]
|
90
|
+
if len(color) == 3 and all([isnumber(c) for c in color]):
|
91
|
+
lower, upper = 0, Turtle._screen.colormode()
|
92
|
+
color = [max(min(upper, round(c)), lower) for c in color]
|
93
|
+
return super()._colorstr(color)
|
94
|
+
|
95
|
+
def _hsv_to_rgb(hsv):
|
96
|
+
rgb = colorsys.hsv_to_rgb(*hsv)
|
97
|
+
return [round(c*Turtle._screen.colormode()) for c in rgb]
|
98
|
+
|
99
|
+
class Turtle(turtle.RawTurtle):
|
100
|
+
|
101
|
+
_pen = None
|
102
|
+
_screen = None
|
103
|
+
MULT = 20
|
104
|
+
|
105
|
+
def __init__(self,
|
106
|
+
shape=turtle._CFG["shape"],
|
107
|
+
undobuffersize=turtle._CFG["undobuffersize"],
|
108
|
+
visible=turtle._CFG["visible"]):
|
109
|
+
if Turtle._screen is None:
|
110
|
+
Turtle._screen = Screen()
|
111
|
+
turtle.RawTurtle.__init__(self, Turtle._screen,
|
112
|
+
shape=shape,
|
113
|
+
undobuffersize=undobuffersize,
|
114
|
+
visible=visible)
|
115
|
+
self.shapesize(Turtle.MULT)
|
116
|
+
self._pen_hsv = HSV(0, 1, 1)
|
117
|
+
self._fill_hsv = HSV(0, 1, 1)
|
118
|
+
|
119
|
+
def __lt__(self, other):
|
120
|
+
if isinstance(other, Turtle):
|
121
|
+
return self.y < other.y
|
122
|
+
return NotImplemented
|
123
|
+
|
124
|
+
def __eq__(self, other):
|
125
|
+
if isinstance(other, Turtle):
|
126
|
+
return self.y == other.y
|
127
|
+
return NotImplemented
|
128
|
+
|
129
|
+
def onenter(self, fun):
|
130
|
+
titem = self.turtle._item
|
131
|
+
if fun is None:
|
132
|
+
self.screen.cv.tag_unbind(titem, "<Enter>")
|
133
|
+
else:
|
134
|
+
def eventfun(event):
|
135
|
+
x, y = (self.screen.cv.canvasx(event.x)/self.screen.xscale,
|
136
|
+
-self.screen.cv.canvasy(event.y)/self.screen.yscale)
|
137
|
+
fun(x, y)
|
138
|
+
self.screen.cv.tag_bind(titem, "<Enter>", eventfun)
|
139
|
+
|
140
|
+
def onexit(self, fun):
|
141
|
+
titem = self.turtle._item
|
142
|
+
if fun is None:
|
143
|
+
self.screen.cv.tag_unbind(titem, "<Leave>")
|
144
|
+
else:
|
145
|
+
def eventfun(event):
|
146
|
+
x, y = (self.screen.cv.canvasx(event.x)/self.screen.xscale,
|
147
|
+
-self.screen.cv.canvasy(event.y)/self.screen.yscale)
|
148
|
+
fun(x, y)
|
149
|
+
self.screen.cv.tag_bind(titem, "<Leave>", eventfun)
|
150
|
+
|
151
|
+
def bring_forward(self):
|
152
|
+
titem = self.turtle._item
|
153
|
+
for item in self.items:
|
154
|
+
self.screen.cv.tag_raise(item)
|
155
|
+
titem = self.turtle._item
|
156
|
+
self.screen.cv.tag_raise(titem)
|
157
|
+
|
158
|
+
def send_backward(self):
|
159
|
+
titem = self.turtle._item
|
160
|
+
self.screen.cv.tag_lower(titem)
|
161
|
+
|
162
|
+
def face(self, x, y):
|
163
|
+
self.setheading(self.towards(x, y))
|
164
|
+
|
165
|
+
@property
|
166
|
+
def x(self):
|
167
|
+
return self.xcor()
|
168
|
+
|
169
|
+
@x.setter
|
170
|
+
def x(self, value):
|
171
|
+
self.setx(value)
|
172
|
+
|
173
|
+
@property
|
174
|
+
def y(self):
|
175
|
+
return self.ycor()
|
176
|
+
|
177
|
+
@y.setter
|
178
|
+
def y(self, value):
|
179
|
+
self.sety(value)
|
180
|
+
|
181
|
+
def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
|
182
|
+
|
183
|
+
if stretch_wid is None and stretch_len is None and outline is None:
|
184
|
+
stretch_wid, stretch_len, outline = super().shapesize()
|
185
|
+
return stretch_wid*Turtle.MULT, stretch_len*Turtle.MULT, outline
|
186
|
+
|
187
|
+
stretch_wid = stretch_wid/Turtle.MULT if stretch_wid else None
|
188
|
+
stretch_len = stretch_len/Turtle.MULT if stretch_len else None
|
189
|
+
ret = super().shapesize(stretch_wid, stretch_len, outline)
|
190
|
+
return ret
|
191
|
+
|
192
|
+
def teleport(self, x, y):
|
193
|
+
pendown = self.isdown()
|
194
|
+
if pendown:
|
195
|
+
self.pen(pendown=False)
|
196
|
+
self.penup()
|
197
|
+
self._position = turtle.Vec2D(x, y)
|
198
|
+
self.pen(pendown=pendown)
|
199
|
+
|
200
|
+
def _write(self, txt, align, font):
|
201
|
+
"""Performs the writing for write()
|
202
|
+
"""
|
203
|
+
item, end = self.screen._write(self._position, txt, align, font,
|
204
|
+
self._pencolor)
|
205
|
+
|
206
|
+
self._update()
|
207
|
+
self.items.append(item)
|
208
|
+
if self.undobuffer:
|
209
|
+
self.undobuffer.push(("wri", item))
|
210
|
+
return end
|
211
|
+
|
212
|
+
def write(self, arg, move=False, align="center", font=("Courier New", 20, "bold")):
|
213
|
+
super().write(arg, move, align, font)
|
214
|
+
self.bring_forward() # TODO this will be undone if Screen.update() is called
|
215
|
+
|
216
|
+
def to_front(self):
|
217
|
+
self.goto(self.position())
|
218
|
+
|
219
|
+
## HSV colour methods
|
220
|
+
def hsv(self, hue, sat, val):
|
221
|
+
return
|
222
|
+
|
223
|
+
def penhsv(self, *args):
|
224
|
+
print(args)
|
225
|
+
|
226
|
+
def fillhsv(self, *args):
|
227
|
+
print(args)
|
228
|
+
|
229
|
+
def hue(self, degrees=None):
|
230
|
+
return (self.penhue(degrees), self.fillhue(degrees))
|
231
|
+
|
232
|
+
def penhue(self, degrees=None):
|
233
|
+
if degrees is None:
|
234
|
+
return self._pen_hsv.hue * 360
|
235
|
+
self._pen_hsv.hue = degrees/360
|
236
|
+
self.pencolor(_hsv_to_rgb(self._pen_hsv))
|
237
|
+
return degrees
|
238
|
+
|
239
|
+
def fillhue(self, degrees=None):
|
240
|
+
if degrees is None:
|
241
|
+
return self._fill_hsv.hue * 360
|
242
|
+
self._fill_hsv.hue = degrees/360
|
243
|
+
self.fillcolor(_hsv_to_rgb(self._fill_hsv))
|
244
|
+
|
245
|
+
def sat(self, value=None):
|
246
|
+
return(self.pensat(value), self.fillsat(value))
|
247
|
+
|
248
|
+
def pensat(self, value=None):
|
249
|
+
if value is None:
|
250
|
+
return self._pen_hsv.sat * 100
|
251
|
+
self._pen_hsv.sat = value/100
|
252
|
+
self.pencolor(_hsv_to_rgb(self._pen_hsv))
|
253
|
+
|
254
|
+
def fillsat(self, value=None):
|
255
|
+
if value is None:
|
256
|
+
return self._fill_hsv.sat * 100
|
257
|
+
self._fill_hsv.sat = value/100
|
258
|
+
self.fillcolor(_hsv_to_rgb(self._fill_hsv))
|
259
|
+
|
260
|
+
def val(self, value=None):
|
261
|
+
return (self.penval(value), self.fillval(value))
|
262
|
+
|
263
|
+
def penval(self, value=None):
|
264
|
+
if value is None:
|
265
|
+
return self._pen_hsv.val * 100
|
266
|
+
self._pen_hsv.val = value/100
|
267
|
+
self.pencolor(_hsv_to_rgb(self._pen_hsv))
|
268
|
+
|
269
|
+
def fillval(self, value=None):
|
270
|
+
if value is None:
|
271
|
+
return self._fill_hsv.val * 100
|
272
|
+
self._fill_hsv.val = value/100
|
273
|
+
self.fillcolor(_hsv_to_rgb(self._fill_hsv))
|
274
|
+
|
275
|
+
Pen = Turtle
|
276
|
+
|
277
|
+
def turtle_test(screen):
|
278
|
+
pen = Turtle()
|
279
|
+
print(f"\n\n***\nTURTLE TYPE: {type(pen)}\nSCREEN TYPE: {type(screen)}\n***\n")
|
280
|
+
pen.shape("square")
|
281
|
+
pen.shapesize(30, 25)
|
282
|
+
pen.hsv(100, 50, 50)
|
283
|
+
pen.stamp()
|
284
|
+
|
285
|
+
if __name__ == "__main__":
|
286
|
+
import os;os.system("clear")
|
287
|
+
screen = Screen()
|
288
|
+
screen.setup(400,400,0,0)
|
289
|
+
screen.bgcolor("gold")
|
290
|
+
turtle_test(screen)
|
291
|
+
screen.exitonclick()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -28,8 +28,10 @@ def test_y(pen):
|
|
28
28
|
def test_shapesize_set(pen):
|
29
29
|
pen.shape("square")
|
30
30
|
pen.shapesize(30,40)
|
31
|
-
|
32
|
-
assert
|
31
|
+
width, height, outline = pen.shapesize()
|
32
|
+
assert width == 30
|
33
|
+
assert height == 40
|
34
|
+
assert outline == 1
|
33
35
|
|
34
36
|
def test_shapesize_get(pen):
|
35
37
|
pen.shape("square")
|
@@ -1,174 +0,0 @@
|
|
1
|
-
"""Convenience wrappers around turtle.Turtle and turtle.Screen
|
2
|
-
See https://github.com/python/cpython/blob/main/Lib/turtle.py
|
3
|
-
"""
|
4
|
-
#TODO handle floats, randcoords, default screensize, default shape
|
5
|
-
|
6
|
-
import colorsys
|
7
|
-
from dataclasses import dataclass
|
8
|
-
import turtle
|
9
|
-
|
10
|
-
@dataclass
|
11
|
-
class HSV:
|
12
|
-
hue: float
|
13
|
-
sat: float
|
14
|
-
val: float
|
15
|
-
def __iter__(self):
|
16
|
-
return iter(self.__dict__.values())
|
17
|
-
|
18
|
-
def Screen():
|
19
|
-
"""Return the singleton screen object."""
|
20
|
-
if Turtle._screen is None:
|
21
|
-
Turtle._screen = _Screen()
|
22
|
-
return Turtle._screen
|
23
|
-
|
24
|
-
class _Screen(turtle._Screen):
|
25
|
-
def __init__(self):
|
26
|
-
super().__init__()
|
27
|
-
turtle.TurtleScreen.__init__(self, _Screen._canvas)
|
28
|
-
if turtle.Turtle._screen is None:
|
29
|
-
turtle.Turtle._screen = self
|
30
|
-
self.colormode(255)
|
31
|
-
|
32
|
-
def _colorstr(self, color):
|
33
|
-
isnumber = lambda x: isinstance(x, (int, float))
|
34
|
-
if len(color) == 3 and all([isnumber(c) for c in color]):
|
35
|
-
lower, upper = 0, Turtle._screen.colormode()
|
36
|
-
color = [max(min(upper, round(c)), lower) for c in color]
|
37
|
-
return super()._colorstr(color)
|
38
|
-
|
39
|
-
def _hsv_to_rgb(hsv):
|
40
|
-
rgb = colorsys.hsv_to_rgb(*hsv)
|
41
|
-
return [round(c*Turtle._screen.colormode()) for c in rgb]
|
42
|
-
|
43
|
-
class Turtle(turtle.RawTurtle):
|
44
|
-
|
45
|
-
_pen = None
|
46
|
-
_screen = None
|
47
|
-
MULT = 20
|
48
|
-
|
49
|
-
def __init__(self,
|
50
|
-
shape=turtle._CFG["shape"],
|
51
|
-
undobuffersize=turtle._CFG["undobuffersize"],
|
52
|
-
visible=turtle._CFG["visible"]):
|
53
|
-
if Turtle._screen is None:
|
54
|
-
Turtle._screen = Screen()
|
55
|
-
turtle.RawTurtle.__init__(self, Turtle._screen,
|
56
|
-
shape=shape,
|
57
|
-
undobuffersize=undobuffersize,
|
58
|
-
visible=visible)
|
59
|
-
self.shapesize(Turtle.MULT)
|
60
|
-
self._pen_hsv = HSV(0, 1, 1)
|
61
|
-
self._fill_hsv = HSV(0, 1, 1)
|
62
|
-
|
63
|
-
@property
|
64
|
-
def x(self):
|
65
|
-
return self.xcor()
|
66
|
-
|
67
|
-
@x.setter
|
68
|
-
def x(self, value):
|
69
|
-
self.setx(value)
|
70
|
-
|
71
|
-
@property
|
72
|
-
def y(self):
|
73
|
-
return self.ycor()
|
74
|
-
|
75
|
-
@y.setter
|
76
|
-
def y(self, value):
|
77
|
-
self.sety(value)
|
78
|
-
|
79
|
-
@property
|
80
|
-
def shapewidth(self):
|
81
|
-
xcoords = [vertex[0] for vertex in self.get_shapepoly()]
|
82
|
-
return max(xcoords) - min(xcoords)
|
83
|
-
|
84
|
-
@property
|
85
|
-
def shapeheight(self):
|
86
|
-
ycoords = [vertex[1] for vertex in self.get_shapepoly()]
|
87
|
-
return max(ycoords) - min(ycoords)
|
88
|
-
|
89
|
-
def hue(self, degrees):
|
90
|
-
self.penhue(degrees)
|
91
|
-
self.fillhue(degrees)
|
92
|
-
|
93
|
-
def penhue(self, degrees):
|
94
|
-
self._pen_hsv.hue = degrees/360
|
95
|
-
self.pencolor(_hsv_to_rgb(self._pen_hsv))
|
96
|
-
|
97
|
-
def fillhue(self, degrees):
|
98
|
-
self._fill_hsv.hue = degrees/360
|
99
|
-
self.fillcolor(_hsv_to_rgb(self._fill_hsv))
|
100
|
-
|
101
|
-
def sat(self, value):
|
102
|
-
self.pensat(value)
|
103
|
-
self.fillsat(value)
|
104
|
-
|
105
|
-
def pensat(self, value):
|
106
|
-
self._pen_hsv.sat = value/100
|
107
|
-
self.pencolor(_hsv_to_rgb(self._pen_hsv))
|
108
|
-
|
109
|
-
def fillsat(self, value):
|
110
|
-
self._fill_hsv.sat = value/100
|
111
|
-
self.fillcolor(_hsv_to_rgb(self._fill_hsv))
|
112
|
-
|
113
|
-
def val(self, value):
|
114
|
-
self.penval(value)
|
115
|
-
self.fillval(value)
|
116
|
-
|
117
|
-
def penval(self, value):
|
118
|
-
self._pen_hsv.val = value/100
|
119
|
-
self.pencolor(_hsv_to_rgb(self._pen_hsv))
|
120
|
-
|
121
|
-
def fillval(self, value):
|
122
|
-
self._fill_hsv.val = value/100
|
123
|
-
self.fillcolor(_hsv_to_rgb(self._fill_hsv))
|
124
|
-
|
125
|
-
def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
|
126
|
-
if stretch_wid is None and stretch_len is None and outline is None:
|
127
|
-
stretch_wid, stretch_len, outline = super().shapesize()
|
128
|
-
return stretch_wid*Turtle.MULT, stretch_len*Turtle.MULT, outline
|
129
|
-
|
130
|
-
stretch_wid = stretch_wid/Turtle.MULT if stretch_wid else None
|
131
|
-
stretch_len = stretch_len/Turtle.MULT if stretch_len else None
|
132
|
-
ret = super().shapesize(stretch_wid, stretch_len, outline)
|
133
|
-
return ret
|
134
|
-
|
135
|
-
def teleport(self, x, y):
|
136
|
-
pendown = self.isdown()
|
137
|
-
if pendown:
|
138
|
-
self.pen(pendown=False)
|
139
|
-
self.penup()
|
140
|
-
self._position = turtle.Vec2D(x, y)
|
141
|
-
self.pen(pendown=pendown)
|
142
|
-
|
143
|
-
def write(self, arg, move=False, align="center", font=("Arial", 18, "bold")):
|
144
|
-
super().write(arg, move, align, font)
|
145
|
-
|
146
|
-
Pen = Turtle
|
147
|
-
|
148
|
-
if __name__ == "__main__":
|
149
|
-
canvas = Screen()
|
150
|
-
canvas.bgcolor("gold")
|
151
|
-
pen = Turtle()
|
152
|
-
print(f"\n\n***\nTURTLE TYPE: {type(pen)}\nSCREEN TYPE: {type(canvas)}\n***\n")
|
153
|
-
|
154
|
-
pen.shape("square")
|
155
|
-
print(f"{pen.shapewidth}, {pen.shapeheight}")
|
156
|
-
pen.shapesize(30, 25)
|
157
|
-
print(f"{pen.shapewidth}, {pen.shapeheight}")
|
158
|
-
|
159
|
-
pen.hue(0)
|
160
|
-
pen.stamp()
|
161
|
-
pen.forward(50)
|
162
|
-
pen.hue(60)
|
163
|
-
pen.stamp()
|
164
|
-
pen.forward(50)
|
165
|
-
pen.hue(120)
|
166
|
-
pen.stamp()
|
167
|
-
pen.forward(50)
|
168
|
-
pen.hue(180)
|
169
|
-
pen.stamp()
|
170
|
-
pen.forward(50)
|
171
|
-
pen.hue(240)
|
172
|
-
pen.stamp()
|
173
|
-
|
174
|
-
canvas.exitonclick()
|