7. 时钟的设计与实现

7.1. 模拟时钟

  • 表盘特征

模拟时钟的表盘是一个圆形,圆周上有60个刻度,每一个短刻度表示1秒(秒针)或1分(分针),每一个长刻度表示1小时(时针)。 我们使用screen.darwline()画短线(刻度)的方法绘制时钟表盘,就首先要知道表盘圆周上60个刻度的坐标。 已知屏幕原点位于左上角,因此我们使用如下三角函数的方法求得圆周上各点的坐标。

clock
  • 表盘绘制

先定义获取圆周上坐标的函数 角度是从0度到360度,逆时针方向。而表的指针是从90度先到360度,再回到90度,顺时针方向。因此获取前15个坐标,再获取后面45个坐标。

# 获取圆周上的60个坐标
def getCoordinateList(self, mul):
    coordinateList = []
    # 0-15
    for n in range(15):
        x = int(self.centerX + self.radius * mul * math.cos(math.pi / 30 * (15 - n)))
        y = int(self.centerY - self.radius * mul * math.sin(math.pi / 30 * (15 - n)))
        coordinate = (x, y)
        coordinateList.append(coordinate)
    # 15-60
    for n in range(45):
        x = int(self.centerX + self.radius * mul * math.cos(math.pi / 30 * (60 - n)))
        y = int(self.centerY - self.radius * mul * math.sin(math.pi / 30 * (60 - n)))
        coordinate = (x, y)
        coordinateList.append(coordinate)
    return coordinateList

获取表盘圆周起始坐标和长短终止坐标列表。

# 表盘圆周坐标
self.cirStart = self.getCoordinateList(1)
self.cirEnd1 = self.getCoordinateList(1.05)
self.cirEnd2 = self.getCoordinateList(1.10)

使用坐标绘制表盘。

# 画表盘
def drawClock(self):
    screen.clear()
    # 画表盘
    for i in range(60):
        if i % 5 == 0:
            # 画时刻度 长线
            screen.drawline(self.cirStart[i][0], self.cirStart[i][1], self.cirEnd2[i][0], self.cirEnd2[i][1], 3, 0x000000)
        else:
            # 画秒刻度 短线
            screen.drawline(self.cirStart[i][0], self.cirStart[i][1], self.cirEnd1[i][0], self.cirEnd1[i][1], 2, 0x000000)
clock
  • 运行原理
    • 秒针每1秒转动一小格,分针每1分(60秒)转动一小格,时针每1小时转动一大格每12分钟(720秒)转动一小格。
    • 时钟表盘最大刻度为12小时,因此12*60*60=43200秒后,时分秒针同时回到最初的位置。
  • 指针绘制
    • 指针的移动我们也使用screen.darwline()来绘制,也需要先获得每时刻指针的头尾坐标位置。
    • 不同的是指针在转动到下一格的时候,需要先擦除当前位置的指针,然后画出下个的指针。
    • 而且表盘有三个指针在同时转动,每次发生擦除时,都需要重新画一下所有当前时刻的指针。
  • 代码实现

导入库

import math
import time
import screen

获取时分秒指针的头尾位置坐标列表

# 时分秒指针头尾坐标
self.hurTail = self.getCoordinateList(-0.1)
self.hurHand = self.getCoordinateList(0.3)
self.minTail = self.getCoordinateList(-0.1)
self.minHand = self.getCoordinateList(0.5)
self.secTail = self.getCoordinateList(-0.1)
self.secHand = self.getCoordinateList(0.7)

根据当前时间计算已经走过的秒数

# 根据当前时间计算走过的秒数
self.totalSec = hour * 3600 + minute * 60 + second

运行函数

