commit
eedb2c53c7
|
@ -18,13 +18,15 @@
|
|||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, Gdk, GdkX11
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
"""
|
||||
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:
|
||||
|
||||
"""
|
||||
|
@ -42,21 +44,18 @@ 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):
|
||||
self.window.show_all()
|
||||
|
||||
|
||||
"""
|
||||
Window close event handler.
|
||||
"""
|
||||
def on_window_delete(self, *args):
|
||||
self.window.destroy()
|
||||
|
||||
|
||||
"""
|
||||
Close button click event handler.
|
||||
"""
|
||||
|
|
|
@ -16,16 +16,17 @@
|
|||
# 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
|
||||
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:
|
||||
|
||||
def __init__(self, context, on_skip, on_postpone, glade_file, style_sheet_path):
|
||||
|
@ -46,7 +47,6 @@ class BreakScreen:
|
|||
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)
|
||||
|
||||
|
||||
def initialize(self, config, language):
|
||||
"""
|
||||
Initialize the internal properties from configuration
|
||||
|
@ -60,7 +60,6 @@ class BreakScreen:
|
|||
self.keycode_shortcut_postpone = config.get('shortcut_postpone', 65)
|
||||
self.shortcut_disable_time = config.get('shortcut_disable_time', 2)
|
||||
|
||||
|
||||
def skip_break(self):
|
||||
"""
|
||||
Skip the break from the break screen
|
||||
|
@ -69,7 +68,7 @@ class BreakScreen:
|
|||
# Must call on_skip before close to lock screen before closing the break screen
|
||||
self.on_skip()
|
||||
self.close()
|
||||
|
||||
|
||||
def postpone_break(self):
|
||||
"""
|
||||
Postpone the break from the break screen
|
||||
|
@ -86,14 +85,12 @@ class BreakScreen:
|
|||
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.
|
||||
|
@ -109,7 +106,6 @@ class BreakScreen:
|
|||
timeformat = '{:02d}:{:02d}'.format(mins, secs)
|
||||
GLib.idle_add(lambda: self.__update_count_down(timeformat))
|
||||
|
||||
|
||||
def show_message(self, message, image_path, plugins_data):
|
||||
"""
|
||||
Show the break screen with the given message on all displays.
|
||||
|
@ -117,7 +113,6 @@ class BreakScreen:
|
|||
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))
|
||||
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Hide the break screen from active window and destroy all other windows
|
||||
|
@ -128,7 +123,6 @@ class BreakScreen:
|
|||
# Destroy other windows if exists
|
||||
GLib.idle_add(lambda: self.__destroy_all_screens())
|
||||
|
||||
|
||||
def __show_break_screen(self, message, image_path, plugins_data):
|
||||
"""
|
||||
Show an empty break screen on all screens.
|
||||
|
@ -175,14 +169,12 @@ 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)
|
||||
|
@ -199,7 +191,6 @@ class BreakScreen:
|
|||
window.present()
|
||||
window.fullscreen()
|
||||
|
||||
|
||||
def __update_count_down(self, count):
|
||||
"""
|
||||
Update the countdown on all break screens.
|
||||
|
@ -207,7 +198,6 @@ class BreakScreen:
|
|||
for label in self.count_labels:
|
||||
label.set_text(count)
|
||||
|
||||
|
||||
def __lock_keyboard(self):
|
||||
"""
|
||||
Lock the keyboard to prevent the user from using keyboard shortcuts
|
||||
|
@ -217,11 +207,11 @@ class BreakScreen:
|
|||
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)
|
||||
root.grab_keyboard(owner_events=False, pointer_mode=X.GrabModeAsync, keyboard_mode=X.GrabModeAsync, time=X.CurrentTime)
|
||||
# Consume keyboard events
|
||||
while self.lock_keyboard:
|
||||
event = display.next_event()
|
||||
display.allow_events(mode = X.AsyncBoth, time = X.CurrentTime)
|
||||
display.allow_events(mode=X.AsyncBoth, time=X.CurrentTime)
|
||||
if self.enable_shortcut and event.type == X.KeyPress:
|
||||
if event.detail == self.keycode_shortcut_skip:
|
||||
self.skip_break()
|
||||
|
@ -235,14 +225,12 @@ class BreakScreen:
|
|||
display.ungrab_keyboard(X.CurrentTime)
|
||||
display.flush()
|
||||
|
||||
|
||||
def __release_keyboard(self):
|
||||
"""
|
||||
Release the locked keyboard.
|
||||
"""
|
||||
self.lock_keyboard = False
|
||||
|
||||
|
||||
def __destroy_all_screens(self):
|
||||
"""
|
||||
Close all the break screens.
|
||||
|
|
|
@ -24,12 +24,12 @@ from safeeyes import Utility
|
|||
|
||||
APPINDICATOR_ID = 'safeeyes'
|
||||
|
||||
|
||||
class Notification:
|
||||
"""
|
||||
This class is responsible for the notification to the user before the break.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, context, language):
|
||||
"""
|
||||
Initialize the notification.
|
||||
|
@ -39,13 +39,11 @@ class Notification:
|
|||
self.context = context
|
||||
self.language = language
|
||||
|
||||
|
||||
def initialize(self, language):
|
||||
"""
|
||||
Initialize the notification object.
|
||||
"""
|
||||
self.language = language
|
||||
|
||||
|
||||
def show(self, warning_time):
|
||||
"""
|
||||
|
@ -59,14 +57,13 @@ class Notification:
|
|||
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.
|
||||
|
@ -78,10 +75,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,12 +23,12 @@ 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.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, config):
|
||||
"""
|
||||
Load the plugins.
|
||||
|
@ -47,18 +47,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 +69,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 +85,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 +103,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 +120,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 +132,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,13 +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:
|
||||
|
||||
"""
|
||||
|
@ -48,14 +50,13 @@ class SafeEyesCore:
|
|||
self.context['skipped'] = False
|
||||
self.context['postponed'] = False
|
||||
|
||||
|
||||
"""
|
||||
Initialize the internal properties from configuration
|
||||
"""
|
||||
def initialize(self, config, language):
|
||||
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,7 +112,6 @@ class SafeEyesCore:
|
|||
|
||||
self.long_break_exercises.append([name, break_time, audible_alert, image])
|
||||
|
||||
|
||||
"""
|
||||
Start Safe Eyes is it is not running already.
|
||||
"""
|
||||
|
@ -125,7 +125,6 @@ class SafeEyesCore:
|
|||
if self.context['idle_pause_enabled']:
|
||||
Utility.start_thread(self.__start_idle_monitor)
|
||||
|
||||
|
||||
"""
|
||||
Stop Safe Eyes if it is running.
|
||||
"""
|
||||
|
@ -150,7 +149,6 @@ class SafeEyesCore:
|
|||
self.idle_condition.notify_all()
|
||||
self.idle_condition.release()
|
||||
|
||||
|
||||
"""
|
||||
Pause Safe Eyes if it is running.
|
||||
"""
|
||||
|
@ -162,7 +160,6 @@ class SafeEyesCore:
|
|||
self.notification_condition.notify_all()
|
||||
self.notification_condition.release()
|
||||
|
||||
|
||||
"""
|
||||
Resume Safe Eyes if it is not running.
|
||||
"""
|
||||
|
@ -172,7 +169,6 @@ class SafeEyesCore:
|
|||
self.running = True
|
||||
Utility.start_thread(self.__scheduler_job)
|
||||
|
||||
|
||||
"""
|
||||
User skipped the break using Skip button
|
||||
"""
|
||||
|
@ -185,7 +181,6 @@ class SafeEyesCore:
|
|||
def postpone_break(self):
|
||||
self.context['postponed'] = True
|
||||
|
||||
|
||||
"""
|
||||
Scheduler task to execute during every interval
|
||||
"""
|
||||
|
@ -193,7 +188,7 @@ class SafeEyesCore:
|
|||
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
|
||||
|
@ -222,7 +217,7 @@ class SafeEyesCore:
|
|||
# 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")
|
||||
|
@ -235,7 +230,6 @@ class SafeEyesCore:
|
|||
self.is_before_break = False
|
||||
Utility.execute_main_thread(self.__check_active_window)
|
||||
|
||||
|
||||
"""
|
||||
Show the notification and start the break after the notification.
|
||||
"""
|
||||
|
@ -252,13 +246,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 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():
|
||||
|
@ -272,8 +265,6 @@ class SafeEyesCore:
|
|||
else:
|
||||
Utility.start_thread(self.__show_notification)
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Start the break screen.
|
||||
"""
|
||||
|
@ -311,7 +302,7 @@ class SafeEyesCore:
|
|||
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
|
||||
time.sleep(1) # Sleep for 1 second
|
||||
seconds -= 1
|
||||
|
||||
# Loop terminated because of timeout (not skipped) -> Close the break alert
|
||||
|
@ -327,22 +318,18 @@ class SafeEyesCore:
|
|||
# Schedule the break again
|
||||
Utility.start_thread(self.__scheduler_job)
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Tells whether Safe Eyes is running or not.
|
||||
"""
|
||||
def __is_running(self):
|
||||
return self.active and self.running
|
||||
|
||||
|
||||
"""
|
||||
Check if the current break is long break or short current
|
||||
"""
|
||||
def __is_long_break(self):
|
||||
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.
|
||||
"""
|
||||
|
|
|
@ -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.
|
||||
|
@ -117,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)
|
||||
|
||||
|
@ -129,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.
|
||||
|
@ -149,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)
|
||||
|
@ -176,14 +174,13 @@ class SettingsDialog:
|
|||
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.
|
||||
|
@ -210,9 +207,8 @@ class SettingsDialog:
|
|||
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):
|
||||
"""
|
||||
|
@ -227,4 +223,4 @@ class SettingsDialog:
|
|||
dialog = Gtk.MessageDialog(self.window, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, primary_text)
|
||||
dialog.format_secondary_text(secondary_text)
|
||||
dialog.run()
|
||||
dialog.destroy()
|
||||
dialog.destroy()
|
||||
|
|
|
@ -231,7 +231,6 @@ 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.
|
||||
"""
|
||||
|
@ -254,7 +253,7 @@ class TrayIcon:
|
|||
|
||||
def __schedule_resume(self, time_minutes):
|
||||
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,7 +18,7 @@
|
|||
|
||||
import gi
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gtk, Gdk, GLib
|
||||
from gi.repository import Gdk, GLib
|
||||
from html.parser import HTMLParser
|
||||
from distutils.version import LooseVersion
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
@ -65,10 +65,11 @@ def play_notification():
|
|||
|
||||
# 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)
|
||||
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)
|
||||
|
@ -110,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
|
||||
|
||||
|
@ -144,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')
|
||||
|
@ -206,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.
|
||||
|
@ -273,7 +275,7 @@ def desktop_environment():
|
|||
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']:
|
||||
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'
|
||||
|
@ -314,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
|
||||
|
@ -417,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)
|
||||
|
||||
|
@ -484,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')
|
||||
|
@ -41,6 +41,7 @@ about_dialog_glade = os.path.join(Utility.bin_directory, "glade/about_dialog.gla
|
|||
is_active = True
|
||||
SAFE_EYES_VERSION = "1.2.1"
|
||||
|
||||
|
||||
"""
|
||||
Listen to tray icon Settings action and send the signal to Settings dialog.
|
||||
"""
|
||||
|
@ -52,6 +53,7 @@ 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.
|
||||
"""
|
||||
|
@ -60,6 +62,7 @@ def show_about():
|
|||
about_dialog = AboutDialog(about_dialog_glade, SAFE_EYES_VERSION, language)
|
||||
about_dialog.show()
|
||||
|
||||
|
||||
"""
|
||||
Receive the signal from core and pass it to the Notification.
|
||||
"""
|
||||
|
@ -69,6 +72,7 @@ def show_notification():
|
|||
plugins.pre_notification(context)
|
||||
notification.show(config['pre_break_warning_time'])
|
||||
|
||||
|
||||
"""
|
||||
Receive the break signal from core and pass it to the break screen.
|
||||
"""
|
||||
|
@ -80,6 +84,7 @@ 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.
|
||||
"""
|
||||
|
@ -101,9 +106,10 @@ def on_quit():
|
|||
logging.info("Quit Safe Eyes")
|
||||
plugins.exit(context)
|
||||
core.stop()
|
||||
notification.quite();
|
||||
notification.quite()
|
||||
Gtk.main_quit()
|
||||
|
||||
|
||||
"""
|
||||
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.
|
||||
|
@ -120,6 +126,7 @@ def handle_suspend_callback(sleeping):
|
|||
core.start()
|
||||
logging.info("Resumed Safe Eyes after system wakeup")
|
||||
|
||||
|
||||
"""
|
||||
Setup system suspend listener.
|
||||
"""
|
||||
|
@ -128,6 +135,7 @@ def handle_system_suspend():
|
|||
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.
|
||||
"""
|
||||
|
@ -139,6 +147,7 @@ def on_skipped():
|
|||
core.skip_break()
|
||||
plugins.post_break(context)
|
||||
|
||||
|
||||
"""
|
||||
Listen to break screen Postpone action and send the signal to core.
|
||||
"""
|
||||
|
@ -149,6 +158,7 @@ def on_postponed():
|
|||
Utility.lock_desktop(system_lock_command)
|
||||
core.postpone_break()
|
||||
|
||||
|
||||
"""
|
||||
Listen to Settings dialog Save action and write to the config file.
|
||||
"""
|
||||
|
@ -180,6 +190,7 @@ def save_settings(config):
|
|||
# 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.
|
||||
"""
|
||||
|
@ -188,6 +199,7 @@ def enable_safeeyes():
|
|||
is_active = True
|
||||
core.start()
|
||||
|
||||
|
||||
"""
|
||||
Listen to tray icon disable action and send the signal to core.
|
||||
"""
|
||||
|
@ -203,7 +215,8 @@ def 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):
|
||||
|
@ -257,7 +270,6 @@ 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()
|
||||
|
@ -279,5 +291,6 @@ def main():
|
|||
logging.info('Another instance of safeeyes is already running')
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
12
setup.py
12
setup.py
|
@ -3,13 +3,13 @@ import setuptools
|
|||
|
||||
|
||||
requires = [
|
||||
'python-xlib',
|
||||
'psutil',
|
||||
'babel']
|
||||
'python-xlib',
|
||||
'psutil',
|
||||
'babel']
|
||||
|
||||
extras = {
|
||||
'audible_alert': ['pyaudio']
|
||||
}
|
||||
'audible_alert': ['pyaudio']
|
||||
}
|
||||
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
|
@ -17,12 +17,14 @@ 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",
|
||||
|
|
Loading…
Reference in New Issue