From 8e8170e827f273946a23a6f64421c9892c2ed2b1 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 5 May 2023 18:44:12 -0400 Subject: [PATCH 1/5] gh-65772: Clean-up turtle module * Remove the unused, private, and undocumented name `_ver` and the commented-out `print` call. * Don't add math functions to `__all__`. Beginners should learn to `import math` to access them. * Gregor Lindel, who wrote this version of turtle, dropped plans to implement turtle on another toolkit at least a decade ago. Drop `_dot` code preparing for this, but add a hint comment. * `_Screen` is meant to be a singleton class. To enforce that, it needs either a `__new__` that returns the singleton or `else...raise` in `__iter__`. Merely removing the `if` clauses as suggested might break something if a user were to call `_Screen` directly. Leave the code alone until a problem is evident. * Turtledemo injects into _Screen both _root and _canvas, configured as it needs them to be. Making _canvas an `__init__` option would require skipping some but not all of the lines under 'if _Screen._canvas is None:`. Leave working code alone. --- Lib/turtle.py | 53 +++++++++++++++++---------------------------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/Lib/turtle.py b/Lib/turtle.py index 2de406e0f517af..e002e8dccb71b2 100644 --- a/Lib/turtle.py +++ b/Lib/turtle.py @@ -21,7 +21,6 @@ # misrepresented as being the original software. # 3. This notice may not be removed or altered from any source distribution. - """ Turtle graphics is a popular way for introducing programming to kids. It was part of the original Logo programming language developed @@ -97,13 +96,8 @@ Behind the scenes there are some features included with possible extensions in mind. These will be commented and documented elsewhere. - """ -_ver = "turtle 1.1b- - for Python 3.1 - 4. 5. 2009" - -# print(_ver) - import tkinter as TK import types import math @@ -141,7 +135,7 @@ _tg_utilities = ['write_docstringdict', 'done'] __all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions + - _tg_utilities + ['Terminator']) # + _math_functions) + _tg_utilities + ['Terminator']) _alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos', 'pu', 'rt', 'seth', 'setpos', 'setposition', 'st', @@ -598,9 +592,6 @@ def _write(self, pos, txt, align, font, pencolor): x0, y0, x1, y1 = self.cv.bbox(item) return item, x1-1 -## def _dot(self, pos, size, color): -## """may be implemented for some other graphics toolkit""" - def _onclick(self, item, fun, num=1, add=None): """Bind fun to mouse-click event on turtle. fun must be a function with two arguments, the coordinates @@ -3455,27 +3446,22 @@ def dot(self, size=None, *color): if size is None: size = self._pensize + max(self._pensize, 4) color = self._colorstr(color) - if hasattr(self.screen, "_dot"): - item = self.screen._dot(self._position, size, color) - self.items.append(item) - if self.undobuffer: - self.undobuffer.push(("dot", item)) - else: - pen = self.pen() - if self.undobuffer: - self.undobuffer.push(["seq"]) - self.undobuffer.cumulate = True - try: - if self.resizemode() == 'auto': - self.ht() - self.pendown() - self.pensize(size) - self.pencolor(color) - self.forward(0) - finally: - self.pen(pen) - if self.undobuffer: - self.undobuffer.cumulate = False + # If screen were to gain a dot function, see GH PR #. + pen = self.pen() + if self.undobuffer: + self.undobuffer.push(["seq"]) + self.undobuffer.cumulate = True + try: + if self.resizemode() == 'auto': + self.ht() + self.pendown() + self.pensize(size) + self.pencolor(color) + self.forward(0) + finally: + self.pen(pen) + if self.undobuffer: + self.undobuffer.cumulate = False def _write(self, txt, align, font): """Performs the writing for write() @@ -3751,11 +3737,6 @@ class _Screen(TurtleScreen): _title = _CFG["title"] def __init__(self): - # XXX there is no need for this code to be conditional, - # as there will be only a single _Screen instance, anyway - # XXX actually, the turtle demo is injecting root window, - # so perhaps the conditional creation of a root should be - # preserved (perhaps by passing it as an optional parameter) if _Screen._root is None: _Screen._root = self._root = _Root() self._root.title(_Screen._title) From 5e17663c1765f0013e4e15d516b486439677ae62 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 5 May 2023 18:53:27 -0400 Subject: [PATCH 2/5] PR# and blurb --- Lib/turtle.py | 2 +- .../next/Library/2023-05-05-18-52-22.gh-issue-65772.w5P5Wv.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-05-05-18-52-22.gh-issue-65772.w5P5Wv.rst diff --git a/Lib/turtle.py b/Lib/turtle.py index e002e8dccb71b2..37fac28630d888 100644 --- a/Lib/turtle.py +++ b/Lib/turtle.py @@ -3446,7 +3446,7 @@ def dot(self, size=None, *color): if size is None: size = self._pensize + max(self._pensize, 4) color = self._colorstr(color) - # If screen were to gain a dot function, see GH PR #. + # If screen were to gain a dot function, see GH #104218. pen = self.pen() if self.undobuffer: self.undobuffer.push(["seq"]) diff --git a/Misc/NEWS.d/next/Library/2023-05-05-18-52-22.gh-issue-65772.w5P5Wv.rst b/Misc/NEWS.d/next/Library/2023-05-05-18-52-22.gh-issue-65772.w5P5Wv.rst new file mode 100644 index 00000000000000..54b0190192863c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-05-18-52-22.gh-issue-65772.w5P5Wv.rst @@ -0,0 +1 @@ +Remove unneeded comments and code in turtle.py. From b6dfddd157327702d4b38c14fa044d1e833f7b3e Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 5 May 2023 21:56:48 -0400 Subject: [PATCH 3/5] Fix trailing whitespace. --- Lib/turtle.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/turtle.py b/Lib/turtle.py index 37fac28630d888..cf111158b7c149 100644 --- a/Lib/turtle.py +++ b/Lib/turtle.py @@ -2717,7 +2717,7 @@ def _cc(self, args): if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)): raise TurtleGraphicsError("bad color sequence: %s" % str(args)) return "#%02x%02x%02x" % (r, g, b) - + def teleport(self, x=None, y=None, *, fill_gap: bool = False) -> None: """Instantly move turtle to an absolute position. @@ -2729,14 +2729,14 @@ def teleport(self, x=None, y=None, *, fill_gap: bool = False) -> None: call: teleport(x, y) # two coordinates --or: teleport(x) # teleport to x position, keeping y as is --or: teleport(y=y) # teleport to y position, keeping x as is - --or: teleport(x, y, fill_gap=True) + --or: teleport(x, y, fill_gap=True) # teleport but fill the gap in between Move turtle to an absolute position. Unlike goto(x, y), a line will not be drawn. The turtle's orientation does not change. If currently filling, the polygon(s) teleported from will be filled after leaving, and filling will begin again after teleporting. This can be disabled - with fill_gap=True, which makes the imaginary line traveled during + with fill_gap=True, which makes the imaginary line traveled during teleporting act as a fill barrier like in goto(x, y). Example (for a Turtle instance named turtle): @@ -2764,7 +2764,7 @@ def teleport(self, x=None, y=None, *, fill_gap: bool = False) -> None: self._position = Vec2D(new_x, new_y) self.pen(pendown=pendown) if was_filling and not fill_gap: - self.begin_fill() + self.begin_fill() def clone(self): """Create and return a clone of the turtle. From 7731087e91fb290fd39efa067478a82b58e92015 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 5 May 2023 23:57:00 -0400 Subject: [PATCH 4/5] Rewrite the turtledemo makeGraphFrame method Replace `self._canvas` and `self.scanvas`, both bound to `canvas`, with `self.canvas, which is accessed in other methods. Replace `_s_` with `screen` and `_s_._canvas` with `canvas`. Add a comment explaining the unorthodox use of function turtle.Screen and singleton class turtle._Screen. --- Lib/turtledemo/__main__.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Lib/turtledemo/__main__.py b/Lib/turtledemo/__main__.py index caea022da4a688..f6c9d6aa6f9a32 100755 --- a/Lib/turtledemo/__main__.py +++ b/Lib/turtledemo/__main__.py @@ -203,10 +203,10 @@ def __init__(self, filename=None): def onResize(self, event): - cwidth = self._canvas.winfo_width() - cheight = self._canvas.winfo_height() - self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth) - self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight) + cwidth = self.canvas.winfo_width() + cheight = self.canvas.winfo_height() + self.canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth) + self.canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight) def makeTextFrame(self, root): self.text_frame = text_frame = Frame(root) @@ -237,19 +237,23 @@ def makeTextFrame(self, root): return text_frame def makeGraphFrame(self, root): + # t._Screen is a singleton class instantiated or retrieved + # by calling Screen. Since tdemo canvas needs a different + # configuration, we manually set class attributes before + # calling Screen and manually call superclass init after. turtle._Screen._root = root + self.canvwidth = 1000 self.canvheight = 800 - turtle._Screen._canvas = self._canvas = canvas = turtle.ScrolledCanvas( + turtle._Screen._canvas = self.canvas = canvas = turtle.ScrolledCanvas( root, 800, 600, self.canvwidth, self.canvheight) canvas.adjustScrolls() canvas._rootwindow.bind('', self.onResize) canvas._canvas['borderwidth'] = 0 - self.screen = _s_ = turtle.Screen() - turtle.TurtleScreen.__init__(_s_, _s_._canvas) - self.scanvas = _s_._canvas - turtle.RawTurtle.screens = [_s_] + self.screen = screen = turtle.Screen() + turtle.TurtleScreen.__init__(screen, canvas) + turtle.RawTurtle.screens = [screen] return canvas def set_txtsize(self, size): @@ -373,7 +377,7 @@ def startDemo(self): def clearCanvas(self): self.refreshCanvas() self.screen._delete("all") - self.scanvas.config(cursor="") + self.canvas.config(cursor="") self.configGUI(NORMAL, DISABLED, DISABLED) def stopIt(self): From fd11377088c9845cdd17a463d841784ca7bc513f Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sat, 6 May 2023 00:08:23 -0400 Subject: [PATCH 5/5] Revert "Rewrite the turtledemo makeGraphFrame method" This reverts commit 7731087e91fb290fd39efa067478a82b58e92015. --- Lib/turtledemo/__main__.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/Lib/turtledemo/__main__.py b/Lib/turtledemo/__main__.py index f6c9d6aa6f9a32..caea022da4a688 100755 --- a/Lib/turtledemo/__main__.py +++ b/Lib/turtledemo/__main__.py @@ -203,10 +203,10 @@ def __init__(self, filename=None): def onResize(self, event): - cwidth = self.canvas.winfo_width() - cheight = self.canvas.winfo_height() - self.canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth) - self.canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight) + cwidth = self._canvas.winfo_width() + cheight = self._canvas.winfo_height() + self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth) + self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight) def makeTextFrame(self, root): self.text_frame = text_frame = Frame(root) @@ -237,23 +237,19 @@ def makeTextFrame(self, root): return text_frame def makeGraphFrame(self, root): - # t._Screen is a singleton class instantiated or retrieved - # by calling Screen. Since tdemo canvas needs a different - # configuration, we manually set class attributes before - # calling Screen and manually call superclass init after. turtle._Screen._root = root - self.canvwidth = 1000 self.canvheight = 800 - turtle._Screen._canvas = self.canvas = canvas = turtle.ScrolledCanvas( + turtle._Screen._canvas = self._canvas = canvas = turtle.ScrolledCanvas( root, 800, 600, self.canvwidth, self.canvheight) canvas.adjustScrolls() canvas._rootwindow.bind('', self.onResize) canvas._canvas['borderwidth'] = 0 - self.screen = screen = turtle.Screen() - turtle.TurtleScreen.__init__(screen, canvas) - turtle.RawTurtle.screens = [screen] + self.screen = _s_ = turtle.Screen() + turtle.TurtleScreen.__init__(_s_, _s_._canvas) + self.scanvas = _s_._canvas + turtle.RawTurtle.screens = [_s_] return canvas def set_txtsize(self, size): @@ -377,7 +373,7 @@ def startDemo(self): def clearCanvas(self): self.refreshCanvas() self.screen._delete("all") - self.canvas.config(cursor="") + self.scanvas.config(cursor="") self.configGUI(NORMAL, DISABLED, DISABLED) def stopIt(self):