Use bugfixed LunarCalendarPy; add queue_draw to force redraw
[indicator-lunar-calendar.git] / indicator-lunar-calendar.py
CommitLineData
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
29import signal
30import datetime
31import os
32import gi
33gi.require_version('Gdk', '3.0')
34gi.require_version('Gtk', '3.0')
35gi.require_version('AppIndicator3', '0.1')
36from gi.repository import Gdk, Gtk, AppIndicator3
37from LunarCalendarPy.LunarCalendar import LunarCalendar
38import schedule
39import threading
40import time
41import dbus
42from dbus.mainloop.glib import DBusGMainLoop
43
44APP_NAME = 'indicator-lunar-calendar-py'
45APP_VERSION = '1.2+py'
46
47SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
48
49
50class 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
99def 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
115def cronThreadBody():
116 while True:
117 run_schedule_one_iteration()
118
119
120def updateJob(i):
121 i.update_indicator()
122
123
124if __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()