turtleshell 1.0.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.
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: turtleshell
3
+ Version: 1.0.0
4
+ Summary: Convenience wrapper around turtle.Turtle and turtle.Screen
5
+ Author-email: Sam Mangan <sam.mangan2@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/SamMangan/turtleshell
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ Provides-Extra: dev
13
+ Requires-Dist: bumpver; extra == "dev"
14
+ Requires-Dist: pytest; extra == "dev"
15
+
16
+ # TurtleShell
17
+
18
+ Convenience wrapper around Python turtle standard library.
@@ -0,0 +1,3 @@
1
+ # TurtleShell
2
+
3
+ Convenience wrapper around Python turtle standard library.
@@ -0,0 +1,39 @@
1
+ [project]
2
+ name = "turtleshell"
3
+ version = "1.0.0"
4
+ authors = [
5
+ { name="Sam Mangan", email="sam.mangan2@gmail.com" },
6
+ ]
7
+ description = "Convenience wrapper around turtle.Turtle and turtle.Screen"
8
+ readme = "README.md"
9
+ requires-python = ">=3.9"
10
+ classifiers = [
11
+ "Programming Language :: Python :: 3",
12
+ "Operating System :: OS Independent",
13
+ ]
14
+ license = "MIT"
15
+ license-files = ["LICEN[CS]E*"]
16
+
17
+ [project.optional-dependencies]
18
+ dev = ["bumpver", "pytest"]
19
+
20
+ [project.urls]
21
+ Homepage = "https://github.com/SamMangan/turtleshell"
22
+
23
+ [tool.pytest.ini_options]
24
+ pythonpath = "src"
25
+ addopts = [
26
+ "--import-mode=importlib",
27
+ ]
28
+
29
+ [tool.bumpver]
30
+ current_version = "1.0.0"
31
+ version_pattern = "MAJOR.MINOR.PATCH"
32
+ commit_message = "Bump version {old_version} -> {new_version}"
33
+ commit = true
34
+ tag = true
35
+ push = false
36
+
37
+ [tool.bumpver.file_patterns]
38
+ "pyproject.toml" = ['current_version = "{version}"', 'version = "{version}"']
39
+ "src/turtleshell/__init__.py" = ["{version}"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from .core import Screen, Turtle
2
+
3
+ __version__ = "1.0.0"
@@ -0,0 +1,168 @@
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
+
48
+ def __init__(self,
49
+ shape=turtle._CFG["shape"],
50
+ undobuffersize=turtle._CFG["undobuffersize"],
51
+ visible=turtle._CFG["visible"]):
52
+ if Turtle._screen is None:
53
+ Turtle._screen = Screen()
54
+ turtle.RawTurtle.__init__(self, Turtle._screen,
55
+ shape=shape,
56
+ undobuffersize=undobuffersize,
57
+ visible=visible)
58
+ self.shapesize(20)
59
+ self._pen_hsv = HSV(0, 1, 1)
60
+ self._fill_hsv = HSV(0, 1, 1)
61
+
62
+ @property
63
+ def x(self):
64
+ return self.xcor()
65
+
66
+ @x.setter
67
+ def x(self, value):
68
+ self.setx(value)
69
+
70
+ @property
71
+ def y(self):
72
+ return self.ycor()
73
+
74
+ @y.setter
75
+ def y(self, value):
76
+ self.sety(value)
77
+
78
+ @property
79
+ def shapewidth(self):
80
+ xcoords = [vertex[0] for vertex in self.get_shapepoly()]
81
+ return max(xcoords) - min(xcoords)
82
+
83
+ @property
84
+ def shapeheight(self):
85
+ ycoords = [vertex[1] for vertex in self.get_shapepoly()]
86
+ return max(ycoords) - min(ycoords)
87
+
88
+ def hue(self, degrees):
89
+ self.penhue(degrees)
90
+ self.fillhue(degrees)
91
+
92
+ def penhue(self, degrees):
93
+ self._pen_hsv.hue = degrees/360
94
+ self.pencolor(_hsv_to_rgb(self._pen_hsv))
95
+
96
+ def fillhue(self, degrees):
97
+ self._fill_hsv.hue = degrees/360
98
+ self.fillcolor(_hsv_to_rgb(self._fill_hsv))
99
+
100
+ def sat(self, value):
101
+ self.pensat(value)
102
+ self.fillsat(value)
103
+
104
+ def pensat(self, value):
105
+ self._pen_hsv.sat = value/100
106
+ self.pencolor(_hsv_to_rgb(self._pen_hsv))
107
+
108
+ def fillsat(self, value):
109
+ self._fill_hsv.sat = value/100
110
+ self.fillcolor(_hsv_to_rgb(self._fill_hsv))
111
+
112
+ def val(self, value):
113
+ self.penval(value)
114
+ self.fillval(value)
115
+
116
+ def penval(self, value):
117
+ self._pen_hsv.val = value/100
118
+ self.pencolor(_hsv_to_rgb(self._pen_hsv))
119
+
120
+ def fillval(self, value):
121
+ self._fill_hsv.val = value/100
122
+ self.fillcolor(_hsv_to_rgb(self._fill_hsv))
123
+
124
+ def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
125
+ stretch_wid = stretch_wid/20 if stretch_wid else None
126
+ stretch_len = stretch_len/20 if stretch_len else None
127
+ super().shapesize(stretch_wid, stretch_len, outline)
128
+
129
+ def teleport(self, x, y):
130
+ pendown = self.isdown()
131
+ if pendown:
132
+ self.pen(pendown=False)
133
+ self.penup()
134
+ self._position = turtle.Vec2D(x, y)
135
+ self.pen(pendown=pendown)
136
+
137
+ def write(self, arg, move=False, align="center", font=("Arial", 18, "bold")):
138
+ super().write(arg, move, align, font)
139
+
140
+ Pen = Turtle
141
+
142
+ if __name__ == "__main__":
143
+ canvas = Screen()
144
+ canvas.bgcolor("gold")
145
+ pen = Turtle()
146
+ print(f"\n\n***\nTURTLE TYPE: {type(pen)}\nSCREEN TYPE: {type(canvas)}\n***\n")
147
+
148
+ pen.shape("square")
149
+ print(f"{pen.shapewidth}, {pen.shapeheight}")
150
+ pen.shapesize(30, 25)
151
+ print(f"{pen.shapewidth}, {pen.shapeheight}")
152
+
153
+ pen.hue(0)
154
+ pen.stamp()
155
+ pen.forward(50)
156
+ pen.hue(60)
157
+ pen.stamp()
158
+ pen.forward(50)
159
+ pen.hue(120)
160
+ pen.stamp()
161
+ pen.forward(50)
162
+ pen.hue(180)
163
+ pen.stamp()
164
+ pen.forward(50)
165
+ pen.hue(240)
166
+ pen.stamp()
167
+
168
+ canvas.exitonclick()
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: turtleshell
3
+ Version: 1.0.0
4
+ Summary: Convenience wrapper around turtle.Turtle and turtle.Screen
5
+ Author-email: Sam Mangan <sam.mangan2@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/SamMangan/turtleshell
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ Provides-Extra: dev
13
+ Requires-Dist: bumpver; extra == "dev"
14
+ Requires-Dist: pytest; extra == "dev"
15
+
16
+ # TurtleShell
17
+
18
+ Convenience wrapper around Python turtle standard library.
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/turtleshell/__init__.py
4
+ src/turtleshell/core.py
5
+ src/turtleshell.egg-info/PKG-INFO
6
+ src/turtleshell.egg-info/SOURCES.txt
7
+ src/turtleshell.egg-info/dependency_links.txt
8
+ src/turtleshell.egg-info/requires.txt
9
+ src/turtleshell.egg-info/top_level.txt
10
+ tests/test_turtle.py
@@ -0,0 +1,4 @@
1
+
2
+ [dev]
3
+ bumpver
4
+ pytest
@@ -0,0 +1 @@
1
+ turtleshell
@@ -0,0 +1,106 @@
1
+ import pytest
2
+ from turtleshell import Screen, Turtle
3
+
4
+ BLACK = (0, 0, 0)
5
+ WHITE = (255, 255, 255)
6
+ RED = (255, 0, 0)
7
+ GREEN = (0, 255, 0)
8
+ BLUE = (0, 0, 255)
9
+
10
+ @pytest.fixture
11
+ def pen():
12
+ Screen().tracer(0)
13
+ _pen = Turtle(visible=False)
14
+ return _pen
15
+
16
+ @pytest.fixture
17
+ def screen():
18
+ return Screen()
19
+
20
+ def test_x(pen):
21
+ pen.setx(100)
22
+ assert pen.x == 100
23
+
24
+ def test_y(pen):
25
+ pen.sety(200)
26
+ assert pen.y == 200
27
+
28
+ def test_shapesize(pen):
29
+ pen.shape("square")
30
+ pen.shapesize(30,40)
31
+ assert pen.shapewidth == 30
32
+ assert pen.shapeheight == 40
33
+
34
+ def test_teleport(pen):
35
+ assert pen.position() == (0, 0)
36
+ pen.pendown()
37
+ pen.teleport(100, 200)
38
+ assert pen.position() == (100, 200)
39
+ assert len(pen.currentLine) == 1 # single point => no line drawn
40
+ assert pen.isdown() # pen still down
41
+
42
+ def test_hsv_hue(pen):
43
+ # pencolor
44
+ pen.penhue(0)
45
+ assert pen.pencolor() == RED
46
+ pen.penhue(120)
47
+ assert pen.pencolor() == GREEN
48
+ pen.penhue(240)
49
+ assert pen.pencolor() == BLUE
50
+
51
+ # fillcolor
52
+ pen.fillhue(0)
53
+ assert pen.fillcolor() == RED
54
+ pen.fillhue(120)
55
+ assert pen.fillcolor() == GREEN
56
+ pen.fillhue(240)
57
+ assert pen.fillcolor() == BLUE
58
+
59
+ def test_hsv_saturation(pen):
60
+ # pencolor
61
+ pen.pensat(0)
62
+ assert pen.pencolor() == WHITE
63
+ pen.pensat(50)
64
+ assert pen.pencolor() == (255, 128, 128) # light red
65
+ pen.pensat(100)
66
+ assert pen.pencolor() == RED
67
+
68
+ # fillcolor
69
+ pen.fillsat(0)
70
+ assert pen.fillcolor() == WHITE
71
+ pen.fillsat(50)
72
+ assert pen.fillcolor() == (255, 128, 128) # light red
73
+ pen.fillsat(100)
74
+ assert pen.fillcolor() == RED
75
+
76
+ def test_hsv_value(pen):
77
+ # pencolor
78
+ pen.penval(0)
79
+ assert pen.pencolor() == BLACK
80
+ pen.penval(50)
81
+ assert pen.pencolor() == (128, 0, 0) # dark red
82
+ pen.penval(100)
83
+ assert pen.pencolor() == RED
84
+
85
+ # fillcolor
86
+ pen.fillval(0)
87
+ assert pen.fillcolor() == BLACK
88
+ pen.fillval(50)
89
+ assert pen.fillcolor() == (128, 0, 0) # dark red
90
+ pen.fillval(100)
91
+ assert pen.fillcolor() == RED
92
+
93
+ def test_color(pen, screen):
94
+ # Colors are clamped to [0, 255]
95
+ pen.pencolor(-20, -5, 0)
96
+ assert pen.pencolor() == BLACK
97
+
98
+ pen.pencolor(300, 300, 300)
99
+ assert pen.pencolor() == WHITE
100
+
101
+ # Floats are rounded
102
+ screen.bgcolor(255, 0.3, 0)
103
+ assert screen.bgcolor() == (255, 0, 0)
104
+
105
+ screen.bgcolor(255, 0.7, 0)
106
+ assert screen.bgcolor() == (255, 1, 0)