# 开始运行
def start(self):
    while True:
        # 获取列表下标
        hi = int(self.totalSec / 720)
        mi = int(self.totalSec / 60) % 60
        si = self.totalSec % 60

        # 画时分秒针并保留一段时间
        screen.drawline(self.hurTail[hi][0], self.hurTail[hi][1], self.hurHand[hi][0], self.hurHand[hi][1], 2, 0x000000)
        screen.drawline(self.minTail[mi][0], self.minTail[mi][1], self.minHand[mi][0], self.minHand[mi][1], 2, 0x000000)
        screen.drawline(self.secTail[si][0], self.secTail[si][1], self.secHand[si][0], self.secHand[si][1], 2, 0x000000)
        time.sleep_ms(980)  # 程序执行延时大约20ms

        # 擦除时分秒针
        screen.drawline(self.secTail[si][0], self.secTail[si][1], self.secHand[si][0], self.secHand[si][1], 2, 0xffffff)
        if self.totalSec % 60 == 59:
            screen.drawline(self.minTail[mi][0], self.minTail[mi][1], self.minHand[mi][0], self.minHand[mi][1], 2, 0xffffff)
        if self.totalSec % 720 == 719:
            screen.drawline(self.hurTail[hi][0], self.hurTail[hi][1], self.hurHand[hi][0], self.hurHand[hi][1], 2, 0xffffff)

        self.totalSec = self.totalSec + 1
        if self.totalSec >= 43200:
            self.totalSec = 0

创建对象,并运行

if __name__ == '__main__':
    cl = AnalogClock(1, 20, 0)  # 设置时间为1:20:00
    cl.start()
clock

7.2. 数字时钟

  • 基本原理

在理解了前面模拟时钟的原理后,数字的设计与实现就会变得十分容易。 数字时钟由时分秒三个数和中间的分割符(一般为冒号)组成。 在这里我们同样使用screen.darwline()函数画线的方法来显示0-9的数字和分隔符。 一个数字由7条边组成,我们根据位置关系将它们定义为1-7号边,如下图所示。

clock

实际的数字时钟通过点亮熄灭对应位置上的数码管显示对应的数字。 在skids的屏幕上我们则通过控制对应边的画线颜色来决定要显示的数字,例如要显示数字7,只需控制2-5号边为白色(屏幕背景为白色),1、6、7号边为黑色即可。 分隔符只需在指定的位置上画上两条短线即可。 数字时钟的运行原理与模拟时钟一致,每60秒分加1,每60分时加1,每24时时分秒都变0.

  • 代码实现

导入库

import time
import screen

构造函数:

def __init__(self, hour, minute, second):
    # 布局变量
    self.startX = 10  # 时钟左上角坐标X
    self.startY = 100  # 时钟左上角坐标Y
    self.margin = 8  # 数字健距
    self.edgeL = 24  # 数字边长
    self.edgeW = 2  # 数字边宽
    self.colon = int(self.edgeL / 2)  # 冒号间距

    # 显示变量
    self.colorDict = {0: 0xffffff, 1: 0x000000}  # 数字边颜色0白色1黑色
    self.numDict = {0: (1, 0, 1, 1, 1, 1, 1),  # 数字对应的显示列表
                    1: (0, 0, 0, 0, 0, 1, 1),
                    2: (1, 1, 1, 0, 1, 1, 0),
                    3: (1, 1, 1, 0, 0, 1, 1),
                    4: (0, 1, 0, 1, 0, 1, 1),
                    5: (1, 1, 1, 1, 0, 0, 1),
                    6: (1, 1, 1, 1, 1, 0, 1),
                    7: (1, 0, 0, 0, 0, 1, 1),
                    8: (1, 1, 1, 1, 1, 1, 1),
                    9: (1, 1, 1, 1, 0, 1, 1)}

    # 时间变量
    self.hur = hour
    self.min = minute
    self.sec = second

    # 初始化显示
    screen.clear()
    self.displayColon()

通过控制7条边的颜色显示出相应的数字

