前言

Turtle是Python自带的画图库,很有意思也很好用。在IDLE里面可以找到一些demo

画表就很经典了,但是我总觉得demo里面那个表,对于一个新手来说很不友好,因为全是函数,画图的逻辑不直观。所以我当年刚玩的时候,是自己摸索着画的。现在再看也是感觉很不错~

思路

总体来看,分为两个部分:固定的表盘,和不断刷新的指针(和时间显示)

在开始之前先准备一下:

from turtle import *
import datetime as dt
import math
import time

tracer(False)    # 完成之后再添加:最快速度
title('时钟')    # 标题
penup()          # 保持画笔抬起

以及提醒一下,如果在某些编辑器里面运行出现画完闪退的情况,可以在代码最后面加一行:

mainloop()

这个是turtle库里面的函数:turtle.mainloop(),用来维持窗口

固定的表盘

我的表盘长这个样子:

分为三个部分:60个dot点(分、秒),12条杠(小时),以及12个数字(小时)

这里还涉及一个取点位置的问题:极坐标

x = r \cdot \cos(\theta)y = r \cdot \sin(\theta)

计算用到的是 math 库里面的 sincos 函数,其输入是弧度,所以要记得转化一下。

        1.    首先是画60个点:(取半径为200)

# 画点
left(90)
for i in range(12*5):
    x=200*math.sin(i/30*math.pi)    # 横坐标
    y=200*math.cos(i/30*math.pi)    # 纵坐标
    goto(x,y)
    dot(7)

        2.    其次,画12条杠,同步写上12个数字:

setheading(60)
pensize(10)

for i in range(1,13):
    x=200*math.sin(i/6*math.pi)
    y=200*math.cos(i/6*math.pi)
    goto(x,y)

    pendown()
    forward(20)
    penup()

    write(i, font=('Times New Roman',20,'normal','bold'))
    right(30)

setheading(0)

至此表盘绘制完成,应该跟上面的截图一样。(细节可以自行修改)

刷新的指针

这一步分为两个部分,一个是画上去,另一个是刷新

画指针,首先就是需要获取现在的时间,用的是datetime库的函数:datetime.datetime.now()

根据现在的时间,可以分别得到秒针、分针、时针所指的角度。:(不考虑微秒)

                \theta_{second} = second \cdot \frac{360}{60}

        ​​​​​​​        \theta_{minute} =minute \cdot \frac{360}{60} + second \cdot \frac{360/60}{60}

        ​​​​​​​        \theta_{hour} = hour \cdot \frac{360}{12} + minute \cdot \frac{360/12}{60} + second \cdot \frac{360/60/12}{60}

但是,由于坐标系本身是逆时针的,而且时钟的起点在坐标系是90度,所以:

#再让表走:
while True:
    T = dt.datetime.now()
    angle_hour = 30*T.hour+T.minute/2+T.second/120
    angle_minute = T.minute*6+T.second/10
    angle_second = T.second*6

    # 小时
    goto(0,0)
    setheading(90-angle_hour)
    pendown()
    pensize(7)
    forward(120)
    penup()
    
    # 分钟
    goto(0,0)
    setheading(90-angle_minute)
    pendown()
    pensize(5)
    forward(150)
    penup()

    # 秒
    goto(0,0)
    setheading(90-angle_second)
    pendown()
    pensize(3)
    forward(170)
    penup()

    # 刷新
    update()
    time.sleep(0.1)

可以看到,这个时候指针已经画好,就差最后一步:把旧的指针清除。

这里有两种方案,一种是现在看来比较常见的:把整个画布清除,再从头开始画

我最开始没有想到这个,反而是想到了撤销 undo() 这个功能

也就是第二种:通过 for 循环,把之前画指针的操作给挨个撤销掉。(要计算画指针用了多少步)

for i in range(18):
    undo()

这个要添加在上面的 time.sleep(0.1) 后面。也就是说,整个刷新操作就是:

先刷新,再停顿,最后撤销。(之后进入下一循环)

至此基本功能全部完成。更多个性化设置(如颜色、文字显示)可以自行研发

附:

  • Turtle库的官方文档:

turtle — Turtle graphics — Python 3.7.14 documentation

  • 整体的代码(有些增删):
from turtle import *
import datetime as dt
import math
import time


# 先画固定的表盘:
tracer(False)
title('时钟')
penup()

left(90)
for i in range(12*5):
    x=200*math.sin(i/30*math.pi)
    y=200*math.cos(i/30*math.pi)
    goto(x,y)
    dot(7)

    if i%5==0:
        pendown()
        pensize(10)
        forward(20)
        penup()
        if i==0:
            write('12',align='center',font=('Times New Roman',20,'normal','bold'))
        else:
            write(str(int(i/5)),font=('Times New Roman',20,'normal','bold'))
        right(30)


# 再安排当前日期:
t=dt.datetime.now()
year=t.year
month=t.month
day=t.day
week=t.isoweekday()
goto(0,100)
Week=['一','二','三','四','五','六','日']
write('星期'+Week[week-1],align='center',font=('楷体',20,'bold'))
goto(0,-100)
date=str(year)+'  '+str(month)+'  '+str(day)
write(date,align='center',font=('Times New Roman',20,'bold'))


# 再让表走:
print('直接叉掉退出')
while 1>0:
    T=dt.datetime.now()
    t=str(T.hour)+': '+str(T.minute)+': '+str(T.second)
    angle1=30*T.hour+T.minute/2+T.second/120
    angle2=T.minute*6+T.second/10
    angle3=T.second*6

    goto(0,0)
    setheading(90-angle1)
    pendown()
    pensize(7)
    forward(120)
    penup()
    
    goto(0,0)
    setheading(90-angle2)
    pendown()
    pensize(5)
    forward(150)
    penup()

    goto(0,0)
    setheading(90-angle3)
    pendown()
    pensize(3)
    forward(170)
    stamp()
    penup()
    
    goto(0,-150)
    write(t,align='center',font=('Times New Roman',20,'bold'))

    try:
        for i in range(20):
            undo()
    except Terminator:
        break

    time.sleep(0.1)

# mainloop()

有问题欢迎以各种形式提出 ~

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