Merge branch 'master' into master

This commit is contained in:
radek-sprta 2017-08-25 22:30:25 +02:00 committed by GitHub
commit aab1d33309
34 changed files with 896 additions and 402 deletions

View File

@ -53,21 +53,26 @@ Optional Features:
For more details: [SafeEyes Features](http://slgobinath.github.io/SafeEyes/#features) For more details: [SafeEyes Features](http://slgobinath.github.io/SafeEyes/#features)
## Currently available translations ## Currently available translations
* [Català](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/ca.json)
* [Čeština](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/cs.json) * [Čeština](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/cs.json)
* [Deutsch](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/de.json) * [Deutsch](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/de.json)
* [English](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/en.json) * [English](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/en.json)
* [Español](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/es.json) * [Español](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/es.json)
* [فارسی](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/fa.json)
* [Français](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/fr.json) * [Français](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/fr.json)
* [ქართული](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/ge.json) * [ქართული](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/ge.json)
* [हिंदी](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/hi.json) * [हिंदी](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/hi.json)
* [Magyar](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/hu.json) * [Magyar](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/hu.json)
* [Bahasa Indonesia](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/id.json) * [Bahasa Indonesia](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/id.json)
* [Македонски](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/mk.json)
* [Polski](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/pl.json) * [Polski](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/pl.json)
* [Português](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/pt.json) * [Português](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/pt.json)
* [Русский](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/ru.json) * [Русский](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/ru.json)
* [Slovenský](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/sk.json) * [Slovenský](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/sk.json)
* [தமிழ்](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/ta.json) * [தமிழ்](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/ta.json)
* [Türkçe](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/tr.json) * [Türkçe](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/tr.json)
* [Українська](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/uk.json)
* [Tiếng Việt](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/vi.json)
Do you want to see your language here? Please translate Safe Eyes to whatever the languages you know. Visit to **Translate Safe Eyes** in [Customize Safe Eyes](http://slgobinath.github.io/SafeEyes/#customize) to see how to translate. Do you want to see your language here? Please translate Safe Eyes to whatever the languages you know. Visit to **Translate Safe Eyes** in [Customize Safe Eyes](http://slgobinath.github.io/SafeEyes/#customize) to see how to translate.

12
debian/changelog vendored
View File

@ -1,4 +1,14 @@
safeeyes (1.2.1-1) xenial; urgency=low safeeyes (1.2.2-1) xenial; urgency=low
* Show next break time in tray icon in supported environments
* Enable keyboard shortcuts
* Fix screen flickering in KDE
* Show the break type in the notification
* Make pyaudio optional
* Support postponing the break * Support postponing the break

View File

@ -18,18 +18,15 @@
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GdkX11 from gi.repository import Gtk
""" class AboutDialog:
"""
AboutDialog reads the about_dialog.glade and build the user interface using that file. AboutDialog reads the about_dialog.glade and build the user interface using that file.
It shows the application name with version, a small description, license and the GitHub url. It shows the application name with version, a small description, license and the GitHub url.
""" """
class AboutDialog:
"""
Read the about_dialog.glade and build the user interface.
"""
def __init__(self, glade_file, version, language): def __init__(self, glade_file, version, language):
builder = Gtk.Builder() builder = Gtk.Builder()
builder.add_from_file(glade_file) builder.add_from_file(glade_file)
@ -42,23 +39,20 @@ class AboutDialog:
# Set the version at the runtime # Set the version at the runtime
builder.get_object("lbl_app_name").set_label("Safe Eyes " + version) builder.get_object("lbl_app_name").set_label("Safe Eyes " + version)
"""
Show the About dialog.
"""
def show(self): def show(self):
"""
Show the About dialog.
"""
self.window.show_all() self.window.show_all()
"""
Window close event handler.
"""
def on_window_delete(self, *args): def on_window_delete(self, *args):
"""
Window close event handler.
"""
self.window.destroy() self.window.destroy()
"""
Close button click event handler.
"""
def on_close_clicked(self, button): def on_close_clicked(self, button):
"""
Close button click event handler.
"""
self.window.destroy() self.window.destroy()

View File

@ -16,104 +16,114 @@
# 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, signal, sys, threading, logging import gi, threading, logging, time
from Xlib import Xatom, Xutil
from Xlib.display import Display, X from Xlib.display import Display, X
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GLib, GdkX11 from gi.repository import Gtk, Gdk, GLib
"""
The fullscreen window which prevents users from using the computer.
"""
class BreakScreen: class BreakScreen:
"""
The fullscreen window which prevents users from using the computer.
This class reads the break_screen.glade and build the user interface.
"""
""" def __init__(self, context, on_skip, on_postpone, glade_file, style_sheet_path):
Read the break_screen.glade and build the user interface. self.context = context
"""
def __init__(self, on_skip, on_postpone, glade_file, style_sheet_path):
self.on_skip = on_skip self.on_skip = on_skip
self.on_postpone = on_postpone self.on_postpone = on_postpone
self.is_pretified = False self.is_pretified = False
self.key_lock_condition = threading.Condition()
self.windows = [] self.windows = []
self.count_labels = [] self.count_labels = []
self.glade_file = glade_file self.glade_file = glade_file
self.enable_shortcut = False
self.display = Display()
# Initialize the theme # Initialize the theme
css_provider = Gtk.CssProvider() css_provider = Gtk.CssProvider()
css_provider.load_from_path(style_sheet_path) css_provider.load_from_path(style_sheet_path)
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
"""
Initialize the internal properties from configuration
"""
def initialize(self, config, language): def initialize(self, config, language):
"""
Initialize the internal properties from configuration
"""
logging.info("Initialize the break screen") logging.info("Initialize the break screen")
self.skip_button_text = language['ui_controls']['skip'] self.skip_button_text = language['ui_controls']['skip']
self.postpone_button_text = language['ui_controls']['postpone'] self.postpone_button_text = language['ui_controls']['postpone']
self.strict_break = config.get('strict_break', False) self.strict_break = config.get('strict_break', False)
self.enable_postpone = config.get('allow_postpone', False) self.enable_postpone = config.get('allow_postpone', False)
self.keycode_shortcut_skip = config.get('shortcut_skip', 9)
self.keycode_shortcut_postpone = config.get('shortcut_postpone', 65)
self.shortcut_disable_time = config.get('shortcut_disable_time', 2)
def skip_break(self):
""" """
Window close event handler. Skip the break from the break screen
""" """
def on_window_delete(self, *args):
logging.info("Closing the break screen")
self.lock_keyboard = False
self.close()
"""
Skip button press event handler.
"""
def on_skip_clicked(self, button):
logging.info("User skipped the break") logging.info("User skipped the break")
# Must call on_skip before close to lock screen before closing the break screen # Must call on_skip before close to lock screen before closing the break screen
self.on_skip() self.on_skip()
self.close() self.close()
def postpone_break(self):
""" """
Postpone button press event handler. Postpone the break from the break screen
""" """
def on_postpone_clicked(self, button):
logging.info("User postponed the break") logging.info("User postponed the break")
self.on_postpone() self.on_postpone()
self.close() self.close()
def on_window_delete(self, *args):
"""
Window close event handler.
"""
logging.info("Closing the break screen")
self.__release_keyboard()
self.close()
""" def on_skip_clicked(self, button):
"""
Skip button press event handler.
"""
self.skip_break()
def on_postpone_clicked(self, button):
"""
Postpone button press event handler.
"""
self.postpone_break()
def show_count_down(self, count_down, seconds):
"""
Show/update the count down on all screens. Show/update the count down on all screens.
""" """
def show_count_down(self, count): self.enable_shortcut = not self.strict_break and self.shortcut_disable_time <= count_down
GLib.idle_add(lambda: self.__update_count_down(count)) mins, secs = divmod(seconds, 60)
timeformat = '{:02d}:{:02d}'.format(mins, secs)
GLib.idle_add(lambda: self.__update_count_down(timeformat))
"""
Show the break screen with the given message on all displays.
"""
def show_message(self, message, image_path, plugins_data): def show_message(self, message, image_path, plugins_data):
"""
Show the break screen with the given message on all displays.
"""
self.enable_shortcut = not self.strict_break and self.shortcut_disable_time <= 0
GLib.idle_add(lambda: self.__show_break_screen(message, image_path, plugins_data)) GLib.idle_add(lambda: self.__show_break_screen(message, image_path, plugins_data))
"""
Hide the break screen from active window and destroy all other windows
"""
def close(self): def close(self):
"""
Hide the break screen from active window and destroy all other windows
"""
logging.info("Close the break screen(s)") logging.info("Close the break screen(s)")
self.__release_keyboard() self.__release_keyboard()
# Destroy other windows if exists # Destroy other windows if exists
GLib.idle_add(lambda: self.__destroy_all_screens()) GLib.idle_add(lambda: self.__destroy_all_screens())
"""
Show an empty break screen on all screens.
"""
def __show_break_screen(self, message, image_path, plugins_data): def __show_break_screen(self, message, image_path, plugins_data):
"""
Show an empty break screen on all screens.
"""
# Lock the keyboard # Lock the keyboard
thread = threading.Thread(target=self.__lock_keyboard) thread = threading.Thread(target=self.__lock_keyboard)
thread.start() thread.start()
@ -156,72 +166,76 @@ class BreakScreen:
btn_skip.set_visible(True) btn_skip.set_visible(True)
box_buttons.pack_start(btn_skip, True, True, 0) box_buttons.pack_start(btn_skip, True, True, 0)
# Set values # Set values
if image_path: if image_path:
img_break.set_from_file(image_path) img_break.set_from_file(image_path)
lbl_message.set_label(message) lbl_message.set_label(message)
lbl_left.set_markup(plugins_data['left']); lbl_left.set_markup(plugins_data['left'])
lbl_right.set_markup(plugins_data['right']); lbl_right.set_markup(plugins_data['right'])
self.windows.append(window) self.windows.append(window)
self.count_labels.append(lbl_count) self.count_labels.append(lbl_count)
# Set visual to apply css theme. It should be called before show method. # Set visual to apply css theme. It should be called before show method.
window.set_visual(window.get_screen().get_rgba_visual()) window.set_visual(window.get_screen().get_rgba_visual())
if self.context['desktop'] == 'kde':
# Fix flickering screen in KDE by setting opacity to 1
window.set_opacity(0.9)
window.move(x, y)
window.stick() window.stick()
window.set_keep_above(True) window.set_keep_above(True)
window.present() window.present()
window.move(x, y)
window.fullscreen() window.fullscreen()
"""
Update the countdown on all break screens.
"""
def __update_count_down(self, count): def __update_count_down(self, count):
"""
Update the countdown on all break screens.
"""
for label in self.count_labels: for label in self.count_labels:
label.set_text(count) label.set_text(count)
"""
Lock the keyboard to prevent the user from using keyboard shortcuts
"""
def __lock_keyboard(self): def __lock_keyboard(self):
"""
Lock the keyboard to prevent the user from using keyboard shortcuts
"""
logging.info("Lock the keyboard") logging.info("Lock the keyboard")
self.lock_keyboard = True self.lock_keyboard = True
display = Display()
root = display.screen().root # Grab the keyboard
# Grap the keyboard root = self.display.screen().root
root.grab_keyboard(owner_events = False, pointer_mode = X.GrabModeAsync, keyboard_mode = X.GrabModeAsync, time = X.CurrentTime) root.change_attributes(event_mask=X.KeyPressMask | X.KeyReleaseMask)
root.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime)
# Consume keyboard events # Consume keyboard events
self.key_lock_condition.acquire()
while self.lock_keyboard: while self.lock_keyboard:
self.key_lock_condition.wait() if self.display.pending_events() > 0:
self.key_lock_condition.release() # Avoid waiting for next event by checking pending events
event = self.display.next_event()
if self.enable_shortcut and event.type == X.KeyPress:
if event.detail == self.keycode_shortcut_skip:
self.skip_break()
break
elif self.enable_postpone and event.detail == self.keycode_shortcut_postpone:
self.postpone_break()
break
else:
# Reduce the CPU usage by sleeping for a second
time.sleep(1)
# Ungrap the keyboard
logging.info("Unlock the keyboard")
display.ungrab_keyboard(X.CurrentTime)
display.flush()
"""
Release the locked keyboard.
"""
def __release_keyboard(self): def __release_keyboard(self):
self.key_lock_condition.acquire() """
Release the locked keyboard.
"""
logging.info("Unlock the keyboard")
self.lock_keyboard = False self.lock_keyboard = False
self.key_lock_condition.notify() self.display.ungrab_keyboard(X.CurrentTime)
self.key_lock_condition.release() self.display.flush()
"""
Close all the break screens.
"""
def __destroy_all_screens(self): def __destroy_all_screens(self):
"""
Close all the break screens.
"""
for win in self.windows: for win in self.windows:
win.destroy() win.destroy()
del self.windows[:] del self.windows[:]

View File

@ -24,33 +24,43 @@ from safeeyes import Utility
APPINDICATOR_ID = 'safeeyes' APPINDICATOR_ID = 'safeeyes'
class Notification: class Notification:
""" """
This class is responsible for the notification to the user before the break. This class is responsible for the notification to the user before the break.
""" """
def __init__(self, context, language):
def __init__(self, language):
"""
Initialize the notification.
"""
logging.info('Initialize the notification') logging.info('Initialize the notification')
Notify.init(APPINDICATOR_ID) Notify.init(APPINDICATOR_ID)
self.context = context
self.language = language self.language = language
def initialize(self, language):
"""
Initialize the notification object.
"""
self.language = language
def show(self, warning_time): def show(self, warning_time):
""" """
Show the notification Show the notification
""" """
logging.info('Show pre-break notification') logging.info('Show pre-break notification')
self.notification = Notify.Notification.new('Safe Eyes', '\n' + self.language['messages']['ready_for_a_break'].format(warning_time), icon='safeeyes_enabled')
# Construct the message based on the type of the next break
message = '\n'
if self.context['break_type'] == 'short':
message += self.language['messages']['ready_for_a_short_break'].format(warning_time)
else:
message += self.language['messages']['ready_for_a_long_break'].format(warning_time)
self.notification = Notify.Notification.new('Safe Eyes', message, icon='safeeyes_enabled')
try: try:
self.notification.show() self.notification.show()
except Exception as e: except Exception as e:
logging.exception('Error in showing notification', e) logging.exception('Error in showing notification', e)
def close(self): def close(self):
""" """
Close the notification if it is not closed by the system already. Close the notification if it is not closed by the system already.
@ -62,10 +72,9 @@ class Notification:
# Some Linux systems automatically close the notification. # Some Linux systems automatically close the notification.
pass pass
def quite(self): def quite(self):
""" """
Uninitialize the notification. Call this method when closing the application. Uninitialize the notification. Call this method when closing the application.
""" """
logging.info('Uninitialize Safe Eyes notification') logging.info('Uninitialize Safe Eyes notification')
Utility.execute_main_thread(Notify.uninit) Utility.execute_main_thread(Notify.uninit)

View File

@ -23,16 +23,13 @@ from safeeyes import Utility
plugins_directory = os.path.join(Utility.config_directory, 'plugins') plugins_directory = os.path.join(Utility.config_directory, 'plugins')
sys.path.append(os.path.abspath(plugins_directory)) sys.path.append(os.path.abspath(plugins_directory))
class Plugins: class Plugins:
""" """
This class manages imports the plugins and calls the methods defined in those plugins. Imports the Safe Eyes plugins and calls the methods defined in those plugins.
""" """
def __init__(self, config): def __init__(self, config):
"""
Load the plugins.
"""
logging.info('Load all the plugins') logging.info('Load all the plugins')
self.__plugins = [] self.__plugins = []
@ -47,18 +44,17 @@ class Plugins:
else: else:
logging.warning('Plugin file ' + str(plugin['name']) + '.py not found') logging.warning('Plugin file ' + str(plugin['name']) + '.py not found')
else: else:
logging.warning('Ignoring the plugin ' + str(plugin['name']) + ' due to invalid location value: ' + plugin['location']) logging.warning('Ignoring the plugin ' + str(plugin['name']) + ' due to invalid location value: ' + plugin['location'])
if self.__plugins: if self.__plugins:
self.__thread_pool = ThreadPool(min([4, len(self.__plugins)])) self.__thread_pool = ThreadPool(min([4, len(self.__plugins)]))
def start(self, context): def start(self, context):
""" """
Call the start function of all the plugins in separate thread. Call the start function of all the plugins in separate thread.
""" """
if self.__plugins: if self.__plugins:
context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes
for plugin in self.__plugins: for plugin in self.__plugins:
try: try:
self.__thread_pool.apply_async(plugin['module'].start, (context,)) self.__thread_pool.apply_async(plugin['module'].start, (context,))
@ -70,14 +66,13 @@ class Plugins:
Call the pre_notification function of all the plugins in separate thread. Call the pre_notification function of all the plugins in separate thread.
""" """
if self.__plugins: if self.__plugins:
context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes
for plugin in self.__plugins: for plugin in self.__plugins:
try: try:
self.__thread_pool.apply_async(plugin['module'].pre_notification, (context,)) self.__thread_pool.apply_async(plugin['module'].pre_notification, (context,))
except Exception as e: except Exception as e:
pass pass
def pre_break(self, context): def pre_break(self, context):
""" """
Call the pre_break function of all the plugins and provide maximum 1 second to return the result. Call the pre_break function of all the plugins and provide maximum 1 second to return the result.
@ -87,14 +82,14 @@ class Plugins:
""" """
output = {'left': ' \n', 'right': ' \n'} output = {'left': ' \n', 'right': ' \n'}
if self.__plugins: if self.__plugins:
context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes
multiple_results = [self.__thread_pool.apply_async(plugin['module'].pre_break, (context,)) for plugin in self.__plugins] multiple_results = [self.__thread_pool.apply_async(plugin['module'].pre_break, (context,)) for plugin in self.__plugins]
for i in range(len(multiple_results)): for i in range(len(multiple_results)):
try: try:
result = multiple_results[i].get(timeout=1) result = multiple_results[i].get(timeout=1)
if result: if result:
# Limit the line length to 50 characters # Limit the line length to 50 characters
large_lines = list(filter(lambda x: len(x) > 50, Utility.html_to_text(result).splitlines())) large_lines = list(filter(lambda x: len(x) > 50, Utility.html_to_text(result).splitlines()))
if large_lines: if large_lines:
logging.warning('Ignoring lengthy result from the plugin ' + self.__plugins[i]['name']) logging.warning('Ignoring lengthy result from the plugin ' + self.__plugins[i]['name'])
continue continue
@ -105,13 +100,12 @@ class Plugins:
return output return output
def post_break(self, context): def post_break(self, context):
""" """
Call the post_break function of all the plugins in separate thread. Call the post_break function of all the plugins in separate thread.
""" """
if self.__plugins: if self.__plugins:
context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes
for plugin in self.__plugins: for plugin in self.__plugins:
try: try:
self.__thread_pool.apply_async(plugin['module'].post_break, (context,)) self.__thread_pool.apply_async(plugin['module'].post_break, (context,))
@ -123,7 +117,7 @@ class Plugins:
Call the exit function of all the plugins in separate thread. Call the exit function of all the plugins in separate thread.
""" """
if self.__plugins: if self.__plugins:
context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes
# Give maximum 1 sec for all plugins before terminating the thread pool # Give maximum 1 sec for all plugins before terminating the thread pool
multiple_results = [self.__thread_pool.apply_async(plugin['module'].exit, (context,)) for plugin in self.__plugins] multiple_results = [self.__thread_pool.apply_async(plugin['module'].exit, (context,)) for plugin in self.__plugins]
@ -135,12 +129,11 @@ class Plugins:
pass pass
try: try:
self.__thread_pool.terminate() # Shutdown the pool self.__thread_pool.terminate() # Shutdown the pool
except Exception as e: except Exception as e:
pass pass
def __has_method(self, module, method_name, no_of_args=1):
def __has_method(self, module, method_name, no_of_args = 1):
""" """
Check whether the given function is defined in the module or not. Check whether the given function is defined in the module or not.
""" """

View File

@ -17,18 +17,15 @@
# 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 time, datetime, threading, sys, subprocess, logging import time, datetime, threading, logging
from safeeyes import Utility from safeeyes import Utility
"""
Core of Safe Eyes which runs the scheduler and notifies the breaks.
"""
class SafeEyesCore: class SafeEyesCore:
"""
Core of Safe Eyes which runs the scheduler and notifies the breaks.
"""
"""
Initialize the internal variables of the core.
"""
def __init__(self, context, show_notification, start_break, end_break, on_countdown, update_next_break_info): def __init__(self, context, show_notification, start_break, end_break, on_countdown, update_next_break_info):
# Initialize the variables # Initialize the variables
self.break_count = -1 self.break_count = -1
@ -48,14 +45,13 @@ class SafeEyesCore:
self.context['skipped'] = False self.context['skipped'] = False
self.context['postponed'] = False self.context['postponed'] = False
"""
Initialize the internal properties from configuration
"""
def initialize(self, config, language): def initialize(self, config, language):
"""
Initialize the internal properties from configuration
"""
logging.info("Initialize the core") logging.info("Initialize the core")
self.short_break_exercises = [] #language['exercises']['short_break_exercises'] self.short_break_exercises = [] # language['exercises']['short_break_exercises']
self.long_break_exercises = [] #language['exercises']['long_break_exercises'] self.long_break_exercises = [] # language['exercises']['long_break_exercises']
self.no_of_short_breaks_per_long_break = config['no_of_short_breaks_per_long_break'] self.no_of_short_breaks_per_long_break = config['no_of_short_breaks_per_long_break']
self.pre_break_warning_time = config['pre_break_warning_time'] self.pre_break_warning_time = config['pre_break_warning_time']
@ -111,11 +107,10 @@ class SafeEyesCore:
self.long_break_exercises.append([name, break_time, audible_alert, image]) self.long_break_exercises.append([name, break_time, audible_alert, image])
"""
Start Safe Eyes is it is not running already.
"""
def start(self): def start(self):
"""
Start Safe Eyes is it is not running already.
"""
with self.lock: with self.lock:
if not self.active: if not self.active:
logging.info("Scheduling next break") logging.info("Scheduling next break")
@ -125,18 +120,23 @@ class SafeEyesCore:
if self.context['idle_pause_enabled']: if self.context['idle_pause_enabled']:
Utility.start_thread(self.__start_idle_monitor) Utility.start_thread(self.__start_idle_monitor)
"""
Stop Safe Eyes if it is running.
"""
def stop(self): def stop(self):
"""
Stop Safe Eyes if it is running.
"""
with self.lock: with self.lock:
if self.active: if self.active:
logging.info("Stop the core") logging.info("Stop the core")
# Reset the state properties in case of restart
# self.break_count = 0 # Prevent resuming from a long break
# self.long_break_message_index = -1 current_break_count = self.break_count = 0
# self.short_break_message_index = -1 self.break_count = ((self.break_count + 1) % self.no_of_short_breaks_per_long_break)
if self.__is_long_break():
# Next break will be a long break. Leave the increment so that next break will be short break after the long
pass
else:
# Reset to the current state
self.break_count = current_break_count
# Stop the break thread # Stop the break thread
self.notification_condition.acquire() self.notification_condition.acquire()
@ -150,11 +150,10 @@ class SafeEyesCore:
self.idle_condition.notify_all() self.idle_condition.notify_all()
self.idle_condition.release() self.idle_condition.release()
"""
Pause Safe Eyes if it is running.
"""
def pause(self): def pause(self):
"""
Pause Safe Eyes if it is running.
"""
with self.lock: with self.lock:
if self.active and self.running: if self.active and self.running:
self.notification_condition.acquire() self.notification_condition.acquire()
@ -162,38 +161,35 @@ class SafeEyesCore:
self.notification_condition.notify_all() self.notification_condition.notify_all()
self.notification_condition.release() self.notification_condition.release()
"""
Resume Safe Eyes if it is not running.
"""
def resume(self): def resume(self):
"""
Resume Safe Eyes if it is not running.
"""
with self.lock: with self.lock:
if self.active and not self.running: if self.active and not self.running:
self.running = True self.running = True
Utility.start_thread(self.__scheduler_job) Utility.start_thread(self.__scheduler_job)
"""
User skipped the break using Skip button
"""
def skip_break(self): def skip_break(self):
"""
User skipped the break using Skip button
"""
self.context['skipped'] = True self.context['skipped'] = True
"""
User postponed the break using Postpone button
"""
def postpone_break(self): def postpone_break(self):
"""
User postponed the break using Postpone button
"""
self.context['postponed'] = True self.context['postponed'] = True
"""
Scheduler task to execute during every interval
"""
def __scheduler_job(self): def __scheduler_job(self):
"""
Scheduler task to execute during every interval
"""
if not self.__is_running(): if not self.__is_running():
return return
time_to_wait = self.break_interval # In minutes time_to_wait = self.break_interval # In minutes
if self.context['postponed']: if self.context['postponed']:
# Reduce the break count by 1 to show the same break again # Reduce the break count by 1 to show the same break again
@ -213,11 +209,16 @@ class SafeEyesCore:
next_break_time = datetime.datetime.now() + datetime.timedelta(minutes=time_to_wait) next_break_time = datetime.datetime.now() + datetime.timedelta(minutes=time_to_wait)
self.update_next_break_info(next_break_time) self.update_next_break_info(next_break_time)
self.break_count = ((self.break_count + 1) % self.no_of_short_breaks_per_long_break)
if self.__is_long_break():
self.context['break_type'] = 'long'
else:
self.context['break_type'] = 'short'
# Wait for the pre break warning period # Wait for the pre break warning period
logging.info("Pre-break waiting for {} minutes".format(time_to_wait)) logging.info("Pre-break waiting for {} minutes".format(time_to_wait))
self.notification_condition.acquire() self.notification_condition.acquire()
self.notification_condition.wait(time_to_wait * 60) # Convert to seconds self.notification_condition.wait(time_to_wait * 60) # Convert to seconds
self.notification_condition.release() self.notification_condition.release()
logging.info("Pre-break waiting is over") logging.info("Pre-break waiting is over")
@ -227,16 +228,13 @@ class SafeEyesCore:
logging.info("Ready to show the break") logging.info("Ready to show the break")
self.break_count = ((self.break_count + 1) % self.no_of_short_breaks_per_long_break)
self.is_before_break = False self.is_before_break = False
Utility.execute_main_thread(self.__check_active_window) Utility.execute_main_thread(self.__check_active_window)
"""
Show the notification and start the break after the notification.
"""
def __show_notification(self): def __show_notification(self):
"""
Show the notification and start the break after the notification.
"""
# Show the notification # Show the notification
self.show_notification() self.show_notification()
@ -249,13 +247,12 @@ class SafeEyesCore:
self.is_before_break = True self.is_before_break = True
Utility.execute_main_thread(self.__check_active_window) Utility.execute_main_thread(self.__check_active_window)
"""
Check the active window for full-screen and user defined exceptions.
"""
def __check_active_window(self): def __check_active_window(self):
"""
Check the active window for full-screen and user defined exceptions.
"""
# Check the active window again. (User might changed the window) # Check the active window again. (User might changed the window)
if self.__is_running() and Utility.is_active_window_skipped(self.skip_break_window_classes, self.take_break_window_classes, self.is_before_break): if self.__is_running() and Utility.is_active_window_skipped(self.skip_break_window_classes, self.take_break_window_classes, self.is_before_break):
# If full screen app found, do not show break screen # If full screen app found, do not show break screen
logging.info("Found a skip_break or full-screen window. Skip the break") logging.info("Found a skip_break or full-screen window. Skip the break")
if self.__is_running(): if self.__is_running():
@ -269,12 +266,10 @@ class SafeEyesCore:
else: else:
Utility.start_thread(self.__show_notification) Utility.start_thread(self.__show_notification)
"""
Start the break screen.
"""
def __start_break(self): def __start_break(self):
"""
Start the break screen.
"""
# User can disable SafeEyes during notification # User can disable SafeEyes during notification
if self.__is_running(): if self.__is_running():
message = "" message = ""
@ -288,7 +283,6 @@ class SafeEyesCore:
seconds = self.long_break_exercises[self.long_break_message_index][1] seconds = self.long_break_exercises[self.long_break_message_index][1]
audible_alert = self.long_break_exercises[self.long_break_message_index][2] audible_alert = self.long_break_exercises[self.long_break_message_index][2]
image = self.long_break_exercises[self.long_break_message_index][3] image = self.long_break_exercises[self.long_break_message_index][3]
self.context['break_type'] = 'long'
else: else:
logging.info("Count is {}; get a short beak message".format(self.break_count)) logging.info("Count is {}; get a short beak message".format(self.break_count))
self.short_break_message_index = (self.short_break_message_index + 1) % len(self.short_break_exercises) self.short_break_message_index = (self.short_break_message_index + 1) % len(self.short_break_exercises)
@ -296,7 +290,6 @@ class SafeEyesCore:
seconds = self.short_break_exercises[self.short_break_message_index][1] seconds = self.short_break_exercises[self.short_break_message_index][1]
audible_alert = self.short_break_exercises[self.short_break_message_index][2] audible_alert = self.short_break_exercises[self.short_break_message_index][2]
image = self.short_break_exercises[self.short_break_message_index][3] image = self.short_break_exercises[self.short_break_message_index][3]
self.context['break_type'] = 'short'
self.context['break_length'] = seconds self.context['break_length'] = seconds
self.context['audible_alert'] = audible_alert self.context['audible_alert'] = audible_alert
@ -307,11 +300,10 @@ class SafeEyesCore:
# Use self.active instead of self.__is_running to avoid idle pause interrupting the break # Use self.active instead of self.__is_running to avoid idle pause interrupting the break
while seconds and self.active and not self.context['skipped'] and not self.context['postponed']: while seconds and self.active and not self.context['skipped'] and not self.context['postponed']:
self.context['count_down'] = total_break_time - seconds count_down = total_break_time - seconds
mins, secs = divmod(seconds, 60) self.context['count_down'] = count_down
timeformat = '{:02d}:{:02d}'.format(mins, secs) self.on_countdown(count_down, seconds)
self.on_countdown(timeformat) time.sleep(1) # Sleep for 1 second
time.sleep(1) # Sleep for 1 second
seconds -= 1 seconds -= 1
# Loop terminated because of timeout (not skipped) -> Close the break alert # Loop terminated because of timeout (not skipped) -> Close the break alert
@ -327,26 +319,22 @@ class SafeEyesCore:
# Schedule the break again # Schedule the break again
Utility.start_thread(self.__scheduler_job) Utility.start_thread(self.__scheduler_job)
"""
Tells whether Safe Eyes is running or not.
"""
def __is_running(self): def __is_running(self):
"""
Tells whether Safe Eyes is running or not.
"""
return self.active and self.running return self.active and self.running
"""
Check if the current break is long break or short current
"""
def __is_long_break(self): def __is_long_break(self):
"""
Check if the current break is long break or short current
"""
return self.break_count == self.no_of_short_breaks_per_long_break - 1 return self.break_count == self.no_of_short_breaks_per_long_break - 1
"""
Continuously check the system idle time and pause/resume Safe Eyes based on it.
"""
def __start_idle_monitor(self): def __start_idle_monitor(self):
"""
Continuously check the system idle time and pause/resume Safe Eyes based on it.
"""
while self.active: while self.active:
# Wait for 2 seconds # Wait for 2 seconds
self.idle_condition.acquire() self.idle_condition.acquire()

View File

@ -18,9 +18,10 @@
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GdkX11, GObject from gi.repository import Gtk, GObject
from safeeyes import Utility from safeeyes import Utility
class SettingsDialog: class SettingsDialog:
""" """
Create and initialize SettingsDialog instance. Create and initialize SettingsDialog instance.
@ -29,11 +30,14 @@ class SettingsDialog:
self.config = config self.config = config
self.on_save_settings = on_save_settings self.on_save_settings = on_save_settings
self.languages = [] self.languages = []
self.language = language
builder = Gtk.Builder() builder = Gtk.Builder()
builder.add_from_file(glade_file) builder.add_from_file(glade_file)
builder.connect_signals(self) builder.connect_signals(self)
xprintidle_available = Utility.command_exist('xprintidle')
# Get the UI components # Get the UI components
self.window = builder.get_object('window_settings') self.window = builder.get_object('window_settings')
self.spin_short_break_duration = builder.get_object('spin_short_break_duration') self.spin_short_break_duration = builder.get_object('spin_short_break_duration')
@ -43,6 +47,7 @@ class SettingsDialog:
self.spin_time_to_prepare = builder.get_object('spin_time_to_prepare') self.spin_time_to_prepare = builder.get_object('spin_time_to_prepare')
self.spin_idle_time_to_pause = builder.get_object('spin_idle_time_to_pause') self.spin_idle_time_to_pause = builder.get_object('spin_idle_time_to_pause')
self.spin_postpone_duration = builder.get_object('spin_postpone_duration') self.spin_postpone_duration = builder.get_object('spin_postpone_duration')
self.spin_disable_keyboard_shortcut = builder.get_object('spin_disable_keyboard_shortcut')
self.switch_show_time_in_tray = builder.get_object('switch_show_time_in_tray') self.switch_show_time_in_tray = builder.get_object('switch_show_time_in_tray')
self.switch_strict_break = builder.get_object('switch_strict_break') self.switch_strict_break = builder.get_object('switch_strict_break')
self.switch_postpone = builder.get_object('switch_postpone') self.switch_postpone = builder.get_object('switch_postpone')
@ -60,6 +65,7 @@ class SettingsDialog:
builder.get_object('lbl_idle_time_to_pause').set_label(language['ui_controls']['idle_time']) builder.get_object('lbl_idle_time_to_pause').set_label(language['ui_controls']['idle_time'])
builder.get_object('lbl_postpone_duration').set_label(language['ui_controls']['postpone_duration']) builder.get_object('lbl_postpone_duration').set_label(language['ui_controls']['postpone_duration'])
builder.get_object('lbl_allow_postpone').set_label(language['ui_controls']['allow_postpone']) builder.get_object('lbl_allow_postpone').set_label(language['ui_controls']['allow_postpone'])
builder.get_object('lbl_disable_keyboard_shortcut').set_label(language['ui_controls']['disable_keyboard_shortcut'])
builder.get_object('lbl_show_time_in_tray').set_label(language['ui_controls']['show_time_in_tray']) builder.get_object('lbl_show_time_in_tray').set_label(language['ui_controls']['show_time_in_tray'])
builder.get_object('lbl_strict_break').set_label(language['ui_controls']['strict_break']) builder.get_object('lbl_strict_break').set_label(language['ui_controls']['strict_break'])
builder.get_object('lbl_audible_alert').set_label(language['ui_controls']['audible_alert']) builder.get_object('lbl_audible_alert').set_label(language['ui_controls']['audible_alert'])
@ -75,15 +81,16 @@ class SettingsDialog:
self.spin_interval_between_two_breaks.set_value(config['break_interval']) self.spin_interval_between_two_breaks.set_value(config['break_interval'])
self.spin_short_between_long.set_value(config['no_of_short_breaks_per_long_break']) self.spin_short_between_long.set_value(config['no_of_short_breaks_per_long_break'])
self.spin_time_to_prepare.set_value(config['pre_break_warning_time']) self.spin_time_to_prepare.set_value(config['pre_break_warning_time'])
self.spin_idle_time_to_pause.set_value(config['idle_time']) self.spin_idle_time_to_pause.set_value(config['idle_time'] and xprintidle_available)
self.spin_postpone_duration.set_value(config['postpone_duration']) self.spin_postpone_duration.set_value(config['postpone_duration'])
self.spin_disable_keyboard_shortcut.set_value(config['shortcut_disable_time'])
self.switch_show_time_in_tray.set_active(config['show_time_in_tray']) self.switch_show_time_in_tray.set_active(config['show_time_in_tray'])
self.switch_strict_break.set_active(config['strict_break']) self.switch_strict_break.set_active(config['strict_break'])
self.switch_audible_alert.set_active(config['audible_alert']) self.switch_audible_alert.set_active(config['audible_alert'] and Utility.pyaudio is None)
self.spin_time_to_screen_lock.set_value(config['time_to_screen_lock']) self.spin_time_to_screen_lock.set_value(config['time_to_screen_lock'])
# Enable idle_time_to_pause only if xprintidle is available # Enable idle_time_to_pause only if xprintidle is available
self.spin_idle_time_to_pause.set_sensitive(Utility.command_exist('xprintidle')) self.spin_idle_time_to_pause.set_sensitive(xprintidle_available)
self.switch_screen_lock.set_sensitive(able_to_lock_screen) self.switch_screen_lock.set_sensitive(able_to_lock_screen)
self.switch_screen_lock.set_active(able_to_lock_screen and config['enable_screen_lock']) self.switch_screen_lock.set_active(able_to_lock_screen and config['enable_screen_lock'])
@ -99,6 +106,9 @@ class SettingsDialog:
self.on_switch_screen_lock_activate(self.switch_screen_lock, self.switch_screen_lock.get_active()) self.on_switch_screen_lock_activate(self.switch_screen_lock, self.switch_screen_lock.get_active())
self.on_switch_postpone_activate(self.switch_postpone, self.switch_postpone.get_active()) self.on_switch_postpone_activate(self.switch_postpone, self.switch_postpone.get_active())
if Utility.pyaudio is None:
self.switch_audible_alert.connect('state-set', self.on_switch_audible_alert_activate)
# Initialize the language combobox # Initialize the language combobox
language_list_store = Gtk.ListStore(GObject.TYPE_STRING) language_list_store = Gtk.ListStore(GObject.TYPE_STRING)
language_index = 2 language_index = 2
@ -108,7 +118,7 @@ class SettingsDialog:
language_list_store.append([language['ui_controls']['system_language']]) language_list_store.append([language['ui_controls']['system_language']])
language_list_store.append(['-']) language_list_store.append(['-'])
self.languages.append('system') self.languages.append('system')
self.languages.append('system') # Dummy record for row separator self.languages.append('system') # Dummy record for row separator
if 'system' == lang_code: if 'system' == lang_code:
self.cmb_language.set_active(0) self.cmb_language.set_active(0)
@ -120,19 +130,17 @@ class SettingsDialog:
language_index += 1 language_index += 1
self.cmb_language.set_model(language_list_store) self.cmb_language.set_model(language_list_store)
self.cmb_language.set_row_separator_func(lambda m,i: m.get_value(i, 0) == '-') self.cmb_language.set_row_separator_func(lambda m, i: m.get_value(i, 0) == '-')
cell = Gtk.CellRendererText() cell = Gtk.CellRendererText()
self.cmb_language.pack_start(cell, True) self.cmb_language.pack_start(cell, True)
self.cmb_language.add_attribute(cell, 'text', 0) self.cmb_language.add_attribute(cell, 'text', 0)
def show(self): def show(self):
""" """
Show the SettingsDialog. Show the SettingsDialog.
""" """
self.window.show_all() self.window.show_all()
def on_switch_screen_lock_activate(self, switch, state): def on_switch_screen_lock_activate(self, switch, state):
""" """
Event handler to the state change of the screen_lock switch. Event handler to the state change of the screen_lock switch.
@ -140,13 +148,12 @@ class SettingsDialog:
""" """
self.spin_time_to_screen_lock.set_sensitive(self.switch_screen_lock.get_active()) self.spin_time_to_screen_lock.set_sensitive(self.switch_screen_lock.get_active())
def on_switch_strict_break_activate(self, switch, state): def on_switch_strict_break_activate(self, switch, state):
""" """
Event handler to the state change of the postpone switch. Event handler to the state change of the postpone switch.
Enable or disable the self.spin_postpone_duration based on the state of the postpone switch. Enable or disable the self.spin_postpone_duration based on the state of the postpone switch.
""" """
strict_break_enable = state #self.switch_strict_break.get_active() strict_break_enable = state # self.switch_strict_break.get_active()
self.switch_postpone.set_sensitive(not strict_break_enable) self.switch_postpone.set_sensitive(not strict_break_enable)
if strict_break_enable: if strict_break_enable:
self.switch_postpone.set_active(False) self.switch_postpone.set_active(False)
@ -158,13 +165,22 @@ class SettingsDialog:
""" """
self.spin_postpone_duration.set_sensitive(self.switch_postpone.get_active()) self.spin_postpone_duration.set_sensitive(self.switch_postpone.get_active())
def on_switch_audible_alert_activate(self, switch, state):
"""
Event handler to the state change of the audible_alert switch.
Show the information message dialog to install pyaudio if not installed.
"""
if state and Utility.pyaudio is None:
self.__show_message_dialog(self.language['messages']['audible_alert_disabled'], self.language['messages']['software_required'].format('pyaudio'))
switch.emit_stop_by_name('state-set')
self.switch_audible_alert.set_active(False)
def on_window_delete(self, *args): def on_window_delete(self, *args):
""" """
Event handler for Settings dialog close action. Event handler for Settings dialog close action.
""" """
self.window.destroy() self.window.destroy()
def on_save_clicked(self, button): def on_save_clicked(self, button):
""" """
Event handler for Save button click. Event handler for Save button click.
@ -176,20 +192,35 @@ class SettingsDialog:
self.config['pre_break_warning_time'] = self.spin_time_to_prepare.get_value_as_int() self.config['pre_break_warning_time'] = self.spin_time_to_prepare.get_value_as_int()
self.config['idle_time'] = self.spin_idle_time_to_pause.get_value_as_int() self.config['idle_time'] = self.spin_idle_time_to_pause.get_value_as_int()
self.config['postpone_duration'] = self.spin_postpone_duration.get_value_as_int() self.config['postpone_duration'] = self.spin_postpone_duration.get_value_as_int()
self.config['shortcut_disable_time'] = self.spin_disable_keyboard_shortcut.get_value_as_int()
self.config['show_time_in_tray'] = self.switch_show_time_in_tray.get_active() self.config['show_time_in_tray'] = self.switch_show_time_in_tray.get_active()
self.config['strict_break'] = self.switch_strict_break.get_active() self.config['strict_break'] = self.switch_strict_break.get_active()
self.config['audible_alert'] = self.switch_audible_alert.get_active()
self.config['language'] = self.languages[self.cmb_language.get_active()] self.config['language'] = self.languages[self.cmb_language.get_active()]
self.config['time_to_screen_lock'] = self.spin_time_to_screen_lock.get_value_as_int() self.config['time_to_screen_lock'] = self.spin_time_to_screen_lock.get_value_as_int()
self.config['enable_screen_lock'] = self.switch_screen_lock.get_active() self.config['enable_screen_lock'] = self.switch_screen_lock.get_active()
self.config['allow_postpone'] = self.switch_postpone.get_active() self.config['allow_postpone'] = self.switch_postpone.get_active()
# Check if pyaudio is installed when turning audible notifications on
if self.switch_audible_alert.get_active() and not Utility.pyaudio:
# Notify user that pyaudio is not installed
self.__show_message_dialog(self.language['messages']['audible_alert_disabled'], self.language['messages']['software_required'].format('pyaudio'))
self.config['audible_alert'] = False
else:
self.config['audible_alert'] = self.switch_audible_alert.get_active()
self.on_save_settings(self.config) # Call the provided save method self.on_save_settings(self.config) # Call the provided save method
self.window.destroy() # Close the settings window self.window.destroy() # Close the settings window
def on_cancel_clicked(self, button): def on_cancel_clicked(self, button):
""" """
Event handler for Cancel button click. Event handler for Cancel button click.
""" """
self.window.destroy() self.window.destroy()
def __show_message_dialog(self, primary_text, secondary_text):
"""
Show a popup message dialog.
"""
dialog = Gtk.MessageDialog(self.window, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, primary_text)
dialog.format_secondary_text(secondary_text)
dialog.run()
dialog.destroy()

View File

@ -28,6 +28,9 @@ APPINDICATOR_ID = 'safeeyes'
class TrayIcon: class TrayIcon:
"""
Create and show the tray icon along with the tray menu.
"""
def __init__(self, config, 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")
@ -133,9 +136,15 @@ class TrayIcon:
self.indicator.set_menu(self.menu) self.indicator.set_menu(self.menu)
def initialize(self, config): def initialize(self, config):
"""
Initialize the tray icon by setting the config.
"""
self.config = config self.config = config
def set_labels(self, language): def set_labels(self, language):
"""
Update the text of menu items based on the selected language.
"""
self.language = language self.language = language
for entry in self.sub_menu_items: for entry in self.sub_menu_items:
entry[0].set_label(self.language['ui_controls'][entry[1]].format(entry[2])) entry[0].set_label(self.language['ui_controls'][entry[1]].format(entry[2]))
@ -158,12 +167,22 @@ class TrayIcon:
self.item_quit.set_label(self.language['ui_controls']['quit']) self.item_quit.set_label(self.language['ui_controls']['quit'])
def show_icon(self): def show_icon(self):
"""
Show the tray icon.
"""
Utility.execute_main_thread(self.indicator.set_status, appindicator.IndicatorStatus.ACTIVE) Utility.execute_main_thread(self.indicator.set_status, appindicator.IndicatorStatus.ACTIVE)
def hide_icon(self): def hide_icon(self):
"""
Hide the tray icon.
"""
Utility.execute_main_thread(self.indicator.set_status, appindicator.IndicatorStatus.PASSIVE) Utility.execute_main_thread(self.indicator.set_status, appindicator.IndicatorStatus.PASSIVE)
def quit_safe_eyes(self, *args): def quit_safe_eyes(self, *args):
"""
Handle Quit menu action.
This action terminates the application.
"""
self.on_quite() self.on_quite()
with self.lock: with self.lock:
self.active = True self.active = True
@ -173,17 +192,31 @@ class TrayIcon:
self.idle_condition.release() self.idle_condition.release()
def show_settings(self, *args): def show_settings(self, *args):
"""
Handle Settings menu action.
This action shows the Settings dialog.
"""
self.on_show_settings() self.on_show_settings()
def show_about(self, *args): def show_about(self, *args):
"""
Handle About menu action.
This action shows the About dialog.
"""
self.on_show_about() self.on_show_about()
def next_break_time(self, dateTime): def next_break_time(self, dateTime):
"""
Update the next break time to be displayed in the menu and optionally in the tray icon.
"""
logging.info("Update next break information") logging.info("Update next break information")
self.dateTime = dateTime self.dateTime = dateTime
self.__set_next_break_info() self.__set_next_break_info()
def __set_next_break_info(self): def __set_next_break_info(self):
"""
A private method to be called within this class to update the next break information using self.dateTime.
"""
formatted_time = Utility.format_time(self.dateTime) formatted_time = Utility.format_time(self.dateTime)
message = self.language['messages']['next_break_at'].format(formatted_time) message = self.language['messages']['next_break_at'].format(formatted_time)
# Update the tray icon label # Update the tray icon label
@ -195,6 +228,10 @@ class TrayIcon:
Utility.execute_main_thread(self.item_info.set_label, message) Utility.execute_main_thread(self.item_info.set_label, message)
def on_enable_clicked(self, *args): def on_enable_clicked(self, *args):
"""
Handle 'Enable Safe Eyes' menu action.
This action enables the application if it is currently disabled.
"""
# active = self.item_enable.get_active() # active = self.item_enable.get_active()
if not self.active: if not self.active:
with self.lock: with self.lock:
@ -211,6 +248,10 @@ class TrayIcon:
self.idle_condition.release() self.idle_condition.release()
def on_disable_clicked(self, *args): def on_disable_clicked(self, *args):
"""
Handle the menu actions of all the sub menus of 'Disable Safe Eyes'.
This action disables the application if it is currently active.
"""
# active = self.item_enable.get_active() # active = self.item_enable.get_active()
if self.active and len(args) > 1: if self.active and len(args) > 1:
logging.info('Disable Safe Eyes') logging.info('Disable Safe Eyes')
@ -231,30 +272,26 @@ class TrayIcon:
Utility.start_thread(self.__schedule_resume, time_minutes=time_to_wait) Utility.start_thread(self.__schedule_resume, time_minutes=time_to_wait)
self.item_info.set_label(self.language['messages']['disabled_until_x'].format(Utility.format_time(self.wakeup_time))) self.item_info.set_label(self.language['messages']['disabled_until_x'].format(Utility.format_time(self.wakeup_time)))
"""
This method is called by the core to prevent user from disabling Safe Eyes after the notification.
"""
def lock_menu(self): def lock_menu(self):
"""
This method is called by the core to prevent user from disabling Safe Eyes after the notification.
"""
if self.active: if self.active:
# self.item_disable.set_sensitive(False)
# self.item_settings.set_sensitive(False)
# self.item_quit.set_sensitive(False)
self.menu.set_sensitive(False) self.menu.set_sensitive(False)
"""
This method is called by the core to activate the disable menu after the the break.
"""
def unlock_menu(self): def unlock_menu(self):
"""
This method is called by the core to activate the menu after the the break.
"""
if self.active: if self.active:
# self.item_disable.set_sensitive(True)
# self.item_settings.set_sensitive(True)
# self.item_quit.set_sensitive(True)
self.menu.set_sensitive(True) self.menu.set_sensitive(True)
def __schedule_resume(self, time_minutes): def __schedule_resume(self, time_minutes):
"""
Schedule a local timer to enable Safe Eyes after the given timeout.
"""
self.idle_condition.acquire() self.idle_condition.acquire()
self.idle_condition.wait(time_minutes * 60) # Convert to seconds self.idle_condition.wait(time_minutes * 60) # Convert to seconds
self.idle_condition.release() self.idle_condition.release()
with self.lock: with self.lock:

View File

@ -18,11 +18,11 @@
import gi import gi
gi.require_version('Gdk', '3.0') gi.require_version('Gdk', '3.0')
from gi.repository import Gdk, GLib from gi.repository import Gtk, Gdk, GLib, GdkX11
from html.parser import HTMLParser from html.parser import HTMLParser
from distutils.version import LooseVersion from distutils.version import LooseVersion
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
import babel.dates, os, errno, re, subprocess, threading, logging, locale, json, shutil, pyaudio, wave import babel.dates, os, errno, re, subprocess, threading, logging, locale, json, shutil, wave
bin_directory = os.path.dirname(os.path.realpath(__file__)) bin_directory = os.path.dirname(os.path.realpath(__file__))
home_directory = os.path.expanduser('~') home_directory = os.path.expanduser('~')
@ -33,43 +33,59 @@ style_sheet_path = os.path.join(config_directory, 'style/safeeyes_style.css')
system_config_file_path = os.path.join(bin_directory, "config/safeeyes.json") system_config_file_path = os.path.join(bin_directory, "config/safeeyes.json")
system_style_sheet_path = os.path.join(bin_directory, "config/style/safeeyes_style.css") system_style_sheet_path = os.path.join(bin_directory, "config/style/safeeyes_style.css")
log_file_path = os.path.join(config_directory, 'safeeyes.log') log_file_path = os.path.join(config_directory, 'safeeyes.log')
pyaudio = None
def import_dependencies():
"""
Import the optional Python dependencies.
"""
try:
# Import pyaudio if exists
global pyaudio
pyaudio = __import__("pyaudio")
except ImportError:
logging.warning('Install pyaudio for audible notifications.')
def play_notification(): def play_notification():
""" """
Play the alert.wav Play the alert.wav
""" """
logging.info('Playing audible alert') if pyaudio:
CHUNK = 1024 logging.info('Playing audible alert')
CHUNK = 1024
try: try:
# Open the sound file # Open the sound file
path = get_resource_path('alert.wav') path = get_resource_path('alert.wav')
if path is None: if path is None:
return return
sound = wave.open(path, 'rb') sound = wave.open(path, 'rb')
# Create a sound stream # Create a sound stream
wrapper = pyaudio.PyAudio() wrapper = pyaudio.PyAudio()
stream = wrapper.open(format=wrapper.get_format_from_width(sound.getsampwidth()), stream = wrapper.open(format=wrapper.get_format_from_width(
channels=sound.getnchannels(), sound.getsampwidth()),
rate=sound.getframerate(), channels=sound.getnchannels(),
output=True) rate=sound.getframerate(),
output=True)
# Write file data into the sound stream # Write file data into the sound stream
data = sound.readframes(CHUNK)
while data != b'':
stream.write(data)
data = sound.readframes(CHUNK) data = sound.readframes(CHUNK)
while data != b'':
stream.write(data)
data = sound.readframes(CHUNK)
# Close steam # Close steam
stream.stop_stream() stream.stop_stream()
stream.close() stream.close()
sound.close() sound.close()
wrapper.terminate() wrapper.terminate()
except Exception as e: except Exception as e:
logging.warning('Unable to play audible alert') logging.warning('Unable to play audible alert')
logging.exception(e) logging.exception(e)
def get_resource_path(resource_name): def get_resource_path(resource_name):
@ -95,7 +111,7 @@ def system_idle_time():
Return the idle time if xprintidle is available, otherwise return 0. Return the idle time if xprintidle is available, otherwise return 0.
""" """
try: try:
return int(subprocess.check_output(['xprintidle']).decode('utf-8')) / 60000 # Convert to minutes return int(subprocess.check_output(['xprintidle']).decode('utf-8')) / 60000 # Convert to minutes
except: except:
return 0 return 0
@ -129,7 +145,7 @@ def is_active_window_skipped(skip_break_window_classes, take_break_window_classe
active_window = screen.get_active_window() active_window = screen.get_active_window()
if active_window: if active_window:
active_xid = str(active_window.get_xid()) active_xid = str(active_window.get_xid())
cmdlist = ['xprop', '-root', '-notype','-id',active_xid, 'WM_CLASS', '_NET_WM_STATE'] cmdlist = ['xprop', '-root', '-notype', '-id', active_xid, 'WM_CLASS', '_NET_WM_STATE']
try: try:
stdout = subprocess.check_output(cmdlist).decode('utf-8') stdout = subprocess.check_output(cmdlist).decode('utf-8')
@ -191,6 +207,7 @@ def mkdir(path):
logging.error('Error while creating ' + str(path)) logging.error('Error while creating ' + str(path))
raise raise
def parse_language_code(lang_code): def parse_language_code(lang_code):
""" """
Convert the user defined language code to a valid one. Convert the user defined language code to a valid one.
@ -249,6 +266,30 @@ def read_lang_files():
return languages return languages
def desktop_environment():
"""
Detect the desktop environment.
"""
desktop_session = os.environ.get('DESKTOP_SESSION')
current_desktop = os.environ.get('XDG_CURRENT_DESKTOP')
if desktop_session is not None:
desktop_session = desktop_session.lower()
if desktop_session in ['gnome', 'unity', 'budgie-desktop', 'cinnamon', 'mate', 'xfce4', 'lxde', 'pantheon', 'fluxbox', 'blackbox', 'openbox', 'icewm', 'jwm', 'afterstep', 'trinity', 'kde']:
return desktop_session
elif (desktop_session.startswith('xubuntu') or (current_desktop is not None and 'xfce' in current_desktop)):
return 'xfce'
elif desktop_session.startswith('ubuntu'):
return 'unity'
elif desktop_session.startswith('lubuntu'):
return 'lxde'
elif 'plasma' in desktop_session or desktop_session.startswith('kubuntu') or os.environ.get('KDE_FULL_SESSION') == 'true':
return 'kde'
elif os.environ.get('GNOME_DESKTOP_SESSION_ID'):
return 'gnome'
return 'unknown'
def lock_screen_command(): def lock_screen_command():
""" """
Function tries to detect the screensaver command based on the current envinroment Function tries to detect the screensaver command based on the current envinroment
@ -275,14 +316,14 @@ def lock_screen_command():
return ['mate-screensaver-command', '--lock'] return ['mate-screensaver-command', '--lock']
elif desktop_session == 'kde' or 'plasma' in desktop_session or desktop_session.startswith('kubuntu') or os.environ.get('KDE_FULL_SESSION') == 'true': elif desktop_session == 'kde' or 'plasma' in desktop_session or desktop_session.startswith('kubuntu') or os.environ.get('KDE_FULL_SESSION') == 'true':
return ['qdbus', 'org.freedesktop.ScreenSaver', '/ScreenSaver', 'Lock'] return ['qdbus', 'org.freedesktop.ScreenSaver', '/ScreenSaver', 'Lock']
elif desktop_session in ['gnome','unity', 'budgie-desktop'] or desktop_session.startswith('ubuntu'): elif desktop_session in ['gnome', 'unity', 'budgie-desktop'] or desktop_session.startswith('ubuntu'):
if command_exist('gnome-screensaver-command'): if command_exist('gnome-screensaver-command'):
return ['gnome-screensaver-command', '--lock'] return ['gnome-screensaver-command', '--lock']
else: else:
# From Gnome 3.8 no gnome-screensaver-command # From Gnome 3.8 no gnome-screensaver-command
return ['dbus-send', '--type=method_call', '--dest=org.gnome.ScreenSaver', '/org/gnome/ScreenSaver', 'org.gnome.ScreenSaver.Lock'] return ['dbus-send', '--type=method_call', '--dest=org.gnome.ScreenSaver', '/org/gnome/ScreenSaver', 'org.gnome.ScreenSaver.Lock']
elif os.environ.get('GNOME_DESKTOP_SESSION_ID'): elif os.environ.get('GNOME_DESKTOP_SESSION_ID'):
if not 'deprecated' in os.environ.get('GNOME_DESKTOP_SESSION_ID') and command_exist('gnome-screensaver-command'): if 'deprecated' not in os.environ.get('GNOME_DESKTOP_SESSION_ID') and command_exist('gnome-screensaver-command'):
# Gnome 2 # Gnome 2
return ['gnome-screensaver-command', '--lock'] return ['gnome-screensaver-command', '--lock']
return None return None
@ -378,7 +419,7 @@ def intialize_logging():
log_formatter = logging.Formatter('%(asctime)s [%(levelname)s]:[%(threadName)s] %(message)s') log_formatter = logging.Formatter('%(asctime)s [%(levelname)s]:[%(threadName)s] %(message)s')
# Apped the logs and overwrite once reached 5MB # Apped the logs and overwrite once reached 5MB
handler = RotatingFileHandler(log_file_path, mode='a', maxBytes=5*1024*1024, backupCount=2, encoding=None, delay=0) handler = RotatingFileHandler(log_file_path, mode='a', maxBytes=5 * 1024 * 1024, backupCount=2, encoding=None, delay=0)
handler.setFormatter(log_formatter) handler.setFormatter(log_formatter)
handler.setLevel(logging.INFO) handler.setLevel(logging.INFO)
@ -445,7 +486,7 @@ class __HTMLTextExtractor(HTMLParser):
def __init__(self): def __init__(self):
self.reset() self.reset()
self.strict = False self.strict = False
self.convert_charrefs= True self.convert_charrefs = True
self.fed = [] self.fed = []
def handle_data(self, d): def handle_data(self, d):

View File

@ -18,7 +18,7 @@
# 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 os, gi, json, dbus, logging, operator, psutil, sys import os, gi, json, dbus, logging, psutil, sys
from threading import Timer from threading import Timer
from dbus.mainloop.glib import DBusGMainLoop from dbus.mainloop.glib import DBusGMainLoop
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
@ -37,14 +37,14 @@ break_screen_glade = os.path.join(Utility.bin_directory, "glade/break_screen.gla
settings_dialog_glade = os.path.join(Utility.bin_directory, "glade/settings_dialog.glade") settings_dialog_glade = os.path.join(Utility.bin_directory, "glade/settings_dialog.glade")
about_dialog_glade = os.path.join(Utility.bin_directory, "glade/about_dialog.glade") about_dialog_glade = os.path.join(Utility.bin_directory, "glade/about_dialog.glade")
is_active = True is_active = True
SAFE_EYES_VERSION = "1.2.1" SAFE_EYES_VERSION = "1.2.2"
"""
Listen to tray icon Settings action and send the signal to Settings dialog.
"""
def show_settings(): def show_settings():
"""
Listen to tray icon Settings action and send the signal to Settings dialog.
"""
logging.info("Show Settings dialog") logging.info("Show Settings dialog")
able_to_lock_screen = False able_to_lock_screen = False
if system_lock_command: if system_lock_command:
@ -52,27 +52,30 @@ def show_settings():
settings_dialog = SettingsDialog(config, language, Utility.read_lang_files(), able_to_lock_screen, save_settings, settings_dialog_glade) settings_dialog = SettingsDialog(config, language, Utility.read_lang_files(), able_to_lock_screen, save_settings, settings_dialog_glade)
settings_dialog.show() settings_dialog.show()
"""
Listen to tray icon About action and send the signal to About dialog.
"""
def show_about(): def show_about():
"""
Listen to tray icon About action and send the signal to About dialog.
"""
logging.info("Show About dialog") logging.info("Show About dialog")
about_dialog = AboutDialog(about_dialog_glade, SAFE_EYES_VERSION, language) about_dialog = AboutDialog(about_dialog_glade, SAFE_EYES_VERSION, language)
about_dialog.show() about_dialog.show()
"""
Receive the signal from core and pass it to the Notification.
"""
def show_notification(): def show_notification():
"""
Receive the signal from core and pass it to the Notification.
"""
if config['strict_break']: if config['strict_break']:
Utility.execute_main_thread(tray_icon.lock_menu) Utility.execute_main_thread(tray_icon.lock_menu)
plugins.pre_notification(context) plugins.pre_notification(context)
notification.show(config['pre_break_warning_time']) notification.show(config['pre_break_warning_time'])
"""
Receive the break signal from core and pass it to the break screen.
"""
def show_alert(message, image_name): def show_alert(message, image_name):
"""
Receive the break signal from core and pass it to the break screen.
"""
logging.info("Show the break screen") logging.info("Show the break screen")
notification.close() notification.close()
plugins_data = plugins.pre_break(context) plugins_data = plugins.pre_break(context)
@ -80,10 +83,11 @@ def show_alert(message, image_name):
if config['strict_break'] and is_active: if config['strict_break'] and is_active:
Utility.execute_main_thread(tray_icon.unlock_menu) Utility.execute_main_thread(tray_icon.unlock_menu)
"""
Receive the stop break signal from core and pass it to the break screen.
"""
def close_alert(audible_alert_on): def close_alert(audible_alert_on):
"""
Receive the stop break signal from core and pass it to the break screen.
"""
logging.info("Close the break screen") logging.info("Close the break screen")
if config['enable_screen_lock'] and context['break_type'] == 'long': if config['enable_screen_lock'] and context['break_type'] == 'long':
# Lock the screen before closing the break screen # Lock the screen before closing the break screen
@ -94,21 +98,22 @@ def close_alert(audible_alert_on):
plugins.post_break(context) plugins.post_break(context)
"""
Listen to the tray menu quit action and stop the core, notification and the app itself.
"""
def on_quit(): def on_quit():
"""
Listen to the tray menu quit action and stop the core, notification and the app itself.
"""
logging.info("Quit Safe Eyes") logging.info("Quit Safe Eyes")
plugins.exit(context) plugins.exit(context)
core.stop() core.stop()
notification.quite(); notification.quite()
Gtk.main_quit() Gtk.main_quit()
"""
def handle_suspend_callback(sleeping):
"""
If the system goes to sleep, Safe Eyes stop the core if it is already active. If the system goes to sleep, Safe Eyes stop the core if it is already active.
If it was active, Safe Eyes will become active after wake up. If it was active, Safe Eyes will become active after wake up.
""" """
def handle_suspend_callback(sleeping):
if sleeping: if sleeping:
# Sleeping / suspending # Sleeping / suspending
if is_active: if is_active:
@ -120,18 +125,20 @@ def handle_suspend_callback(sleeping):
core.start() core.start()
logging.info("Resumed Safe Eyes after system wakeup") logging.info("Resumed Safe Eyes after system wakeup")
"""
Setup system suspend listener.
"""
def handle_system_suspend(): def handle_system_suspend():
"""
Setup system suspend listener.
"""
DBusGMainLoop(set_as_default=True) DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus() bus = dbus.SystemBus()
bus.add_signal_receiver(handle_suspend_callback, 'PrepareForSleep', 'org.freedesktop.login1.Manager', 'org.freedesktop.login1') bus.add_signal_receiver(handle_suspend_callback, 'PrepareForSleep', 'org.freedesktop.login1.Manager', 'org.freedesktop.login1')
"""
Listen to break screen Skip action and send the signal to core.
"""
def on_skipped(): def on_skipped():
"""
Listen to break screen Skip action and send the signal to core.
"""
logging.info("User skipped the break") logging.info("User skipped the break")
if config['enable_screen_lock'] and context['break_type'] == 'long' and context.get('count_down', 0) >= config['time_to_screen_lock']: if config['enable_screen_lock'] and context['break_type'] == 'long' and context.get('count_down', 0) >= config['time_to_screen_lock']:
# Lock the screen before closing the break screen # Lock the screen before closing the break screen
@ -139,20 +146,22 @@ def on_skipped():
core.skip_break() core.skip_break()
plugins.post_break(context) plugins.post_break(context)
"""
Listen to break screen Postpone action and send the signal to core.
"""
def on_postponed(): def on_postponed():
"""
Listen to break screen Postpone action and send the signal to core.
"""
logging.info("User postponed the break") logging.info("User postponed the break")
if config['enable_screen_lock'] and context['break_type'] == 'long' and context.get('count_down', 0) >= config['time_to_screen_lock']: if config['enable_screen_lock'] and context['break_type'] == 'long' and context.get('count_down', 0) >= config['time_to_screen_lock']:
# Lock the screen before closing the break screen # Lock the screen before closing the break screen
Utility.lock_desktop(system_lock_command) Utility.lock_desktop(system_lock_command)
core.postpone_break() core.postpone_break()
"""
Listen to Settings dialog Save action and write to the config file.
"""
def save_settings(config): def save_settings(config):
"""
Listen to Settings dialog Save action and write to the config file.
"""
global language global language
logging.info("Saving settings to safeeyes.json") logging.info("Saving settings to safeeyes.json")
@ -175,22 +184,25 @@ def save_settings(config):
# Restart the core and intialize the components # Restart the core and intialize the components
core.initialize(config, language) core.initialize(config, language)
break_screen.initialize(config, language) break_screen.initialize(config, language)
notification.initialize(language)
if is_active: if is_active:
# 1 sec delay is required to give enough time for core to be stopped # 1 sec delay is required to give enough time for core to be stopped
Timer(1.0, core.start).start() Timer(1.0, core.start).start()
"""
Listen to tray icon enable action and send the signal to core.
"""
def enable_safeeyes(): def enable_safeeyes():
"""
Listen to tray icon enable action and send the signal to core.
"""
global is_active global is_active
is_active = True is_active = True
core.start() core.start()
"""
Listen to tray icon disable action and send the signal to core.
"""
def disable_safeeyes(): def disable_safeeyes():
"""
Listen to tray icon disable action and send the signal to core.
"""
global is_active global is_active
is_active = False is_active = False
core.stop() core.stop()
@ -198,11 +210,12 @@ def disable_safeeyes():
def running(): def running():
""" """
Check if SafeEyes is already running. Check if SafeEyes is already running.
""" """
process_count = 0 process_count = 0
for proc in psutil.process_iter(): for proc in psutil.process_iter():
if not proc.cmdline: continue if not proc.cmdline:
continue
try: try:
# Check if safeeyes is in process arguments # Check if safeeyes is in process arguments
if callable(proc.cmdline): if callable(proc.cmdline):
@ -231,6 +244,9 @@ def main():
logging.info("Starting Safe Eyes") logging.info("Starting Safe Eyes")
# Import the dependencies
Utility.import_dependencies()
if not running(): if not running():
global break_screen global break_screen
@ -253,14 +269,14 @@ def main():
else: else:
system_lock_command = Utility.lock_screen_command() system_lock_command = Utility.lock_screen_command()
# Initialize the Safe Eyes Context # Initialize the Safe Eyes Context
context['version'] = SAFE_EYES_VERSION context['version'] = SAFE_EYES_VERSION
context['desktop'] = Utility.desktop_environment()
tray_icon = TrayIcon(config, 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, on_postponed, break_screen_glade, Utility.style_sheet_path) break_screen = BreakScreen(context, on_skipped, on_postponed, break_screen_glade, Utility.style_sheet_path)
break_screen.initialize(config, language) break_screen.initialize(config, language)
notification = Notification(language) notification = Notification(context, language)
plugins = Plugins(config) plugins = Plugins(config)
core = SafeEyesCore(context, show_notification, show_alert, close_alert, break_screen.show_count_down, tray_icon.next_break_time) core = SafeEyesCore(context, show_notification, show_alert, close_alert, break_screen.show_count_down, tray_icon.next_break_time)
core.initialize(config, language) core.initialize(config, language)
@ -274,5 +290,6 @@ def main():
logging.info('Another instance of safeeyes is already running') logging.info('Another instance of safeeyes is already running')
sys.exit(0) sys.exit(0)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@ -0,0 +1,62 @@
{
"meta_info": {
"language_name": "Català",
"language_name_en": "Catalan"
},
"app_info": {
"description": "Safe Eyes protegeix els vostres ulls de la fatiga visual (astenopia) recordant-vos que feu petits descansos mentre esteu treballant amb l'ordinador."
},
"exercises": {
"short_break_close_eyes": "Tanqueu fortament els ulls",
"short_break_roll_eyes": "Moveu els ulls a banda i banda",
"short_break_rotate_clockwise": "Gireu els ulls en sentit horari",
"short_break_rotate_counter_clockwise": "Gireu els ulls en sentit antihorari",
"short_break_blink": "Parpallegeu",
"short_break_focus_far_distance": "Fixeu la mirada en un punt llunyà",
"short_break_drink_water": "Bebeu una mica d'aigua",
"long_break_walk": "Camineu una estona",
"long_break_lean_back": "Reclineu-vos sobre la cadira i relaxeu-vos"
},
"messages": {
"audible_alert_disabled": "Cannot enable audible notifications",
"ready_for_a_short_break": "Prepareu-vos per a una pausa en {} segons",
"ready_for_a_long_break": "Prepareu-vos per a una pausa en {} segons",
"disabled_until_restart": "Inhabilita fins que es reinicïi",
"disabled_until_x": "Inhabilitat fins {}",
"next_break_at": "Propera pausa a les {}",
"software_required": "To enable this feature, please install {}"
},
"ui_controls": {
"about": "Quant a",
"allow_postpone": "Permet posposar les pauses",
"audible_alert": "Alerta sonora en finalitzar una pausa",
"cancel": "Canceŀla",
"close": "Tanca",
"disable": "Desactiva Safe Eyes",
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
"enable": "Activa Safe Eyes",
"enable_screen_lock": "Bloca la pantalla després de cada pausa llarga",
"for_x_hour": "Durant {} hora",
"for_x_hours": "Durant {} hores",
"for_x_minutes": "Durant {} minuts",
"idle_time": "Temps mínim inactiu per a pausar (en minuts)",
"interval_between_two_breaks": "Interval entre dues pauses (en minuts)",
"language": "Llengua",
"license": "Llicència",
"long_break_duration": "Durada d'una pausa llarga (en segons)",
"no_of_short_breaks_between_two_long_breaks": "Nombre de pauses breus entre dues pauses llargues",
"postpone": "Posposa",
"postpone_duration": "Durada d'una posposició (en minuts)",
"quit": "Tanca",
"save": "Desa",
"settings": "Configuració",
"short_break_duration": "Durada d'una pausa breu (en segons)",
"show_time_in_tray": "Mostra l'hora de la propera pausa a l'àrea de notificacions",
"skip": "Omet",
"strict_break": "Pausa estrica (sense botó per ometre-la)",
"system_language": "Idioma del sistema",
"time_to_prepare_for_break": "Temps per preparar-se per a una pausa (en segons)",
"time_to_screen_lock": "Temps màxim per saltar-se una pausa, evitant la pantalla de bloqueig (en segons)",
"until_restart": "Fins que es reinicïi"
}
}

View File

@ -18,10 +18,13 @@
"long_break_lean_back": "Lehnen Sie sich zurück und entspannen Sie sich" "long_break_lean_back": "Lehnen Sie sich zurück und entspannen Sie sich"
}, },
"messages": { "messages": {
"ready_for_a_break": "Nächste Pause in {} Sekunden", "audible_alert_disabled": "Cannot enable audible notifications",
"ready_for_a_short_break": "Nächste Pause in {} Sekunden",
"ready_for_a_long_break": "Nächste Pause in {} Sekunden",
"disabled_until_restart": "Deaktiviert bis zum Neustart", "disabled_until_restart": "Deaktiviert bis zum Neustart",
"disabled_until_x": "Deaktiviert bis {}", "disabled_until_x": "Deaktiviert bis {}",
"next_break_at": "Nächste Pause um {}" "next_break_at": "Nächste Pause um {}",
"software_required": "To enable this feature, please install {}"
}, },
"ui_controls": { "ui_controls": {
"about": "Über", "about": "Über",
@ -30,6 +33,7 @@
"cancel": "Abbrechen", "cancel": "Abbrechen",
"close": "Schließen", "close": "Schließen",
"disable": "Safe Eyes deaktivieren", "disable": "Safe Eyes deaktivieren",
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
"enable": "Safe Eyes aktivieren", "enable": "Safe Eyes aktivieren",
"enable_screen_lock": "Sperrt den Bildschirm nach einer langen Pause", "enable_screen_lock": "Sperrt den Bildschirm nach einer langen Pause",
"for_x_hour": "Für {} Stunde", "for_x_hour": "Für {} Stunde",
@ -47,7 +51,7 @@
"save": "Speichern", "save": "Speichern",
"settings": "Einstellungen", "settings": "Einstellungen",
"short_break_duration": "Kleine-Pause-Intervall (in Sekunden)", "short_break_duration": "Kleine-Pause-Intervall (in Sekunden)",
"show_time_in_tray": "Show the next break time in system tray", "show_time_in_tray": "Zeige Zeit für nächste Pause im Bereich der Systemnotifikationen",
"skip": "Überspringen", "skip": "Überspringen",
"strict_break": "Strikte Pause (Überspringen nicht möglich)", "strict_break": "Strikte Pause (Überspringen nicht möglich)",
"system_language": "Systemsprache", "system_language": "Systemsprache",

View File

@ -18,10 +18,13 @@
"long_break_lean_back": "Lean back at your seat and relax" "long_break_lean_back": "Lean back at your seat and relax"
}, },
"messages": { "messages": {
"ready_for_a_break": "Ready for a break in {} seconds", "audible_alert_disabled": "Cannot enable audible notifications",
"ready_for_a_short_break": "Ready for a short break in {} seconds",
"ready_for_a_long_break": "Ready for a long break in {} seconds",
"disabled_until_restart": "Disabled until restart", "disabled_until_restart": "Disabled until restart",
"disabled_until_x": "Disabled until {}", "disabled_until_x": "Disabled until {}",
"next_break_at": "Next break at {}" "next_break_at": "Next break at {}",
"software_required": "To enable this feature, please install {}"
}, },
"ui_controls": { "ui_controls": {
"about": "About", "about": "About",
@ -30,6 +33,7 @@
"cancel": "Cancel", "cancel": "Cancel",
"close": "Close", "close": "Close",
"disable": "Disable Safe Eyes", "disable": "Disable Safe Eyes",
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
"enable": "Enable Safe Eyes", "enable": "Enable Safe Eyes",
"enable_screen_lock": "Lock the screen after every long break", "enable_screen_lock": "Lock the screen after every long break",
"for_x_hour": "For {} Hour", "for_x_hour": "For {} Hour",

View File

@ -18,10 +18,13 @@
"long_break_lean_back": "Reclínate sobre tu silla y relájate" "long_break_lean_back": "Reclínate sobre tu silla y relájate"
}, },
"messages": { "messages": {
"ready_for_a_break": "Listo para una pausa en {} segundos", "audible_alert_disabled": "Cannot enable audible notifications",
"ready_for_a_short_break": "Listo para una pausa en {} segundos",
"ready_for_a_long_break": "Listo para una pausa en {} segundos",
"disabled_until_restart": "Deshabilitado hasta reinicio", "disabled_until_restart": "Deshabilitado hasta reinicio",
"disabled_until_x": "Deshabilitado hasta {}", "disabled_until_x": "Deshabilitado hasta {}",
"next_break_at": "Próxima pausa a las {}" "next_break_at": "Próxima pausa a las {}",
"software_required": "To enable this feature, please install {}"
}, },
"ui_controls": { "ui_controls": {
"about": "Acerca de", "about": "Acerca de",
@ -30,6 +33,7 @@
"cancel": "Cancelar", "cancel": "Cancelar",
"close": "Close", "close": "Close",
"disable": "Desactivar Safe Eyes", "disable": "Desactivar Safe Eyes",
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
"enable": "Activar Safe Eyes", "enable": "Activar Safe Eyes",
"enable_screen_lock": "Bloquear la pantalla despues de cada pausa larga", "enable_screen_lock": "Bloquear la pantalla despues de cada pausa larga",
"for_x_hour": "Durante {} hora", "for_x_hour": "Durante {} hora",

View File

@ -0,0 +1,62 @@
{
"meta_info": {
"language_name": "Eesti",
"language_name_en": "Estonian"
},
"app_info": {
"description": "Safe Eyes aitab vähendada arvutiga töötamisel silmade väsimust, tuletades meelde puhkepause."
},
"exercises": {
"short_break_close_eyes": "Sulge silmad",
"short_break_roll_eyes": "Vaata vasakule ja paremale",
"short_break_rotate_clockwise": "Liiguta silmi kellaosuti suunas",
"short_break_rotate_counter_clockwise": "Liiguta silmi kellaosutile vastupidiselt",
"short_break_blink": "Pilguta silmi",
"short_break_focus_far_distance": "Vaata kaugusesse",
"short_break_drink_water": "Joo vett",
"long_break_walk": "Jaluta ringi",
"long_break_lean_back": "Toetu seljatoele ja lõõgastu"
},
"messages": {
"audible_alert_disabled": "Helimärguandeid ei tekitata",
"ready_for_a_short_break": "Lühike paus {} sekundi pärast",
"ready_for_a_long_break": "Pikk paus {} sekundi pärast",
"disabled_until_restart": "Peatatud taaskäivituseni",
"disabled_until_x": "Peatatud kuni {}",
"next_break_at": "Järgmine paus {}",
"software_required": "Selle võimaluse jaoks tuleb paigaldada tarkvara {}"
},
"ui_controls": {
"about": "Programmist",
"allow_postpone": "Pauside edasilükkamine lubatud",
"audible_alert": "Pausi lõpus helimärguanne",
"cancel": "Tühista",
"close": "Sulge",
"disable": "Peata Safe Eyes",
"disable_keyboard_shortcut": "Klaviatuurikäskude eiramise aeg, et vältida juhuslikku edasilükkamist (sekundit)",
"enable": "Luba Safe Eyes",
"enable_screen_lock": "Pikka pausi lõpus ekraani lukustamine",
"for_x_hour": "{}-ks tunniks",
"for_x_hours": "{}-ks tunniks",
"for_x_minutes": "{}-ks minutiks",
"idle_time": "Minimaalne pausi aeg (minutites)",
"interval_between_two_breaks": "Kahe pausi vaheline aeg (minutites)",
"language": "Keel",
"license": "Litsents",
"long_break_duration": "Pika pausi kestvus (sekundites)",
"no_of_short_breaks_between_two_long_breaks": "Kui mitu lühikest pausi tehaks pikkade pauside vahel",
"postpone": "Lükka edasi",
"postpone_duration": "Edasilükkamise aeg (minutites)",
"quit": "Välju",
"save": "Salvesta",
"settings": "Seaded",
"short_break_duration": "Lühikese pausi kestvus (sekundites)",
"show_time_in_tray": "Kuvatakse järgmise pausi aega",
"skip": "Jäta vahele",
"strict_break": "Range paus (vahelejätmise nuppu ei näidata)",
"system_language": "Süsteemi keel",
"time_to_prepare_for_break": "Kui kaua enne pausi kuvatakse pausi hoiatust (sekundites)",
"time_to_screen_lock": "Kui kaua enne ekraani lukustamist saab pausi edasi lükata (sekundites)",
"until_restart": "Kuni taaskäivituseni"
}
}

View File

@ -0,0 +1,62 @@
{
"meta_info": {
"language_name": "فارسی",
"language_name_en": "Persian"
},
"app_info": {
"description": "Safe Eyes .با یاد آوری شما برای استراحت کردن، وقتی ساعت های طولانی را در پشت کامپیوتر سپری میکنید. از تضعیف چشم های شما جلوگیری میکند "
},
"exercises": {
"short_break_close_eyes": "چشم هایتان را محکم ببندید",
"short_break_roll_eyes": "چشم هایتان را چندین مرتبه به طرفین بچرخانید",
"short_break_rotate_clockwise": "چشم هایتان را در جهت عقربه های ساعت بچرخانید",
"short_break_rotate_counter_clockwise": "چشم هایتان را در خلاف جهت عقربه های ساعت بچرخانید",
"short_break_blink": "پلک بزنید",
"short_break_focus_far_distance": "روی یک نقطه در دور دست تمرکز کنید",
"short_break_drink_water": "مقداری آب بنوشید",
"long_break_walk": "مدتی قدم بزنید",
"long_break_lean_back": "به صندلی تکیه داده و راحت باشید"
},
"messages": {
"audible_alert_disabled": "Cannot enable audible notifications",
"ready_for_a_short_break": "آماده استراحت در {} ثانیه بعد باشید",
"ready_for_a_long_break": "آماده استراحت در {} ثانیه بعد باشید",
"disabled_until_restart": "غیرفعال تا بارگذاری مجدد",
"disabled_until_x": "غیرفعال تا {}",
"next_break_at": "استراحت بعد در {}",
"software_required": "To enable this feature, please install {}"
},
"ui_controls": {
"about": "درباره",
"allow_postpone": "اجازه به تعویق انداختن استراحت",
"audible_alert": "هشدار همراه با صدا در پایان استراحت",
"cancel": "لغو",
"close": "بستن",
"disable": "غیر فعال کردن Safe Eyes",
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
"enable": "فعال کردن Safe Eyes",
"enable_screen_lock": "قفل کردن صفحه بعد از هر استراحت طولانی",
"for_x_hour": "برای {} ساعت",
"for_x_hours": "برای {} ساعت",
"for_x_minutes": "برای {} دقیقه",
"idle_time": "حداقل زمان بیکاری برای توقف کردن برنامه (‌به دقیقه)",
"interval_between_two_breaks": "فاصله بین دو استراحت (به ثانیه‌)",
"language": "زبان",
"license": "لایسنس",
"long_break_duration": "زمان استراحت بلند مدت (به دقیقه)",
"no_of_short_breaks_between_two_long_breaks": "تعداد استراحت های کوتاه بین هر دو استراحت بلند",
"postpone": "به تعویق انداختن",
"postpone_duration": "زمان به تعویق انداختن (به دقیقه)",
"quit": "خروج",
"save": "ذخیره",
"settings": "تنظیمات",
"short_break_duration": "مدت زمان استراحت کوتاه",
"show_time_in_tray": "نشان دادن زمان استراحت بعدی در بخش Tray",
"skip": "رد کردن",
"strict_break": "استراحت سخت گیرانه (پنهان کردن دکمه رد کردن استراحت)",
"system_language": "زبان سیستم",
"time_to_prepare_for_break": "زمان آماده شدن برای استراحت (به ثانیه)",
"time_to_screen_lock": "حداکثر زمان برای رد کردن، جلوگیری از قفل شدن صفحه (به ثانیه‌)",
"until_restart": "تا زمان بارگذاری مجدد"
}
}

View File

@ -18,10 +18,13 @@
"long_break_lean_back": "Adossez-vous à votre siège et relaxez-vous" "long_break_lean_back": "Adossez-vous à votre siège et relaxez-vous"
}, },
"messages": { "messages": {
"ready_for_a_break": "Préparez-vous à une pause dans {} secondes", "audible_alert_disabled": "Impossible d'activer les notifications sonores",
"ready_for_a_short_break": "Préparez-vous à une pause dans {} secondes",
"ready_for_a_long_break": "Préparez-vous à une pause dans {} secondes",
"disabled_until_restart": "Désactivé jusqu'au redémarrage", "disabled_until_restart": "Désactivé jusqu'au redémarrage",
"disabled_until_x": "Désactivé jusqu'à {}", "disabled_until_x": "Désactivé jusqu'à {}",
"next_break_at": "Prochaine pause à {}" "next_break_at": "Prochaine pause à {}",
"software_required": "Pour activer cette fonction, veuillez installer {}"
}, },
"ui_controls": { "ui_controls": {
"about": "À propos", "about": "À propos",
@ -30,6 +33,7 @@
"cancel": "Annuler", "cancel": "Annuler",
"close": "Fermer", "close": "Fermer",
"disable": "Désactiver Safe Eyes", "disable": "Désactiver Safe Eyes",
"disable_keyboard_shortcut": "Période de désactivation du raccourci pour empêcher d'ignorer involontairement (en secondes)",
"enable": "Activer Safe Eyes", "enable": "Activer Safe Eyes",
"enable_screen_lock": "Verrouiller l'écran après chaque pause longue", "enable_screen_lock": "Verrouiller l'écran après chaque pause longue",
"for_x_hour": "Pendant {} heure", "for_x_hour": "Pendant {} heure",
@ -47,12 +51,12 @@
"save": "Enregistrer", "save": "Enregistrer",
"settings": "Paramètres", "settings": "Paramètres",
"short_break_duration": "Durée d'une pause courte (en secondes)", "short_break_duration": "Durée d'une pause courte (en secondes)",
"show_time_in_tray": "Show the next break time in system tray", "show_time_in_tray": "Afficher la prochaine heure de pause dans la zone de notification ",
"skip": "Ignorer", "skip": "Ignorer",
"strict_break": "Pause stricte (cacher le bouton Ignorer)", "strict_break": "Pause stricte (cacher le bouton Ignorer)",
"system_language": "Langue du système ", "system_language": "Langue du système ",
"time_to_prepare_for_break": "Temps de préparation à une pause (en secondes)", "time_to_prepare_for_break": "Durée de préparation à une pause (en secondes)",
"time_to_screen_lock": "Temps maximal pour ignorer, en passant outre le verrouillage (en secondes)", "time_to_screen_lock": "Durée maximale pour ignorer, en passant outre le verrouillage (en secondes)",
"until_restart": "Jusqu'au redémarrage" "until_restart": "Jusqu'au redémarrage"
} }
} }

