Skip to content

Commit 74a2b79

Browse files
liam-gerstenterryjreedyhugovkgpshead
authored
gh-88773: Added teleport method to Turtle library (#103974)
Add a `teleport` method to `turtle` module turtle instances that acts a lot like `goto`, _but_ ensures the pen is up while warping to the new position to and can control shape filling behavior as part of the jump. Based on an educator user feature request. --------- Co-authored-by: Terry Jan Reedy <[email protected]> Co-authored-by: Hugo van Kemenade <[email protected]> Co-authored-by: Gregory P. Smith <[email protected]>
1 parent 654d44b commit 74a2b79

File tree

4 files changed

+128
-12
lines changed

4 files changed

+128
-12
lines changed

Doc/library/turtle.rst

+42-11
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ Turtle motion
107107
| :func:`right` | :func:`rt`
108108
| :func:`left` | :func:`lt`
109109
| :func:`goto` | :func:`setpos` | :func:`setposition`
110+
| :func:`teleport`
110111
| :func:`setx`
111112
| :func:`sety`
112113
| :func:`setheading` | :func:`seth`
@@ -372,6 +373,44 @@ Turtle motion
372373
(0.00,0.00)
373374

374375

376+
.. function:: teleport(x, y=None, *, fill_gap=False)
377+
378+
:param x: a number or ``None``
379+
:param y: a number or ``None``
380+
:param fill_gap: a boolean
381+
382+
Move turtle to an absolute position. Unlike goto(x, y), a line will not
383+
be drawn. The turtle's orientation does not change. If currently
384+
filling, the polygon(s) teleported from will be filled after leaving,
385+
and filling will begin again after teleporting. This can be disabled
386+
with fill_gap=True, which makes the imaginary line traveled during
387+
teleporting act as a fill barrier like in goto(x, y).
388+
389+
.. doctest::
390+
:skipif: _tkinter is None
391+
:hide:
392+
393+
>>> turtle.goto(0, 0)
394+
395+
.. doctest::
396+
:skipif: _tkinter is None
397+
398+
>>> tp = turtle.pos()
399+
>>> tp
400+
(0.00,0.00)
401+
>>> turtle.teleport(60)
402+
>>> turtle.pos()
403+
(60.00,0.00)
404+
>>> turtle.teleport(y=10)
405+
>>> turtle.pos()
406+
(60.00,10.00)
407+
>>> turtle.teleport(20, 30)
408+
>>> turtle.pos()
409+
(20.00,30.00)
410+
411+
.. versionadded: 3.12
412+
413+
375414
.. function:: setx(x)
376415

377416
:param x: a number (integer or float)
@@ -537,8 +576,7 @@ Turtle motion
537576
:skipif: _tkinter is None
538577

539578
>>> turtle.color("blue")
540-
>>> turtle.stamp()
541-
11
579+
>>> stamp_id = turtle.stamp()
542580
>>> turtle.fd(50)
543581

544582

@@ -575,15 +613,8 @@ Turtle motion
575613
.. doctest::
576614

577615
>>> for i in range(8):
578-
... turtle.stamp(); turtle.fd(30)
579-
13
580-
14
581-
15
582-
16
583-
17
584-
18
585-
19
586-
20
616+
... unused_stamp_id = turtle.stamp()
617+
... turtle.fd(30)
587618
>>> turtle.clearstamps(2)
588619
>>> turtle.clearstamps(-2)
589620
>>> turtle.clearstamps()

Lib/test/test_turtle.py

+20
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,14 @@ def test_goto(self):
267267
self.assertAlmostEqual(self.nav.xcor(), 100)
268268
self.assertAlmostEqual(self.nav.ycor(), -100)
269269

270+
def test_teleport(self):
271+
self.nav.teleport(20, -30, fill_gap=True)
272+
self.assertAlmostEqual(self.nav.xcor(), 20)
273+
self.assertAlmostEqual(self.nav.ycor(), -30)
274+
self.nav.teleport(-20, 30, fill_gap=False)
275+
self.assertAlmostEqual(self.nav.xcor(), -20)
276+
self.assertAlmostEqual(self.nav.ycor(), 30)
277+
270278
def test_pos(self):
271279
self.assertEqual(self.nav.pos(), self.nav._position)
272280
self.nav.goto(100, -100)
@@ -440,6 +448,18 @@ def test_showturtle_hideturtle_and_isvisible(self):
440448
tpen.showturtle()
441449
self.assertTrue(tpen.isvisible())
442450

451+
def test_teleport(self):
452+
453+
tpen = turtle.TPen()
454+
455+
for fill_gap_value in [True, False]:
456+
tpen.penup()
457+
tpen.teleport(100, 100, fill_gap=fill_gap_value)
458+
self.assertFalse(tpen.isdown())
459+
tpen.pendown()
460+
tpen.teleport(-100, -100, fill_gap=fill_gap_value)
461+
self.assertTrue(tpen.isdown())
462+
443463

444464
if __name__ == '__main__':
445465
unittest.main()

Lib/turtle.py

+65-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135
'pu', 'radians', 'right', 'reset', 'resizemode', 'rt',
136136
'seth', 'setheading', 'setpos', 'setposition', 'settiltangle',
137137
'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'shapetransform', 'shearfactor', 'showturtle',
138-
'speed', 'st', 'stamp', 'tilt', 'tiltangle', 'towards',
138+
'speed', 'st', 'stamp', 'teleport', 'tilt', 'tiltangle', 'towards',
139139
'turtlesize', 'undo', 'undobufferentries', 'up', 'width',
140140
'write', 'xcor', 'ycor']
141141
_tg_utilities = ['write_docstringdict', 'done']
@@ -1614,6 +1614,13 @@ def _goto(self, end):
16141614
"""move turtle to position end."""
16151615
self._position = end
16161616

