前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用python画模拟时钟表盘

用python画模拟时钟表盘

原创
作者头像
mariolu
修改2024-03-15 20:04:51
15700
代码可运行
修改2024-03-15 20:04:51
举报
运行总次数:0
代码可运行

一、 终端模拟时钟

本篇文章通过使用简单的ascii字符在终端上画模拟时钟表盘,可以展示当前时间、天数、周数。

这里不使用第三方库。完整代码如下,你可以使用云社区自带的python运行组件,跑去来看看效果。

这里看到了是15日,也是一个周五。显示了当前时间11点51分27秒。

二、代码及流程

完整代码如下,或者可以在这里下到lumanyu/ascii_clock: Python script that prints out a clock in ASCII art style to the console (github.com)

代码语言:python
代码运行次数:0
复制
#-*- coding: utf-8 -*-
#-----------------------------------------------------------------------
# 
#-----------------------------------------------------------------------

class AsciiCanvas(object):
    """
    ASCII canvas for drawing in console using ASCII chars
    """

    def __init__(self, cols, lines, fill_char=' '):
        """
        Initialize ASCII canvas
        初始化画板,填充fill_char
        """
        if cols < 1 or cols > 1000 or lines < 1 or lines > 1000:
            raise Exception('Canvas cols/lines must be in range [1..1000]')
        self.cols = cols
        self.lines = lines
        if not fill_char:
            fill_char = ' '
        elif len(fill_char) > 1:
            fill_char = fill_char[0]
        self.fill_char = fill_char
        self.canvas = [[fill_char] * (cols) for _ in range(lines)]  # 得到一个cols列的一维数组,然后lines个数组组成二维数组

    def clear(self):
        """
        Fill canvas with empty chars
        """
        self.canvas = [[self.fill_char] * (self.cols) for _ in range(self.lines)]  # 清空,用fill_char清空

    def print_out(self):
        """
        Print out canvas to console
        """
        print(self.get_canvas_as_str())  # 打印到荧幕

    def add_line(self, x0, y0, x1, y1, fill_char='o'):
        """
        Add ASCII line (x0, y0 -> x1, y1) to the canvas, fill line with `fill_char`
        用fill_char画线
        """
        if not fill_char:
            fill_char = 'o'
        elif len(fill_char) > 1:
            fill_char = fill_char[0]
        if x0 > x1: # 从左边画到右边 
            # swap A and B
            x1, x0 = x0, x1
            y1, y0 = y0, y1
        # get delta x, y
        dx = x1 - x0
        dy = y1 - y0
        # if a length of line is zero just add point
        if dx == 0 and dy == 0:
            if self.check_coord_in_range(x0, y0): # 这个点在画布内
                self.canvas[y0][x0] = fill_char
            return
        # when dx >= dy use fill by x-axis, and use fill by y-axis otherwise
        # 哪条边长度长,就填哪边
        if abs(dx) >= abs(dy):
            for x in range(x0, x1 + 1):  # 闭区间[x0,x1+1)
                # 如果是竖线,y就是y0,如果是一条斜线,y的坐标按照比例重新计算绘图点y坐标
                y = y0 if dx == 0 else y0 + int(round((x - x0) * dy / float((dx)))) #
                if self.check_coord_in_range(x, y):
                    self.canvas[y][x] = fill_char  # 填充点
        else:
            if y0 < y1: # 从下往上画
                for y in range(y0, y1 + 1):
                    x = x0 if dy == 0 else x0 + int(round((y - y0) * dx / float((dy))))
                    if self.check_coord_in_range(x, y):
                        self.canvas[y][x] = fill_char
            else:  # 从上往下画
                for y in range(y1, y0 + 1):
                    x = x0 if dy == 0 else x1 + int(round((y - y1) * dx / float((dy))))
                    if self.check_coord_in_range(x, y):
                        self.canvas[y][x] = fill_char

    def add_text(self, x, y, text):
        """
        Add text to canvas at position (x, y)
        在指定位置添加文本
        """
        for i, c in enumerate(text): # i为字的个数
            if self.check_coord_in_range(x + i, y):
                self.canvas[y][x + i] = c  # 在[x,x+i]位置添加char

    def add_rect(self, x, y, w, h, fill_char=' ', outline_char='o'):
        """
        Add rectangle filled with `fill_char` and outline with `outline_char`
        在画板外围添加方框,增加画面感
        """
        if not fill_char:
            fill_char = ' '
        elif len(fill_char) > 1:
            fill_char = fill_char[0]
        if not outline_char:
            outline_char = 'o'
        elif len(outline_char) > 1:
            outline_char = outline_char[0]
        for px in range(x, x + w):
            for py in range(y, y + h):
                if self.check_coord_in_range(px, py):
                    if px == x or px == x + w - 1 or py == y or py == y + h - 1: #如果坐标位于画板四周
                        self.canvas[py][px] = outline_char
                    else:
                        self.canvas[py][px] = fill_char

    def add_nine_patch_rect(self, x, y, w, h, outline_3x3_chars=None):  #画方框格
        """
        Add nine-patch rectangle
        """
        default_outline_3x3_chars = (
            '.', '-', '.', 
            '|', ' ', '|', 
            '`', '-', "'"
        )
        if not outline_3x3_chars:
            outline_3x3_chars = default_outline_3x3_chars
        # filter chars
        filtered_outline_3x3_chars = []
        for index, char in enumerate(outline_3x3_chars[0:9]):
            if not char:
                char = default_outline_3x3_chars[index]
            elif len(char) > 1:
                char = char[0]
            filtered_outline_3x3_chars.append(char)
        for px in range(x, x + w):  #在方格绘图区域
            for py in range(y, y + h): #在方格绘图区域
                if self.check_coord_in_range(px, py):  #在绘图区域把9个元素放上去
                    if px == x and py == y:
                        self.canvas[py][px] = filtered_outline_3x3_chars[0]
                    elif px == x and y < py < y + h - 1:
                        self.canvas[py][px] = filtered_outline_3x3_chars[3]
                    elif px == x and py == y + h - 1:
                        self.canvas[py][px] = filtered_outline_3x3_chars[6]
                    elif x < px < x + w - 1 and py == y:
                        self.canvas[py][px] = filtered_outline_3x3_chars[1]
                    elif x < px < x + w - 1 and py == y + h - 1:
                        self.canvas[py][px] = filtered_outline_3x3_chars[7]
                    elif px == x + w - 1 and py == y:
                        self.canvas[py][px] = filtered_outline_3x3_chars[2]
                    elif px == x + w - 1 and y < py < y + h - 1:
                        self.canvas[py][px] = filtered_outline_3x3_chars[5]
                    elif px == x + w - 1 and py == y + h - 1:
                        self.canvas[py][px] = filtered_outline_3x3_chars[8]
                    else:
                        self.canvas[py][px] = filtered_outline_3x3_chars[4]

    def check_coord_in_range(self, x, y):
        """
        检查坐标位于画布有效范围
        Check that coordinate (x, y) is in range, to prevent out of range error
        """
        return 0 <= x < self.cols and 0 <= y < self.lines

    def get_canvas_as_str(self):
        """
        Return canvas as a string
        画布输出成可打印字符串,打印到荧幕
        """
        return '\n'.join([''.join(col) for col in self.canvas])

    def __str__(self):
        """
        Return canvas as a string
        """
        return self.get_canvas_as_str()
        
        
