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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: turtleshell
3
- Version: 1.1.0
3
+ Version: 1.3.0
4
4
  Summary: Convenience wrapper around turtle.Turtle and turtle.Screen
5
5
  Author-email: Sam Mangan <sam.mangan2@gmail.com>
6
6
  License-Expression: MIT
File without changes
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "turtleshell"
3
- version = "1.1.0"
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.1.0"
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
@@ -1,3 +1,3 @@
1
1
  from .core import Screen, Turtle
2
2
 
3
- __version__ = "1.1.0"
3
+ __version__ = "1.3.0"
@@ -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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: turtleshell
3
- Version: 1.1.0
3
+ Version: 1.3.0
4
4
  Summary: Convenience wrapper around turtle.Turtle and turtle.Screen
5
5
  Author-email: Sam Mangan <sam.mangan2@gmail.com>
6
6
  License-Expression: MIT
@@ -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
- assert pen.shapewidth == 30
32
- assert pen.shapeheight == 40
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()