1617+
def teleport(self, x=None, y=None, *, fill_gap: bool = False) -> None:
1618+
"""To be overwritten by child class RawTurtle.
1619+
Includes no TPen references."""
1620+
new_x = x if x is not None else self._position[0]
1621+
new_y = y if y is not None else self._position[1]
1622+
self._position = Vec2D(new_x, new_y)
1623+
16171624
def forward(self, distance):
16181625
"""Move the turtle forward by the specified distance.
16191626
@@ -2293,6 +2300,15 @@ def fillcolor(self, *args):
22932300
else:
22942301
return self._color(self._fillcolor)
22952302

2303+
def teleport(self, x=None, y=None, *, fill_gap: bool = False) -> None:
2304+
"""To be overwritten by child class RawTurtle.
2305+
Includes no TNavigator references.
2306+
"""
2307+
pendown = self.isdown()
2308+
if pendown:
2309+
self.pen(pendown=False)
2310+
self.pen(pendown=pendown)
2311+
22962312
def showturtle(self):
22972313
"""Makes the turtle visible.
22982314
@@ -2710,6 +2726,54 @@ def _cc(self, args):
27102726
if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
27112727
raise TurtleGraphicsError("bad color sequence: %s" % str(args))
27122728
return "#%02x%02x%02x" % (r, g, b)
2729+
2730+
def teleport(self, x=None, y=None, *, fill_gap: bool = False) -> None:
2731+
"""Instantly move turtle to an absolute position.
2732+
2733+
Arguments:
2734+
x -- a number or None
2735+
y -- a number None
2736+
fill_gap -- a boolean This argument must be specified by name.
2737+
2738+
call: teleport(x, y) # two coordinates
2739+
--or: teleport(x) # teleport to x position, keeping y as is
2740+
--or: teleport(y=y) # teleport to y position, keeping x as is
2741+
--or: teleport(x, y, fill_gap=True)
2742+
# teleport but fill the gap in between
2743+
2744+
Move turtle to an absolute position. Unlike goto(x, y), a line will not
2745+
be drawn. The turtle's orientation does not change. If currently
2746+
filling, the polygon(s) teleported from will be filled after leaving,
2747+
and filling will begin again after teleporting. This can be disabled
2748+
with fill_gap=True, which makes the imaginary line traveled during
2749+
teleporting act as a fill barrier like in goto(x, y).
2750+
2751+
Example (for a Turtle instance named turtle):
2752+
>>> tp = turtle.pos()
2753+
>>> tp
2754+
(0.00,0.00)
2755+
>>> turtle.teleport(60)
2756+
>>> turtle.pos()
2757+
(60.00,0.00)
2758+
>>> turtle.teleport(y=10)
2759+
>>> turtle.pos()
2760+
(60.00,10.00)
2761+
>>> turtle.teleport(20, 30)
2762+
>>> turtle.pos()
2763+
(20.00,30.00)
2764+
"""
2765+
pendown = self.isdown()
2766+
was_filling = self.filling()
2767+
if pendown:
2768+
self.pen(pendown=False)
2769+
if was_filling and not fill_gap:
2770+
self.end_fill()
2771+
new_x = x if x is not None else self._position[0]
2772+
new_y = y if y is not None else self._position[1]
2773+
self._position = Vec2D(new_x, new_y)
2774+
self.pen(pendown=pendown)
2775+
if was_filling and not fill_gap:
2776+
self.begin_fill()
27132777

27142778
def clone(self):
27152779
"""Create and return a clone of the turtle.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added :func:`turtle.teleport` to the :mod:`turtle` module to move a turtle to a new point without tracing a line, visible or invisible. Patch by Liam Gersten.

0 commit comments

Comments
 (0)