Trying to make a clock with turtle graphics

I recommend you don’t run this code, it crashed idle
I ran it and it like flashed the hands and then started infinitely printing errors and then it crashed and I had to close it with task manager, it’s supposed to make a clock, can anyone help with spotting the error?

from turtle import *
from datetime import datetime
import math

color("black")
shape("circle")
pensize(1)
hideturtle()
title("Clock")
speed(0)

print(datetime.now().strftime("%H:%M:%S"))

def drawClock():
    drawHand(int(datetime.now().strftime("%H")) * 5, 4, 60)
    drawHand(int(datetime.now().strftime("%M")), 3, 80)
    drawHand(int(datetime.now().strftime("%S")), 1, 80)

def drawHand(hand, thickness, length):
    clear()
    
    penup()
    goto(0, 0)
    pendown()
    
    pensize(thickness)
    shapesize(thickness)
    
    goto(math.sin(360 * hand / 60) * length, math.cos(360 * hand / 60) * length)
    
    penup()
    goto(0, 0)
    
    shapesize(1)
    ontimer(drawClock, 1000)

while True:
    drawClock()
    done()

Ok so i moved “clear()” into the while true loop instead of drawHand, which solved the error with the flashing, the infinite printing of errors only starts when i try to close the turtle window. Its also not infinite its just a LOT
the error: (I couldn’t tell you what a word of this means, i just replaced my user)

Exception in Tkinter callback Traceback (most recent call last): File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2032.0_x64__qbz5n2kfra8p0\Lib\tkinter_*init* _.py", line 1948, in **call** return self.func(*args) ^^^^^^^^^^^^^^^^ File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2032.0_x64__qbz5n2kfra8p0\Lib\tkinter_*init* _.py", line 861, in callit func(*args) File "C:/Users/-/OneDrive/Third year/Other/TestModule.py", line 20, in drawClock drawHand(int(datetime.now().strftime("%M")), 3, 80) File "C:/Users/-/OneDrive/Third year/Other/TestModule.py", line 35, in drawHand goto(0, 0) File "<string>", line 8, in goto turtle.Terminator Exception in Tkinter callback Traceback (most recent call last): File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2032.0_x64__qbz5n2kfra8p0\Lib\tkinter_*init* _.py", line 1948, in **call** return self.func(*args) ^^^^^^^^^^^^^^^^ File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2032.0_x64__qbz5n2kfra8p0\Lib\tkinter_*init* _.py", line 861, in callit func(*args) File "C:/Users/-/OneDrive/Third year/Other/TestModule.py", line 19, in drawClock drawHand(int(datetime.now().strftime("%H")) * 5, 4, 60) File "C:/Users/-/OneDrive/Third year/Other/TestModule.py", line 35, in drawHand goto(0, 0) File "<string>", line 8, in goto turtle.Terminator Exception in Tkinter callback Traceback (most recent call last): File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2032.0_x64__qbz5n2kfra8p0\Lib\tkinter_*init* _.py", line 1948, in **call** return self.func(*args) ^^^^^^^^^^^^^^^^

turtledemo, which you can run from IDLE (Help => turtledemo) has a clock demo. Don’t know how similar it is to what you are doing.

This is one problem.

Every time you call drawClock, it will call drawHand three times, in order to draw each hand of the clock. But each of those drawHand calls says “one second from now, call drawClock” again. That means, three separate calls for drawClock will be scheduled, one second after the initial call. Then, one second later, Python should try to call drawClock three times in a row, draw nine hands in total, and schedule nine calls for the next second… eventually this will explode. (Of course, it’s limited by the amount of time taken to do the drawing; but very soon it will be constantly drawing instead of waiting.)

This is another problem.

done should only get called once, in order to keep the Tkinter window open (to run the “main loop”). Each extra call will cause problems. This code also means that drawClock() will get repeatedly called, as fast as Python can manage it - without the one-second wait. (Any calls caused by ontimer are in addition to that - assuming that the timer can get a word in edgewise.)


Instead, the ontimer call should be inside drawClock, and there should be only one initial call each to drawClock and done. Thus, to fix just the timing and error messages:

from turtle import *
from datetime import datetime
import math

color("black")
shape("circle")
pensize(1)
hideturtle()
title("Clock")
speed(0)

print(datetime.now().strftime("%H:%M:%S"))

def drawClock():
    drawHand(int(datetime.now().strftime("%H")) * 5, 4, 60)
    drawHand(int(datetime.now().strftime("%M")), 3, 80)
    drawHand(int(datetime.now().strftime("%S")), 1, 80)
    ontimer(drawClock, 1000)

def drawHand(hand, thickness, length):
    clear()
    
    penup()
    goto(0, 0)
    pendown()
    
    pensize(thickness)
    shapesize(thickness)
    
    goto(math.sin(360 * hand / 60) * length, math.cos(360 * hand / 60) * length)
    
    penup()
    goto(0, 0)
    
    shapesize(1)

drawClock()
done()

But there are still many logical errors in the script, and also better ways to do the calculations. Here is a fixed version, with some commentary:

from turtle import *
from datetime import datetime
from math import sin, cos, pi

def drawClock():
    # disable the tracer while we are updating the clock, to avoid flicker.
    tracer(False)
    # Only clear before drawing each clock face, not before each hand.
    # We want to be able to see all the hands at once.
    clear()
    now = datetime.now()
    # Provide the position as a value from 0..1 instead of 0..60.
    # The logic is simpler this way.
    drawHand(now.hour / 12, 4, 60)
    drawHand(now.minute / 60, 3, 80)
    drawHand(now.second / 60, 1, 80)
    # restore the tracer so that all the drawing is shown on screen.
    tracer(True)
    # Set up the next drawing.
    # We want to know how long until the *next* second, *after drawing*
    # So, we need to call `datetime.now` again, and not use the old value
    # The `datetime` object tells us how many microseconds have passed
    # since the beginning of the second. We divide by 1000 to convert that
    # to milliseconds.
    until_next_second = 1000 - int(datetime.now().microsecond / 1000)
    ontimer(drawClock, until_next_second)

def drawHand(hand, thickness, length):
    pensize(thickness)
    # The sin and cos functions require a value in radians, not degrees.
    goto(sin(2 * pi * hand) * length, cos(2 * pi * hand) * length)
    # It's okay if we retrace the line without lifting the pen.
    goto(0, 0)
    # We don't really need to "restore" any turtle state afterward,
    # because the next call will just change it again anyway.

color("black")
hideturtle()
title("Clock")
speed(0)
drawClock()
done()

This is really helpful, thank you so much!!