1+ import widget
2+ import datetime
3+ import calendar
4+ import requests
5+ import random
6+
7+ BG_COLOR = "#FFFFFF"
8+ MAIN_TEXT_COLOR = "#1C1C1E"
9+ SECONDARY_TEXT_COLOR = "#8E8E93"
10+ ACCENT_COLOR = "#007AFF"
11+ FESTIVAL_COLOR = "#34C759"
12+ TODAY_COLOR = "#FF3B30"
13+ DATE_TEXT_COLOR = "#3A3A3C"
14+ QUOTE_MAX_LENGTH = 20
15+ REQUEST_TIMEOUT = 1.5
16+ SHOW_DATE_COUNT = 5
17+
18+ def get_festivals (month , day ):
19+ festival_dict = {
20+ 1 : {1 : "元旦节" }, 2 : {14 : "情人节" }, 3 : {8 : "妇女节" , 12 : "植树节" , 21 : "世界森林日" },
21+ 4 : {4 : "清明节" }, 5 : {1 : "劳动节" , 4 : "青年节" }, 6 : {1 : "儿童节" , 14 : "端午节" },
22+ 7 : {1 : "建党节" }, 8 : {1 : "建军节" }, 9 : {10 : "教师节" , 17 : "中秋节" },
23+ 10 : {1 : "国庆节" , 7 : "重阳节" }, 12 : {25 : "圣诞节" }
24+ }
25+ month_fest = festival_dict .get (month , {})
26+ today_text = month_fest .get (day , "宜: 保持热爱" )
27+ month_all = "·" .join (list (month_fest .values ())[:2 ]) if month_fest else "岁华寻常"
28+ return today_text , month_all
29+
30+ def get_hitokoto ():
31+ default_quotes = [
32+ "代码敲累了,记得喝杯水..." , "生活明朗,万物可爱。" , "凡是过往,皆为序章。" ,
33+ "平安喜乐,得偿所愿。" , "保持热爱,奔赴山海。" , "慢慢来,谁还没有一个努力的过程。" ,
34+ "心有山海,静而无边。" , "人间值得,未来可期。"
35+ ]
36+ try :
37+ headers = {"User-Agent" : "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)" }
38+ r = requests .get ("https://v1.hitokoto.cn/?c=i" , headers = headers , timeout = REQUEST_TIMEOUT ).json ()
39+ return r .get ("hitokoto" , random .choice (default_quotes ))
40+ except :
41+ return random .choice (default_quotes )
42+
43+ def build ():
44+ w = widget .Widget (widget .LARGE )
45+ w ._background = BG_COLOR
46+ w ._color = MAIN_TEXT_COLOR
47+
48+ now = datetime .datetime .now ()
49+ y , m , d = now .year , now .month , now .day
50+ today_fest , month_all = get_festivals (m , d )
51+ total_days = 366 if calendar .isleap (y ) else 365
52+ year_progress = now .timetuple ().tm_yday / total_days
53+ today_week_idx = now .weekday ()
54+ week_list = ["一" , "二" , "三" , "四" , "五" , "六" , "日" ]
55+
56+ date_list = [now + datetime .timedelta (days = i ) for i in range (SHOW_DATE_COUNT )]
57+ date_rows = [date_list [i :i + 5 ] for i in range (0 , len (date_list ), 5 )]
58+
59+ pre_week = " " .join (week_list [:today_week_idx ]) + (" " if today_week_idx > 0 else "" )
60+ today_week = week_list [today_week_idx ]
61+ suf_week = (" " if today_week_idx < 6 else "" ) + " " .join (week_list [today_week_idx + 1 :])
62+
63+ with w .vstack (spacing = 12 ):
64+ with w .hstack ():
65+ w .icon ("calendar.badge.clock" , size = 24 , color = ACCENT_COLOR )
66+ w .text (f" { y } 年 { m } 月" , size = 24 , weight = "heavy" )
67+ w .spacer ()
68+
69+ w .spacer ()
70+
71+ with w .hstack ():
72+ with w .vstack (spacing = 6 ):
73+ w .text (str (d ), size = 90 , weight = "heavy" )
74+ w .text (f"{ now .strftime ('%B' ).upper ()} ◈ { today_fest } " , size = 13 , color = FESTIVAL_COLOR , weight = "bold" )
75+ w .text (f"本月: { month_all } " , size = 11 , color = SECONDARY_TEXT_COLOR )
76+
77+ w .spacer ()
78+
79+ with w .vstack (spacing = 10 ):
80+ with w .hstack ():
81+ if pre_week .strip ():
82+ w .text (pre_week , size = 12 , color = SECONDARY_TEXT_COLOR , weight = "bold" )
83+ w .text (today_week , size = 12 , color = TODAY_COLOR , weight = "bold" )
84+ if suf_week .strip ():
85+ w .text (suf_week , size = 12 , color = SECONDARY_TEXT_COLOR , weight = "bold" )
86+
87+ for row in date_rows :
88+ row_days = [date .day for date in row ]
89+ has_today = d in row_days
90+
91+ if not has_today :
92+ row_str = ""
93+ for day_num in row_days :
94+ row_str += f" { day_num :02d} "
95+ w .text (row_str , size = 17 , color = DATE_TEXT_COLOR , weight = "bold" )
96+
97+ else :
98+ with w .hstack (spacing = 0 ):
99+ for date in row :
100+ day_num = date .day
101+ if day_num == d :
102+ w .text (f" { day_num :02d} " , size = 13 , color = TODAY_COLOR , weight = "heavy" )
103+ else :
104+ w .text (f" { day_num :02d} " , size = 13 , color = DATE_TEXT_COLOR , weight = "bold" )
105+
106+ w .spacer ()
107+ w .divider ()
108+
109+ with w .hstack ():
110+ try :
111+ w .progress (year_progress )._set_color (ACCENT_COLOR )
112+ except :
113+ pass
114+ w .text (f" { int (year_progress * 100 )} %" , size = 13 , color = SECONDARY_TEXT_COLOR , weight = "bold" )
115+
116+ with w .hstack ():
117+ w .icon ("quote.bubble.fill" , size = 14 , color = ACCENT_COLOR )
118+ quote = get_hitokoto ()
119+ if len (quote ) > QUOTE_MAX_LENGTH :
120+ short_quote = quote [:QUOTE_MAX_LENGTH ].rsplit ("," , 1 )[0 ].rsplit ("。" , 1 )[0 ]
121+ w .text (f" { short_quote } ..." , size = 12 , color = SECONDARY_TEXT_COLOR )
122+ else :
123+ w .text (f" { quote } " , size = 12 , color = SECONDARY_TEXT_COLOR )
124+ w .spacer ()
125+
126+ w .render ()
127+ widget .show (w )
128+
129+ if __name__ == "__main__" :
130+ build ()
0 commit comments