Commit | Line | Data |
---|---|---|
95f1a195 AIL |
1 | #!/usr/bin/env python3 |
2 | # -*- coding: utf-8 -*- | |
3 | # | |
4 | # indicator-lunar-calendar - shows lunar calendar information | |
5 | # Copyright (c) 2019 Adrian I Lam <spam@adrianiainlam.tk> s/spam/me/ | |
6 | # | |
7 | # Permission is hereby granted, free of charge, to any person obtaining a | |
8 | # copy of this software and associated documentation files (the "Software"), | |
9 | # to deal in the Software without restriction, including without limitation | |
10 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
11 | # and/or sell copies of the Software, and to permit persons to whom the | |
12 | # Software is furnished to do so, subject to the following conditions: | |
13 | # | |
14 | # The above copyright notice and this permission notice shall be included in | |
15 | # all copies or substantial portions of the Software. | |
16 | # | |
17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
20 | # THE AUTHOR OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
23 | # IN THE SOFTWARE. | |
24 | # | |
25 | # I would like to thank Tobias Schlitt <toby@php.net>, who wrote | |
26 | # indicator-chars <https://github.com/tobyS/indicator-chars> which I used | |
27 | # as a reference when writing this software. | |
28 | ||
29 | import signal | |
30 | import datetime | |
31 | import os | |
32 | import gi | |
33 | gi.require_version('Gdk', '3.0') | |
34 | gi.require_version('Gtk', '3.0') | |
35 | gi.require_version('AppIndicator3', '0.1') | |
36 | from gi.repository import Gdk, Gtk, AppIndicator3 | |
37 | from LunarCalendarPy.LunarCalendar import LunarCalendar | |
38 | import schedule | |
39 | import threading | |
40 | import time | |
41 | import dbus | |
42 | from dbus.mainloop.glib import DBusGMainLoop | |
43 | ||
44 | APP_NAME = 'indicator-lunar-calendar-py' | |
45 | APP_VERSION = '1.2+py' | |
46 | ||
47 | SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) | |
48 | ||
49 | ||
50 | class IndicatorLunarCalendar: | |
51 | def __init__(self): | |
52 | self.indicator = AppIndicator3.Indicator.new( | |
53 | 'lunar-indicator', | |
54 | os.path.join(SCRIPT_DIR, 'icons', '鼠.svg'), | |
55 | AppIndicator3.IndicatorCategory.APPLICATION_STATUS) | |
56 | self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE) | |
57 | ||
58 | self.menu = Gtk.Menu() | |
59 | self.item = Gtk.MenuItem() | |
60 | self.item.connect("activate", self.do_nothing) | |
61 | self.menu.append(self.item) | |
62 | self.indicator.set_menu(self.menu) | |
63 | self.menu.show_all() | |
64 | ||
65 | self.lc = LunarCalendar() | |
66 | self.update_indicator() | |
67 | ||
68 | def update_indicator(self): | |
69 | # get current time at UTC+8, add 1 to date if after 23:00 (子時) | |
70 | now = datetime.datetime.utcnow() + datetime.timedelta(hours=8) | |
71 | hour = now.hour | |
72 | if hour >= 23: | |
73 | now = now.date() + datetime.timedelta(days=1) | |
74 | ||
75 | lunar = self.lc.solarToLunar(now.year, now.month, now.day) | |
76 | lunar['hour'] = '子丑寅卯辰巳午未申酉戌亥'[(hour + 1) % 24 // 2] | |
77 | ||
78 | compact_date = lunar['lunarMonthName'] + lunar['lunarDayName'] | |
79 | long_date = ( | |
80 | lunar['GanZhiYear'] + '年(' + lunar['zodiac'] + '年)\n' + | |
81 | lunar['lunarMonthName'] + lunar['lunarDayName'] | |
82 | ) | |
83 | if lunar['term']: | |
34211e2b AIL |
84 | compact_date += ' ' + lunar['term'] |
85 | long_date += ' ' + lunar['term'] | |
95f1a195 AIL |
86 | long_date += '\n' + lunar['hour'] + '時' |
87 | ||
88 | self.indicator.set_icon( | |
89 | os.path.join(SCRIPT_DIR, 'icons', lunar['zodiac'] + '.svg') | |
90 | ) | |
91 | self.indicator.set_label(compact_date, '') | |
92 | self.item.set_label(long_date) | |
2fd59c07 | 93 | self.item.queue_draw() |
95f1a195 AIL |
94 | |
95 | def do_nothing(self, arg): | |
96 | pass | |
97 | ||
98 | ||
99 | def run_schedule_one_iteration(): | |
100 | prev_idle_sec = 60 * 60 | |
101 | idle_sec = schedule.idle_seconds() | |
102 | while idle_sec < prev_idle_sec: | |
103 | prev_idle_sec = idle_sec | |
104 | ||
105 | if idle_sec > 2: | |
106 | time.sleep(idle_sec - 2) | |
107 | elif idle_sec > 0.199: | |
108 | time.sleep(idle_sec - 0.199) | |
109 | else: | |
110 | schedule.run_pending() | |
111 | ||
112 | idle_sec = schedule.idle_seconds() | |
113 | ||
114 | ||
115 | def cronThreadBody(): | |
116 | while True: | |
117 | run_schedule_one_iteration() | |
118 | ||
119 | ||
120 | def updateJob(i): | |
121 | i.update_indicator() | |
122 | ||
123 | ||
124 | if __name__ == '__main__': | |
125 | signal.signal(signal.SIGINT, lambda signum, frame: Gtk.main_quit()) | |
126 | ||
127 | i = IndicatorLunarCalendar() | |
128 | ||
129 | schedule.every().hour.at(":00").do(updateJob, i=i) | |
130 | cronThread = threading.Thread(target=cronThreadBody) | |
131 | cronThread.start() | |
132 | ||
133 | dbusloop = DBusGMainLoop() | |
134 | bus = dbus.SystemBus(mainloop=dbusloop) | |
135 | obj = bus.get_object('org.freedesktop.login1', '/org/freedesktop/login1') | |
136 | iface = dbus.Interface(obj, | |
137 | dbus_interface='org.freedesktop.login1.Manager') | |
138 | ||
139 | def sleepHandler(arg): | |
140 | if arg == 0: | |
141 | i.update_indicator() | |
142 | schedule.clear() | |
143 | schedule.every().minute.at(":00").do(updateJob, i=i) | |
144 | newThread = threading.Thread(target=run_schedule_one_iteration) | |
145 | newThread.start() | |
146 | ||
147 | iface.connect_to_signal('PrepareForSleep', sleepHandler) | |
148 | ||
149 | Gtk.main() |