-
-
Notifications
You must be signed in to change notification settings - Fork 31.7k
gh-65276: Fixed Turtle library circle method bug #104022
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Could you run |
Looks like some trailing whitespace that slipped in from #103974. Tip: set your code editor to trim trailing whitespace on save. Or check it can read the |
I have made the requested changes; please review again. |
Tip: usually there's no need to update the PR branch from We can also save some CI resources and avoid triggering notifications. |
Behavior:
Having used multiple turtle instances to create various object for an interactive Tic-Tac-Toe game, a user noticed that setting turtle speeds to 0 (instant) lead to "disappearing" objects. The bug was with the
circle
method, which was called on click. The user had created tiles and lines to display the "board" using multiple turtle instances, and when thecircle
method was called for speed 0 turtles, the board would display on top of all lines and circle drawn, effectively making them disappear.The issue:
When setting speed to 0 in particular, the
circle
method would call the_tracer
method to setself._tracing
andself._delayvalue
to zero, before drawing the circle, and then resetting both of these values via the_tracer
method. The issue is in the final call to_tracer
that resets the two values. This is because_tracer
actually callsscreen.tracer
, and when resetting with a nonzero first argument,screen.tracer
actually callsscreen.update()
on line 1272. Thisupdate
method redraws EVERY turtle object that shares the current screen, which is both unnecessary in this case, and also creates the actual bug itself.Eventually, the
screen.update()
method callst._drawturtle()
for every turtle, which can (and does in this case) include background turtle objects like the user's board objects. Despite it actually iterating through the turtle objects in the right order, thet._drawturtle()
method tries to draw the first object on top. This takes place in multiplescreen._drawpoly
calls with atop=True
argument. One might thinktop=True
would behave like a stack, i.e., the most recent call like this would actually be on top. In reality, it either behaves like a queue, or just negates every call except the first. In the case of the user's code, the first-created turtle object (the board) got thescreen._drawpoly(..., top=True)
call first, and thus, displayed on top of all other turtle creations.The fix:
The
screen._drawpoly
ends with external library code, most likely tkinter, so I won't be attempting to change its behavior. Generally, changing_drawturtle()
,update
,_update
,tracer
, or_tracer
would be inappropriate for such a bug, as doing such would be a large (potentially bug-creating) alteration for such a minor bug. Because of this, the fix should be with thecircle
method itself.Due to how the
screen._drawpoly(..., top=True)
call behaves in_drawturtle
, we only actually want the call to be made for the current turtle that is drawing the circle. Unfortunately, theupdate
method calls_drawturtle
for every turtle, so we can't avoid_drawturtle
calls for the turtles we don't want to draw. Fortunately,_drawturtle
won't actually callscreen._drawpoly(..., top=True)
whenself._shown = False
andself._hidden_from_screen = True
. These are attributes that we CAN set from the circle method for any and every turtle object on the same screen. Thus, for every other turtle sharing the screen, we save the values of these two attributes, set them so they don't get redrawn, callself._tracer
, and then restore the prior attribute values for all other turtle objects.Resolves #65276