#!/usr/bin/env python
#-*- coding: utf-8 -*-
#-----------------------------------------------------------------------
#
#-----------------------------------------------------------------------

import os
import time
import math
import datetime


x_scale_ratio = 1.75 #x轴调整系数,以y为基础长度,x=y乘以这个系数


def draw_second_hand(ascii_canvas, seconds, length, fill_char):
    """
    Draw second hand
    画秒针
    """
    x0 = int(math.ceil(ascii_canvas.cols / 2.0))
    y0 = int(math.ceil(ascii_canvas.lines / 2.0))
    x1 = x0 + int(math.cos((seconds + 45) * 6 * math.pi / 180) * length * x_scale_ratio)
    y1 = y0 + int(math.sin((seconds + 45) * 6 * math.pi / 180) * length)
    ascii_canvas.add_line(int(x0), int(y0), int(x1), int(y1), fill_char=fill_char)


def draw_minute_hand(ascii_canvas, minutes, length, fill_char):
    """
    Draw minute hand
    画分针
    """
    x0 = int(math.ceil(ascii_canvas.cols / 2.0))
    y0 = int(math.ceil(ascii_canvas.lines / 2.0))
    x1 = x0 + int(math.cos((minutes + 45) * 6 * math.pi / 180) * length * x_scale_ratio)
    y1 = y0 + int(math.sin((minutes + 45) * 6 * math.pi / 180) * length)
    ascii_canvas.add_line(int(x0), int(y0), int(x1), int(y1), fill_char=fill_char)


