1+ from widget import Widget
2+ from datetime import datetime , date , timedelta
3+
4+ LUNAR_FESTIVALS = {
5+ "春节" : {2026 : (2 ,17 ), 2027 : (2 ,6 ), 2028 : (1 ,26 ), 2029 : (2 ,13 ), 2030 : (2 ,3 )},
6+ "端午节" : {2026 : (6 ,19 ), 2027 : (6 ,9 ), 2028 : (5 ,28 ), 2029 : (6 ,16 ), 2030 : (6 ,5 )},
7+ "七夕节" : {2026 : (8 ,20 ), 2027 : (8 ,9 ), 2028 : (7 ,26 ), 2029 : (8 ,15 ), 2030 : (8 ,4 )},
8+ "中秋节" : {2026 : (9 ,27 ), 2027 : (9 ,15 ), 2028 : (10 ,3 ), 2029 : (9 ,22 ), 2030 : (9 ,12 )},
9+ "重阳节" : {2026 : (10 ,30 ), 2027 : (10 ,9 ), 2028 : (10 ,26 ), 2029 : (10 ,16 ), 2030 : (10 ,5 )},
10+ }
11+
12+ FESTIVAL_CONFIG = [
13+ {"name" : "元旦" , "slogan" : "新岁伊始,万事可期" , "icon" : "sparkles" , "color" : ("#007aff" , "#409cff" ), "is_lunar" : False , "month" : 1 , "day" : 1 },
14+ {"name" : "春节" , "slogan" : "辞旧迎新,岁岁平安" , "icon" : "gift.fill" , "color" : ("#e63946" , "#ff4757" ), "is_lunar" : True },
15+ {"name" : "情人节" , "slogan" : "爱意随风起,浪漫不停歇" , "icon" : "heart.fill" , "color" : ("#ff6b81" , "#ff85a1" ), "is_lunar" : False , "month" : 2 , "day" : 14 },
16+ {"name" : "清明节" , "slogan" : "清明踏青,不负春光" , "icon" : "leaf.fill" , "color" : ("#2ecc71" , "#34d399" ), "is_lunar" : False , "month" : 4 , "day" : 5 },
17+ {"name" : "劳动节" , "slogan" : "劳逸结合,快乐放假" , "icon" : "hammer.fill" , "color" : ("#f39c12" , "#fbbf24" ), "is_lunar" : False , "month" : 5 , "day" : 1 },
18+ {"name" : "端午节" , "slogan" : "粽有安康,岁岁年年" , "icon" : "takeoutbag.and.cup.and.straw.fill" , "color" : ("#16a085" , "#2dd4bf" ), "is_lunar" : True },
19+ {"name" : "七夕节" , "slogan" : "星河万里,人间欢喜" , "icon" : "heart.circle.fill" , "color" : ("#e84393" , "#f472b6" ), "is_lunar" : True },
20+ {"name" : "中秋节" , "slogan" : "月圆人圆,事事圆满" , "icon" : "moon.stars.fill" , "color" : ("#f9ca24" , "#fde047" ), "is_lunar" : True },
21+ {"name" : "国庆节" , "slogan" : "山河锦绣,国泰民安" , "icon" : "flag.fill" , "color" : ("#dc2626" , "#ef4444" ), "is_lunar" : False , "month" : 10 , "day" : 1 },
22+ {"name" : "重阳节" , "slogan" : "登高望远,敬老安康" , "icon" : "figure.walk" , "color" : ("#78350f" , "#92400e" ), "is_lunar" : True },
23+ ]
24+
25+ QUOTES = [
26+ "生活不止眼前的苟且,还有诗和远方.--海子" ,
27+ "天行健,君子以自强不息.«周易»" ,
28+ "路漫漫其修远兮,吾将上下而求索.--屈原" ,
29+ "不积跬步,无以至千里.--荀子" ,
30+ "静以修身,俭以养德.--诸葛亮" ,
31+ "三人行,必有我师焉.--孔子" ,
32+ "欲速则不达.--«论语»" ,
33+ "长风破浪会有时,直挂云帆济沧海.--李白" ,
34+ "海阔凭鱼跃,天高任鸟飞.--«增广贤文»" ,
35+ "胜不骄,败不馁.--俗语" ,
36+ "天生我材必有用.--李白" ,
37+ "莫愁前路无知己,天下谁人不识君.--高适" ,
38+ "千里之行,始于足下.--老子" ,
39+ "知足者常乐.--老子" ,
40+ "宠辱不惊,看庭前花开花落.--«菜根谭»" ,
41+ "是非成败转头空.--罗贯中" ,
42+ "人生得意须尽欢,莫使金樽空对月.--李白" ,
43+ "不以物喜,不以己悲.--范仲淹"
44+ ]
45+
46+ now = datetime .now ()
47+ today = date .today ()
48+ current_year = today .year
49+
50+ processed_festivals = []
51+ for fest in FESTIVAL_CONFIG :
52+ if fest ["is_lunar" ]:
53+ fn = fest ["name" ]
54+ m , d = LUNAR_FESTIVALS [fn ].get (current_year , LUNAR_FESTIVALS [fn ][2026 ])
55+ else :
56+ m , d = fest ["month" ], fest ["day" ]
57+ fd = datetime (current_year , m , d )
58+ if fd < now :
59+ next_year = current_year + 1
60+ if fest ["is_lunar" ]:
61+ m , d = LUNAR_FESTIVALS [fn ].get (next_year , LUNAR_FESTIVALS [fn ][2026 ])
62+ fd = datetime (next_year , m , d )
63+ processed_festivals .append ({** fest , "date" : fd , "date_text" : f"{ fd .month } 月{ fd .day } 日" , "days_left" : (fd - now ).days })
64+
65+ processed_festivals .sort (key = lambda x : x ["days_left" ])
66+ nearest_fest = processed_festivals [0 ]
67+
68+ daily_quote = QUOTES [(today .month * 31 + today .day ) % len (QUOTES )]
69+ daily_quote = daily_quote .replace ("--" , " —— " ).replace ("«" , "《" ).replace ("»" , "》" )
70+
71+ accent = nearest_fest ["color" ]
72+
73+ w = Widget (
74+ background = ("#FFFFFF" , "#0B0F1A" ),
75+ padding = 15
76+ )
77+
78+ with w .hstack (spacing = 0 ):
79+
80+ with w .vstack (align = "leading" , spacing = 0 ):
81+ with w .hstack (spacing = 6 , align = "center" ):
82+ w .icon (nearest_fest ["icon" ], size = 23 , color = accent )
83+ w .text (f"{ nearest_fest ['date_text' ]} { nearest_fest ['name' ]} " ,
84+ size = 15.5 , weight = "semibold" , color = ("#1f2937" , "#f3f4f6" ))
85+
86+ w .spacer (14 )
87+
88+ with w .hstack (spacing = 3 , align = "center" ):
89+ w .spacer ()
90+ w .text (str (nearest_fest ["days_left" ]), size = 58 , weight = "black" , color = accent )
91+ w .text ("天" , size = 26 , weight = "semibold" , color = accent )
92+ w .spacer ()
93+
94+ w .spacer (8 )
95+ w .text (nearest_fest ["slogan" ], size = 13.5 , weight = "medium" ,
96+ color = ("#475569" , "#cbd5e1" ), max_lines = 1 )
97+
98+ w .spacer (12 )
99+ w .text (daily_quote , size = 11.5 , color = ("#64748b" , "#94a3b8" ), max_lines = 2 )
100+
101+ w .spacer (16 )
102+
103+ with w .vstack (align = "leading" , spacing = 7 ):
104+ w .text ("即将到来" , size = 13 , weight = "semibold" , color = ("#64748b" , "#94a3b8" ))
105+
106+ for fest in processed_festivals [1 :4 ]:
107+ with w .hstack (spacing = 9 , align = "center" ):
108+ w .icon (fest ["icon" ], size = 18 , color = fest ["color" ])
109+ with w .vstack (spacing = 0.5 , align = "leading" ):
110+ w .text (f"{ fest ['date' ].month } 月{ fest ['date' ].day } 日" , size = 10.5 , color = ("#64748b" , "#94a3b8" ))
111+ w .text (f"还有 { fest ['days_left' ]} 天 { fest ['name' ][:2 ]} " , size = 13 , weight = "semibold" , color = fest ["color" ])
112+
113+ w .render (url = "calshow://" )
0 commit comments