# 通过控制7条边的颜色显示出相应的数字
def displayNum(self, num, x, y):
    # 1号边
    x1 = x
    y1 = y
    x2 = x + self.edgeL
    y2 = y
    screen.drawline(x1, y1, x2, y2, self.edgeW, self.colorDict[num[0]])

    # 2号边
    x1 = x
    y1 = y + self.edgeL
    x2 = x + self.edgeL
    y2 = y + self.edgeL
    screen.drawline(x1, y1, x2, y2, self.edgeW, self.colorDict[num[1]])

    # 3号边
    x1 = x
    y1 = y + self.edgeL * 2
    x2 = x + self.edgeL
    y2 = y + self.edgeL * 2
    screen.drawline(x1, y1, x2, y2, self.edgeW, self.colorDict[num[2]])

    # 4号边
    x1 = x
    y1 = y
    x2 = x
    y2 = y + self.edgeL
    screen.drawline(x1, y1, x2, y2, self.edgeW, self.colorDict[num[3]])

    # 5号边
    x1 = x
    y1 = y + self.edgeL
    x2 = x
    y2 = y + self.edgeL * 2
    screen.drawline(x1, y1, x2, y2, self.edgeW, self.colorDict[num[4]])

    # 6号边
    x1 = x + self.edgeL
    y1 = y
    x2 = x + self.edgeL
    y2 = y + self.edgeL
    screen.drawline(x1, y1, x2, y2, self.edgeW, self.colorDict[num[5]])

    # 7号边
    x1 = x + self.edgeL
    y1 = y + self.edgeL
    x2 = x + self.edgeL
    y2 = y + self.edgeL * 2
    screen.drawline(x1, y1, x2, y2, self.edgeW, self.colorDict[num[6]])

分隔符的位置确定与显示

# 显示分隔符
def displayColon(self):
    # 时分间
    x1 = self.startX + self.edgeL * 2 + self.margin + self.colon
    y1 = self.startY + self.margin
    x2 = x1
    y2 = y1 + self.margin
    screen.drawline(x1, y1, x2, y2, self.edgeW, 0x000000)
    y1 = self.startY + self.margin * 4
    y2 = y1 + self.margin
    screen.drawline(x1, y1, x2, y2, self.edgeW, 0x000000)

    # 分秒间
    x1 = self.startX + self.edgeL * 4 + self.margin * 2 + self.colon * 3
    y1 = self.startY + self.margin
    x2 = x1
    y2 = y1 + self.margin
    screen.drawline(x1, y1, x2, y2, self.edgeW, 0x000000)
    y1 = self.startY + self.margin * 4
    y2 = y1 + self.margin
    screen.drawline(x1, y1, x2, y2, self.edgeW, 0x000000)

运行函数

# 开始运行
def start(self):
    while True:
        # 显示时
        hurH = int(self.hur / 10)
        hurL = self.hur % 10
        x = self.startX
        self.displayNum(self.numDict[hurH], x, self.startY)
        x = self.startX + self.edgeL + self.margin
        self.displayNum(self.numDict[hurL], x, self.startY)

        # 显示分
        minH = int(self.min / 10)
        minL = self.min % 10
        x = self.startX + self.edgeL * 2 + self.margin + self.colon * 2
        self.displayNum(self.numDict[minH], x, self.startY)
        x = self.startX + self.edgeL * 3 + self.margin * 2 + self.colon * 2
        self.displayNum(self.numDict[minL], x, self.startY)

        # 显示秒
        secH = int(self.sec / 10)
        secL = self.sec % 10
        x = self.startX + self.edgeL * 4 + self.margin * 2 + self.colon * 4
        self.displayNum(self.numDict[secH], x, self.startY)
        x = self.startX + self.edgeL * 5 + self.margin * 3 + self.colon * 4
        self.displayNum(self.numDict[secL], x, self.startY)

        # 计算下一时刻
        self.sec = self.sec + 1
        if self.sec >= 60:
            self.min = self.min + 1
            self.sec = 0
            if self.min >= 60:
                self.hur = self.hur + 1
                self.min = 0
                if self.hur >= 24:
                    self.hur = 0

        # 一个循环执行1s
        time.sleep_ms(912)  # 程序执行时延大约88ms

创建对象,并运行

if __name__ == '__main__':
    dc = DigitalClock(23, 58, 30)  # 设置初始时间为23:58:30
    dc.start()
clock