学习笔记分享

分享与展示个人专业知识学习笔记

0%

Python月相设计思路

要求可以展示任一月份月相,并单击日期实现绘图。本文浅析一下该题设计思路。

本题关键:

  1. 月历中每日对应星期
  2. 月相与日期对应关系
  3. turtle绘制月相

月历每日对应星期

日历相关问题可以直接用 calendar 标准库

月历里面有对应实现:

1
2
3
4
5
>>> import calendar
>>> year = 2021
>>> month = 12
>>> calendar.monthcalendar(year, month)
[[0, 0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17, 18, 19], [20, 21, 22, 23, 24, 25, 26], [27, 28, 29, 30, 31, 0, 0]]

看到该函数返回一个二维数组,表示月历中每一行(每一周)对应的日期,没有则为 0

应用到本题,只需要读取用户选择的年月,然后得到该函数对应的数据,用循环显示出控件即可

月相与日期对应关系

做了点研究,月相与农历大致能一一对应,可以从这里切入。

农历月相对照图

所以本题就可以转化为农历和公历的对应关系。

然而,农历和公历的关系是没有规律的,农历都是天文观测的结果,因此这里只需要下载农历公历对应关系的数据即可。

这里网上其实有很多实现,可以直接借鉴。

另外,这里推荐几个可用的库:sxtwl、zhdate

sxtwl库使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import sxtwl

# 从 阳历(公历)日期 获取一天的日期对象
day = sxtwl.fromSolar(2021, 12, 14)

# 获取对应的农历信息
lunar_year = day.getLunarYear()
lunar_month = day.getLunarMonth()
lunar_day = day.getLunarDay()

# 是否是闰月
is_lunar_leap = day.isLunarLeap()

# 获取农历月的天数
day_cnt = sxtwl.getLunarMonthNum(lunar_year, lunar_month, is_lunar_leap)

详情请参考https://pypi.org/project/sxtwl/

turtle绘制月相

思路是根据当前农历日与对应的农历月的总天数之比绘制月相。

月相可以通过类比球体投影的方式绘制。

把月亮想象成一个球体,则月相的变化可以看作是与其旋转轴与球面相交的两点所在的弧在旋转形成的一个投影。

绘制该图的关键在于绘制将月相分为明暗两面的弧。

想象球体的一个鸟瞰图,一半是明面,一半是暗面,明暗交界的线按农历作周期性旋转,然后月相展示的是一个正面的一个投影。

(此处应有图片配合,但是可惜没有…)

这里之前计算明暗交界线的代码有个错误,感谢qq1337361309纠正,我顺便把填充亮面的代码补全了,大家可以直接套用相关代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import turtle
import math

# orgin 为圆心坐标,此处假设为原点(0,0),请根据实际修改
# radius 为球体半径
radius = 100
# step 为绘制弧的精度,建议画圆的时候也用类似的逻辑,这样整体样式统一一点
step = 1
# lunar_day 为农历日
lunar_day = 3
# day_cnt 为农历当月的天数
day_cnt = 29

# 计算偏转角
# fix: 农历初一偏转角应该为 0, 感谢qq1337361309纠正
angle = angle = (lunar_day - 1) / day_cnt * 360
# 是否从右边填充,此处填充为填充亮面
fromRight = angle >= 180
degree = angle % 180


# 填充
turtle.penup()
turtle.goto(0, radius)
turtle.fillcolor("yellow")
turtle.begin_fill()

# 描边
if fromRight:
signal = 1
else:
signal = -1

for y in range(radius, -(radius+step), -step):
x = signal*(radius**2 - y**2)**0.5
turtle.goto(x, y)

# 画弧
for y in range(-radius, radius+step, step):
x = -(radius**2 - y**2)**0.5 * math.cos(math.radians(degree))
turtle.goto(x, y)

turtle.end_fill()

input("Press Enter to continue...")

demo

创作不易,支持一下吧!