Merge branch 'master' into master
This commit is contained in:
commit
aab1d33309
|
@ -53,21 +53,26 @@ Optional Features:
|
|||
For more details: [SafeEyes Features](http://slgobinath.github.io/SafeEyes/#features)
|
||||
|
||||
## 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)
|
||||
* [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)
|
||||
* [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)
|
||||
* [ქართული](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/ge.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)
|
||||
* [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)
|
||||
* [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)
|
||||
* [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)
|
||||
* [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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -18,18 +18,15 @@
|
|||
|
||||
import gi
|
||||
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.
|
||||
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):
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(glade_file)
|
||||
|
@ -42,23 +39,20 @@ class AboutDialog:
|
|||
# Set the version at the runtime
|
||||
builder.get_object("lbl_app_name").set_label("Safe Eyes " + version)
|
||||
|
||||
|
||||
"""
|
||||
Show the About dialog.
|
||||
"""
|
||||
def show(self):
|
||||
"""
|
||||
Show the About dialog.
|
||||
"""
|
||||
self.window.show_all()
|
||||
|
||||
|
||||
"""
|
||||
Window close event handler.
|
||||
"""
|
||||
def on_window_delete(self, *args):
|
||||
"""
|
||||
Window close event handler.
|
||||
"""
|
||||
self.window.destroy()
|
||||
|
||||
|
||||
"""
|
||||
Close button click event handler.
|
||||
"""
|
||||
def on_close_clicked(self, button):
|
||||
"""
|
||||
Close button click event handler.
|
||||
"""
|
||||
self.window.destroy()
|
||||
|
|
|
@ -16,104 +16,114 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import gi, signal, sys, threading, logging
|
||||
from Xlib import Xatom, Xutil
|
||||
import gi, threading, logging, time
|
||||
from Xlib.display import Display, X
|
||||
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:
|
||||
"""
|
||||
The fullscreen window which prevents users from using the computer.
|
||||
This class reads the break_screen.glade and build the user interface.
|
||||
"""
|
||||
|
||||
"""
|
||||
Read the break_screen.glade and build the user interface.
|
||||
"""
|
||||
def __init__(self, on_skip, on_postpone, glade_file, style_sheet_path):
|
||||
def __init__(self, context, on_skip, on_postpone, glade_file, style_sheet_path):
|
||||
self.context = context
|
||||
self.on_skip = on_skip
|
||||
self.on_postpone = on_postpone
|
||||
self.is_pretified = False
|
||||
self.key_lock_condition = threading.Condition()
|
||||
self.windows = []
|
||||
self.count_labels = []
|
||||
self.glade_file = glade_file
|
||||
self.enable_shortcut = False
|
||||
self.display = Display()
|
||||
|
||||
# Initialize the theme
|
||||
css_provider = Gtk.CssProvider()
|
||||
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)
|
||||
|
||||
|
||||
"""
|
||||
Initialize the internal properties from configuration
|
||||
"""
|
||||
def initialize(self, config, language):
|
||||
"""
|
||||
Initialize the internal properties from configuration
|
||||
"""
|
||||
logging.info("Initialize the break screen")
|
||||
self.skip_button_text = language['ui_controls']['skip']
|
||||
self.postpone_button_text = language['ui_controls']['postpone']
|
||||
self.strict_break = config.get('strict_break', 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)
|
||||
|
||||
|
||||
"""
|
||||
Window close event handler.
|
||||
"""
|
||||
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):
|
||||
def skip_break(self):
|
||||
"""
|
||||
Skip the break from the break screen
|
||||
"""
|
||||
logging.info("User skipped the break")
|
||||
# Must call on_skip before close to lock screen before closing the break screen
|
||||
self.on_skip()
|
||||
self.close()
|
||||
|
||||
|
||||
"""
|
||||
Postpone button press event handler.
|
||||
"""
|
||||
def on_postpone_clicked(self, button):
|
||||
def postpone_break(self):
|
||||
"""
|
||||
Postpone the break from the break screen
|
||||
"""
|
||||
logging.info("User postponed the break")
|
||||
self.on_postpone()
|
||||
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.
|
||||
"""
|
||||
def show_count_down(self, count):
|
||||
GLib.idle_add(lambda: self.__update_count_down(count))
|
||||
"""
|
||||
self.enable_shortcut = not self.strict_break and self.shortcut_disable_time <= count_down
|
||||
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):
|
||||
"""
|
||||
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))
|
||||
|
||||
|
||||
"""
|
||||
Hide the break screen from active window and destroy all other windows
|
||||
"""
|
||||
def close(self):
|
||||
"""
|
||||
Hide the break screen from active window and destroy all other windows
|
||||
"""
|
||||
logging.info("Close the break screen(s)")
|
||||
self.__release_keyboard()
|
||||
|
||||
# Destroy other windows if exists
|
||||
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):
|
||||
"""
|
||||
Show an empty break screen on all screens.
|
||||
"""
|
||||
# Lock the keyboard
|
||||
thread = threading.Thread(target=self.__lock_keyboard)
|
||||
thread.start()
|
||||
|
@ -156,72 +166,76 @@ class BreakScreen:
|
|||
btn_skip.set_visible(True)
|
||||
box_buttons.pack_start(btn_skip, True, True, 0)
|
||||
|
||||
|
||||
|
||||
# Set values
|
||||
if image_path:
|
||||
img_break.set_from_file(image_path)
|
||||
lbl_message.set_label(message)
|
||||
lbl_left.set_markup(plugins_data['left']);
|
||||
lbl_right.set_markup(plugins_data['right']);
|
||||
lbl_left.set_markup(plugins_data['left'])
|
||||
lbl_right.set_markup(plugins_data['right'])
|
||||
|
||||
self.windows.append(window)
|
||||
self.count_labels.append(lbl_count)
|
||||
|
||||
# Set visual to apply css theme. It should be called before show method.
|
||||
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.set_keep_above(True)
|
||||
window.present()
|
||||
window.move(x, y)
|
||||
window.fullscreen()
|
||||
|
||||
|
||||
"""
|
||||
Update the countdown on all break screens.
|
||||
"""
|
||||
def __update_count_down(self, count):
|
||||
"""
|
||||
Update the countdown on all break screens.
|
||||
"""
|
||||
for label in self.count_labels:
|
||||
label.set_text(count)
|
||||
|
||||
|
||||
"""
|
||||
Lock the keyboard to prevent the user from using keyboard shortcuts
|
||||
"""
|
||||
def __lock_keyboard(self):
|
||||
"""
|
||||
Lock the keyboard to prevent the user from using keyboard shortcuts
|
||||
"""
|
||||
logging.info("Lock the keyboard")
|
||||
self.lock_keyboard = True
|
||||
display = Display()
|
||||
root = display.screen().root
|
||||
# Grap the keyboard
|
||||
root.grab_keyboard(owner_events = False, pointer_mode = X.GrabModeAsync, keyboard_mode = X.GrabModeAsync, time = X.CurrentTime)
|
||||
|
||||
# Grab the keyboard
|
||||
root = self.display.screen().root
|
||||
root.change_attributes(event_mask=X.KeyPressMask | X.KeyReleaseMask)
|
||||
root.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime)
|
||||
|
||||
# Consume keyboard events
|
||||
self.key_lock_condition.acquire()
|
||||
while self.lock_keyboard:
|
||||
self.key_lock_condition.wait()
|
||||
self.key_lock_condition.release()
|
||||
if self.display.pending_events() > 0:
|
||||
# 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):
|
||||
self.key_lock_condition.acquire()
|
||||
"""
|
||||
Release the locked keyboard.
|
||||
"""
|
||||
logging.info("Unlock the keyboard")
|
||||
self.lock_keyboard = False
|
||||
self.key_lock_condition.notify()
|
||||
self.key_lock_condition.release()
|
||||
self.display.ungrab_keyboard(X.CurrentTime)
|
||||
self.display.flush()
|
||||
|
||||
|
||||
"""
|
||||
Close all the break screens.
|
||||
"""
|
||||
def __destroy_all_screens(self):
|
||||
"""
|
||||
Close all the break screens.
|
||||
"""
|
||||
for win in self.windows:
|
||||
win.destroy()
|
||||
del self.windows[:]
|
||||
|
|
|
@ -24,33 +24,43 @@ from safeeyes import Utility
|
|||
|
||||
APPINDICATOR_ID = 'safeeyes'
|
||||
|
||||
|
||||
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, language):
|
||||
"""
|
||||
Initialize the notification.
|
||||
"""
|
||||
def __init__(self, context, language):
|
||||
logging.info('Initialize the notification')
|
||||
Notify.init(APPINDICATOR_ID)
|
||||
self.context = context
|
||||
self.language = language
|
||||
|
||||
def initialize(self, language):
|
||||
"""
|
||||
Initialize the notification object.
|
||||
"""
|
||||
self.language = language
|
||||
|
||||
def show(self, warning_time):
|
||||
"""
|
||||
Show the 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:
|
||||
self.notification.show()
|
||||
except Exception as e:
|
||||
logging.exception('Error in showing notification', e)
|
||||
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
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.
|
||||
pass
|
||||
|
||||
|
||||
def quite(self):
|
||||
"""
|
||||
Uninitialize the notification. Call this method when closing the application.
|
||||
"""
|
||||
logging.info('Uninitialize Safe Eyes notification')
|
||||
Utility.execute_main_thread(Notify.uninit)
|
||||
Utility.execute_main_thread(Notify.uninit)
|
||||
|
|
|
@ -23,16 +23,13 @@ from safeeyes import Utility
|
|||
plugins_directory = os.path.join(Utility.config_directory, 'plugins')
|
||||
sys.path.append(os.path.abspath(plugins_directory))
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
Load the plugins.
|
||||
"""
|
||||
logging.info('Load all the plugins')
|
||||
self.__plugins = []
|
||||
|
||||
|
@ -47,18 +44,17 @@ class Plugins:
|
|||
else:
|
||||
logging.warning('Plugin file ' + str(plugin['name']) + '.py not found')
|
||||
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:
|
||||
self.__thread_pool = ThreadPool(min([4, len(self.__plugins)]))
|
||||
|
||||
|
||||
def start(self, context):
|
||||
"""
|
||||
Call the start function of all the plugins in separate thread.
|
||||
"""
|
||||
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:
|
||||
try:
|
||||
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.
|
||||
"""
|
||||
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:
|
||||
try:
|
||||
self.__thread_pool.apply_async(plugin['module'].pre_notification, (context,))
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
def pre_break(self, context):
|
||||
"""
|
||||
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'}
|
||||
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]
|
||||
for i in range(len(multiple_results)):
|
||||
try:
|
||||
result = multiple_results[i].get(timeout=1)
|
||||
if result:
|
||||
# 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:
|
||||
logging.warning('Ignoring lengthy result from the plugin ' + self.__plugins[i]['name'])
|
||||
continue
|
||||
|
@ -105,13 +100,12 @@ class Plugins:
|
|||
|
||||
return output
|
||||
|
||||
|
||||
def post_break(self, context):
|
||||
"""
|
||||
Call the post_break function of all the plugins in separate thread.
|
||||
"""
|
||||
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:
|
||||
try:
|
||||
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.
|
||||
"""
|
||||
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
|
||||
multiple_results = [self.__thread_pool.apply_async(plugin['module'].exit, (context,)) for plugin in self.__plugins]
|
||||
|
@ -135,12 +129,11 @@ class Plugins:
|
|||
pass
|
||||
|
||||
try:
|
||||
self.__thread_pool.terminate() # Shutdown the pool
|
||||
self.__thread_pool.terminate() # Shutdown the pool
|
||||
except Exception as e:
|
||||
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.
|
||||
"""
|
||||
|
|
|
@ -17,18 +17,15 @@
|
|||
# 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
|
||||
|
||||
|
||||
"""
|
||||
Core of Safe Eyes which runs the scheduler and notifies the breaks.
|
||||
"""
|
||||
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):
|
||||
# Initialize the variables
|
||||
self.break_count = -1
|
||||
|
@ -48,14 +45,13 @@ class SafeEyesCore:
|
|||
self.context['skipped'] = False
|
||||
self.context['postponed'] = False
|
||||
|
||||
|
||||
"""
|
||||
Initialize the internal properties from configuration
|
||||
"""
|
||||
def initialize(self, config, language):
|
||||
"""
|
||||
Initialize the internal properties from configuration
|
||||
"""
|
||||
logging.info("Initialize the core")
|
||||
self.short_break_exercises = [] #language['exercises']['short_break_exercises']
|
||||
self.long_break_exercises = [] #language['exercises']['long_break_exercises']
|
||||
self.short_break_exercises = [] # language['exercises']['short_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.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])
|
||||
|
||||
|
||||
"""
|
||||
Start Safe Eyes is it is not running already.
|
||||
"""
|
||||
def start(self):
|
||||
"""
|
||||
Start Safe Eyes is it is not running already.
|
||||
"""
|
||||
with self.lock:
|
||||
if not self.active:
|
||||
logging.info("Scheduling next break")
|
||||
|
@ -125,18 +120,23 @@ class SafeEyesCore:
|
|||
if self.context['idle_pause_enabled']:
|
||||
Utility.start_thread(self.__start_idle_monitor)
|
||||
|
||||
|
||||
"""
|
||||
Stop Safe Eyes if it is running.
|
||||
"""
|
||||
def stop(self):
|
||||
"""
|
||||
Stop Safe Eyes if it is running.
|
||||
"""
|
||||
with self.lock:
|
||||
if self.active:
|
||||
logging.info("Stop the core")
|
||||
# Reset the state properties in case of restart
|
||||
# self.break_count = 0
|
||||
# self.long_break_message_index = -1
|
||||
# self.short_break_message_index = -1
|
||||
|
||||
# Prevent resuming from a long break
|
||||
current_break_count = self.break_count = 0
|
||||
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
|
||||
self.notification_condition.acquire()
|
||||
|
@ -150,11 +150,10 @@ class SafeEyesCore:
|
|||
self.idle_condition.notify_all()
|
||||
self.idle_condition.release()
|
||||
|
||||
|
||||
"""
|
||||
Pause Safe Eyes if it is running.
|
||||
"""
|
||||
def pause(self):
|
||||
"""
|
||||
Pause Safe Eyes if it is running.
|
||||
"""
|
||||
with self.lock:
|
||||
if self.active and self.running:
|
||||
self.notification_condition.acquire()
|
||||
|
@ -162,38 +161,35 @@ class SafeEyesCore:
|
|||
self.notification_condition.notify_all()
|
||||
self.notification_condition.release()
|
||||
|
||||
|
||||
"""
|
||||
Resume Safe Eyes if it is not running.
|
||||
"""
|
||||
def resume(self):
|
||||
"""
|
||||
Resume Safe Eyes if it is not running.
|
||||
"""
|
||||
with self.lock:
|
||||
if self.active and not self.running:
|
||||
self.running = True
|
||||
Utility.start_thread(self.__scheduler_job)
|
||||
|
||||
|
||||
"""
|
||||
User skipped the break using Skip button
|
||||
"""
|
||||
def skip_break(self):
|
||||
"""
|
||||
User skipped the break using Skip button
|
||||
"""
|
||||
self.context['skipped'] = True
|
||||
|
||||
"""
|
||||
User postponed the break using Postpone button
|
||||
"""
|
||||
def postpone_break(self):
|
||||
"""
|
||||
User postponed the break using Postpone button
|
||||
"""
|
||||
self.context['postponed'] = True
|
||||
|
||||
|
||||
"""
|
||||
Scheduler task to execute during every interval
|
||||
"""
|
||||
def __scheduler_job(self):
|
||||
"""
|
||||
Scheduler task to execute during every interval
|
||||
"""
|
||||
if not self.__is_running():
|
||||
return
|
||||
|
||||
time_to_wait = self.break_interval # In minutes
|
||||
time_to_wait = self.break_interval # In minutes
|
||||
|
||||
if self.context['postponed']:
|
||||
# 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)
|
||||
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
|
||||
logging.info("Pre-break waiting for {} minutes".format(time_to_wait))
|
||||
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()
|
||||
|
||||
logging.info("Pre-break waiting is over")
|
||||
|
@ -227,16 +228,13 @@ class SafeEyesCore:
|
|||
|
||||
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
|
||||
Utility.execute_main_thread(self.__check_active_window)
|
||||
|
||||
|
||||
"""
|
||||
Show the notification and start the break after the notification.
|
||||
"""
|
||||
def __show_notification(self):
|
||||
"""
|
||||
Show the notification and start the break after the notification.
|
||||
"""
|
||||
# Show the notification
|
||||
self.show_notification()
|
||||
|
||||
|
@ -249,13 +247,12 @@ class SafeEyesCore:
|
|||
self.is_before_break = True
|
||||
Utility.execute_main_thread(self.__check_active_window)
|
||||
|
||||
|
||||
"""
|
||||
Check the active window for full-screen and user defined exceptions.
|
||||
"""
|
||||
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)
|
||||
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
|
||||
logging.info("Found a skip_break or full-screen window. Skip the break")
|
||||
if self.__is_running():
|
||||
|
@ -269,12 +266,10 @@ class SafeEyesCore:
|
|||
else:
|
||||
Utility.start_thread(self.__show_notification)
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Start the break screen.
|
||||
"""
|
||||
def __start_break(self):
|
||||
"""
|
||||
Start the break screen.
|
||||
"""
|
||||
# User can disable SafeEyes during notification
|
||||
if self.__is_running():
|
||||
message = ""
|
||||
|
@ -288,7 +283,6 @@ class SafeEyesCore:
|
|||
seconds = self.long_break_exercises[self.long_break_message_index][1]
|
||||
audible_alert = self.long_break_exercises[self.long_break_message_index][2]
|
||||
image = self.long_break_exercises[self.long_break_message_index][3]
|
||||
self.context['break_type'] = 'long'
|
||||
else:
|
||||
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)
|
||||
|
@ -296,7 +290,6 @@ class SafeEyesCore:
|
|||
seconds = self.short_break_exercises[self.short_break_message_index][1]
|
||||
audible_alert = self.short_break_exercises[self.short_break_message_index][2]
|
||||
image = self.short_break_exercises[self.short_break_message_index][3]
|
||||
self.context['break_type'] = 'short'
|
||||
|
||||
self.context['break_length'] = seconds
|
||||
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
|
||||
while seconds and self.active and not self.context['skipped'] and not self.context['postponed']:
|
||||
self.context['count_down'] = total_break_time - seconds
|
||||
mins, secs = divmod(seconds, 60)
|
||||
timeformat = '{:02d}:{:02d}'.format(mins, secs)
|
||||
self.on_countdown(timeformat)
|
||||
time.sleep(1) # Sleep for 1 second
|
||||
count_down = total_break_time - seconds
|
||||
self.context['count_down'] = count_down
|
||||
self.on_countdown(count_down, seconds)
|
||||
time.sleep(1) # Sleep for 1 second
|
||||
seconds -= 1
|
||||
|
||||
# Loop terminated because of timeout (not skipped) -> Close the break alert
|
||||
|
@ -327,26 +319,22 @@ class SafeEyesCore:
|
|||
# Schedule the break again
|
||||
Utility.start_thread(self.__scheduler_job)
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Tells whether Safe Eyes is running or not.
|
||||
"""
|
||||
def __is_running(self):
|
||||
"""
|
||||
Tells whether Safe Eyes is running or not.
|
||||
"""
|
||||
return self.active and self.running
|
||||
|
||||
|
||||
"""
|
||||
Check if the current break is long break or short current
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
"""
|
||||
Continuously check the system idle time and pause/resume Safe Eyes based on it.
|
||||
"""
|
||||
def __start_idle_monitor(self):
|
||||
"""
|
||||
Continuously check the system idle time and pause/resume Safe Eyes based on it.
|
||||
"""
|
||||
while self.active:
|
||||
# Wait for 2 seconds
|
||||
self.idle_condition.acquire()
|
||||
|
|
|
@ -18,9 +18,10 @@
|
|||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, Gdk, GdkX11, GObject
|
||||
from gi.repository import Gtk, GObject
|
||||
from safeeyes import Utility
|
||||
|
||||
|
||||
class SettingsDialog:
|
||||
"""
|
||||
Create and initialize SettingsDialog instance.
|
||||
|
@ -29,11 +30,14 @@ class SettingsDialog:
|
|||
self.config = config
|
||||
self.on_save_settings = on_save_settings
|
||||
self.languages = []
|
||||
self.language = language
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(glade_file)
|
||||
builder.connect_signals(self)
|
||||
|
||||
xprintidle_available = Utility.command_exist('xprintidle')
|
||||
|
||||
# Get the UI components
|
||||
self.window = builder.get_object('window_settings')
|
||||
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_idle_time_to_pause = builder.get_object('spin_idle_time_to_pause')
|
||||
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_strict_break = builder.get_object('switch_strict_break')
|
||||
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_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_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_strict_break').set_label(language['ui_controls']['strict_break'])
|
||||
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_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_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_disable_keyboard_shortcut.set_value(config['shortcut_disable_time'])
|
||||
self.switch_show_time_in_tray.set_active(config['show_time_in_tray'])
|
||||
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'])
|
||||
|
||||
# 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_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_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
|
||||
language_list_store = Gtk.ListStore(GObject.TYPE_STRING)
|
||||
language_index = 2
|
||||
|
@ -108,7 +118,7 @@ class SettingsDialog:
|
|||
language_list_store.append([language['ui_controls']['system_language']])
|
||||
language_list_store.append(['-'])
|
||||
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:
|
||||
self.cmb_language.set_active(0)
|
||||
|
||||
|
@ -120,19 +130,17 @@ class SettingsDialog:
|
|||
language_index += 1
|
||||
|
||||
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()
|
||||
self.cmb_language.pack_start(cell, True)
|
||||
self.cmb_language.add_attribute(cell, 'text', 0)
|
||||
|
||||
|
||||
def show(self):
|
||||
"""
|
||||
Show the SettingsDialog.
|
||||
"""
|
||||
self.window.show_all()
|
||||
|
||||
|
||||
def on_switch_screen_lock_activate(self, switch, state):
|
||||
"""
|
||||
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())
|
||||
|
||||
|
||||
def on_switch_strict_break_activate(self, switch, state):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
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)
|
||||
if strict_break_enable:
|
||||
self.switch_postpone.set_active(False)
|
||||
|
@ -158,13 +165,22 @@ class SettingsDialog:
|
|||
"""
|
||||
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):
|
||||
"""
|
||||
Event handler for Settings dialog close action.
|
||||
"""
|
||||
self.window.destroy()
|
||||
|
||||
|
||||
def on_save_clicked(self, button):
|
||||
"""
|
||||
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['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['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['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['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['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.window.destroy() # Close the settings window
|
||||
|
||||
self.on_save_settings(self.config) # Call the provided save method
|
||||
self.window.destroy() # Close the settings window
|
||||
|
||||
def on_cancel_clicked(self, button):
|
||||
"""
|
||||
Event handler for Cancel button click.
|
||||
"""
|
||||
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()
|
||||
|
|
|
@ -28,6 +28,9 @@ APPINDICATOR_ID = 'safeeyes'
|
|||
|
||||
|
||||
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):
|
||||
logging.info("Initialize the tray icon")
|
||||
|
@ -133,9 +136,15 @@ class TrayIcon:
|
|||
self.indicator.set_menu(self.menu)
|
||||
|
||||
def initialize(self, config):
|
||||
"""
|
||||
Initialize the tray icon by setting the config.
|
||||
"""
|
||||
self.config = config
|
||||
|
||||
def set_labels(self, language):
|
||||
"""
|
||||
Update the text of menu items based on the selected language.
|
||||
"""
|
||||
self.language = language
|
||||
for entry in self.sub_menu_items:
|
||||
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'])
|
||||
|
||||
def show_icon(self):
|
||||
"""
|
||||
Show the tray icon.
|
||||
"""
|
||||
Utility.execute_main_thread(self.indicator.set_status, appindicator.IndicatorStatus.ACTIVE)
|
||||
|
||||
def hide_icon(self):
|
||||
"""
|
||||
Hide the tray icon.
|
||||
"""
|
||||
Utility.execute_main_thread(self.indicator.set_status, appindicator.IndicatorStatus.PASSIVE)
|
||||
|
||||
def quit_safe_eyes(self, *args):
|
||||
"""
|
||||
Handle Quit menu action.
|
||||
This action terminates the application.
|
||||
"""
|
||||
self.on_quite()
|
||||
with self.lock:
|
||||
self.active = True
|
||||
|
@ -173,17 +192,31 @@ class TrayIcon:
|
|||
self.idle_condition.release()
|
||||
|
||||
def show_settings(self, *args):
|
||||
"""
|
||||
Handle Settings menu action.
|
||||
This action shows the Settings dialog.
|
||||
"""
|
||||
self.on_show_settings()
|
||||
|
||||
def show_about(self, *args):
|
||||
"""
|
||||
Handle About menu action.
|
||||
This action shows the About dialog.
|
||||
"""
|
||||
self.on_show_about()
|
||||
|
||||
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")
|
||||
self.dateTime = dateTime
|
||||
self.__set_next_break_info()
|
||||
|
||||
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)
|
||||
message = self.language['messages']['next_break_at'].format(formatted_time)
|
||||
# Update the tray icon label
|
||||
|
@ -195,6 +228,10 @@ class TrayIcon:
|
|||
Utility.execute_main_thread(self.item_info.set_label, message)
|
||||
|
||||
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()
|
||||
if not self.active:
|
||||
with self.lock:
|
||||
|
@ -211,6 +248,10 @@ class TrayIcon:
|
|||
self.idle_condition.release()
|
||||
|
||||
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()
|
||||
if self.active and len(args) > 1:
|
||||
logging.info('Disable Safe Eyes')
|
||||
|
@ -231,30 +272,26 @@ class TrayIcon:
|
|||
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)))
|
||||
|
||||
|
||||
"""
|
||||
This method is called by the core to prevent user from disabling Safe Eyes after the notification.
|
||||
"""
|
||||
def lock_menu(self):
|
||||
"""
|
||||
This method is called by the core to prevent user from disabling Safe Eyes after the notification.
|
||||
"""
|
||||
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)
|
||||
|
||||
"""
|
||||
This method is called by the core to activate the disable menu after the the break.
|
||||
"""
|
||||
def unlock_menu(self):
|
||||
"""
|
||||
This method is called by the core to activate the menu after the the break.
|
||||
"""
|
||||
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)
|
||||
|
||||
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.wait(time_minutes * 60) # Convert to seconds
|
||||
self.idle_condition.wait(time_minutes * 60) # Convert to seconds
|
||||
self.idle_condition.release()
|
||||
|
||||
with self.lock:
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
|
||||
import gi
|
||||
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 distutils.version import LooseVersion
|
||||
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__))
|
||||
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_style_sheet_path = os.path.join(bin_directory, "config/style/safeeyes_style.css")
|
||||
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():
|
||||
"""
|
||||
Play the alert.wav
|
||||
"""
|
||||
logging.info('Playing audible alert')
|
||||
CHUNK = 1024
|
||||
if pyaudio:
|
||||
logging.info('Playing audible alert')
|
||||
CHUNK = 1024
|
||||
|
||||
try:
|
||||
# Open the sound file
|
||||
path = get_resource_path('alert.wav')
|
||||
if path is None:
|
||||
return
|
||||
sound = wave.open(path, 'rb')
|
||||
try:
|
||||
# Open the sound file
|
||||
path = get_resource_path('alert.wav')
|
||||
if path is None:
|
||||
return
|
||||
sound = wave.open(path, 'rb')
|
||||
|
||||
# Create a sound stream
|
||||
wrapper = pyaudio.PyAudio()
|
||||
stream = wrapper.open(format=wrapper.get_format_from_width(sound.getsampwidth()),
|
||||
channels=sound.getnchannels(),
|
||||
rate=sound.getframerate(),
|
||||
output=True)
|
||||
# Create a sound stream
|
||||
wrapper = pyaudio.PyAudio()
|
||||
stream = wrapper.open(format=wrapper.get_format_from_width(
|
||||
sound.getsampwidth()),
|
||||
channels=sound.getnchannels(),
|
||||
rate=sound.getframerate(),
|
||||
output=True)
|
||||
|
||||
# Write file data into the sound stream
|
||||
data = sound.readframes(CHUNK)
|
||||
while data != b'':
|
||||
stream.write(data)
|
||||
# Write file data into the sound stream
|
||||
data = sound.readframes(CHUNK)
|
||||
while data != b'':
|
||||
stream.write(data)
|
||||
data = sound.readframes(CHUNK)
|
||||
|
||||
# Close steam
|
||||
stream.stop_stream()
|
||||
stream.close()
|
||||
sound.close()
|
||||
wrapper.terminate()
|
||||
# Close steam
|
||||
stream.stop_stream()
|
||||
stream.close()
|
||||
sound.close()
|
||||
wrapper.terminate()
|
||||
|
||||
except Exception as e:
|
||||
logging.warning('Unable to play audible alert')
|
||||
logging.exception(e)
|
||||
except Exception as e:
|
||||
logging.warning('Unable to play audible alert')
|
||||
logging.exception(e)
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
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:
|
||||
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()
|
||||
if active_window:
|
||||
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:
|
||||
stdout = subprocess.check_output(cmdlist).decode('utf-8')
|
||||
|
@ -191,6 +207,7 @@ def mkdir(path):
|
|||
logging.error('Error while creating ' + str(path))
|
||||
raise
|
||||
|
||||
|
||||
def parse_language_code(lang_code):
|
||||
"""
|
||||
Convert the user defined language code to a valid one.
|
||||
|
@ -249,6 +266,30 @@ def read_lang_files():
|
|||
|
||||
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():
|
||||
"""
|
||||
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']
|
||||
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']
|
||||
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'):
|
||||
return ['gnome-screensaver-command', '--lock']
|
||||
else:
|
||||
# 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']
|
||||
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
|
||||
return ['gnome-screensaver-command', '--lock']
|
||||
return None
|
||||
|
@ -378,7 +419,7 @@ def intialize_logging():
|
|||
log_formatter = logging.Formatter('%(asctime)s [%(levelname)s]:[%(threadName)s] %(message)s')
|
||||
|
||||
# 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.setLevel(logging.INFO)
|
||||
|
||||
|
@ -445,7 +486,7 @@ class __HTMLTextExtractor(HTMLParser):
|
|||
def __init__(self):
|
||||
self.reset()
|
||||
self.strict = False
|
||||
self.convert_charrefs= True
|
||||
self.convert_charrefs = True
|
||||
self.fed = []
|
||||
|
||||
def handle_data(self, d):
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# 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 dbus.mainloop.glib import DBusGMainLoop
|
||||
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")
|
||||
about_dialog_glade = os.path.join(Utility.bin_directory, "glade/about_dialog.glade")
|
||||
|
||||
|
||||
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():
|
||||
"""
|
||||
Listen to tray icon Settings action and send the signal to Settings dialog.
|
||||
"""
|
||||
logging.info("Show Settings dialog")
|
||||
able_to_lock_screen = False
|
||||
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.show()
|
||||
|
||||
"""
|
||||
Listen to tray icon About action and send the signal to About dialog.
|
||||
"""
|
||||
|
||||
def show_about():
|
||||
"""
|
||||
Listen to tray icon About action and send the signal to About dialog.
|
||||
"""
|
||||
logging.info("Show About dialog")
|
||||
about_dialog = AboutDialog(about_dialog_glade, SAFE_EYES_VERSION, language)
|
||||
about_dialog.show()
|
||||
|
||||
"""
|
||||
Receive the signal from core and pass it to the Notification.
|
||||
"""
|
||||
|
||||
def show_notification():
|
||||
"""
|
||||
Receive the signal from core and pass it to the Notification.
|
||||
"""
|
||||
if config['strict_break']:
|
||||
Utility.execute_main_thread(tray_icon.lock_menu)
|
||||
plugins.pre_notification(context)
|
||||
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):
|
||||
"""
|
||||
Receive the break signal from core and pass it to the break screen.
|
||||
"""
|
||||
logging.info("Show the break screen")
|
||||
notification.close()
|
||||
plugins_data = plugins.pre_break(context)
|
||||
|
@ -80,10 +83,11 @@ def show_alert(message, image_name):
|
|||
if config['strict_break'] and is_active:
|
||||
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):
|
||||
"""
|
||||
Receive the stop break signal from core and pass it to the break screen.
|
||||
"""
|
||||
logging.info("Close the break screen")
|
||||
if config['enable_screen_lock'] and context['break_type'] == 'long':
|
||||
# Lock the screen before closing the break screen
|
||||
|
@ -94,21 +98,22 @@ def close_alert(audible_alert_on):
|
|||
plugins.post_break(context)
|
||||
|
||||
|
||||
"""
|
||||
Listen to the tray menu quit action and stop the core, notification and the app itself.
|
||||
"""
|
||||
def on_quit():
|
||||
"""
|
||||
Listen to the tray menu quit action and stop the core, notification and the app itself.
|
||||
"""
|
||||
logging.info("Quit Safe Eyes")
|
||||
plugins.exit(context)
|
||||
core.stop()
|
||||
notification.quite();
|
||||
notification.quite()
|
||||
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 it was active, Safe Eyes will become active after wake up.
|
||||
"""
|
||||
def handle_suspend_callback(sleeping):
|
||||
"""
|
||||
if sleeping:
|
||||
# Sleeping / suspending
|
||||
if is_active:
|
||||
|
@ -120,18 +125,20 @@ def handle_suspend_callback(sleeping):
|
|||
core.start()
|
||||
logging.info("Resumed Safe Eyes after system wakeup")
|
||||
|
||||
"""
|
||||
Setup system suspend listener.
|
||||
"""
|
||||
|
||||
def handle_system_suspend():
|
||||
"""
|
||||
Setup system suspend listener.
|
||||
"""
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
bus = dbus.SystemBus()
|
||||
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():
|
||||
"""
|
||||
Listen to break screen Skip action and send the signal to core.
|
||||
"""
|
||||
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']:
|
||||
# Lock the screen before closing the break screen
|
||||
|
@ -139,20 +146,22 @@ def on_skipped():
|
|||
core.skip_break()
|
||||
plugins.post_break(context)
|
||||
|
||||
"""
|
||||
Listen to break screen Postpone action and send the signal to core.
|
||||
"""
|
||||
|
||||
def on_postponed():
|
||||
"""
|
||||
Listen to break screen Postpone action and send the signal to core.
|
||||
"""
|
||||
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']:
|
||||
# Lock the screen before closing the break screen
|
||||
Utility.lock_desktop(system_lock_command)
|
||||
core.postpone_break()
|
||||
|
||||
"""
|
||||
Listen to Settings dialog Save action and write to the config file.
|
||||
"""
|
||||
|
||||
def save_settings(config):
|
||||
"""
|
||||
Listen to Settings dialog Save action and write to the config file.
|
||||
"""
|
||||
global language
|
||||
|
||||
logging.info("Saving settings to safeeyes.json")
|
||||
|
@ -175,22 +184,25 @@ def save_settings(config):
|
|||
# Restart the core and intialize the components
|
||||
core.initialize(config, language)
|
||||
break_screen.initialize(config, language)
|
||||
notification.initialize(language)
|
||||
if is_active:
|
||||
# 1 sec delay is required to give enough time for core to be stopped
|
||||
Timer(1.0, core.start).start()
|
||||
|
||||
"""
|
||||
Listen to tray icon enable action and send the signal to core.
|
||||
"""
|
||||
|
||||
def enable_safeeyes():
|
||||
"""
|
||||
Listen to tray icon enable action and send the signal to core.
|
||||
"""
|
||||
global is_active
|
||||
is_active = True
|
||||
core.start()
|
||||
|
||||
"""
|
||||
Listen to tray icon disable action and send the signal to core.
|
||||
"""
|
||||
|
||||
def disable_safeeyes():
|
||||
"""
|
||||
Listen to tray icon disable action and send the signal to core.
|
||||
"""
|
||||
global is_active
|
||||
is_active = False
|
||||
core.stop()
|
||||
|
@ -198,11 +210,12 @@ def disable_safeeyes():
|
|||
|
||||
def running():
|
||||
"""
|
||||
Check if SafeEyes is already running.
|
||||
Check if SafeEyes is already running.
|
||||
"""
|
||||
process_count = 0
|
||||
for proc in psutil.process_iter():
|
||||
if not proc.cmdline: continue
|
||||
if not proc.cmdline:
|
||||
continue
|
||||
try:
|
||||
# Check if safeeyes is in process arguments
|
||||
if callable(proc.cmdline):
|
||||
|
@ -231,6 +244,9 @@ def main():
|
|||
|
||||
logging.info("Starting Safe Eyes")
|
||||
|
||||
# Import the dependencies
|
||||
Utility.import_dependencies()
|
||||
|
||||
if not running():
|
||||
|
||||
global break_screen
|
||||
|
@ -253,14 +269,14 @@ def main():
|
|||
else:
|
||||
system_lock_command = Utility.lock_screen_command()
|
||||
|
||||
|
||||
# Initialize the Safe Eyes Context
|
||||
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)
|
||||
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)
|
||||
notification = Notification(language)
|
||||
notification = Notification(context, language)
|
||||
plugins = Plugins(config)
|
||||
core = SafeEyesCore(context, show_notification, show_alert, close_alert, break_screen.show_count_down, tray_icon.next_break_time)
|
||||
core.initialize(config, language)
|
||||
|
@ -274,5 +290,6 @@ def main():
|
|||
logging.info('Another instance of safeeyes is already running')
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -18,10 +18,13 @@
|
|||
"long_break_lean_back": "Lehnen Sie sich zurück und entspannen Sie sich"
|
||||
},
|
||||
"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_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": {
|
||||
"about": "Über",
|
||||
|
@ -30,6 +33,7 @@
|
|||
"cancel": "Abbrechen",
|
||||
"close": "Schließen",
|
||||
"disable": "Safe Eyes deaktivieren",
|
||||
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
|
||||
"enable": "Safe Eyes aktivieren",
|
||||
"enable_screen_lock": "Sperrt den Bildschirm nach einer langen Pause",
|
||||
"for_x_hour": "Für {} Stunde",
|
||||
|
@ -47,7 +51,7 @@
|
|||
"save": "Speichern",
|
||||
"settings": "Einstellungen",
|
||||
"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",
|
||||
"strict_break": "Strikte Pause (Überspringen nicht möglich)",
|
||||
"system_language": "Systemsprache",
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
"long_break_lean_back": "Lean back at your seat and relax"
|
||||
},
|
||||
"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_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": {
|
||||
"about": "About",
|
||||
|
@ -30,6 +33,7 @@
|
|||
"cancel": "Cancel",
|
||||
"close": "Close",
|
||||
"disable": "Disable Safe Eyes",
|
||||
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
|
||||
"enable": "Enable Safe Eyes",
|
||||
"enable_screen_lock": "Lock the screen after every long break",
|
||||
"for_x_hour": "For {} Hour",
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
"long_break_lean_back": "Reclínate sobre tu silla y relájate"
|
||||
},
|
||||
"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_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": {
|
||||
"about": "Acerca de",
|
||||
|
@ -30,6 +33,7 @@
|
|||
"cancel": "Cancelar",
|
||||
"close": "Close",
|
||||
"disable": "Desactivar Safe Eyes",
|
||||
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
|
||||
"enable": "Activar Safe Eyes",
|
||||
"enable_screen_lock": "Bloquear la pantalla despues de cada pausa larga",
|
||||
"for_x_hour": "Durante {} hora",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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": "تا زمان بارگذاری مجدد"
|
||||
}
|
||||
}
|
|
@ -18,10 +18,13 @@
|
|||
"long_break_lean_back": "Adossez-vous à votre siège et relaxez-vous"
|
||||
},
|
||||
"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_x": "Désactivé jusqu'à {}",
|
||||
"next_break_at": "Prochaine pause à {}"
|
||||
"next_break_at": "Prochaine pause à {}",
|
||||
"software_required": "Pour activer cette fonction, veuillez installer {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "À propos",
|
||||
|
@ -30,6 +33,7 @@
|
|||
"cancel": "Annuler",
|
||||
"close": "Fermer",
|
||||
"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_screen_lock": "Verrouiller l'écran après chaque pause longue",
|
||||
"for_x_hour": "Pendant {} heure",
|
||||
|
@ -47,12 +51,12 @@
|
|||
"save": "Enregistrer",
|
||||
"settings": "Paramètres",
|
||||
"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",
|
||||
"strict_break": "Pause stricte (cacher le bouton Ignorer)",
|
||||
"system_language": "Langue du système ",
|
||||
"time_to_prepare_for_break": "Temps de préparation à une pause (en secondes)",
|
||||
"time_to_screen_lock": "Temps maximal pour ignorer, en passant outre le verrouillage (en secondes)",
|
||||
"time_to_prepare_for_break": "Durée de préparation à une pause (en secondes)",
|
||||
"time_to_screen_lock": "Durée maximale pour ignorer, en passant outre le verrouillage (en secondes)",
|
||||
"until_restart": "Jusqu'au redémarrage"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
"long_break_lean_back": "სავარძლის საზურგეზე გადაწექით და ცოტა დაისვენეთ"
|
||||
},
|
||||
"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_x": "გაუქმებულია {} მდე",
|
||||
"next_break_at": "შემდეგი შესვენება {}"
|
||||
"next_break_at": "შემდეგი შესვენება {}",
|
||||
"software_required": "To enable this feature, please install {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "პროგრამის შესახებ",
|
||||
|
@ -30,6 +33,7 @@
|
|||
"cancel": "უარყოფა",
|
||||
"close": "Close",
|
||||
"disable": "Safe Eyes გამორთვა",
|
||||
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
|
||||
"enable": "Safe Eyes ჩართვა",
|
||||
"enable_screen_lock": "Lock the screen after every long break",
|
||||
"for_x_hour": "{} საათით",
|
||||
|
@ -47,7 +51,7 @@
|
|||
"save": "დამახსოვრება",
|
||||
"settings": "პარამეტრები",
|
||||
"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": "გამოტოვება",
|
||||
"strict_break": "აუცილებელი შესვენება (დავმალოთ ღილაკი 'გამოტოვება')",
|
||||
"system_language": "System Language",
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
"long_break_lean_back": "पीछे हट कर आराम करें"
|
||||
},
|
||||
"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_x": "{} तक बंद",
|
||||
"next_break_at": "अगला आराम {} में"
|
||||
"next_break_at": "अगला आराम {} में",
|
||||
"software_required": "To enable this feature, please install {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "हमारे बारे में",
|
||||
|
@ -30,6 +33,7 @@
|
|||
"cancel": "रखना नहीं",
|
||||
"close": "Close",
|
||||
"disable": "सेफ आईज बंद",
|
||||
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
|
||||
"enable": "सेफ आईज शुरू",
|
||||
"enable_screen_lock": "Lock the screen after every long break",
|
||||
"for_x_hour": "{} घंटे के लिए",
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
"long_break_lean_back": "Dőljön hátra és pihenjen!"
|
||||
},
|
||||
"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_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": {
|
||||
"about": "Ról ről",
|
||||
|
@ -30,6 +33,7 @@
|
|||
"cancel": "Mégse",
|
||||
"close": "Close",
|
||||
"disable": "Disable Safe Eyes",
|
||||
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
|
||||
"enable": "Safe Eyes Bekapcsolása",
|
||||
"enable_screen_lock": "Lock the screen after every long break",
|
||||
"for_x_hour": "For {} Hour",
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
"long_break_lean_back": "Silakan bersandar ke kursi dan bersantai"
|
||||
},
|
||||
"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_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": {
|
||||
"about": "Tentang",
|
||||
|
@ -30,6 +33,7 @@
|
|||
"cancel": "Batal",
|
||||
"close": "Close",
|
||||
"disable": "Matikan Safe Eyes",
|
||||
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
|
||||
"enable": "Hidupkan Safe Eyes",
|
||||
"enable_screen_lock": "Lock the screen after every long break",
|
||||
"for_x_hour": "Selama {} Jam",
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
"long_break_lean_back": "Наслонете се на столот и одморете"
|
||||
},
|
||||
"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_x": "Оневозможено до {}",
|
||||
"next_break_at": "Следна пауза во {}"
|
||||
"next_break_at": "Следна пауза во {}",
|
||||
"software_required": "To enable this feature, please install {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "За",
|
||||
|
@ -30,6 +33,7 @@
|
|||
"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": "За {} час",
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
"long_break_lean_back": "Oprzyj się wygodnie na krześle i zrelaksuj"
|
||||
},
|
||||
"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_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": {
|
||||
"about": "O programie",
|
||||
|
@ -30,6 +33,7 @@
|
|||
"cancel": "Anuluj",
|
||||
"close": "Zamknij",
|
||||
"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_screen_lock": "Zablokuj ekran po każdej długiej przerwie",
|
||||
"for_x_hour": "Na {} godzinę",
|
||||
|
@ -47,7 +51,7 @@
|
|||
"save": "Zapisz",
|
||||
"settings": "Ustawienia",
|
||||
"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ń",
|
||||
"strict_break": "Bezwzględna przerwa (ukryj przycisk pominięcia)",
|
||||
"system_language": "Język systemu",
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
"long_break_lean_back": "Encoste na cadeira e relaxe"
|
||||
},
|
||||
"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_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": {
|
||||
"about": "Sobre",
|
||||
|
@ -30,6 +33,7 @@
|
|||
"cancel": "Cancelar",
|
||||
"close": "Fechar",
|
||||
"disable": "Desativar Safe Eyes",
|
||||
"disable_keyboard_shortcut": "Shortcut disabled period to prevent unintentional skip (in seconds)",
|
||||
"enable": "Habilitar Safe Eyes",
|
||||
"enable_screen_lock": "Bloqueie a tela após cada pausa longa",
|
||||
"for_x_hour": "Por {} Hora",
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
"long_break_lean_back": "Откиньтесь на спинку стула и расслабьтесь"
|
||||
},
|
||||
"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_x": "Отключено до {}",
|
||||
"next_break_at": "Следующий перерыв в {}"
|
||||
"next_break_at": "Следующий перерыв в {}",
|
||||
"software_required": "To enable this feature, please install {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "О программе",
|
||||
|
@ -30,6 +33,7 @@
|
|||
"cancel": "Отменить",
|
||||
"close": "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": "На {} час",
|
||||
|
@ -47,7 +51,7 @@
|
|||
"save": "Сохранить",
|
||||
"settings": "Настройки",
|
||||
"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": "Пропустить",
|
||||
"strict_break": "Обязательный перерыв (Скрыть кнопку 'Пропустить')",
|
||||
"system_language": "System Language",
|
||||
|
|
|
@ -18,18 +18,22 @@
|
|||
"long_break_lean_back": "கதிைரயில் பின்பக்கமாக சாய்ந்து ஓய்வெடுங்கள்"
|
||||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "{} விநாடிகளில் இடைவேளைக்கு தயாராகுங்கள்",
|
||||
"audible_alert_disabled": "ஒலிச் சமிக்ஞையை செயற்படுத்த முடியாதுள்ளது",
|
||||
"ready_for_a_short_break": "{} விநாடிகளில் குறுகிய இடைவேளைக்கு தயாராகுங்கள்",
|
||||
"ready_for_a_long_break": "{} விநாடிகளில் நீண்ட இடைவேளைக்கு தயாராகுங்கள்",
|
||||
"disabled_until_restart": "மறுதுவக்கம் வரை நிறுத்தி வைக்கப்பட்டுள்ளது",
|
||||
"disabled_until_x": "{} வரை நிறுத்தி வைக்கப்பட்டுள்ளது",
|
||||
"next_break_at": "அடுத்த இடைவேளை {}"
|
||||
"next_break_at": "அடுத்த இடைவேளை {}",
|
||||
"software_required": "இந்த வசதியை செயற்படுத்த {} ஐ நிறுவவும்"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "Safe Eyes குறித்த தகவல்கள்",
|
||||
"allow_postpone": "ஒத்திவைக்க அனுமதிக்கவும்",
|
||||
"audible_alert": "இடைவேளையின் இறுதியில் ஒலி சமிக்கை",
|
||||
"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": "{} மணித்தியாலத்திற்கு",
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
"long_break_lean_back": "Arkanıza yaslanın ve biraz gevşeyin"
|
||||
},
|
||||
"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_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": {
|
||||
"about": "Hakkında",
|
||||
|
@ -30,6 +33,7 @@
|
|||
"cancel": "İptal",
|
||||
"close": "Kapat",
|
||||
"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_screen_lock": "Her uzun mola sonunda ekranı kilitle",
|
||||
"for_x_hour": "{} Saat",
|
||||
|
|
|
@ -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": "До перезапуску"
|
||||
}
|
||||
}
|
|
@ -22,10 +22,13 @@
|
|||
},
|
||||
"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_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":
|
||||
{
|
||||
|
@ -35,12 +38,13 @@
|
|||
"cancel": "Huỷ",
|
||||
"close": "Đóng",
|
||||
"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_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_hours": "Trong {} giờ",
|
||||
"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)",
|
||||
"language": "Language (Ngôn ngữ)",
|
||||
"license": "Giấy phép",
|
||||
|
|
|
@ -10,7 +10,11 @@
|
|||
"pre_break_warning_time": 10,
|
||||
"short_break_duration": 15,
|
||||
"idle_time": 5,
|
||||
"persist_state": true,
|
||||
"postpone_duration": 5,
|
||||
"shortcut_disable_time": 2,
|
||||
"shortcut_skip": 9,
|
||||
"shortcut_postpone": 65,
|
||||
"show_time_in_tray": false,
|
||||
"strict_break": false,
|
||||
"audible_alert": false,
|
||||
|
|
|
@ -52,6 +52,12 @@
|
|||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">5</property>
|
||||
</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">
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">60</property>
|
||||
|
@ -189,7 +195,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">9</property>
|
||||
<property name="top_attach">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -202,7 +208,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="top_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -215,7 +221,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">7</property>
|
||||
<property name="top_attach">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -346,7 +352,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">9</property>
|
||||
<property name="top_attach">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -358,7 +364,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="top_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -370,7 +376,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">7</property>
|
||||
<property name="top_attach">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -382,7 +388,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">10</property>
|
||||
<property name="top_attach">11</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -395,7 +401,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">10</property>
|
||||
<property name="top_attach">11</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -405,7 +411,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">13</property>
|
||||
<property name="top_attach">14</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -418,7 +424,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">13</property>
|
||||
<property name="top_attach">14</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -431,7 +437,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">11</property>
|
||||
<property name="top_attach">12</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -442,7 +448,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">11</property>
|
||||
<property name="top_attach">12</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -455,7 +461,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">12</property>
|
||||
<property name="top_attach">13</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -474,7 +480,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">12</property>
|
||||
<property name="top_attach">13</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -487,7 +493,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">8</property>
|
||||
<property name="top_attach">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -498,14 +504,40 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">8</property>
|
||||
<property name="top_attach">9</property>
|
||||
</packing>
|
||||
</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>
|
||||
<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>
|
||||
</object>
|
||||
<packing>
|
||||
|
|
19
setup.py
19
setup.py
|
@ -1,13 +1,15 @@
|
|||
import os
|
||||
|
||||
import setuptools
|
||||
|
||||
|
||||
requires = [
|
||||
'python-xlib',
|
||||
'pyaudio',
|
||||
'psutil',
|
||||
'babel']
|
||||
'python-xlib',
|
||||
'psutil',
|
||||
'babel']
|
||||
|
||||
extras = {
|
||||
'audible_alert': ['pyaudio']
|
||||
}
|
||||
|
||||
|
||||
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:
|
||||
long_description = '\n' + f.read()
|
||||
|
||||
|
||||
def _data_files(path):
|
||||
for root, dirs, files in os.walk(path):
|
||||
if not files:
|
||||
continue
|
||||
yield (os.path.join('/usr', root), [os.path.join(root, f) for f in files])
|
||||
|
||||
|
||||
setuptools.setup(
|
||||
name="safeeyes",
|
||||
version="1.2.1",
|
||||
version="1.2.2",
|
||||
description="Protect your eyes from eye strain using this continuous breaks reminder.",
|
||||
long_description=long_description,
|
||||
author="Gobinath Loganathan",
|
||||
author_email="slgobinath@gmail.com",
|
||||
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(),
|
||||
package_data={'safeeyes': ['config/*.json',
|
||||
'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'])
|
||||
],
|
||||
install_requires=requires,
|
||||
extras_require=extras,
|
||||
entry_points={'console_scripts': ['safeeyes = safeeyes.__main__:main']},
|
||||
keywords='linux utility health eye-strain safe-eyes',
|
||||
classifiers=[
|
||||
|
|
|
@ -1,19 +1,25 @@
|
|||
[Desktop Entry]
|
||||
Name=Safe Eyes
|
||||
Comment=Protect your eyes from eye strain
|
||||
Comment[ge]=დაიცავით თქვენი თვალები დაღლილობისაგან
|
||||
Comment[de]=Schützt die Augen vor Überanstrengung
|
||||
Comment[ca]=Protegiu-vos els ulls de la fatiga visual
|
||||
Comment[cs]=Chraňte své oči před únavou
|
||||
Comment[fr]=Protégez vos yeux de la fatigue
|
||||
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[de]=Schützt die Augen vor Überanstrengung
|
||||
Comment[es]=Protege tus ojos de la fatiga ocular
|
||||
Comment[ru]=Защитите свои глаза от зрительного перенапряжения
|
||||
Comment[pl]=Chroń oczy przed zmęczeniem
|
||||
Comment[et]=Kaitse oma silmi väsimuse eest
|
||||
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[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
|
||||
Exec=env GDK_BACKEND=x11 safeeyes
|
||||
Icon=safeeyes
|
||||
|
|
Loading…
Reference in New Issue