def draw_hour_hand(ascii_canvas, hours, minutes, length, fill_char):
    """
    Draw hour hand
    画小时针
    """
    x0 = int(math.ceil(ascii_canvas.cols / 2.0))
    y0 = int(math.ceil(ascii_canvas.lines / 2.0))
    total_hours = hours + minutes / 60.0
    x1 = x0 + int(math.cos((total_hours + 45) * 30 * math.pi / 180) * length * x_scale_ratio)
    y1 = y0 + int(math.sin((total_hours + 45) * 30 * math.pi / 180) * length)
    ascii_canvas.add_line(int(x0), int(y0), int(x1), int(y1), fill_char=fill_char)


def draw_clock_face(ascii_canvas, radius, mark_char):
    """
    Draw clock face with hour and minute marks
    画表盘,表盘上添加小时和分钟 数字形式
    """
    x0 = ascii_canvas.cols // 2  #带四舍五入的除法,比如说10//3在python3中等于3,相当于int
    y0 = ascii_canvas.lines // 2
    # draw marks first
    for mark in range(1, 12 * 5 + 1): #总共有60分钟,就是外面60个、刻度
        x1 = x0 + int(math.cos((mark + 45) * 6 * math.pi / 180) * radius * x_scale_ratio)
        y1 = y0 + int(math.sin((mark + 45) * 6 * math.pi / 180) * radius)
        if mark % 5 != 0: #画刻度、
            ascii_canvas.add_text(x1, y1, mark_char)
    # start from 1 because at 0 index - 12 hour
    for mark in range(1, 12 + 1): # 画小时数,圆周围的12个小时数
        x1 = x0 + int(math.cos((mark + 45) * 30 * math.pi / 180) * radius * x_scale_ratio)
        y1 = y0 + int(math.sin((mark + 45) * 30 * math.pi / 180) * radius)
        ascii_canvas.add_text(x1, y1, '%s' % mark) #画小时数


def draw_clock(cols, lines):
    """
    Draw clock
    """
    if cols < 25 or lines < 25:
        print('Too little columns/lines for print out the clock!')
        exit()
    # prepare chars
    single_line_border_chars = ('.', '-', '.', '|', ' ', '|', '`', '-', "'")
    second_hand_char = '.' #秒针像素点
    minute_hand_char = 'o' #分针像素点
    hour_hand_char = 'O' #小时针像素点
    mark_char = '`'
    if os.name == 'nt':
        single_line_border_chars = ('.', '-', '.', '|', ' ', '|', '`', '-', "'")  # ('\xDA', '\xC4', '\xBF', '\xB3', '\x20', '\xB3', '\xC0', '\xC4', '\xD9')
        second_hand_char = '.'  # '\xFA'
        minute_hand_char = 'o'  # '\xF9'
        hour_hand_char = 'O'  # 'o'
        mark_char = '`'  # '\xF9'
    # create ascii canvas for clock and eval vars
    ascii_canvas = AsciiCanvas(cols, lines)  #创建大表盘
    center_x = int(math.ceil(cols / 2.0))
    center_y = int(math.ceil(lines / 2.0))
    radius = center_y - 5 #表盘半径
    second_hand_length = int(radius / 1.17) #秒针长度
    minute_hand_length = int(radius / 1.25) #分针长度
    hour_hand_length = int(radius / 1.95) #小时针长度
    # add clock region and clock face
    ascii_canvas.add_rect(5, 3, int(math.floor(cols / 2.0)) * 2 - 9, int(math.floor(lines / 2.0)) * 2 - 5) #添加外围方框
    draw_clock_face(ascii_canvas, radius, mark_char) #画表盘
    now = datetime.datetime.now()
    # add regions with weekday and day if possible
    if center_x > 25: #如果有绘图空间,添加周数和天数
        left_pos = int(radius * x_scale_ratio) / 2 - 4
        ascii_canvas.add_nine_patch_rect(int(center_x + left_pos), int(center_y - 1), 5, 3, single_line_border_chars) #添加小方框
        ascii_canvas.add_text(int(center_x + left_pos + 1), int(center_y), now.strftime('%a'))  #添加周数
        ascii_canvas.add_nine_patch_rect(int(center_x + left_pos + 5), int(center_y - 1), 4, 3, single_line_border_chars)
        ascii_canvas.add_text(int(center_x + left_pos + 1 + 5), int(center_y), now.strftime('%d')) #添加天数
    # add clock hands
    draw_second_hand(ascii_canvas, now.second, second_hand_length, fill_char=second_hand_char) #添加秒针
    draw_minute_hand(ascii_canvas, now.minute, minute_hand_length, fill_char=minute_hand_char) #添加分针
    draw_hour_hand(ascii_canvas, now.hour, now.minute, hour_hand_length, fill_char=hour_hand_char) #添加小时针
    # print out canvas
    ascii_canvas.print_out() #打印到荧幕


