Python pygame实现动态时钟效果教程

宋时安
宋时安
发布时间:2026-10-14 | 4人浏览过
原创

本文介绍如何使用Python的pygame库制作一个带有指针的动态时钟。通过结合datetime获取实时时间,利用math模块进行三角函数计算确定表针坐标,实现时针、分针和秒针的精准绘制与动态更新。

使用pygame制作一个动态时钟示例,供参考学习。

初次接触pygame时,由于基础较为薄弱,每个示例都需要逐句研读才能理解。本文演示如何制作一个包含时针、分针和秒针且能正确走时的简易时钟。虽然示例简单,但涵盖了坐标计算、圆形绘制及文字处理等基础知识点,适合初学者借鉴。学习编程重在逻辑,因此本文将尽量还原初学时的思考步骤,直接开始实现。

前期准备

本例主要依赖pygame、math和datetime三个库。其中,datetime.today()用于获取当前系统时间,math.sin()和math.cos()用于计算表针的坐标位置。核心绘图方法包括pygame.draw.circle()用于绘制表盘,以及pygame.draw.line()用于绘制表针。

开始下手

首先初始化pygame,并创建一个显示窗口,完成基础环境搭建。


import math, pygame
from pygame.locals import *
from datetime import datetime, date, time

pygame.init()
screen = pygame.display.set_mode((600, 600))
pygame.display.set_caption("Clock")

接着绘制一个圆形作为表盘,需要确定圆心的位置和半径大小。


pos_x = 300
pos_y = 300
radius = 250

为了区分不同表针的颜色,预先定义四种颜色变量。


white = 255, 255, 255
red = 240, 0, 0
green = 0, 240, 0
blue = 0, 0, 240

计算表针坐标需要角度参数,因此先初始化角度变量。


hour_angle = 0
minute_angle = 0
second_angle = 0

在表盘上显示数字需要处理文字渲染,预先设置好字体样式。


font = pygame.font.Font(None, 24)

最后封装两个常用方法:一个用于输出文字,另一个用于规范角度范围(确保在0-360度之间),这两个方法将在后续代码中频繁调用。


# abs()是取绝对值,%运算符号很有意思,效果是360取余数,保证了角度在360以内
def wrap_angle(angle):
    return abs(angle % 360)

# 把打印文字的步骤封装在一个方法里,每次方便调用,简化代码
def print_text(font, x, y, text, color):
    imgText = font.render(text, True, color)
    screen.blit(imgText, (x, y))

准备工作完成后,开始编写主循环逻辑。


while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()
    keys = pygame.key.get_pressed()
    if keys[K_ESCAPE]:
        exit()

这部分是标准的窗口事件处理格式,用于响应用户关闭窗口的操作。

在每帧绘制前,先用背景色填充屏幕。这里选择了一种颜色,虽然可能不够美观,但足以作为背景使用。


screen.fill((131, 139, 139))

首先绘制表盘主体。


# 画表盘
pygame.draw.circle(screen, white, (pos_x, pos_y), radius, 2)

在表盘上绘制数字刻度。


# 写表盘数字
for n in range(1, 13):  
    # 一共12个数字,平均分到一个圆内 每两个数字之间的角度为 360/12
    x = math.cos(math.radians(angle)) * (radius - 10) - 5
    y = math.sin(math.radians(angle)) * (radius - 10) - 7
    print_text(font, pos_x + x, pos_y + y, str(n), white)

此处解释几个常用函数和概念,以便日后复习时不会遗忘。

range(x,y)

该函数生成从x开始到y结束(不包含y)的整数序列。例如range(1, 13)生成1到12的数字。若省略起始值x,则默认从0开始。

math.radians(angle)、math.sin()、math.cos()

这部分逻辑较为复杂。要遍历圆周,需要三个参数:圆心坐标(pos_x, pos_y)、半径(radius)和角度(angle)。通过三角函数中的正弦和余弦值乘以半径,可以计算出圆周上每个点的坐标(x, y)。由于正弦和余弦函数接收的是弧度制参数,因此需要使用math.radians(angle)将角度转换为弧度。

这里有一个问题:为什么正弦和余弦乘以半径就能得出圆周上点的坐标?

参考示意图可自行理解,原理很简单。

假设A点是圆心,B点是圆周上的点,AB是圆的半径。那么B点的x坐标等于cos(A)乘以AB的长度,y坐标等于sin(A)乘以AB的长度。

在计算x和y坐标时,为什么要减去5和7?

因为在pygame中绘制模型时,坐标点指的是模型的左上角,而非中心点。无论是图片还是文字,都需要减去相应的像素值,以确保模型显示在正确的位置,否则会出现向右下偏移的情况。这一技巧在后续绘制中会经常用到。

到目前为止,表盘已经绘制完成,效果如下。

接下来开始绘制表针。首先获取当前的系统时间。


# 获取时间
time = datetime.today()
hour = time.hour % 12
minute = time.minute
second = time.second

绘制秒针。


# 画秒针
second_angle = wrap_angle(second * (360 / 60) - 90)  # 秒针是60进制,所以一秒的角度为 360/60
second_x = math.cos(math.radians(second_angle)) * (radius - 3)
second_y = math.sin(math.radians(second_angle)) * (radius - 3)
pygame.draw.line(screen, blue, (pos_x, pos_y), (pos_x + second_x, pos_y + second_y), 2)

这里又有一个问题:为什么要减去90度?

因为在绘制圆形时,0度默认位于3点钟方向。而在实际钟表设计中,我们希望0度(12点位置)位于12点钟方向。因此减去90度,以调整角度基准。

radius - 3是为了让秒针短一些,避免其尖端触碰到表盘边缘。解决了秒针后,分针和时针的绘制就简单多了。