View File

@ -18,10 +18,13 @@
"long_break_lean_back": "სავარძლის საზურგეზე გადაწექით და ცოტა დაისვენეთ" "long_break_lean_back": "სავარძლის საზურგეზე გადაწექით და ცოტა დაისვენეთ"
}, },
"messages": { "messages": {
"ready_for_a_break": "მოემზადეთ შესვენებისთვის {} წამში", "audible_alert_disabled": "Cannot enable audible notifications",
"ready_for_a_short_break": "მოემზადეთ შესვენებისთვის {} წამში",
"ready_for_a_long_break": "მოემზადეთ შესვენებისთვის {} წამში",
"disabled_until_restart": "გავაუქმოთ შემდეგ რესტარტამდე", "disabled_until_restart": "გავაუქმოთ შემდეგ რესტარტამდე",
"disabled_until_x": "გაუქმებულია {} მდე", "disabled_until_x": "გაუქმებულია {} მდე",
"next_break_at": "შემდეგი შესვენება {}" "next_break_at": "შემდეგი შესვენება {}",
"software_required": "To enable this feature, please install {}"
}, },
"ui_controls": { "ui_controls": {
"about": "პროგრამის შესახებ", "about": "პროგრამის შესახებ",
@ -30,6 +33,7 @@
"cancel": "უარყოფა", "cancel": "უარყოფა",
"close": "Close", "close": "Close",
"disable": "Safe Eyes გამორთვა", "disable": "Safe Eyes გამორთვა",
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
"enable": "Safe Eyes ჩართვა", "enable": "Safe Eyes ჩართვა",
"enable_screen_lock": "Lock the screen after every long break", "enable_screen_lock": "Lock the screen after every long break",
"for_x_hour": "{} საათით", "for_x_hour": "{} საათით",
@ -47,7 +51,7 @@
"save": "დამახსოვრება", "save": "დამახსოვრება",
"settings": "პარამეტრები", "settings": "პარამეტრები",
"short_break_duration": "მცირე შესვენების ხანგრძლივობა (წამებში)", "short_break_duration": "მცირე შესვენების ხანგრძლივობა (წამებში)",
"show_time_in_tray": "Show the next break time in system tray", "show_time_in_tray": "Show the next break time in system tray",
"skip": "გამოტოვება", "skip": "გამოტოვება",
"strict_break": "აუცილებელი შესვენება (დავმალოთ ღილაკი 'გამოტოვება')", "strict_break": "აუცილებელი შესვენება (დავმალოთ ღილაკი 'გამოტოვება')",
"system_language": "System Language", "system_language": "System Language",

View File

@ -18,10 +18,13 @@
"long_break_lean_back": "पीछे हट कर आराम करें" "long_break_lean_back": "पीछे हट कर आराम करें"
}, },
"messages": { "messages": {
"ready_for_a_break": "{} पलों में आराम", "audible_alert_disabled": "Cannot enable audible notifications",
"ready_for_a_short_break": "{} पलों में आराम",
"ready_for_a_long_break": "{} पलों में आराम",
"disabled_until_restart": "अगले आरंभ तक बंद", "disabled_until_restart": "अगले आरंभ तक बंद",
"disabled_until_x": "{} तक बंद", "disabled_until_x": "{} तक बंद",
"next_break_at": "अगला आराम {} में" "next_break_at": "अगला आराम {} में",
"software_required": "To enable this feature, please install {}"
}, },
"ui_controls": { "ui_controls": {
"about": "हमारे बारे में", "about": "हमारे बारे में",
@ -30,6 +33,7 @@
"cancel": "रखना नहीं", "cancel": "रखना नहीं",
"close": "Close", "close": "Close",
"disable": "सेफ आईज बंद", "disable": "सेफ आईज बंद",
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
"enable": "सेफ आईज शुरू", "enable": "सेफ आईज शुरू",
"enable_screen_lock": "Lock the screen after every long break", "enable_screen_lock": "Lock the screen after every long break",
"for_x_hour": "{} घंटे के लिए", "for_x_hour": "{} घंटे के लिए",

View File

@ -18,10 +18,13 @@
"long_break_lean_back": "Dőljön hátra és pihenjen!" "long_break_lean_back": "Dőljön hátra és pihenjen!"
}, },
"messages": { "messages": {
"ready_for_a_break": "Tervezett szünet {} másodperc múlva!", "audible_alert_disabled": "Cannot enable audible notifications",
"ready_for_a_short_break": "Tervezett szünet {} másodperc múlva!",
"ready_for_a_long_break": "Tervezett szünet {} másodperc múlva!",
"disabled_until_restart": "Disabled until restart", "disabled_until_restart": "Disabled until restart",
"disabled_until_x": "Disabled until {}", "disabled_until_x": "Disabled until {}",
"next_break_at": "A következő szünet {}" "next_break_at": "A következő szünet {}",
"software_required": "To enable this feature, please install {}"
}, },
"ui_controls": { "ui_controls": {
"about": "Ról ről", "about": "Ról ről",
@ -30,6 +33,7 @@
"cancel": "Mégse", "cancel": "Mégse",
"close": "Close", "close": "Close",
"disable": "Disable Safe Eyes", "disable": "Disable Safe Eyes",
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
"enable": "Safe Eyes Bekapcsolása", "enable": "Safe Eyes Bekapcsolása",
"enable_screen_lock": "Lock the screen after every long break", "enable_screen_lock": "Lock the screen after every long break",
"for_x_hour": "For {} Hour", "for_x_hour": "For {} Hour",

View File

@ -18,10 +18,13 @@
"long_break_lean_back": "Silakan bersandar ke kursi dan bersantai" "long_break_lean_back": "Silakan bersandar ke kursi dan bersantai"
}, },
"messages": { "messages": {
"ready_for_a_break": "Bersiap beristirahat dalam {} detik", "audible_alert_disabled": "Cannot enable audible notifications",
"ready_for_a_short_break": "Bersiap beristirahat dalam {} detik",
"ready_for_a_long_break": "Bersiap beristirahat dalam {} detik",
"disabled_until_restart": "Dimatikan hingga dijalankan ulang", "disabled_until_restart": "Dimatikan hingga dijalankan ulang",
"disabled_until_x": "Dimatikan hingga {}", "disabled_until_x": "Dimatikan hingga {}",
"next_break_at": "Istirahat selanjutnya pada {}" "next_break_at": "Istirahat selanjutnya pada {}",
"software_required": "To enable this feature, please install {}"
}, },
"ui_controls": { "ui_controls": {
"about": "Tentang", "about": "Tentang",
@ -30,6 +33,7 @@
"cancel": "Batal", "cancel": "Batal",
"close": "Close", "close": "Close",
"disable": "Matikan Safe Eyes", "disable": "Matikan Safe Eyes",
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
"enable": "Hidupkan Safe Eyes", "enable": "Hidupkan Safe Eyes",
"enable_screen_lock": "Lock the screen after every long break", "enable_screen_lock": "Lock the screen after every long break",
"for_x_hour": "Selama {} Jam", "for_x_hour": "Selama {} Jam",

View File

@ -18,10 +18,13 @@
"long_break_lean_back": "Наслонете се на столот и одморете" "long_break_lean_back": "Наслонете се на столот и одморете"
}, },
"messages": { "messages": {
"ready_for_a_break": "Пауза за {} секунди", "audible_alert_disabled": "Cannot enable audible notifications",
"ready_for_a_short_break": "Пауза за {} секунди",
"ready_for_a_long_break": "Пауза за {} секунди",
"disabled_until_restart": "Оневозможено до рестарт", "disabled_until_restart": "Оневозможено до рестарт",
"disabled_until_x": "Оневозможено до {}", "disabled_until_x": "Оневозможено до {}",
"next_break_at": "Следна пауза во {}" "next_break_at": "Следна пауза во {}",
"software_required": "To enable this feature, please install {}"
}, },
"ui_controls": { "ui_controls": {
"about": "За", "about": "За",
@ -30,6 +33,7 @@
"cancel": "Откажи", "cancel": "Откажи",
"close": "Затвори", "close": "Затвори",
"disable": "Оневозможете го Safe Eyes", "disable": "Оневозможете го Safe Eyes",
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
"enable": "Овозможете го Safe Eyes", "enable": "Овозможете го Safe Eyes",
"enable_screen_lock": "Заклучување на екранот по секоја долга пауза", "enable_screen_lock": "Заклучување на екранот по секоја долга пауза",
"for_x_hour": "За {} час", "for_x_hour": "За {} час",

View File

@ -18,10 +18,13 @@
"long_break_lean_back": "Oprzyj się wygodnie na krześle i zrelaksuj" "long_break_lean_back": "Oprzyj się wygodnie na krześle i zrelaksuj"
}, },
"messages": { "messages": {
"ready_for_a_break": "Przygotuj się! Przerwa za {} sekund(y).", "audible_alert_disabled": "Nie można aktywować powiadomień dźwiękowych",
"ready_for_a_short_break": "Przygotuj się! Przerwa za {} sekund(y).",
"ready_for_a_long_break": "Przygotuj się! Przerwa za {} sekund(y).",
"disabled_until_restart": "Wyłączony do ponownego uruchomienia", "disabled_until_restart": "Wyłączony do ponownego uruchomienia",
"disabled_until_x": "Wyłączony do {}", "disabled_until_x": "Wyłączony do {}",
"next_break_at": "Następna przerwa o {}" "next_break_at": "Następna przerwa o {}",
"software_required": "Aby aktywować tę funkcję, zainstaluj {}"
}, },
"ui_controls": { "ui_controls": {
"about": "O programie", "about": "O programie",
@ -30,6 +33,7 @@
"cancel": "Anuluj", "cancel": "Anuluj",
"close": "Zamknij", "close": "Zamknij",
"disable": "Zatrzymaj Safe Eyes", "disable": "Zatrzymaj Safe Eyes",
"disable_keyboard_shortcut": "Czas ignorowania skrótu klawiszowego zapobiegający niezamierzonemu pominięciu przerwy (w sekundach)",
"enable": "Uruchom Safe Eyes", "enable": "Uruchom Safe Eyes",
"enable_screen_lock": "Zablokuj ekran po każdej długiej przerwie", "enable_screen_lock": "Zablokuj ekran po każdej długiej przerwie",
"for_x_hour": "Na {} godzinę", "for_x_hour": "Na {} godzinę",
@ -47,7 +51,7 @@
"save": "Zapisz", "save": "Zapisz",
"settings": "Ustawienia", "settings": "Ustawienia",
"short_break_duration": "Czas trwania krótkiej przerwy (w sekundach)", "short_break_duration": "Czas trwania krótkiej przerwy (w sekundach)",
"show_time_in_tray": "Show the next break time in system tray", "show_time_in_tray": "Pokaż czas następnej przerwy w zasobniku systemowym",
"skip": "Pomiń", "skip": "Pomiń",
"strict_break": "Bezwzględna przerwa (ukryj przycisk pominięcia)", "strict_break": "Bezwzględna przerwa (ukryj przycisk pominięcia)",
"system_language": "Język systemu", "system_language": "Język systemu",

View File

@ -18,10 +18,13 @@
"long_break_lean_back": "Encoste na cadeira e relaxe" "long_break_lean_back": "Encoste na cadeira e relaxe"
}, },
"messages": { "messages": {
"ready_for_a_break": "Pronto para uma pausa em {} segundos", "audible_alert_disabled": "Cannot enable audible notifications",
"ready_for_a_short_break": "Pronto para uma pausa em {} segundos",
"ready_for_a_long_break": "Pronto para uma pausa em {} segundos",
"disabled_until_restart": "Desativado até reiniciar", "disabled_until_restart": "Desativado até reiniciar",
"disabled_until_x": "Desativado até {}", "disabled_until_x": "Desativado até {}",
"next_break_at": "Próxima pausa em {}" "next_break_at": "Próxima pausa em {}",
"software_required": "To enable this feature, please install {}"
}, },
"ui_controls": { "ui_controls": {
"about": "Sobre", "about": "Sobre",
@ -30,6 +33,7 @@
"cancel": "Cancelar", "cancel": "Cancelar",
"close": "Fechar", "close": "Fechar",
"disable": "Desativar Safe Eyes", "disable": "Desativar Safe Eyes",
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
"enable": "Habilitar Safe Eyes", "enable": "Habilitar Safe Eyes",
"enable_screen_lock": "Bloqueie a tela após cada pausa longa", "enable_screen_lock": "Bloqueie a tela após cada pausa longa",
"for_x_hour": "Por {} Hora", "for_x_hour": "Por {} Hora",

View File

@ -18,10 +18,13 @@
"long_break_lean_back": "Откиньтесь на спинку стула и расслабьтесь" "long_break_lean_back": "Откиньтесь на спинку стула и расслабьтесь"
}, },
"messages": { "messages": {
"ready_for_a_break": "Приготовьтесь к перерыву через {} секунд", "audible_alert_disabled": "Cannot enable audible notifications",
"ready_for_a_short_break": "Приготовьтесь к перерыву через {} секунд",
"ready_for_a_long_break": "Приготовьтесь к перерыву через {} секунд",
"disabled_until_restart": "Отключено до перезагрузки", "disabled_until_restart": "Отключено до перезагрузки",
"disabled_until_x": "Отключено до {}", "disabled_until_x": "Отключено до {}",
"next_break_at": "Следующий перерыв в {}" "next_break_at": "Следующий перерыв в {}",
"software_required": "To enable this feature, please install {}"
}, },
"ui_controls": { "ui_controls": {
"about": "О программе", "about": "О программе",
@ -30,6 +33,7 @@
"cancel": "Отменить", "cancel": "Отменить",
"close": "Close", "close": "Close",
"disable": "Отключить Safe Eyes", "disable": "Отключить Safe Eyes",
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
"enable": "Активировать Safe Eyes", "enable": "Активировать Safe Eyes",
"enable_screen_lock": "Включить блокировку экрана", "enable_screen_lock": "Включить блокировку экрана",
"for_x_hour": "На {} час", "for_x_hour": "На {} час",
@ -47,7 +51,7 @@
"save": "Сохранить", "save": "Сохранить",
"settings": "Настройки", "settings": "Настройки",
"short_break_duration": "Продолжительность короткого перерыва (в секундах)", "short_break_duration": "Продолжительность короткого перерыва (в секундах)",
"show_time_in_tray": "Show the next break time in system tray", "show_time_in_tray": "Show the next break time in system tray",
"skip": "Пропустить", "skip": "Пропустить",
"strict_break": "Обязательный перерыв (Скрыть кнопку 'Пропустить')", "strict_break": "Обязательный перерыв (Скрыть кнопку 'Пропустить')",
"system_language": "System Language", "system_language": "System Language",

View File

@ -18,18 +18,22 @@
"long_break_lean_back": "கதிைரயில் பின்பக்கமாக சாய்ந்து ஓய்வெடுங்கள்" "long_break_lean_back": "கதிைரயில் பின்பக்கமாக சாய்ந்து ஓய்வெடுங்கள்"
}, },
"messages": { "messages": {
"ready_for_a_break": "{} விநாடிகளில் இடைவேளைக்கு தயாராகுங்கள்", "audible_alert_disabled": "ஒலிச் சமிக்ஞையை செயற்படுத்த முடியாதுள்ளது",
"ready_for_a_short_break": "{} விநாடிகளில் குறுகிய இடைவேளைக்கு தயாராகுங்கள்",
"ready_for_a_long_break": "{} விநாடிகளில் நீண்ட இடைவேளைக்கு தயாராகுங்கள்",
"disabled_until_restart": "மறுதுவக்கம் வரை நிறுத்தி வைக்கப்பட்டுள்ளது", "disabled_until_restart": "மறுதுவக்கம் வரை நிறுத்தி வைக்கப்பட்டுள்ளது",
"disabled_until_x": "{} வரை நிறுத்தி வைக்கப்பட்டுள்ளது", "disabled_until_x": "{} வரை நிறுத்தி வைக்கப்பட்டுள்ளது",
"next_break_at": "அடுத்த இடைவேளை {}" "next_break_at": "அடுத்த இடைவேளை {}",
"software_required": "இந்த வசதியை செயற்படுத்த {} ஐ நிறுவவும்"
}, },
"ui_controls": { "ui_controls": {
"about": "Safe Eyes குறித்த தகவல்கள்", "about": "Safe Eyes குறித்த தகவல்கள்",
"allow_postpone": "ஒத்திவைக்க அனுமதிக்கவும்", "allow_postpone": "ஒத்திவைக்க அனுமதிக்கவும்",
"audible_alert": "இடைவேளையின் இறுதியில் ஒலி சமிக்ை", "audible_alert": "இடைவேளையின் இறுதியில் ஒலி சமிக்ை",
"cancel": "ரத்து", "cancel": "ரத்து",
"close": "மூடு", "close": "மூடு",
"disable": "Safe Eyes ஐ நிறுத்துக", "disable": "Safe Eyes ஐ நிறுத்துக",
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
"enable": "Safe Eyes ஐ செயல்படுத்துக", "enable": "Safe Eyes ஐ செயல்படுத்துக",
"enable_screen_lock": "நீண்ட கால இடைவேளைகளின் பின்னர் திரையை பூட்டுக", "enable_screen_lock": "நீண்ட கால இடைவேளைகளின் பின்னர் திரையை பூட்டுக",
"for_x_hour": "{} மணித்தியாலத்திற்கு", "for_x_hour": "{} மணித்தியாலத்திற்கு",

View File

@ -18,10 +18,13 @@
"long_break_lean_back": "Arkanıza yaslanın ve biraz gevşeyin" "long_break_lean_back": "Arkanıza yaslanın ve biraz gevşeyin"
}, },
"messages": { "messages": {
"ready_for_a_break": "{} Saniye içerisinde bir molaya hazır olun", "audible_alert_disabled": "Cannot enable audible notifications",
"ready_for_a_short_break": "{} Saniye içerisinde bir molaya hazır olun",
"ready_for_a_long_break": "{} Saniye içerisinde bir molaya hazır olun",
"disabled_until_restart": "Tekrar başlatılana kadar devre dışı", "disabled_until_restart": "Tekrar başlatılana kadar devre dışı",
"disabled_until_x": "{}'e kadar devre dışı", "disabled_until_x": "{}'e kadar devre dışı",
"next_break_at": "Bir sonraki mola zamanı: {}" "next_break_at": "Bir sonraki mola zamanı: {}",
"software_required": "Bu özelliği etkinleştirmek için lütfen {} yükleyiniz."
}, },
"ui_controls": { "ui_controls": {
"about": "Hakkında", "about": "Hakkında",
@ -30,6 +33,7 @@
"cancel": "İptal", "cancel": "İptal",
"close": "Kapat", "close": "Kapat",
"disable": "Safe Eyes'ı devre dışı bırak", "disable": "Safe Eyes'ı devre dışı bırak",
"disable_keyboard_shortcut": "İstem dışı geçişi engellemek için kısayol tuşunun etkisiz kalma süresi (saniye)",
"enable": "Safe Eyes'ı etkinleştir", "enable": "Safe Eyes'ı etkinleştir",
"enable_screen_lock": "Her uzun mola sonunda ekranı kilitle", "enable_screen_lock": "Her uzun mola sonunda ekranı kilitle",
"for_x_hour": "{} Saat", "for_x_hour": "{} Saat",

View File

@ -0,0 +1,62 @@
{
"meta_info": {
"language_name": "Українська",
"language_name_en": "Ukrainian"
},
"app_info": {
"description": "Safe Eyes нагадує вам робити перерви, при довгій роботі за комп'ютером, захищаючи ваші очі від втоми (asthenopia)."
},
"exercises": {
"short_break_close_eyes": "Сильно заплющіть очі",
"short_break_roll_eyes": "Закотіть очі в різні боки",
"short_break_rotate_clockwise": "Покрутіть очима за годинниковою стрілкою",
"short_break_rotate_counter_clockwise": "Покрутіть очима проти годинникової стрілки",
"short_break_blink": "Поморгайте",
"short_break_focus_far_distance": "Подивіться на далекий об'єкт",
"short_break_drink_water": "Випийте води",
"long_break_walk": "Трохи пройдіться",
"long_break_lean_back": "Відкиньтеся на спинку і розслабтеся"
},
"messages": {
"audible_alert_disabled": "Cannot enable audible notifications",
"ready_for_a_short_break": "Приготуйтеся до перерви через {} секунд",
"ready_for_a_long_break": "Приготуйтеся до перерви через {} секунд",
"disabled_until_restart": "Відключено до перезапуску",
"disabled_until_x": "Відключено до {}",
"next_break_at": "Наступна перерва о {}",
"software_required": "To enable this feature, please install {}"
},
"ui_controls": {
"about": "Про SafeEyes",
"allow_postpone": "Дозволити відкладати перерви",
"audible_alert": "Звуковий сигнал вкінці перерви",
"cancel": "Відмінити",
"close": "Закрити",
"disable": "Відключити Safe Eyes",
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
"enable": "Включити Safe Eyes",
"enable_screen_lock": "Блокувати екран після довгих перерв",
"for_x_hour": "На {} годину",
"for_x_hours": "На {} години",
"for_x_minutes": "На {} хвилин",
"idle_time": "Час бездіяльності для паузи (в хвилинах)",
"interval_between_two_breaks": "Інтервал між двома перервами (в хвилинах)",
"language": "Мова",
"license": "Ліцензія",
"long_break_duration": "Тривалість довгої перерви (в секундах)",
"no_of_short_breaks_between_two_long_breaks": "Кількість коротких перерв між двома довгими",
"postpone": "Відкласти",
"postpone_duration": "На скільки відкласти (в хвилинах)",
"quit": "Вийти",
"save": "Зберегти",
"settings": "Налаштування",
"short_break_duration": "Тривалість короткої перерви (в секундах)",
"show_time_in_tray": "Показувати час наступної перерви в системному треї",
"skip": "Пропустити",
"strict_break": "Обов'язкова перерва (прибрати кнопку для пропускання)",
"system_language": "Мова системи",
"time_to_prepare_for_break": "Час підготовки до перерви (в секундах)",
"time_to_screen_lock": "Найбільший час для пропуску, без блокування екрану (в секундах)",
"until_restart": "До перезапуску"
}
}

View File

@ -22,10 +22,13 @@
}, },
"messages": "messages":
{ {
"ready_for_a_break": "Sẵn sàng để nghỉ ngơi {} giây", "audible_alert_disabled": "Không thể bật thông báo âm thanh",
"ready_for_a_short_break": "Sẵn sàng để nghỉ ngơi {} giây",
"ready_for_a_long_break": "Sẵn sàng để nghỉ ngơi {} giây",
"disabled_until_restart": "Vô hiệu hóa cho đến khi khởi động lại", "disabled_until_restart": "Vô hiệu hóa cho đến khi khởi động lại",
"disabled_until_x": "Vô hiệu hóa cho đến khi {}", "disabled_until_x": "Vô hiệu hóa cho đến khi {}",
"next_break_at": "Giờ nghỉ ngơi tiếp theo là {}" "next_break_at": "Giờ nghỉ ngơi tiếp theo là {}",
"software_required": "Để bật tính năng này, vui lòng cài đặt {}"
}, },
"ui_controls": "ui_controls":
{ {
@ -35,12 +38,13 @@
"cancel": "Huỷ", "cancel": "Huỷ",
"close": "Đóng", "close": "Đóng",
"disable": "Tắt Safe Eyes", "disable": "Tắt Safe Eyes",
"disable_keyboard_shortcut": "Th.gian vô hiệu hóa phím tắt để tránh tắt nhầm Nhắc nhở khi nó hiện lên (giây)",
"enable": "Bật Safe Eyes", "enable": "Bật Safe Eyes",
"enable_screen_lock": "Khoá màn hình ở mỗi thời điểm nghỉ ngơi dài", "enable_screen_lock": "Khoá màn hình ở mỗi thời điểm nghỉ ngơi dài",
"for_x_hour": "Trong {} giờ", "for_x_hour": "Trong {} giờ",
"for_x_hours": "Trong {} giờ", "for_x_hours": "Trong {} giờ",
"for_x_minutes": "Trong {} phút", "for_x_minutes": "Trong {} phút",
"idle_time": "Minimum idle time to pause (tính bằng phút)", "idle_time": "Thơi gian rỗi nhỏ nhất để tạm dừng (tính bằng phút)",
"interval_between_two_breaks": "Khoảng thời gian giữa hai lần nghỉ ngơi (tính bằng phút)", "interval_between_two_breaks": "Khoảng thời gian giữa hai lần nghỉ ngơi (tính bằng phút)",
"language": "Language (Ngôn ngữ)", "language": "Language (Ngôn ngữ)",
"license": "Giấy phép", "license": "Giấy phép",

View File

@ -10,7 +10,11 @@
"pre_break_warning_time": 10, "pre_break_warning_time": 10,
"short_break_duration": 15, "short_break_duration": 15,
"idle_time": 5, "idle_time": 5,
"persist_state": true,
"postpone_duration": 5, "postpone_duration": 5,
"shortcut_disable_time": 2,
"shortcut_skip": 9,
"shortcut_postpone": 65,
"show_time_in_tray": false, "show_time_in_tray": false,
"strict_break": false, "strict_break": false,
"audible_alert": false, "audible_alert": false,

View File

@ -52,6 +52,12 @@
<property name="step_increment">1</property> <property name="step_increment">1</property>
<property name="page_increment">5</property> <property name="page_increment">5</property>
</object> </object>
<object class="GtkAdjustment" id="adjust_disable_keyboard_shortcut_duration">
<property name="lower">0</property>
<property name="upper">15</property>
<property name="step_increment">1</property>
<property name="page_increment">5</property>
</object>
<object class="GtkAdjustment" id="adjust_short_break_duration"> <object class="GtkAdjustment" id="adjust_short_break_duration">
<property name="lower">1</property> <property name="lower">1</property>
<property name="upper">60</property> <property name="upper">60</property>
@ -189,7 +195,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">9</property> <property name="top_attach">10</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -202,7 +208,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">6</property> <property name="top_attach">7</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -215,7 +221,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">7</property> <property name="top_attach">8</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -346,7 +352,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="top_attach">9</property> <property name="top_attach">10</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -358,7 +364,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="top_attach">6</property> <property name="top_attach">7</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -370,7 +376,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="top_attach">7</property> <property name="top_attach">8</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -382,7 +388,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="top_attach">10</property> <property name="top_attach">11</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -395,7 +401,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">10</property> <property name="top_attach">11</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -405,7 +411,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="top_attach">13</property> <property name="top_attach">14</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -418,7 +424,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">13</property> <property name="top_attach">14</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -431,7 +437,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">11</property> <property name="top_attach">12</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -442,7 +448,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="top_attach">11</property> <property name="top_attach">12</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -455,7 +461,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">12</property> <property name="top_attach">13</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -474,7 +480,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="top_attach">12</property> <property name="top_attach">13</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -487,7 +493,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">8</property> <property name="top_attach">9</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -498,14 +504,40 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="top_attach">8</property> <property name="top_attach">9</property>
</packing> </packing>
</child> </child>
<child> <child>
<placeholder/> <object class="GtkLabel" id="lbl_disable_keyboard_shortcut">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">center</property>
<property name="label" translatable="yes">Disable keyboard shortcut</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
</packing>
</child> </child>
<child> <child>
<placeholder/> <object class="GtkSpinButton" id="spin_disable_keyboard_shortcut">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="halign">end</property>
<property name="valign">center</property>
<property name="text" translatable="yes">1</property>
<property name="input_purpose">number</property>
<property name="adjustment">adjust_disable_keyboard_shortcut_duration</property>
<property name="climb_rate">1</property>
<property name="numeric">True</property>
<property name="update_policy">if-valid</property>
<property name="value">1</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">6</property>
</packing>
</child> </child>
</object> </object>
<packing> <packing>

View File

@ -1,13 +1,15 @@
import os import os
import setuptools import setuptools
requires = [ requires = [
'python-xlib', 'python-xlib',
'pyaudio', 'psutil',
'psutil', 'babel']
'babel']
extras = {
'audible_alert': ['pyaudio']
}
here = os.path.abspath(os.path.dirname(__file__)) here = os.path.abspath(os.path.dirname(__file__))
@ -15,21 +17,23 @@ here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'README.md')) as f: with open(os.path.join(here, 'README.md')) as f:
long_description = '\n' + f.read() long_description = '\n' + f.read()
def _data_files(path): def _data_files(path):
for root, dirs, files in os.walk(path): for root, dirs, files in os.walk(path):
if not files: if not files:
continue continue
yield (os.path.join('/usr', root), [os.path.join(root, f) for f in files]) yield (os.path.join('/usr', root), [os.path.join(root, f) for f in files])
setuptools.setup( setuptools.setup(
name="safeeyes", name="safeeyes",
version="1.2.1", version="1.2.2",
description="Protect your eyes from eye strain using this continuous breaks reminder.", description="Protect your eyes from eye strain using this continuous breaks reminder.",
long_description=long_description, long_description=long_description,
author="Gobinath Loganathan", author="Gobinath Loganathan",
author_email="slgobinath@gmail.com", author_email="slgobinath@gmail.com",
url="https://github.com/slgobinath/SafeEyes", url="https://github.com/slgobinath/SafeEyes",
download_url="https://github.com/slgobinath/SafeEyes/archive/v1.2.1.tar.gz", download_url="https://github.com/slgobinath/SafeEyes/archive/v1.2.2.tar.gz",
packages=setuptools.find_packages(), packages=setuptools.find_packages(),
package_data={'safeeyes': ['config/*.json', package_data={'safeeyes': ['config/*.json',
'config/style/*.css', 'config/style/*.css',
@ -49,6 +53,7 @@ setuptools.setup(
('/usr/share/icons/hicolor/16x16/status', ['share/icons/hicolor/16x16/status/safeeyes_enabled.png', 'share/icons/hicolor/16x16/status/safeeyes_disabled.png', 'share/icons/hicolor/16x16/status/safeeyes_timer.png']) ('/usr/share/icons/hicolor/16x16/status', ['share/icons/hicolor/16x16/status/safeeyes_enabled.png', 'share/icons/hicolor/16x16/status/safeeyes_disabled.png', 'share/icons/hicolor/16x16/status/safeeyes_timer.png'])
], ],
install_requires=requires, install_requires=requires,
extras_require=extras,
entry_points={'console_scripts': ['safeeyes = safeeyes.__main__:main']}, entry_points={'console_scripts': ['safeeyes = safeeyes.__main__:main']},
keywords='linux utility health eye-strain safe-eyes', keywords='linux utility health eye-strain safe-eyes',
classifiers=[ classifiers=[

View File

@ -1,19 +1,25 @@
[Desktop Entry] [Desktop Entry]
Name=Safe Eyes Name=Safe Eyes
Comment=Protect your eyes from eye strain Comment=Protect your eyes from eye strain
Comment[ge]=დაიცავით თქვენი თვალები დაღლილობისაგან Comment[ca]=Protegiu-vos els ulls de la fatiga visual
Comment[de]=Schützt die Augen vor Überanstrengung
Comment[cs]=Chraňte své oči před únavou Comment[cs]=Chraňte své oči před únavou
Comment[fr]=Protégez vos yeux de la fatigue Comment[de]=Schützt die Augen vor Überanstrengung
Comment[id]=Melindungi mata Anda dari kelelahan
Comment[ta]=உங்கள் கண்களை சோர்வடையாது பாதுகாத்திடுங்கள்
Comment[pt]=Proteja seus olhos da tensão ocular
Comment[tr]=Gözünüzü yorgunluğa karşı koruyun
Comment[hi]=तनाव से आंखों की रक्षा
Comment[es]=Protege tus ojos de la fatiga ocular Comment[es]=Protege tus ojos de la fatiga ocular
Comment[ru]=Защитите свои глаза от зрительного перенапряжения Comment[et]=Kaitse oma silmi väsimuse eest
Comment[pl]=Chroń oczy przed zmęczeniem Comment[fa]=محافظت چشم هااز ضعیف شدن
Comment[fr]=Protégez vos yeux de la fatigue
Comment[ge]=დაიცავით თქვენი თვალები დაღლილობისაგან
Comment[hi]=तनाव से आंखों की रक्षा
Comment[hu]=Protect your eyes from eye strain
Comment[id]=Melindungi mata Anda dari kelelahan
Comment[mk]=Заштитете се од замор на очите Comment[mk]=Заштитете се од замор на очите
Comment[pl]=Chroń oczy przed zmęczeniem
Comment[pt]=Proteja seus olhos da tensão ocular
Comment[ru]=Защитите свои глаза от зрительного перенапряжения
Comment[sk]=Chráňte svoje oči pred únavou
Comment[ta]=உங்கள் கண்களை சோர்வடையாது பாதுகாத்திடுங்கள்
Comment[tr]=Gözünüzü yorgunluğa karşı koruyun
Comment[uk]=Захистіть свої очі від втоми
Comment[vi]=Bảo vệ đôi mắt của bạn khỏi sự mệt mỏi Comment[vi]=Bảo vệ đôi mắt của bạn khỏi sự mệt mỏi
Exec=env GDK_BACKEND=x11 safeeyes Exec=env GDK_BACKEND=x11 safeeyes
Icon=safeeyes Icon=safeeyes