def main():
    lines = 30
    cols = int(lines * x_scale_ratio)
    # set console window size and screen buffer size
    if os.name == 'nt':
        os.system('mode con: cols=%s lines=%s' % (cols + 1, lines + 1))
    while True:
       os.system('cls' if os.name == 'nt' else 'clear') #清屏
       draw_clock(cols, lines) #画时钟
       time.sleep(0.2) #每0.2秒进行刷新,如果觉得屏幕太闪,把这个数调大点


if __name__ == '__main__':
    main()

这份代码里,我们声明了一个class AsciiCanvas类来模拟画布,并提供了以下方法

  • clear(self):清除画布
  • print_out(self): 打印到屏幕
  • add_line(self, x0, y0, x1, y1, fill_char='o'): 用fill_char画线
  • add_text(self, x, y, text):在指定位置写ascii char
  • add_rect(self, x, y, w, h, fill_char=' ', outline_char='o'):在画板外围添加方框,增加画面感
  • add_nine_patch_rect(self, x, y, w, h, outline_3x3_chars=None):画方框格
  • check_coord_in_range(self, x, y):检查绘图点在有效区域画布内
  • get_canvas_as_str(self):序列化成可打印字符串

于是我们的主体程序里就可以这样做

  • # create ascii canvas for clock and eval vars
  • ascii_canvas = AsciiCanvas(cols, lines)#创建大表盘
  • ascii_canvas.add_rect(5,3,int(math.floor(cols /2.0))*2-9,int(math.floor(lines /2.0))*2-5)#添加外围方框 draw_clock_face(ascii_canvas, radius, mark_char)#画表盘
  • ascii_canvas.add_nine_patch_rect(int(center_x + left_pos),int(center_y -1),5,3, single_line_border_chars)#添加小方框
  • ascii_canvas.add_text(int(center_x + left_pos +1),int(center_y), now.strftime('%a'))#添加周数
  • ascii_canvas.add_nine_patch_rect(int(center_x + left_pos +5),int(center_y -1),4,3, single_line_border_chars)
  • ascii_canvas.add_text(int(center_x + left_pos +1+5),int(center_y), now.strftime('%d'))#添加天数# add clock hands
  • draw_second_hand(ascii_canvas, now.second, second_hand_length, fill_char=second_hand_char)#添加秒针
  • draw_minute_hand(ascii_canvas, now.minute, minute_hand_length, fill_char=minute_hand_char)#添加分针
  • draw_hour_hand(ascii_canvas, now.hour, now.minute, hour_hand_length, fill_char=hour_hand_char)#添加小时针
  • ascii_canvas.print_out()#打印到荧幕

这里推荐腾讯云的海外主机,因为对于开发人员,有时候需要GitHub能够快速稳定的连接。所以我的开发机都会去买腾讯云香港或者新加坡地区机器,而且现在有活动2024新春采购节也很优惠。

比如这个新加坡地区的活动,我们个人用开发机选择最便宜配置基础上再打2折。按照小时数收费,用多久收费多久。

云服务器CVM购买_云服务器CVM选购 - 腾讯云 (tencent.com)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、 终端模拟时钟
  • 二、代码及流程
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com