绘制分针和时针。


# 画分针
    minute_angle = wrap_angle(minute * (360 / 60) - 90)  # 分针也是60进制,原理同秒针
    minute_x = math.cos(math.radians(minute_angle)) * (radius - 40)
    minute_y = math.sin(math.radians(minute_angle)) * (radius - 40)
    pygame.draw.line(screen, green, (pos_x, pos_y), (pos_x + minute_x, pos_y + minute_y), 4)
    # 画时针
    # 时针算角度时增加了一部分,因为钟表分针走的时候,时针也在走一个很小的角度 即30/60,
    # 加上这个角度,表才更加逼真,否则分针走的时候,时针一直保持指到整点,是错误的
    hour_angle = wrap_angle(hour * (360 / 12) - 90) + minute * 30 / 60
    hour_x = math.cos(math.radians(hour_angle)) * (radius - 80)
    hour_y = math.sin(math.radians(hour_angle)) * (radius - 80)
    pygame.draw.line(screen, red, (pos_x, pos_y), (pos_x + hour_x, pos_y + hour_y), 6)

这里有一个技巧:在计算hour_angle时,后面加上了minute * 30 / 60。这部分的逻辑是:minute / 60 * (360 / 12)。minute是当前分钟数,除以60得到比例,12个小时对应360度,每两个数字之间的角度为360/12即30度。加上这一部分,使得分针每走一分钟,时针也会相应移动一点角度,从而使时钟显示更加逼真。

在屏幕上显示当前时间字符串,并检查表针位置是否与当前时间一致。


# 写时间
    print_text(font, 10, 10, str(hour) + " : " + str(minute) + " : " + str(second), white)

最后在圆心位置绘制一个小圆点,覆盖三个表针交叉的中心点,使外观更美观。


# 画表中心的圆点 之所以放在最后是想盖住三个针的原点
    pygame.draw.circle(screen, white, (pos_x, pos_y), 8, 0)

最后一步,别忘了刷新屏幕以显示最新画面。


pygame.display.update()

完整代码如下。


import math, pygame
from pygame.locals import *
from datetime import datetime, date, time

pygame.init()
screen = pygame.display.set_mode((600, 600))
pygame.display.set_caption("Clock")

pos_x = 300
pos_y = 300
radius = 250

white = 255, 255, 255
red = 240, 0, 0
green = 0, 240, 0
blue = 0, 0, 240

hour_angle = 0
minute_angle = 0
second_angle = 0

font = pygame.font.Font(None, 24)


def wrap_angle(angle):
    return abs(angle % 360)


def print_text(font, x, y, text, color):
    imgText = font.render(text, True, color)
    screen.blit(imgText, (x, y))


while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()
    keys = pygame.key.get_pressed()
    if keys[K_ESCAPE]:
        exit()

    screen.fill((131, 139, 139))

    # 画表盘
    pygame.draw.circle(screen, white, (pos_x, pos_y), radius, 2)

    # 写表盘数字
    for n in range(1, 13):  # range(x,y)是从x开始到y结束 但不包括y,所以这里是1-13
        # 一共12个数字,平均分到一个圆内 每两个数字之间的角度为 360/12
        # 减90是因为默认开始点是3点钟方向,而实际上是12点钟方向,下面画分针、时针、秒针时也是这个道理
        angle = n * 360 / 12 - 90
        # 这里x,y减5减7,是因为写文字的时候坐标不是文字的中心点而是文字的左上角
        # ,所以文字会显得往右下角偏,稍微的修正看起来更舒服
        x = math.cos(math.radians(angle)) * (radius - 10) - 5
        y = math.sin(math.radians(angle)) * (radius - 10) - 7
        print_text(font, pos_x + x, pos_y + y, str(n), white)

    # 获取时间
    time = datetime.today()
    hour = time.hour % 12
    minute = time.minute
    second = time.second

    # 画秒针
    second_angle = wrap_angle(second * (360 / 60) - 90)  # 秒针是60进制,所以一秒的角度为 360/60
    second_x = math.cos(math.radians(second_angle)) * (radius - 3)
    second_y = math.sin(math.radians(second_angle)) * (radius - 3)
    pygame.draw.line(screen, blue, (pos_x, pos_y), (pos_x + second_x, pos_y + second_y), 2)
    # 画分针
    minute_angle = wrap_angle(minute * (360 / 60) - 90)  # 分针也是60进制,原理同秒针
    minute_x = math.cos(math.radians(minute_angle)) * (radius - 40)
    minute_y = math.sin(math.radians(minute_angle)) * (radius - 40)
    pygame.draw.line(screen, green, (pos_x, pos_y), (pos_x + minute_x, pos_y + minute_y), 4)
    # 画时针
    # 时针算角度时增加了一部分,因为钟表分针走的时候,时针也在走一个很小的角度 即30/60,
    # 加上这个角度,表才更加逼真,否则分针走的时候,时针一直保持指到整点,是错误的
    hour_angle = wrap_angle(hour * (360 / 12) - 90) + minute * 30 / 60
    hour_x = math.cos(math.radians(hour_angle)) * (radius - 80)
    hour_y = math.sin(math.radians(hour_angle)) * (radius - 80)
    pygame.draw.line(screen, red, (pos_x, pos_y), (pos_x + hour_x, pos_y + hour_y), 6)

    # 写时间
    print_text(font, 10, 10, str(hour) + " : " + str(minute) + " : " + str(second), white)

    # 画表中心的圆点 之所以放在最后是想盖住三个针的原点
    pygame.draw.circle(screen, white, (pos_x, pos_y), 8, 0)


    pygame.display.update()

效果图

虽然界面可能不够美观,但基本功能均已实现。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表