Add disable for given time

This commit is contained in:
Gobinath 2017-02-09 20:06:04 -05:00
parent 74e48de67c
commit ef156bfb0c
14 changed files with 199 additions and 58 deletions

View File

@ -16,22 +16,19 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import gi import gi, logging, threading, datetime, Utility
import locale
import logging
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1') gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, Gdk, GLib, GdkX11 from gi.repository import Gtk, Gdk, GLib, GdkX11
from gi.repository import AppIndicator3 as appindicator from gi.repository import AppIndicator3 as appindicator
# Global variables # Global variables
active = True
APPINDICATOR_ID = 'safeeyes' APPINDICATOR_ID = 'safeeyes'
class TrayIcon: class TrayIcon:
def __init__(self, language, on_show_settings, on_show_about, on_enable, on_disable, on_quite): def __init__(self, config, language, on_show_settings, on_show_about, on_enable, on_disable, on_quite):
logging.info("Initialize the tray icon") logging.info("Initialize the tray icon")
self.on_show_settings = on_show_settings self.on_show_settings = on_show_settings
self.on_show_about = on_show_about self.on_show_about = on_show_about
@ -40,6 +37,9 @@ class TrayIcon:
self.on_disable = on_disable self.on_disable = on_disable
self.language = language self.language = language
self.dateTime = None self.dateTime = None
self.active = True
self.idle_condition = threading.Condition()
self.lock = threading.Lock()
# Construct the tray icon # Construct the tray icon
self.indicator = appindicator.Indicator.new( self.indicator = appindicator.Indicator.new(
@ -57,11 +57,46 @@ class TrayIcon:
self.item_separator = Gtk.SeparatorMenuItem() self.item_separator = Gtk.SeparatorMenuItem()
# Enable menu item with check box # Enable/Disable menu item
self.item_enable = Gtk.CheckMenuItem() self.item_enable = Gtk.MenuItem()
self.item_enable.set_active(True)
self.item_enable.connect('activate', self.on_toogle_enable) self.item_enable.connect('activate', self.on_toogle_enable)
self.sub_menu_disable = Gtk.Menu()
# Read disable options and build the sub menu
for disable_option in config['disable_options']:
time_in_minutes = disable_option['time']
# Validate time value
if not isinstance(time_in_minutes, int) or time_in_minutes <= 0:
logging.error('Invalid time in disable option: ' + str(time_in_minutes))
continue
time_unit = disable_option['unit'].lower()
if time_unit == 'seconds' or time_unit == 'second':
time_in_minutes = int(time_in_minutes / 60)
elif time_unit == 'minutes' or time_unit == 'minute':
time_in_minutes = int(time_in_minutes * 1)
elif time_unit == 'hours' or time_unit == 'hour':
time_in_minutes = int(time_in_minutes * 60)
else:
# Invalid unit
logging.error('Invalid unit in disable option: ' + str(disable_option))
continue
# Create submenu
sub_menu_item = Gtk.MenuItem()
sub_menu_item.connect('activate', self.on_toogle_enable, time_in_minutes)
sub_menu_item.set_label(self.language['ui_controls'][disable_option['label']].format(disable_option['time']))
self.sub_menu_disable.append(sub_menu_item)
# Disable until restart submenu
sub_menu_item = Gtk.MenuItem()
sub_menu_item.connect('activate', self.on_toogle_enable, -1)
sub_menu_item.set_label(self.language['ui_controls']['until_restart'])
self.sub_menu_disable.append(sub_menu_item)
# Add the sub menu to the enable/disable menu
self.item_enable.set_submenu(self.sub_menu_disable)
# Settings menu item # Settings menu item
self.item_settings = Gtk.MenuItem() self.item_settings = Gtk.MenuItem()
self.item_settings.connect('activate', self.show_settings) self.item_settings.connect('activate', self.show_settings)
@ -74,12 +109,17 @@ class TrayIcon:
self.item_quit = Gtk.MenuItem() self.item_quit = Gtk.MenuItem()
self.item_quit.connect('activate', self.quit_safe_eyes) self.item_quit.connect('activate', self.quit_safe_eyes)
self.set_labels(language) self.item_info.set_label(self.language['messages']['disabled_until_restart'])
self.item_enable.set_label(self.language['ui_controls']['disable'])
self.item_settings.set_label(self.language['ui_controls']['settings'])
self.item_about.set_label(self.language['ui_controls']['about'])
self.item_quit.set_label(self.language['ui_controls']['quit'])
# Append all menu items and show the menu # Append all menu items and show the menu
self.menu.append(self.item_info) self.menu.append(self.item_info)
self.menu.append(self.item_separator) self.menu.append(self.item_separator)
self.menu.append(self.item_enable) self.menu.append(self.item_enable)
# self.menu.append(self.item_disable)
self.menu.append(self.item_settings) self.menu.append(self.item_settings)
self.menu.append(self.item_about) self.menu.append(self.item_about)
self.menu.append(self.item_quit) self.menu.append(self.item_quit)
@ -87,30 +127,21 @@ class TrayIcon:
self.indicator.set_menu(self.menu) self.indicator.set_menu(self.menu)
def set_labels(self, language):
self.language = language
active = self.item_enable.get_active()
if active:
if self.dateTime:
self.set_next_break_info(self.dateTime)
else:
self.item_info.set_label(self.language['messages'][
'safe_eyes_is_disabled'])
self.item_enable.set_label(self.language['ui_controls']['enable'])
self.item_settings.set_label(self.language['ui_controls']['settings'])
self.item_about.set_label(self.language['ui_controls']['about'])
self.item_quit.set_label(self.language['ui_controls']['quit'])
def show_icon(self): def show_icon(self):
GLib.idle_add(lambda: self.indicator.set_status( Utility.execute_main_thread(self.indicator.set_status, appindicator.IndicatorStatus.ACTIVE)
appindicator.IndicatorStatus.ACTIVE))
def hide_icon(self): def hide_icon(self):
GLib.idle_add(lambda: self.indicator.set_status( Utility.execute_main_thread(self.indicator.set_status, appindicator.IndicatorStatus.PASSIVE)
appindicator.IndicatorStatus.PASSIVE))
def quit_safe_eyes(self, *args): def quit_safe_eyes(self, *args):
self.on_quite() self.on_quite()
with self.lock:
self.active = True
# Notify all schedulers
self.idle_condition.acquire()
self.idle_condition.notify_all()
self.idle_condition.release()
def show_settings(self, *args): def show_settings(self, *args):
self.on_show_settings() self.on_show_settings()
@ -124,23 +155,50 @@ class TrayIcon:
self.set_next_break_info(self.dateTime) self.set_next_break_info(self.dateTime)
def set_next_break_info(self, dateTime): def set_next_break_info(self, dateTime):
formatted_time = dateTime.strftime(locale.nl_langinfo(locale.T_FMT)) formatted_time = Utility.format_time(dateTime)
message = self.language['messages'][ message = self.language['messages'][
'next_break_at'].format(formatted_time) 'next_break_at'].format(formatted_time)
GLib.idle_add(lambda: self.item_info.set_label(message)) Utility.execute_main_thread(self.item_info.set_label, message)
def on_toogle_enable(self, *args): def on_toogle_enable(self, *args):
active = self.item_enable.get_active() # active = self.item_enable.get_active()
if active: if args[0] == self.item_enable and not self.active:
logging.info("Enable Safe Eyes") with self.lock:
logging.info('Enable Safe Eyes')
self.active = True
self.indicator.set_icon("safeeyes_enabled") self.indicator.set_icon("safeeyes_enabled")
self.item_info.set_sensitive(True) self.item_info.set_sensitive(True)
self.on_enable() self.on_enable()
else: self.item_enable.set_label(self.language['ui_controls']['disable'])
logging.info("Disable Safe Eyes") self.item_enable.set_submenu(self.sub_menu_disable)
# Notify all schedulers
self.idle_condition.acquire()
self.idle_condition.notify_all()
self.idle_condition.release()
elif args[0] != self.item_enable and self.active:
logging.info('Disable Safe Eyes')
self.active = False
self.indicator.set_icon("safeeyes_disabled") self.indicator.set_icon("safeeyes_disabled")
self.item_info.set_label(self.language['messages'][
'safe_eyes_is_disabled'])
self.item_info.set_sensitive(False) self.item_info.set_sensitive(False)
self.on_disable() self.on_disable()
self.item_enable.set_label(self.language['ui_controls']['enable'])
self.item_enable.set_submenu(None)
time_to_wait = args[1]
if time_to_wait <= 0:
self.item_info.set_label(self.language['messages']['disabled_until_restart'])
else:
wakeup_time = datetime.datetime.now() + datetime.timedelta(minutes=time_to_wait)
Utility.start_thread(self.__schedule_resume, args={'time_minutes': time_to_wait})
self.item_info.set_label(self.language['messages']['disabled_until_x'].format(Utility.format_time(wakeup_time)))
def __schedule_resume(self, time_minutes):
self.idle_condition.acquire()
self.idle_condition.wait(time_minutes * 60) # Convert to seconds
self.idle_condition.release()
with self.lock:
if not self.active:
Utility.execute_main_thread(self.item_enable.activate)

View File

@ -53,7 +53,10 @@ def start_thread(target_function, args=None):
""" """
Execute the given function in main thread. Execute the given function in main thread.
""" """
def execute_main_thread(target_function): def execute_main_thread(target_function, args=None):
if args:
GLib.idle_add(lambda: target_function(args))
else:
GLib.idle_add(lambda: target_function()) GLib.idle_add(lambda: target_function())

View File

@ -20,7 +20,8 @@
}, },
"messages": { "messages": {
"ready_for_a_break": "Připravte se na přestávku za {} sekund", "ready_for_a_break": "Připravte se na přestávku za {} sekund",
"safe_eyes_is_disabled": "Safe Eyes je zakázáno", "disabled_until_restart": "Disabled until restart",
"disabled_until_x": "Disabled until {}",
"next_break_at": "Příští přestávka v {}" "next_break_at": "Příští přestávka v {}"
}, },
"ui_controls": { "ui_controls": {
@ -35,6 +36,11 @@
"audible_alert": "Zvukové upozornění na konec přestávky", "audible_alert": "Zvukové upozornění na konec přestávky",
"language": "Jazyk", "language": "Jazyk",
"enable": "Povolit Safe Eyes", "enable": "Povolit Safe Eyes",
"disable": "Disable Safe Eyes",
"for_x_minutes": "For {} Minutes",
"for_x_hour": "For {} Hour",
"for_x_hours": "For {} Hours",
"until_restart": "Until restart",
"settings": "Nastavení", "settings": "Nastavení",
"about": "O programu", "about": "O programu",
"quit": "Ukončit", "quit": "Ukončit",

View File

@ -20,10 +20,9 @@
}, },
"messages": { "messages": {
"ready_for_a_break": "Nächste Pause in {} Sekunden", "ready_for_a_break": "Nächste Pause in {} Sekunden",
"safe_eyes_is_disabled": "Safe Eyes ist deaktiviert", "disabled_until_restart": "Disabled until restart",
"next_break_at_noon": "Nächste Pause um {}", "disabled_until_x": "Disabled until {}",
"next_break_at_am": "Nächste Pause um {} AM", "next_break_at": "Nächste Pause um {}"
"next_break_at_pm": "Nächste Pause um {} PM"
}, },
"ui_controls": { "ui_controls": {
"skip": "Überspringen", "skip": "Überspringen",
@ -37,6 +36,11 @@
"audible_alert": "Akustisches Signal am Ende der Pause", "audible_alert": "Akustisches Signal am Ende der Pause",
"language": "Sprache", "language": "Sprache",
"enable": "Safe Eyes Aktivieren", "enable": "Safe Eyes Aktivieren",
"disable": "Disable Safe Eyes",
"for_x_minutes": "For {} Minutes",
"for_x_hour": "For {} Hour",
"for_x_hours": "For {} Hours",
"until_restart": "Until restart",
"settings": "Einstellungen", "settings": "Einstellungen",
"about": "About", "about": "About",
"quit": "Schließen", "quit": "Schließen",

View File

@ -20,14 +20,15 @@
}, },
"messages": { "messages": {
"ready_for_a_break": "Ready for a break in {} seconds", "ready_for_a_break": "Ready for a break in {} seconds",
"safe_eyes_is_disabled": "Safe Eyes is disabled", "disabled_until_restart": "Disabled until restart",
"disabled_until_x": "Disabled until {}",
"next_break_at": "Next break at {}" "next_break_at": "Next break at {}"
}, },
"ui_controls": { "ui_controls": {
"skip": "Skip", "skip": "Skip",
"short_break_duration": "Short break duration (in seconds)", "short_break_duration": "Short break duration (in seconds)",
"long_break_duration": "Long break duration (in seconds)", "long_break_duration": "Long break duration (in seconds)",
"interval_between_two_breaks": "Interval between two breaks", "interval_between_two_breaks": "Interval between two breaks (in minutes)",
"no_of_short_breaks_between_two_long_breaks": "Number of short breaks between two long breaks", "no_of_short_breaks_between_two_long_breaks": "Number of short breaks between two long breaks",
"time_to_prepare_for_break": "Time to prepare for break (in seconds)", "time_to_prepare_for_break": "Time to prepare for break (in seconds)",
"idle_time": "Minimum idle time to pause (in minutes)", "idle_time": "Minimum idle time to pause (in minutes)",
@ -35,6 +36,11 @@
"audible_alert": "Audible alert at the end of break", "audible_alert": "Audible alert at the end of break",
"language": "Language", "language": "Language",
"enable": "Enable Safe Eyes", "enable": "Enable Safe Eyes",
"disable": "Disable Safe Eyes",
"for_x_minutes": "For {} Minutes",
"for_x_hour": "For {} Hour",
"for_x_hours": "For {} Hours",
"until_restart": "Until restart",
"settings": "Settings", "settings": "Settings",
"about": "About", "about": "About",
"quit": "Quit", "quit": "Quit",

View File

@ -20,7 +20,8 @@
}, },
"messages": { "messages": {
"ready_for_a_break": "Listo para una pausa en {} segundos", "ready_for_a_break": "Listo para una pausa en {} segundos",
"safe_eyes_is_disabled": "Safe Eyes está desactivado", "disabled_until_restart": "Disabled until restart",
"disabled_until_x": "Disabled until {}",
"next_break_at": "Próxima pausa a las {}" "next_break_at": "Próxima pausa a las {}"
}, },
"ui_controls": { "ui_controls": {
@ -35,6 +36,11 @@
"audible_alert": "Audible alert at the end of break", "audible_alert": "Audible alert at the end of break",
"language": "Idioma", "language": "Idioma",
"enable": "Activar Safe Eyes", "enable": "Activar Safe Eyes",
"disable": "Disable Safe Eyes",
"for_x_minutes": "For {} Minutes",
"for_x_hour": "For {} Hour",
"for_x_hours": "For {} Hours",
"until_restart": "Until restart",
"settings": "Preferencias", "settings": "Preferencias",
"about": "Acerca de", "about": "Acerca de",
"quit": "Salir", "quit": "Salir",

View File

@ -19,7 +19,8 @@
}, },
"messages": { "messages": {
"ready_for_a_break": "Prêt pour une pause dans {} secondes", "ready_for_a_break": "Prêt pour une pause dans {} secondes",
"safe_eyes_is_disabled": "Safe Eyes est désactivé", "disabled_until_restart": "Disabled until restart",
"disabled_until_x": "Disabled until {}",
"next_break_at": "Prochaine pause à {}" "next_break_at": "Prochaine pause à {}"
}, },
"ui_controls": { "ui_controls": {
@ -34,6 +35,11 @@
"audible_alert": "Audible alert at the end of break", "audible_alert": "Audible alert at the end of break",
"language": "La language", "language": "La language",
"enable": "Activer Safe Eyes", "enable": "Activer Safe Eyes",
"disable": "Disable Safe Eyes",
"for_x_minutes": "For {} Minutes",
"for_x_hour": "For {} Hour",
"for_x_hours": "For {} Hours",
"until_restart": "Until restart",
"settings": "Réglages", "settings": "Réglages",
"about": "À propos", "about": "À propos",
"quit": "Quitter", "quit": "Quitter",

View File

@ -18,7 +18,8 @@
}, },
"messages": { "messages": {
"ready_for_a_break": "Tervezett szünet {} másodperc múlva!", "ready_for_a_break": "Tervezett szünet {} másodperc múlva!",
"safe_eyes_is_disabled": "Safe Eyes kikapcsolva", "disabled_until_restart": "Disabled until restart",
"disabled_until_x": "Disabled until {}",
"next_break_at": "A következő szünet {}" "next_break_at": "A következő szünet {}"
}, },
"ui_controls": { "ui_controls": {
@ -33,6 +34,11 @@
"audible_alert": "Audible alert at the end of break", "audible_alert": "Audible alert at the end of break",
"language": "Nyelv", "language": "Nyelv",
"enable": "Safe Eyes Bekapcsolása", "enable": "Safe Eyes Bekapcsolása",
"disable": "Disable Safe Eyes",
"for_x_minutes": "For {} Minutes",
"for_x_hour": "For {} Hour",
"for_x_hours": "For {} Hours",
"until_restart": "Until restart",
"settings": "Beállítások", "settings": "Beállítások",
"about": "Ról ről", "about": "Ról ről",
"quit": "Kilépés", "quit": "Kilépés",

View File

@ -20,7 +20,8 @@
}, },
"messages": { "messages": {
"ready_for_a_break": "Pronto para uma pausa em {} segundos", "ready_for_a_break": "Pronto para uma pausa em {} segundos",
"safe_eyes_is_disabled": "Safe Eyes está desabilitado", "disabled_until_restart": "Disabled until restart",
"disabled_until_x": "Disabled until {}",
"next_break_at": "Próxima pausa em {}" "next_break_at": "Próxima pausa em {}"
}, },
"ui_controls": { "ui_controls": {
@ -35,6 +36,11 @@
"audible_alert": "Audible alert at the end of break", "audible_alert": "Audible alert at the end of break",
"language": "Língua", "language": "Língua",
"enable": "Habilitar Safe Eyes", "enable": "Habilitar Safe Eyes",
"disable": "Disable Safe Eyes",
"for_x_minutes": "For {} Minutes",
"for_x_hour": "For {} Hour",
"for_x_hours": "For {} Hours",
"until_restart": "Until restart",
"settings": "Configuração", "settings": "Configuração",
"about": "Sobre", "about": "Sobre",
"quit": "Sair", "quit": "Sair",

View File

@ -20,7 +20,8 @@
}, },
"messages": { "messages": {
"ready_for_a_break": "Приготовьтесь к перерыву через {} секунд", "ready_for_a_break": "Приготовьтесь к перерыву через {} секунд",
"safe_eyes_is_disabled": "Safe Eyes отключен", "disabled_until_restart": "Disabled until restart",
"disabled_until_x": "Disabled until {}",
"next_break_at": "Следующий перерыв в {}" "next_break_at": "Следующий перерыв в {}"
}, },
"ui_controls": { "ui_controls": {
@ -35,6 +36,11 @@
"audible_alert": "Audible alert at the end of break", "audible_alert": "Audible alert at the end of break",
"language": "Язык", "language": "Язык",
"enable": "Активировать Safe Eyes", "enable": "Активировать Safe Eyes",
"disable": "Disable Safe Eyes",
"for_x_minutes": "For {} Minutes",
"for_x_hour": "For {} Hour",
"for_x_hours": "For {} Hours",
"until_restart": "Until restart",
"settings": "Настройки", "settings": "Настройки",
"about": "О программе", "about": "О программе",
"quit": "Выйти", "quit": "Выйти",

View File

@ -19,7 +19,8 @@
}, },
"messages": { "messages": {
"ready_for_a_break": "Priprav sa na prestávku o {} sekúnd", "ready_for_a_break": "Priprav sa na prestávku o {} sekúnd",
"safe_eyes_is_disabled": "Safe Eyes sú zablokované", "disabled_until_restart": "Disabled until restart",
"disabled_until_x": "Disabled until {}",
"next_break_at": "Ďalšia prestávka o {}" "next_break_at": "Ďalšia prestávka o {}"
}, },
"ui_controls": { "ui_controls": {
@ -34,6 +35,11 @@
"audible_alert": "Audible alert at the end of break", "audible_alert": "Audible alert at the end of break",
"language": "Jazyk", "language": "Jazyk",
"enable": "Povoliť Safe Eyes", "enable": "Povoliť Safe Eyes",
"disable": "Disable Safe Eyes",
"for_x_minutes": "For {} Minutes",
"for_x_hour": "For {} Hour",
"for_x_hours": "For {} Hours",
"until_restart": "Until restart",
"settings": "Nastavenia", "settings": "Nastavenia",
"about": "Ohľadom", "about": "Ohľadom",
"quit": "Koniec", "quit": "Koniec",

View File

@ -20,12 +20,18 @@
}, },
"messages": { "messages": {
"ready_for_a_break": "{} விநாடிகளில் இடைவேளைக்கு தயாராகுங்கள்", "ready_for_a_break": "{} விநாடிகளில் இடைவேளைக்கு தயாராகுங்கள்",
"safe_eyes_is_disabled": "Safe Eyes நிறுத்தி வைக்கப்பட்டுள்ளது", "disabled_until_restart": "மறுதுவக்கம் வரை நிறுத்தி வைக்கப்பட்டுள்ளது",
"disabled_until_x": "{} வரை நிறுத்தி வைக்கப்பட்டுள்ளது",
"next_break_at": "அடுத்த இடைவேளை {}" "next_break_at": "அடுத்த இடைவேளை {}"
}, },
"ui_controls": { "ui_controls": {
"cancel": "ரத்து", "cancel": "ரத்து",
"enable": "Safe Eyes செயல்படுகிறது", "enable": "Safe Eyes செயல்படுகிறது",
"disable": "Disable Safe Eyes",
"for_x_minutes": "For {} Minutes",
"for_x_hour": "For {} Hour",
"for_x_hours": "For {} Hours",
"until_restart": "Until restart",
"interval_between_two_breaks": "இரண்டு இடைவேளைகளுக்கிடையிலான இடைவெளி (விநாடிகளில்)", "interval_between_two_breaks": "இரண்டு இடைவேளைகளுக்கிடையிலான இடைவெளி (விநாடிகளில்)",
"long_break_duration": "நீண்ட கால இடைவேளை (விநாடிகளில்)", "long_break_duration": "நீண்ட கால இடைவேளை (விநாடிகளில்)",
"idle_time": "இடைநிறுத்துவதற்கான குறைந்தபட்ச செயலற்ற நேரம் (நிமிடங்களில்)", "idle_time": "இடைநிறுத்துவதற்கான குறைந்தபட்ச செயலற்ற நேரம் (நிமிடங்களில்)",

View File

@ -1,6 +1,6 @@
{ {
"meta": { "meta": {
"config_version": 2 "config_version": 3
}, },
"break_interval": 15, "break_interval": 15,
"long_break_duration": 60, "long_break_duration": 60,
@ -10,5 +10,27 @@
"idle_time": 5, "idle_time": 5,
"strict_break": false, "strict_break": false,
"audible_alert": false, "audible_alert": false,
"language": "en" "language": "en",
"disable_options": [
{
"label": "for_x_minutes",
"time": 30,
"unit": "minute"
},
{
"label": "for_x_hour",
"time": 1,
"unit": "hour"
},
{
"label": "for_x_hours",
"time": 2,
"unit": "hour"
},
{
"label": "for_x_hours",
"time": 3,
"unit": "hour"
}
]
} }

View File

@ -43,7 +43,7 @@ system_style_sheet_path = os.path.join(bin_directory, "config/style/safeeyes_sty
system_language_directory = os.path.join(bin_directory, "config/lang") system_language_directory = os.path.join(bin_directory, "config/lang")
is_active = True is_active = True
CONFIGURATION_VERSION = 2 CONFIGURATION_VERSION = 3
SAFE_EYES_VERSION = "1.1.4" SAFE_EYES_VERSION = "1.1.4"
""" """
@ -285,7 +285,7 @@ def main():
with open(language_file_path) as language_file: with open(language_file_path) as language_file:
language = json.load(language_file) language = json.load(language_file)
tray_icon = TrayIcon(language, show_settings, show_about, enable_safeeyes, disable_safeeyes, on_quit) tray_icon = TrayIcon(config, language, show_settings, show_about, enable_safeeyes, disable_safeeyes, on_quit)
break_screen = BreakScreen(on_skipped, break_screen_glade, style_sheet_path) break_screen = BreakScreen(on_skipped, break_screen_glade, style_sheet_path)
break_screen.initialize(config, language) break_screen.initialize(config, language)
core = SafeEyesCore(show_notification, show_alert, close_alert, on_countdown, tray_icon.next_break_time) core = SafeEyesCore(show_notification, show_alert, close_alert, on_countdown, tray_icon.next_break_time)