Avoid import *
; there's only a few symbols you need from turtle.
There's a lot of repetition in your hand logic. Consider factoring out a class that does the common operations, and derive from this class for varying behaviours and attributes in each of your hands.
Do not speed(10)
. Under realistic (i.e. non-beginner, non-debug) circumstances, the speed should be fastest
, and tracing should be disabled so that updates are required to be explicit and flicker is removed.
Do not left(90)
, for several reasons. First, this is a relative rotation when you should start with an absolute setheading
. Also, this assumes the use of the default coordinate system, which does not suit your application. You should set up a different world coordinate system where "zero degrees" points north, and positive rotation is clockwise. It would be nice for this coordinate system to be normalised - coordinates on the order of 1 - independent of the window pixel dimensions.
Do not right(.06)
. This assumes that the hand had been pointing at the correct direction in the previous frame, and also assumes that the exact correct amount of time has elapsed. Instead, do an absolute setheading
call based on the current time.
Similarly, do not backward()
. Just return to the origin with goto(0, 0)
. Do this at the beginning of your draw routine rather than at the end: if, instead of hiding the cursor you decide to show the cursor, your hand ends will change from bare:
to decorated:
This is not possible if you return to the origin between drawing and updating.
Do not time.sleep
. turtle
has its own event scheduler that you should use.
Suggested
from turtle import Turtle, Screen, ontimer, mainloop
from datetime import datetime, time
class Hand:
def __init__(self, pensize: int, color: str, degrees: float, length: float) -> None:
t = Turtle(undobuffersize=0, visible=False)
t.speed('fastest')
t.color(color)
t.pensize(pensize)
t.degrees(degrees)
self.turtle = t
self.length = length
def draw(self, now: time) -> None:
t = self.turtle
t.clear()
t.penup()
t.goto(0, 0)
t.pendown()
t.setheading(self.angle_from_time(now))
t.forward(self.length)
@staticmethod
def angle_from_time(now: time) -> float:
raise NotImplementedError()
class SecondHand(Hand):
def __init__(self) -> None:
super().__init__(pensize=1, color='red', degrees=60, length=0.94)
@staticmethod
def angle_from_time(now: time) -> float:
return now.second + now.microsecond / 1e6
class MinuteHand(Hand):
def __init__(self) -> None:
super().__init__(pensize=2, color='black', degrees=60, length=0.89)
@staticmethod
def angle_from_time(now: time) -> float:
return now.minute
class HourHand(Hand):
def __init__(self) -> None:
super().__init__(pensize=4, color='black', degrees=12, length=0.64)
@staticmethod
def angle_from_time(now: time) -> float:
return now.hour
class Clock:
def __init__(self, width: int = 425, update_ms: int = 200) -> None:
s = Screen()
s.setup(width, width)
# 0 degrees points north; positive rotation is clockwise
s.setworldcoordinates(llx=-1, lly=1, urx=1, ury=-1)
s.title('Clock')
s.tracer(n=0)
self.screen = s
self.update_ms = update_ms
self.hands = (SecondHand(), MinuteHand(), HourHand())
def update(self) -> None:
now = datetime.now().time()
for hand in self.hands:
hand.draw(now)
self.screen.update()
ontimer(self.update, t=self.update_ms)
if __name__ == '__main__':
Clock().update()
mainloop()