diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..8b079ee4
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,13 @@
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file
+[*]
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
+indent_style = space
+indent_size = 4
+
+[*.{py}]
+trim_trailing_whitespace = true
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 00000000..eb7d934b
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,12 @@
+name: Lint
+
+on: [push, pull_request]
+
+jobs:
+ lint-ruff:
+ runs-on: ubuntu-latest
+ name: ruff
+ steps:
+ - name: Check out source repository
+ uses: actions/checkout@v3
+ - uses: chartboost/ruff-action@v1
diff --git a/ruff.toml b/ruff.toml
new file mode 100644
index 00000000..4724771d
--- /dev/null
+++ b/ruff.toml
@@ -0,0 +1,17 @@
+target-version = "py310"
+
+# gettext
+builtins = ["_"]
+
+[lint]
+select = ["E", "W", "F", "D2", "D3", "D4"]
+ignore = [
+ # PyGObject needs require_version before imports
+ "E402",
+
+ # these are disabled for now, but should probably be cleaned up at some point
+ "D205", "D401", "D404",
+]
+
+[lint.pydocstyle]
+convention = "numpy"
diff --git a/safeeyes/__main__.py b/safeeyes/__main__.py
index 71234544..6b269a83 100755
--- a/safeeyes/__main__.py
+++ b/safeeyes/__main__.py
@@ -16,9 +16,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+"""Safe Eyes is a utility to remind you to take break frequently to protect
+your eyes from eye strain.
"""
-Safe Eyes is a utility to remind you to take break frequently to protect your eyes from eye strain.
-"""
+
import argparse
import gettext
import locale
@@ -33,13 +34,11 @@
from safeeyes.safeeyes import SAFE_EYES_VERSION
from safeeyes.rpc import RPCClient
-gettext.install('safeeyes', utility.LOCALE_PATH)
+gettext.install("safeeyes", utility.LOCALE_PATH)
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:
@@ -52,7 +51,9 @@ def __running():
else:
# In older versions cmdline was a list object
cmd_line = proc.cmdline
- if ('python3' in cmd_line[0] or 'python' in cmd_line[0]) and ('safeeyes' in cmd_line[1] or 'safeeyes' in cmd_line):
+ if ("python3" in cmd_line[0] or "python" in cmd_line[0]) and (
+ "safeeyes" in cmd_line[1] or "safeeyes" in cmd_line
+ ):
process_count += 1
if process_count > 1:
return True
@@ -64,29 +65,63 @@ def __running():
def main():
- """
- Start the Safe Eyes.
- """
- system_locale = gettext.translation('safeeyes', localedir=utility.LOCALE_PATH, languages=[utility.system_locale(), 'en_US'], fallback=True)
+ """Start the Safe Eyes."""
+ system_locale = gettext.translation(
+ "safeeyes",
+ localedir=utility.LOCALE_PATH,
+ languages=[utility.system_locale(), "en_US"],
+ fallback=True,
+ )
system_locale.install()
try:
# locale.bindtextdomain is required for Glade files
- locale.bindtextdomain('safeeyes', utility.LOCALE_PATH)
+ locale.bindtextdomain("safeeyes", utility.LOCALE_PATH)
except AttributeError:
- logging.warning('installed python\'s gettext module does not support locale.bindtextdomain. locale.bindtextdomain is required for Glade files')
-
+ logging.warning(
+ "installed python's gettext module does not support locale.bindtextdomain."
+ " locale.bindtextdomain is required for Glade files"
+ )
- parser = argparse.ArgumentParser(prog='safeeyes')
+ parser = argparse.ArgumentParser(prog="safeeyes")
group = parser.add_mutually_exclusive_group()
- group.add_argument('-a', '--about', help=_('show the about dialog'), action='store_true')
- group.add_argument('-d', '--disable', help=_('disable the currently running safeeyes instance'), action='store_true')
- group.add_argument('-e', '--enable', help=_('enable the currently running safeeyes instance'), action='store_true')
- group.add_argument('-q', '--quit', help=_('quit the running safeeyes instance and exit'), action='store_true')
- group.add_argument('-s', '--settings', help=_('show the settings dialog'), action='store_true')
- group.add_argument('-t', '--take-break', help=_('Take a break now').lower(), action='store_true')
- parser.add_argument('--debug', help=_('start safeeyes in debug mode'), action='store_true')
- parser.add_argument('--status', help=_('print the status of running safeeyes instance and exit'), action='store_true')
- parser.add_argument('--version', action='version', version='%(prog)s ' + SAFE_EYES_VERSION)
+ group.add_argument(
+ "-a", "--about", help=_("show the about dialog"), action="store_true"
+ )
+ group.add_argument(
+ "-d",
+ "--disable",
+ help=_("disable the currently running safeeyes instance"),
+ action="store_true",
+ )
+ group.add_argument(
+ "-e",
+ "--enable",
+ help=_("enable the currently running safeeyes instance"),
+ action="store_true",
+ )
+ group.add_argument(
+ "-q",
+ "--quit",
+ help=_("quit the running safeeyes instance and exit"),
+ action="store_true",
+ )
+ group.add_argument(
+ "-s", "--settings", help=_("show the settings dialog"), action="store_true"
+ )
+ group.add_argument(
+ "-t", "--take-break", help=_("Take a break now").lower(), action="store_true"
+ )
+ parser.add_argument(
+ "--debug", help=_("start safeeyes in debug mode"), action="store_true"
+ )
+ parser.add_argument(
+ "--status",
+ help=_("print the status of running safeeyes instance and exit"),
+ action="store_true",
+ )
+ parser.add_argument(
+ "--version", action="version", version="%(prog)s " + SAFE_EYES_VERSION
+ )
args = parser.parse_args()
# Initialize the logging
@@ -99,10 +134,15 @@ def main():
logging.info("Safe Eyes is already running")
if not config.get("use_rpc_server", True):
# RPC sever is disabled
- print(_('Safe Eyes is running without an RPC server. Turn it on to use command-line arguments.'))
+ print(
+ _(
+ "Safe Eyes is running without an RPC server. Turn it on to use"
+ " command-line arguments."
+ )
+ )
sys.exit(0)
return
- rpc_client = RPCClient(config.get('rpc_port'))
+ rpc_client = RPCClient(config.get("rpc_port"))
if args.about:
rpc_client.show_about()
elif args.disable:
@@ -123,7 +163,7 @@ def main():
sys.exit(0)
else:
if args.status:
- print(_('Safe Eyes is not running'))
+ print(_("Safe Eyes is not running"))
sys.exit(0)
elif not args.quit:
logging.info("Starting Safe Eyes")
@@ -131,6 +171,6 @@ def main():
safe_eyes.start()
-if __name__ == '__main__':
- signal.signal(signal.SIGINT, signal.SIG_DFL) # Handle Ctrl + C
+if __name__ == "__main__":
+ signal.signal(signal.SIGINT, signal.SIG_DFL) # Handle Ctrl + C
main()
diff --git a/safeeyes/core.py b/safeeyes/core.py
index e0fcbee5..44a68efb 100644
--- a/safeeyes/core.py
+++ b/safeeyes/core.py
@@ -16,9 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-SafeEyesCore provides the core functionalities of Safe Eyes.
-"""
+"""SafeEyesCore provides the core functionalities of Safe Eyes."""
import datetime
import logging
@@ -26,7 +24,6 @@
import time
from safeeyes import utility
-from safeeyes.model import Break
from safeeyes.model import BreakType
from safeeyes.model import BreakQueue
from safeeyes.model import EventHook
@@ -34,14 +31,10 @@
class SafeEyesCore:
- """
- Core of Safe Eyes runs the scheduler and notifies the breaks.
- """
+ """Core of Safe Eyes runs the scheduler and notifies the breaks."""
def __init__(self, context):
- """
- Create an instance of SafeEyesCore and initialize the variables.
- """
+ """Create an instance of SafeEyesCore and initialize the variables."""
self.break_queue = None
self.postpone_duration = 0
self.default_postpone_duration = 0
@@ -65,26 +58,24 @@ def __init__(self, context):
self.waiting_condition = threading.Condition()
self.lock = threading.Lock()
self.context = context
- self.context['skipped'] = False
- self.context['postponed'] = False
- self.context['skip_button_disabled'] = False
- self.context['postpone_button_disabled'] = False
- self.context['state'] = State.WAITING
+ self.context["skipped"] = False
+ self.context["postponed"] = False
+ self.context["skip_button_disabled"] = False
+ self.context["postpone_button_disabled"] = False
+ self.context["state"] = State.WAITING
def initialize(self, config):
- """
- Initialize the internal properties from configuration
- """
+ """Initialize the internal properties from configuration."""
logging.info("Initialize the core")
- self.pre_break_warning_time = config.get('pre_break_warning_time')
+ self.pre_break_warning_time = config.get("pre_break_warning_time")
self.break_queue = BreakQueue(config, self.context)
- self.default_postpone_duration = config.get('postpone_duration') * 60 # Convert to seconds
+ self.default_postpone_duration = (
+ config.get("postpone_duration") * 60
+ ) # Convert to seconds
self.postpone_duration = self.default_postpone_duration
def start(self, next_break_time=-1, reset_breaks=False):
- """
- Start Safe Eyes is it is not running already.
- """
+ """Start Safe Eyes is it is not running already."""
if self.break_queue.is_empty():
return
with self.lock:
@@ -99,9 +90,7 @@ def start(self, next_break_time=-1, reset_breaks=False):
utility.start_thread(self.__scheduler_job)
def stop(self, is_resting=False):
- """
- Stop Safe Eyes if it is running.
- """
+ """Stop Safe Eyes if it is running."""
with self.lock:
if not self.running:
return
@@ -111,59 +100,54 @@ def stop(self, is_resting=False):
# Stop the break thread
self.waiting_condition.acquire()
self.running = False
- if self.context['state'] != State.QUIT:
- self.context['state'] = State.RESTING if (is_resting) else State.STOPPED
+ if self.context["state"] != State.QUIT:
+ self.context["state"] = State.RESTING if (is_resting) else State.STOPPED
self.waiting_condition.notify_all()
self.waiting_condition.release()
def skip(self):
- """
- User skipped the break using Skip button
- """
- self.context['skipped'] = True
+ """User skipped the break using Skip button."""
+ self.context["skipped"] = True
def postpone(self, duration=-1):
- """
- User postponed the break using Postpone button
- """
+ """User postponed the break using Postpone button."""
if duration > 0:
self.postpone_duration = duration
else:
self.postpone_duration = self.default_postpone_duration
logging.debug("Postpone the break for %d seconds", self.postpone_duration)
- self.context['postponed'] = True
+ self.context["postponed"] = True
- def get_break_time(self, break_type = None):
- """
- Returns the next break time
- """
+ def get_break_time(self, break_type=None):
+ """Returns the next break time."""
break_obj = self.break_queue.get_break(break_type)
if not break_obj:
return False
- time = self.scheduled_next_break_time + datetime.timedelta(minutes=break_obj.time - self.break_queue.get_break().time)
+ time = self.scheduled_next_break_time + datetime.timedelta(
+ minutes=break_obj.time - self.break_queue.get_break().time
+ )
return time
- def take_break(self, break_type = None):
- """
- Calling this method stops the scheduler and show the next break screen
+ def take_break(self, break_type=None):
+ """Calling this method stops the scheduler and show the next break
+ screen.
"""
if self.break_queue.is_empty():
return
- if not self.context['state'] == State.WAITING:
+ if not self.context["state"] == State.WAITING:
return
utility.start_thread(self.__take_break, break_type=break_type)
- def has_breaks(self, break_type = None):
- """
- Check whether Safe Eyes has breaks or not. Use the break_type to check for either short or long break.
+ def has_breaks(self, break_type=None):
+ """Check whether Safe Eyes has breaks or not.
+
+ Use the break_type to check for either short or long break.
"""
return not self.break_queue.is_empty(break_type)
- def __take_break(self, break_type = None):
- """
- Show the next break screen
- """
- logging.info('Take a break due to external request')
+ def __take_break(self, break_type=None):
+ """Show the next break screen."""
+ logging.info("Take a break due to external request")
with self.lock:
if not self.running:
@@ -184,40 +168,51 @@ def __take_break(self, break_type = None):
utility.execute_main_thread(self.__fire_start_break)
def __scheduler_job(self):
- """
- Scheduler task to execute during every interval
- """
+ """Scheduler task to execute during every interval."""
if not self.running:
return
current_time = datetime.datetime.now()
current_timestamp = current_time.timestamp()
- if self.context['state'] == State.RESTING and self.paused_time > -1:
+ if self.context["state"] == State.RESTING and self.paused_time > -1:
# Safe Eyes was resting
paused_duration = int(current_timestamp - self.paused_time)
self.paused_time = -1
- if paused_duration > self.break_queue.get_break(BreakType.LONG_BREAK).duration:
- logging.info('Skip next long break due to the pause %ds longer than break duration', paused_duration)
+ if (
+ paused_duration
+ > self.break_queue.get_break(BreakType.LONG_BREAK).duration
+ ):
+ logging.info(
+ "Skip next long break due to the pause %ds longer than break"
+ " duration",
+ paused_duration,
+ )
# Skip the next long break
self.break_queue.reset()
- if self.context['postponed']:
+ if self.context["postponed"]:
# Previous break was postponed
- logging.info('Prepare for postponed break')
+ logging.info("Prepare for postponed break")
time_to_wait = self.postpone_duration
- self.context['postponed'] = False
+ self.context["postponed"] = False
elif current_timestamp < self.scheduled_next_break_timestamp:
# Non-standard break was set.
- time_to_wait = round(self.scheduled_next_break_timestamp - current_timestamp)
+ time_to_wait = round(
+ self.scheduled_next_break_timestamp - current_timestamp
+ )
self.scheduled_next_break_timestamp = -1
else:
# Use next break, convert to seconds
time_to_wait = self.break_queue.get_break().time * 60
- self.scheduled_next_break_time = current_time + datetime.timedelta(seconds=time_to_wait)
- self.context['state'] = State.WAITING
- utility.execute_main_thread(self.__fire_on_update_next_break, self.scheduled_next_break_time)
+ self.scheduled_next_break_time = current_time + datetime.timedelta(
+ seconds=time_to_wait
+ )
+ self.context["state"] = State.WAITING
+ utility.execute_main_thread(
+ self.__fire_on_update_next_break, self.scheduled_next_break_time
+ )
# Wait for the pre break warning period
logging.info("Waiting for %d minutes until next break", (time_to_wait / 60))
@@ -230,16 +225,12 @@ def __scheduler_job(self):
utility.execute_main_thread(self.__fire_pre_break)
def __fire_on_update_next_break(self, next_break_time):
- """
- Pass the next break information to the registered listeners.
- """
+ """Pass the next break information to the registered listeners."""
self.on_update_next_break.fire(self.break_queue.get_break(), next_break_time)
def __fire_pre_break(self):
- """
- Show the notification and start the break after the notification.
- """
- self.context['state'] = State.PRE_BREAK
+ """Show the notification and start the break after the notification."""
+ self.context["state"] = State.PRE_BREAK
if not self.on_pre_break.fire(self.break_queue.get_break()):
# Plugins wanted to ignore this break
self.__start_next_break()
@@ -247,7 +238,9 @@ def __fire_pre_break(self):
utility.start_thread(self.__wait_until_prepare)
def __wait_until_prepare(self):
- logging.info("Wait for %d seconds before the break", self.pre_break_warning_time)
+ logging.info(
+ "Wait for %d seconds before the break", self.pre_break_warning_time
+ )
# Wait for the pre break warning period
self.__wait_for(self.pre_break_warning_time)
if not self.running:
@@ -265,11 +258,14 @@ def __fire_start_break(self):
# Plugins want to ignore this break
self.__start_next_break()
return
- if self.context['postponed']:
+ if self.context["postponed"]:
# Plugins want to postpone this break
- self.context['postponed'] = False
+ self.context["postponed"] = False
# Update the next break time
- self.scheduled_next_break_time = self.scheduled_next_break_time + datetime.timedelta(seconds=self.postpone_duration)
+ self.scheduled_next_break_time = (
+ self.scheduled_next_break_time
+ + datetime.timedelta(seconds=self.postpone_duration)
+ )
self.__fire_on_update_next_break(self.scheduled_next_break_time)
# Wait in user thread
utility.start_thread(self.__postpone_break)
@@ -278,43 +274,44 @@ def __fire_start_break(self):
utility.start_thread(self.__start_break)
def __start_break(self):
- """
- Start the break screen.
- """
- self.context['state'] = State.BREAK
+ """Start the break screen."""
+ self.context["state"] = State.BREAK
break_obj = self.break_queue.get_break()
countdown = break_obj.duration
total_break_time = countdown
- while countdown and self.running and not self.context['skipped'] and not self.context['postponed']:
+ while (
+ countdown
+ and self.running
+ and not self.context["skipped"]
+ and not self.context["postponed"]
+ ):
seconds = total_break_time - countdown
self.on_count_down.fire(countdown, seconds)
- time.sleep(1) # Sleep for 1 second
+ time.sleep(1) # Sleep for 1 second
countdown -= 1
utility.execute_main_thread(self.__fire_stop_break)
def __fire_stop_break(self):
# Loop terminated because of timeout (not skipped) -> Close the break alert
- if not self.context['skipped'] and not self.context['postponed']:
+ if not self.context["skipped"] and not self.context["postponed"]:
logging.info("Break is terminated automatically")
self.on_stop_break.fire()
# Reset the skipped flag
- self.context['skipped'] = False
- self.context['skip_button_disabled'] = False
- self.context['postpone_button_disabled'] = False
+ self.context["skipped"] = False
+ self.context["skip_button_disabled"] = False
+ self.context["postpone_button_disabled"] = False
self.__start_next_break()
def __wait_for(self, duration):
- """
- Wait until someone wake up or the timeout happens.
- """
+ """Wait until someone wake up or the timeout happens."""
self.waiting_condition.acquire()
self.waiting_condition.wait(duration)
self.waiting_condition.release()
def __start_next_break(self):
- if not self.context['postponed']:
+ if not self.context["postponed"]:
self.break_queue.next()
if self.running:
diff --git a/safeeyes/model.py b/safeeyes/model.py
index 2f8f9b88..30871236 100644
--- a/safeeyes/model.py
+++ b/safeeyes/model.py
@@ -16,8 +16,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-This module contains the entity classes used by Safe Eyes and its plugins.
+"""This module contains the entity classes used by Safe Eyes and its
+plugins.
"""
import logging
@@ -31,9 +31,7 @@
class Break:
- """
- An entity class which represents a break.
- """
+ """An entity class which represents a break."""
def __init__(self, break_type, name, time, duration, image, plugins):
self.type = break_type
@@ -45,27 +43,23 @@ def __init__(self, break_type, name, time, duration, image, plugins):
self.next = None
def __str__(self):
- return 'Break: {{name: "{}", type: {}, duration: {}}}\n'.format(self.name, self.type, self.duration)
+ return 'Break: {{name: "{}", type: {}, duration: {}}}\n'.format(
+ self.name, self.type, self.duration
+ )
def __repr__(self):
return str(self)
def is_long_break(self):
- """
- Check whether this break is a long break.
- """
+ """Check whether this break is a long break."""
return self.type == BreakType.LONG_BREAK
def is_short_break(self):
- """
- Check whether this break is a short break.
- """
+ """Check whether this break is a short break."""
return self.type == BreakType.SHORT_BREAK
def plugin_enabled(self, plugin_id, is_plugin_enabled):
- """
- Check whether this break supports the given plugin.
- """
+ """Check whether this break supports the given plugin."""
if self.plugins:
return plugin_id in self.plugins
else:
@@ -73,9 +67,8 @@ def plugin_enabled(self, plugin_id, is_plugin_enabled):
class BreakType(Enum):
- """
- Type of Safe Eyes breaks.
- """
+ """Type of Safe Eyes breaks."""
+
SHORT_BREAK = 1
LONG_BREAK = 2
@@ -86,19 +79,18 @@ def __init__(self, config, context):
self.__current_break = None
self.__current_long = 0
self.__current_short = 0
- self.__short_break_time = config.get('short_break_interval')
- self.__long_break_time = config.get('long_break_interval')
- self.__is_random_order = config.get('random_order')
+ self.__short_break_time = config.get("short_break_interval")
+ self.__long_break_time = config.get("long_break_interval")
+ self.__is_random_order = config.get("random_order")
self.__config = config
self.__build_longs()
self.__build_shorts()
-
# Interface guarantees that short_interval >= 1
# And that long_interval is a multiple of short_interval
- short_interval = config.get('short_break_interval')
- long_interval = config.get('long_break_interval')
+ short_interval = config.get("short_break_interval")
+ long_interval = config.get("long_break_interval")
self.__cycle_len = int(long_interval / short_interval)
# To count every long break as a cycle in .next() if there are no short breaks
if self.__short_queue is None:
@@ -106,7 +98,7 @@ def __init__(self, config, context):
# Restore the last break from session
if not self.is_empty():
- last_break = context['session'].get('break')
+ last_break = context["session"].get("break")
if last_break is not None:
current_break = self.get_break()
if last_break != current_break.name:
@@ -114,7 +106,7 @@ def __init__(self, config, context):
while brk != current_break and brk.name != last_break:
brk = self.next()
- def get_break(self, break_type = None):
+ def get_break(self, break_type=None):
if self.__current_break is None:
self.__current_break = self.next()
@@ -123,20 +115,23 @@ def get_break(self, break_type = None):
if break_type == BreakType.LONG_BREAK:
if self.__long_queue is None:
- return None;
+ return None
return self.__long_queue[self.__current_long]
if self.__short_queue is None:
- return None;
+ return None
return self.__short_queue[self.__current_short]
def is_long_break(self):
- return self.__current_break is not None and self.__current_break.type == BreakType.LONG_BREAK
+ return (
+ self.__current_break is not None
+ and self.__current_break.type == BreakType.LONG_BREAK
+ )
- def next(self, break_type = None):
+ def next(self, break_type=None):
break_obj = None
shorts = self.__short_queue
- longs = self.__long_queue
+ longs = self.__long_queue
# Reset break that has just ended
if self.is_long_break():
@@ -158,13 +153,16 @@ def next(self, break_type = None):
break_obj = self.__next_long()
elif longs is None:
break_obj = self.__next_short()
- elif break_type == BreakType.LONG_BREAK or longs[self.__current_long].time <= shorts[self.__current_short].time:
+ elif (
+ break_type == BreakType.LONG_BREAK
+ or longs[self.__current_long].time <= shorts[self.__current_short].time
+ ):
break_obj = self.__next_long()
else:
break_obj = self.__next_short()
self.__current_break = break_obj
- self.context['session']['break'] = self.__current_break.name
+ self.context["session"]["break"] = self.__current_break.name
return break_obj
@@ -175,9 +173,10 @@ def reset(self):
for break_object in self.__long_queue:
break_object.time = self.__long_break_time
- def is_empty(self, break_type = None):
- """
- Check if the given break type is empty or not. If the break_type is None, check for both short and long breaks.
+ def is_empty(self, break_type=None):
+ """Check if the given break type is empty or not.
+
+ If the break_type is None, check for both short and long breaks.
"""
if break_type == BreakType.SHORT_BREAK:
return self.__short_queue is None
@@ -187,10 +186,9 @@ def is_empty(self, break_type = None):
return self.__short_queue is None and self.__long_queue is None
def __next_short(self):
- longs = self.__long_queue
shorts = self.__short_queue
break_obj = shorts[self.__current_short]
- self.context['break_type'] = 'short'
+ self.context["break_type"] = "short"
# Update the index to next
self.__current_short = (self.__current_short + 1) % len(shorts)
@@ -198,9 +196,9 @@ def __next_short(self):
return break_obj
def __next_long(self):
- longs = self.__long_queue
+ longs = self.__long_queue
break_obj = longs[self.__current_long]
- self.context['break_type'] = 'long'
+ self.context["break_type"] = "long"
# Update the index to next
self.__current_long = (self.__current_long + 1) % len(longs)
@@ -208,9 +206,7 @@ def __next_long(self):
return break_obj
def __build_queue(self, break_type, break_configs, break_time, break_duration):
- """
- Build a queue of breaks.
- """
+ """Build a queue of breaks."""
size = len(break_configs)
if 0 == size:
@@ -224,55 +220,53 @@ def __build_queue(self, break_type, break_configs, break_time, break_duration):
queue = [None] * size
for i, break_config in enumerate(breaks_order):
- name = _(break_config['name'])
- duration = break_config.get('duration', break_duration)
- image = break_config.get('image')
- plugins = break_config.get('plugins', None)
- interval = break_config.get('interval', break_time)
+ name = _(break_config["name"])
+ duration = break_config.get("duration", break_duration)
+ image = break_config.get("image")
+ plugins = break_config.get("plugins", None)
+ interval = break_config.get("interval", break_time)
# Validate time value
if not isinstance(duration, int) or duration <= 0:
- logging.error('Invalid break duration in: ' +
- str(break_config))
+ logging.error("Invalid break duration in: " + str(break_config))
continue
- break_obj = Break(break_type, name, interval,
- duration, image, plugins)
+ break_obj = Break(break_type, name, interval, duration, image, plugins)
queue[i] = break_obj
return queue
def __build_shorts(self):
- self.__short_queue = self.__build_queue(BreakType.SHORT_BREAK,
- self.__config.get('short_breaks'),
- self.__short_break_time,
- self.__config.get('short_break_duration'))
+ self.__short_queue = self.__build_queue(
+ BreakType.SHORT_BREAK,
+ self.__config.get("short_breaks"),
+ self.__short_break_time,
+ self.__config.get("short_break_duration"),
+ )
def __build_longs(self):
- self.__long_queue = self.__build_queue(BreakType.LONG_BREAK,
- self.__config.get('long_breaks'),
- self.__long_break_time,
- self.__config.get('long_break_duration'))
-
+ self.__long_queue = self.__build_queue(
+ BreakType.LONG_BREAK,
+ self.__config.get("long_breaks"),
+ self.__long_break_time,
+ self.__config.get("long_break_duration"),
+ )
class State(Enum):
- """
- Possible states of Safe Eyes.
- """
- START = 0, # Starting scheduler
- WAITING = 1, # User is working (waiting for next break)
- PRE_BREAK = 2, # Preparing for break
- BREAK = 3, # Break
- STOPPED = 4, # Disabled
- QUIT = 5, # Quitting
- RESTING = 6 # Resting (natural break)
+ """Possible states of Safe Eyes."""
+
+ START = (0,) # Starting scheduler
+ WAITING = (1,) # User is working (waiting for next break)
+ PRE_BREAK = (2,) # Preparing for break
+ BREAK = (3,) # Break
+ STOPPED = (4,) # Disabled
+ QUIT = (5,) # Quitting
+ RESTING = 6 # Resting (natural break)
class EventHook:
- """
- Hook to attach and detach listeners to system events.
- """
+ """Hook to attach and detach listeners to system events."""
def __init__(self):
self.__handlers = []
@@ -286,9 +280,7 @@ def __isub__(self, handler):
return self
def fire(self, *args, **keywargs):
- """
- Fire all listeners attached with.
- """
+ """Fire all listeners attached with."""
for handler in self.__handlers:
if not handler(*args, **keywargs):
return False
@@ -296,39 +288,38 @@ def fire(self, *args, **keywargs):
class Config:
- """
- The configuration of Safe Eyes.
- """
+ """The configuration of Safe Eyes."""
def __init__(self, init=True):
# Read the config files
self.__user_config = utility.load_json(utility.CONFIG_FILE_PATH)
- self.__system_config = utility.load_json(
- utility.SYSTEM_CONFIG_FILE_PATH)
- # If there any breaking changes in long_breaks, short_breaks or any other keys, use the __force_upgrade list
+ self.__system_config = utility.load_json(utility.SYSTEM_CONFIG_FILE_PATH)
+ # If there any breaking changes in long_breaks, short_breaks or any other keys,
+ # use the __force_upgrade list
self.__force_upgrade = []
# self.__force_upgrade = ['long_breaks', 'short_breaks']
if init:
- # if create_startup_entry finds a broken autostart symlink, it will repair it
+ # if create_startup_entry finds a broken autostart symlink, it will repair
+ # it
utility.create_startup_entry(force=False)
if self.__user_config is None:
utility.initialize_safeeyes()
self.__user_config = self.__system_config
self.save()
else:
- system_config_version = self.__system_config['meta']['config_version']
- meta_obj = self.__user_config.get('meta', None)
+ system_config_version = self.__system_config["meta"]["config_version"]
+ meta_obj = self.__user_config.get("meta", None)
if meta_obj is None:
# Corrupted user config
self.__user_config = self.__system_config
else:
- user_config_version = str(
- meta_obj.get('config_version', '0.0.0'))
+ user_config_version = str(meta_obj.get("config_version", "0.0.0"))
if parse(user_config_version) != parse(system_config_version):
# Update the user config
self.__merge_dictionary(
- self.__user_config, self.__system_config)
+ self.__user_config, self.__system_config
+ )
self.__user_config = self.__system_config
# Update the style sheet
utility.replace_style_sheet()
@@ -337,9 +328,7 @@ def __init__(self, init=True):
self.save()
def __merge_dictionary(self, old_dict, new_dict):
- """
- Merge the dictionaries.
- """
+ """Merge the dictionaries."""
for key in new_dict:
if key == "meta" or key in self.__force_upgrade:
continue
@@ -358,24 +347,18 @@ def clone(self):
return config
def save(self):
- """
- Save the configuration to file.
- """
+ """Save the configuration to file."""
utility.write_json(utility.CONFIG_FILE_PATH, self.__user_config)
def get(self, key, default_value=None):
- """
- Get the value.
- """
+ """Get the value."""
value = self.__user_config.get(key, default_value)
if value is None:
value = self.__system_config.get(key, None)
return value
def set(self, key, value):
- """
- Set the value.
- """
+ """Set the value."""
self.__user_config[key] = value
def __eq__(self, config):
@@ -386,9 +369,7 @@ def __ne__(self, config):
class TrayAction:
- """
- Data object wrapping name, icon and action.
- """
+ """Data object wrapping name, icon and action."""
def __init__(self, name, icon, action, system_icon):
self.name = name
@@ -421,14 +402,16 @@ def build(cls, name, icon_path, icon_id, action):
else:
return TrayAction(name, icon_path, action, False)
+
@dataclass
class PluginDependency:
message: str
- link: str|None = None
+ link: str | None = None
retryable: bool = False
+
class RequiredPluginException(Exception):
- def __init__(self, plugin_id, plugin_name: str, message: str|PluginDependency):
+ def __init__(self, plugin_id, plugin_name: str, message: str | PluginDependency):
if isinstance(message, PluginDependency):
msg = message.message
else:
diff --git a/safeeyes/plugin_manager.py b/safeeyes/plugin_manager.py
index 41ea4130..35cc4107 100644
--- a/safeeyes/plugin_manager.py
+++ b/safeeyes/plugin_manager.py
@@ -16,8 +16,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-PluginManager loads all enabled plugins and call their lifecycle methods.
+"""PluginManager loads all enabled plugins and call their lifecycle methods.
+
A plugin must have the following directory structure:
|- config.json
@@ -28,7 +28,8 @@
- description()
If a custom description has to be displayed, use this function
- init(context, safeeyes_config, plugin_config)
- Initialize the plugin. Will be called after loading and after every changes in configuration
+ Initialize the plugin. Will be called after loading and after every changes in
+ configuration
- on_start()
Executes when Safe Eyes is enabled
- on_stop()
@@ -64,34 +65,36 @@
HORIZONTAL_LINE_LENGTH = 64
+
class PluginManager:
- """
- Imports the Safe Eyes 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):
- logging.info('Load all the plugins')
+ logging.info("Load all the plugins")
self.__plugins = {}
self.last_break = None
- self.horizontal_line = '─' * HORIZONTAL_LINE_LENGTH
+ self.horizontal_line = "─" * HORIZONTAL_LINE_LENGTH
def init(self, context, config):
- """
- Initialize all the plugins with init(context, safe_eyes_config, plugin_config) function.
+ """Initialize all the plugins with init(context, safe_eyes_config,
+ plugin_config) function.
"""
# Load the plugins
- for plugin in config.get('plugins'):
+ for plugin in config.get("plugins"):
try:
loaded_plugin = LoadedPlugin(plugin)
self.__plugins[loaded_plugin.id] = loaded_plugin
except RequiredPluginException as e:
raise e
except BaseException as e:
- traceback_wanted = logging.getLogger().getEffectiveLevel() == logging.DEBUG
+ traceback_wanted = (
+ logging.getLogger().getEffectiveLevel() == logging.DEBUG
+ )
if traceback_wanted:
import traceback
+
traceback.print_exc()
- logging.error('Error in loading the plugin %s: %s', plugin['id'], e)
+ logging.error("Error in loading the plugin %s: %s", plugin["id"], e)
continue
# Initialize the plugins
for plugin in self.__plugins.values():
@@ -104,11 +107,12 @@ def needs_retry(self):
def get_retryable_error(self):
for plugin in self.__plugins.values():
if plugin.required_plugin and plugin.errored and plugin.enabled:
- if isinstance(plugin.last_error, PluginDependency) and plugin.last_error.retryable:
+ if (
+ isinstance(plugin.last_error, PluginDependency)
+ and plugin.last_error.retryable
+ ):
return RequiredPluginException(
- plugin.id,
- plugin.get_name(),
- plugin.last_error
+ plugin.id, plugin.get_name(), plugin.last_error
)
return None
@@ -116,46 +120,39 @@ def get_retryable_error(self):
def retry_errored_plugins(self):
for plugin in self.__plugins.values():
if plugin.required_plugin and plugin.errored and plugin.enabled:
- if isinstance(plugin.last_error, PluginDependency) and plugin.last_error.retryable:
+ if (
+ isinstance(plugin.last_error, PluginDependency)
+ and plugin.last_error.retryable
+ ):
plugin.reload_errored()
def start(self):
- """
- Execute the on_start() function of plugins.
- """
+ """Execute the on_start() function of plugins."""
for plugin in self.__plugins.values():
plugin.call_plugin_method("on_start")
return True
def stop(self):
- """
- Execute the on_stop() function of plugins.
- """
+ """Execute the on_stop() function of plugins."""
for plugin in self.__plugins.values():
plugin.call_plugin_method("on_stop")
return True
def exit(self):
- """
- Execute the on_exit() function of plugins.
- """
+ """Execute the on_exit() function of plugins."""
for plugin in self.__plugins.values():
plugin.call_plugin_method("on_exit")
return True
def pre_break(self, break_obj):
- """
- Execute the on_pre_break(break_obj) function of plugins.
- """
+ """Execute the on_pre_break(break_obj) function of plugins."""
for plugin in self.__plugins.values():
if plugin.call_plugin_method_break_obj("on_pre_break", 1, break_obj):
return False
return True
def start_break(self, break_obj):
- """
- Execute the start_break(break_obj) function of plugins.
- """
+ """Execute the start_break(break_obj) function of plugins."""
self.last_break = break_obj
for plugin in self.__plugins.values():
if plugin.call_plugin_method_break_obj("on_start_break", 1, break_obj):
@@ -164,56 +161,59 @@ def start_break(self, break_obj):
return True
def stop_break(self):
- """
- Execute the stop_break() function of plugins.
- """
+ """Execute the stop_break() function of plugins."""
for plugin in self.__plugins.values():
plugin.call_plugin_method("on_stop_break")
def countdown(self, countdown, seconds):
- """
- Execute the on_countdown(countdown, seconds) function of plugins.
- """
+ """Execute the on_countdown(countdown, seconds) function of plugins."""
for plugin in self.__plugins.values():
plugin.call_plugin_method("on_countdown", 2, countdown, seconds)
def update_next_break(self, break_obj, break_time):
- """
- Execute the update_next_break(break_time) function of plugins.
- """
+ """Execute the update_next_break(break_time) function of plugins."""
for plugin in self.__plugins.values():
- plugin.call_plugin_method_break_obj("update_next_break", 2, break_obj, break_time)
+ plugin.call_plugin_method_break_obj(
+ "update_next_break", 2, break_obj, break_time
+ )
return True
def get_break_screen_widgets(self, break_obj):
+ """Return the HTML widget generated by the plugins.
+
+ The widget is generated by calling the get_widget_title and
+ get_widget_content functions of plugins.
"""
- Return the HTML widget generated by the plugins.
- The widget is generated by calling the get_widget_title and get_widget_content functions of plugins.
- """
- widget = ''
+ widget = ""
for plugin in self.__plugins.values():
try:
- title = plugin.call_plugin_method_break_obj("get_widget_title", 1, break_obj)
- if title is None or not isinstance(title, str) or title == '':
+ title = plugin.call_plugin_method_break_obj(
+ "get_widget_title", 1, break_obj
+ )
+ if title is None or not isinstance(title, str) or title == "":
continue
- content = plugin.call_plugin_method_break_obj("get_widget_content", 1, break_obj)
- if content is None or not isinstance(content, str) or content == '':
+ content = plugin.call_plugin_method_break_obj(
+ "get_widget_content", 1, break_obj
+ )
+ if content is None or not isinstance(content, str) or content == "":
continue
title = title.upper().strip()
- if title == '':
+ if title == "":
continue
- widget += '{}\n{}\n{}\n\n\n'.format(title, self.horizontal_line, content)
+ widget += "{}\n{}\n{}\n\n\n".format(
+ title, self.horizontal_line, content
+ )
except BaseException:
continue
return widget.strip()
def get_break_screen_tray_actions(self, break_obj):
- """
- Return Tray Actions.
- """
+ """Return Tray Actions."""
actions = []
for plugin in self.__plugins.values():
- action = plugin.call_plugin_method_break_obj("get_tray_action", 1, break_obj)
+ action = plugin.call_plugin_method_break_obj(
+ "get_tray_action", 1, break_obj
+ )
if action:
actions.append(action)
@@ -228,7 +228,8 @@ class LoadedPlugin:
required_plugin: bool = False
# misc data
- # FIXME: rename to plugin_config to plugin_json? plugin_config and config are easy to confuse
+ # FIXME: rename to plugin_config to plugin_json? plugin_config and config are easy
+ # to confuse
config = None
plugin_config = None
plugin_dir = None
@@ -237,56 +238,54 @@ class LoadedPlugin:
id = None
def __init__(self, plugin):
- (plugin_config, plugin_dir) = self._load_config_json(plugin['id'])
+ (plugin_config, plugin_dir) = self._load_config_json(plugin["id"])
- self.id = plugin['id']
+ self.id = plugin["id"]
self.plugin_config = plugin_config
self.plugin_dir = plugin_dir
self.enabled = plugin["enabled"]
- self.break_override_allowed = plugin_config.get('break_override_allowed', False)
+ self.break_override_allowed = plugin_config.get("break_override_allowed", False)
self.required_plugin = plugin_config.get("required_plugin", False)
- self.config = dict(plugin.get('settings', {}))
- self.config['path'] = os.path.join(plugin_dir, plugin['id'])
+ self.config = dict(plugin.get("settings", {}))
+ self.config["path"] = os.path.join(plugin_dir, plugin["id"])
if self.enabled or self.break_override_allowed:
plugin_path = os.path.join(plugin_dir, self.id)
- message = utility.check_plugin_dependencies(plugin['id'], plugin_config, plugin.get('settings', {}), plugin_path)
+ message = utility.check_plugin_dependencies(
+ plugin["id"], plugin_config, plugin.get("settings", {}), plugin_path
+ )
if message:
self.errored = True
self.last_error = message
- if self.required_plugin and not (isinstance(message, PluginDependency) and message.retryable):
+ if self.required_plugin and not (
+ isinstance(message, PluginDependency) and message.retryable
+ ):
raise RequiredPluginException(
- plugin['id'],
- plugin_config['meta']['name'],
- message
+ plugin["id"], plugin_config["meta"]["name"], message
)
return
self._import_plugin()
-
def reload_config(self, plugin):
if self.enabled and not plugin["enabled"]:
self.enabled = False
- if not self.errored and utility.has_method(self.module, 'disable'):
+ if not self.errored and utility.has_method(self.module, "disable"):
self.module.disable()
if not self.enabled and plugin["enabled"]:
self.enabled = True
# Update the config
- self.config = dict(plugin.get('settings', {}))
- self.config['path'] = os.path.join(self.plugin_dir, plugin['id'])
+ self.config = dict(plugin.get("settings", {}))
+ self.config["path"] = os.path.join(self.plugin_dir, plugin["id"])
if self.enabled or self.break_override_allowed:
plugin_path = os.path.join(self.plugin_dir, self.id)
message = utility.check_plugin_dependencies(
- self.id,
- self.plugin_config,
- self.config,
- plugin_path
+ self.id, self.plugin_config, self.config, plugin_path
)
if message:
@@ -300,7 +299,6 @@ def reload_config(self, plugin):
# No longer errored, import the module now
self._import_plugin()
-
def reload_errored(self):
if not self.errored:
return
@@ -308,10 +306,7 @@ def reload_errored(self):
if self.enabled or self.break_override_allowed:
plugin_path = os.path.join(self.plugin_dir, self.id)
message = utility.check_plugin_dependencies(
- self.id,
- self.plugin_config,
- self.config,
- plugin_path
+ self.id, self.plugin_config, self.config, plugin_path
)
if message:
@@ -325,37 +320,40 @@ def reload_errored(self):
# No longer errored, import the module now
self._import_plugin()
-
def get_name(self):
- return self.plugin_config['meta']['name']
+ return self.plugin_config["meta"]["name"]
def _import_plugin(self):
if self.errored:
# do not try to import errored plugin
return
- self.module = importlib.import_module((self.id + '.plugin'))
+ self.module = importlib.import_module((self.id + ".plugin"))
logging.info("Successfully loaded %s", str(self.module))
- if utility.has_method(self.module, 'enable'):
+ if utility.has_method(self.module, "enable"):
self.module.enable()
def _load_config_json(self, plugin_id):
# Look for plugin.py
- if os.path.isfile(os.path.join(utility.SYSTEM_PLUGINS_DIR, plugin_id, 'plugin.py')):
+ if os.path.isfile(
+ os.path.join(utility.SYSTEM_PLUGINS_DIR, plugin_id, "plugin.py")
+ ):
plugin_dir = utility.SYSTEM_PLUGINS_DIR
- elif os.path.isfile(os.path.join(utility.USER_PLUGINS_DIR, plugin_id, 'plugin.py')):
+ elif os.path.isfile(
+ os.path.join(utility.USER_PLUGINS_DIR, plugin_id, "plugin.py")
+ ):
plugin_dir = utility.USER_PLUGINS_DIR
else:
- raise Exception('plugin.py not found for the plugin: %s', plugin_id)
+ raise Exception("plugin.py not found for the plugin: %s", plugin_id)
# Look for config.json
plugin_path = os.path.join(plugin_dir, plugin_id)
- plugin_config_path = os.path.join(plugin_path, 'config.json')
+ plugin_config_path = os.path.join(plugin_path, "config.json")
if not os.path.isfile(plugin_config_path):
- raise Exception('config.json not found for the plugin: %s', plugin_id)
+ raise Exception("config.json not found for the plugin: %s", plugin_id)
plugin_config = utility.load_json(plugin_config_path)
if plugin_config is None:
- raise Exception('config.json empty/invalid for the plugin: %s', plugin_id)
+ raise Exception("config.json empty/invalid for the plugin: %s", plugin_id)
return (plugin_config, plugin_dir)
@@ -363,10 +361,12 @@ def init_plugin(self, context, safeeyes_config):
if self.errored:
return
if self.break_override_allowed or self.enabled:
- if utility.has_method(self.module, 'init', 3):
+ if utility.has_method(self.module, "init", 3):
self.module.init(context, safeeyes_config, self.config)
- def call_plugin_method_break_obj(self, method_name: str, num_args, break_obj, *args, **kwargs):
+ def call_plugin_method_break_obj(
+ self, method_name: str, num_args, break_obj, *args, **kwargs
+ ):
if self.errored:
return None
@@ -377,7 +377,9 @@ def call_plugin_method_break_obj(self, method_name: str, num_args, break_obj, *a
enabled = self.enabled
if enabled:
- return self._call_plugin_method_internal(method_name, num_args, break_obj, *args, **kwargs)
+ return self._call_plugin_method_internal(
+ method_name, num_args, break_obj, *args, **kwargs
+ )
return None
@@ -386,11 +388,15 @@ def call_plugin_method(self, method_name: str, num_args=0, *args, **kwargs):
return None
if self.enabled:
- return self._call_plugin_method_internal(method_name, num_args, *args, **kwargs)
+ return self._call_plugin_method_internal(
+ method_name, num_args, *args, **kwargs
+ )
return None
- def _call_plugin_method_internal(self, method_name: str, num_args=0, *args, **kwargs):
+ def _call_plugin_method_internal(
+ self, method_name: str, num_args=0, *args, **kwargs
+ ):
# FIXME: cache if method exists
if utility.has_method(self.module, method_name, num_args):
return getattr(self.module, method_name)(*args, **kwargs)
diff --git a/safeeyes/plugins/audiblealert/plugin.py b/safeeyes/plugins/audiblealert/plugin.py
index 0d9d4c1b..23002e10 100644
--- a/safeeyes/plugins/audiblealert/plugin.py
+++ b/safeeyes/plugins/audiblealert/plugin.py
@@ -16,8 +16,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-Audible Alert plugin plays a sound after each breaks to notify the user that the break has end.
+"""Audible Alert plugin plays a sound after each breaks to notify the user that
+the break has end.
"""
import logging
@@ -32,48 +32,48 @@ def play_sound(resource_name):
"""Play the audio resource.
Arguments:
+ ---------
resource_name {string} -- name of the wav file resource
+
"""
- logging.info('Playing audible alert %s', resource_name)
+ logging.info("Playing audible alert %s", resource_name)
try:
# Open the sound file
path = utility.get_resource_path(resource_name)
if path is None:
return
- utility.execute_command('aplay', ['-q', path])
+ utility.execute_command("aplay", ["-q", path])
except BaseException:
- logging.error('Failed to play audible alert %s', resource_name)
+ logging.error("Failed to play audible alert %s", resource_name)
def init(ctx, safeeyes_config, plugin_config):
- """
- Initialize the plugin.
- """
+ """Initialize the plugin."""
global context
global pre_break_alert
global post_break_alert
- logging.debug('Initialize Audible Alert plugin')
+ logging.debug("Initialize Audible Alert plugin")
context = ctx
- pre_break_alert = plugin_config['pre_break_alert']
- post_break_alert = plugin_config['post_break_alert']
+ pre_break_alert = plugin_config["pre_break_alert"]
+ post_break_alert = plugin_config["post_break_alert"]
def on_pre_break(break_obj):
"""Play the pre_break sound if the option is enabled.
Arguments:
+ ---------
break_obj {safeeyes.model.Break} -- the break object
+
"""
if pre_break_alert:
- play_sound('on_pre_break.wav')
+ play_sound("on_pre_break.wav")
def on_stop_break():
- """
- After the break, play the alert sound
- """
+ """After the break, play the alert sound."""
# Do not play if the break is skipped or postponed
- if context['skipped'] or context['postponed'] or not post_break_alert:
+ if context["skipped"] or context["postponed"] or not post_break_alert:
return
- play_sound('on_stop_break.wav')
+ play_sound("on_stop_break.wav")
diff --git a/safeeyes/plugins/donotdisturb/plugin.py b/safeeyes/plugins/donotdisturb/plugin.py
index 0fdefa5c..0aa4f9f5 100644
--- a/safeeyes/plugins/donotdisturb/plugin.py
+++ b/safeeyes/plugins/donotdisturb/plugin.py
@@ -16,8 +16,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-Skip Fullscreen plugin skips the break if the active window is fullscreen.
+"""Skip Fullscreen plugin skips the break if the active window is fullscreen.
+
NOTE: Do not remove the unused import 'GdkX11' because it is required in Ubuntu 14.04
"""
@@ -27,7 +27,8 @@
import subprocess
import gi
-gi.require_version('Gdk', '3.0')
+
+gi.require_version("Gdk", "3.0")
from gi.repository import Gdk
from gi.repository import GdkX11 # noqa F401
from gi.repository import Gio
@@ -41,7 +42,7 @@
def is_active_window_skipped_wayland(pre_break):
- cmdlist = ['wlrctl', 'toplevel', 'find', 'state:fullscreen']
+ cmdlist = ["wlrctl", "toplevel", "find", "state:fullscreen"]
try:
process = subprocess.Popen(cmdlist, stdout=subprocess.PIPE)
process.communicate()[0]
@@ -50,34 +51,44 @@ def is_active_window_skipped_wayland(pre_break):
elif process.returncode == 1:
return False
elif process.returncode == 127:
- logging.warning('Could not find wlrctl needed to detect fullscreen under wayland')
+ logging.warning(
+ "Could not find wlrctl needed to detect fullscreen under wayland"
+ )
return False
except subprocess.CalledProcessError:
- logging.warning('Error in finding full-screen application')
+ logging.warning("Error in finding full-screen application")
return False
def is_active_window_skipped_xorg(pre_break):
+ """Check for full-screen applications.
+
+ This method must be executed by the main thread. If not, it will
+ cause random failure.
"""
- Check for full-screen applications.
- This method must be executed by the main thread. If not, it will cause random failure.
- """
- logging.info('Searching for full-screen application')
+ logging.info("Searching for full-screen application")
screen = Gdk.Screen.get_default()
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')
+ stdout = subprocess.check_output(cmdlist).decode("utf-8")
except subprocess.CalledProcessError:
- logging.warning('Error in finding full-screen application')
+ logging.warning("Error in finding full-screen application")
else:
if stdout:
- is_fullscreen = 'FULLSCREEN' in stdout
+ is_fullscreen = "FULLSCREEN" in stdout
# Extract the process name
process_names = re.findall('"(.+?)"', stdout)
if process_names:
@@ -89,7 +100,10 @@ def is_active_window_skipped_xorg(pre_break):
try:
active_window.unfullscreen()
except BaseException as e:
- logging.error('Error in unfullscreen the window ' + process_name, exc_info=e)
+ logging.error(
+ "Error in unfullscreen the window " + process_name,
+ exc_info=e,
+ )
return False
return is_fullscreen
@@ -98,23 +112,21 @@ def is_active_window_skipped_xorg(pre_break):
def is_idle_inhibited_gnome():
- """
- GNOME Shell doesn't work with wlrctl, and there is no way to enumerate
+ """GNOME Shell doesn't work with wlrctl, and there is no way to enumerate
fullscreen windows, but GNOME does expose whether idle actions like
- starting a screensaver are inhibited, which is a close approximation if
- not a better metric.
+ starting a screensaver are inhibited, which is a close approximation if not
+ a better metric.
"""
-
dbus_proxy = Gio.DBusProxy.new_for_bus_sync(
bus_type=Gio.BusType.SESSION,
flags=Gio.DBusProxyFlags.NONE,
info=None,
- name='org.gnome.SessionManager',
- object_path='/org/gnome/SessionManager',
- interface_name='org.gnome.SessionManager',
+ name="org.gnome.SessionManager",
+ object_path="/org/gnome/SessionManager",
+ interface_name="org.gnome.SessionManager",
cancellable=None,
)
- result = dbus_proxy.get_cached_property('InhibitedActions').unpack()
+ result = dbus_proxy.get_cached_property("InhibitedActions").unpack()
# The result is a bitfield, documented here:
# https://gitlab.gnome.org/GNOME/gnome-session/-/blob/9aa419397b7f6d42bee6e66cc5c5aad12902fba0/gnome-session/org.gnome.SessionManager.xml#L155
@@ -127,27 +139,28 @@ def _window_class_matches(window_class: str, classes: list) -> bool:
def is_on_battery():
- """
- Check if the computer is running on battery.
- """
+ """Check if the computer is running on battery."""
on_battery = False
- available_power_sources = os.listdir('/sys/class/power_supply')
- logging.info('Looking for battery status in available power sources: %s' % str(
- available_power_sources))
+ available_power_sources = os.listdir("/sys/class/power_supply")
+ logging.info(
+ "Looking for battery status in available power sources: %s"
+ % str(available_power_sources)
+ )
for power_source in available_power_sources:
- if 'BAT' in power_source:
+ if "BAT" in power_source:
# Found battery
battery_status = os.path.join(
- '/sys/class/power_supply', power_source, 'status')
+ "/sys/class/power_supply", power_source, "status"
+ )
if os.path.isfile(battery_status):
# Additional check to confirm that the status file exists
try:
- with open(battery_status, 'r') as status_file:
+ with open(battery_status, "r") as status_file:
status = status_file.read()
if status:
- on_battery = 'discharging' in status.lower()
+ on_battery = "discharging" in status.lower()
except BaseException:
- logging.error('Failed to read %s' % battery_status)
+ logging.error("Failed to read %s" % battery_status)
break
return on_battery
@@ -158,12 +171,16 @@ def init(ctx, safeeyes_config, plugin_config):
global take_break_window_classes
global unfullscreen_allowed
global dnd_while_on_battery
- logging.debug('Initialize Skip Fullscreen plugin')
+ logging.debug("Initialize Skip Fullscreen plugin")
context = ctx
- skip_break_window_classes = _normalize_window_classes(plugin_config['skip_break_windows'])
- take_break_window_classes = _normalize_window_classes(plugin_config['take_break_windows'])
- unfullscreen_allowed = plugin_config['unfullscreen']
- dnd_while_on_battery = plugin_config['while_on_battery']
+ skip_break_window_classes = _normalize_window_classes(
+ plugin_config["skip_break_windows"]
+ )
+ take_break_window_classes = _normalize_window_classes(
+ plugin_config["take_break_windows"]
+ )
+ unfullscreen_allowed = plugin_config["unfullscreen"]
+ dnd_while_on_battery = plugin_config["while_on_battery"]
def _normalize_window_classes(classes_as_str: str):
@@ -171,11 +188,9 @@ def _normalize_window_classes(classes_as_str: str):
def on_pre_break(break_obj):
- """
- Lifecycle method executes before the pre-break period.
- """
+ """Lifecycle method executes before the pre-break period."""
if utility.IS_WAYLAND:
- if utility.DESKTOP_ENVIRONMENT == 'gnome':
+ if utility.DESKTOP_ENVIRONMENT == "gnome":
skip_break = is_idle_inhibited_gnome()
else:
skip_break = is_active_window_skipped_wayland(True)
@@ -187,11 +202,9 @@ def on_pre_break(break_obj):
def on_start_break(break_obj):
- """
- Lifecycle method executes just before the break.
- """
+ """Lifecycle method executes just before the break."""
if utility.IS_WAYLAND:
- if utility.DESKTOP_ENVIRONMENT == 'gnome':
+ if utility.DESKTOP_ENVIRONMENT == "gnome":
skip_break = is_idle_inhibited_gnome()
else:
skip_break = is_active_window_skipped_wayland(True)
diff --git a/safeeyes/plugins/healthstats/dependency_checker.py b/safeeyes/plugins/healthstats/dependency_checker.py
index 4453819e..daa3c942 100644
--- a/safeeyes/plugins/healthstats/dependency_checker.py
+++ b/safeeyes/plugins/healthstats/dependency_checker.py
@@ -16,9 +16,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-import datetime
from safeeyes import utility
+
def validate(plugin_config, plugin_settings):
if not utility.module_exist("croniter"):
return _("Please install the Python module '%s'") % "croniter"
diff --git a/safeeyes/plugins/healthstats/plugin.py b/safeeyes/plugins/healthstats/plugin.py
index 126c1c86..b90da138 100644
--- a/safeeyes/plugins/healthstats/plugin.py
+++ b/safeeyes/plugins/healthstats/plugin.py
@@ -16,9 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-Show health statistics on the break screen.
-"""
+"""Show health statistics on the break screen."""
import croniter
import datetime
@@ -27,39 +25,40 @@
context = None
session = None
statistics_reset_cron = None
-default_statistics_reset_cron = '0 0 * * *' # Every midnight
+default_statistics_reset_cron = "0 0 * * *" # Every midnight
next_reset_time = None
start_time = None
+
def init(ctx, safeeyes_config, plugin_config):
- """
- Initialize the plugin.
- """
+ """Initialize the plugin."""
global context
global session
global statistics_reset_cron
- logging.debug('Initialize Health Stats plugin')
+ logging.debug("Initialize Health Stats plugin")
context = ctx
- statistics_reset_cron = plugin_config.get('statistics_reset_cron', default_statistics_reset_cron)
+ statistics_reset_cron = plugin_config.get(
+ "statistics_reset_cron", default_statistics_reset_cron
+ )
if session is None:
# Read the session
defaults = {
- 'breaks': 0,
- 'skipped_breaks': 0,
- 'screen_time': 0,
- 'total_breaks': 0,
- 'total_skipped_breaks': 0,
- 'total_screen_time': 0,
- 'total_resets': 0,
+ "breaks": 0,
+ "skipped_breaks": 0,
+ "screen_time": 0,
+ "total_breaks": 0,
+ "total_skipped_breaks": 0,
+ "total_screen_time": 0,
+ "total_resets": 0,
}
- session = context['session']['plugin'].get('healthstats', {}) | defaults
- if 'no_of_breaks' in session:
+ session = context["session"]["plugin"].get("healthstats", {}) | defaults
+ if "no_of_breaks" in session:
# Ignore old format session.
session = defaults
- context['session']['plugin']['healthstats'] = session
+ context["session"]["plugin"]["healthstats"] = session
_get_next_reset_time()
@@ -67,8 +66,8 @@ def init(ctx, safeeyes_config, plugin_config):
def on_stop_break():
# Check if break was skipped.
global session
- if context['skipped']:
- session['skipped_breaks'] += 1
+ if context["skipped"]:
+ session["skipped_breaks"] += 1
# Screen time is starting again.
on_start()
@@ -76,7 +75,7 @@ def on_stop_break():
def on_start_break(break_obj):
global session
- session['breaks'] += 1
+ session["breaks"] += 1
# Screen time has stopped.
on_stop()
@@ -87,15 +86,13 @@ def on_stop():
_reset_stats()
if start_time:
screen_time = datetime.datetime.now() - start_time
- session['screen_time'] += round(screen_time.total_seconds())
+ session["screen_time"] += round(screen_time.total_seconds())
start_time = None
def get_widget_title(break_obj):
- """
- Return the widget title.
- """
- return _('Health Statistics')
+ """Return the widget title."""
+ return _("Health Statistics")
def _reset_stats():
@@ -109,33 +106,34 @@ def _reset_stats():
_get_next_reset_time()
# Reset statistics
- session['total_breaks'] += session['breaks']
- session['total_skipped_breaks'] += session['skipped_breaks']
- session['total_screen_time'] += session['screen_time']
- session['total_resets'] += 1
- session['breaks'] = 0
- session['skipped_breaks'] = 0
- session['screen_time'] = 0
+ session["total_breaks"] += session["breaks"]
+ session["total_skipped_breaks"] += session["skipped_breaks"]
+ session["total_screen_time"] += session["screen_time"]
+ session["total_resets"] += 1
+ session["breaks"] = 0
+ session["skipped_breaks"] = 0
+ session["screen_time"] = 0
def get_widget_content(break_obj):
- """
- Return the statistics.
- """
+ """Return the statistics."""
global next_reset_time
- resets = session['total_resets']
- if session['screen_time'] > 21600 or (session['breaks'] and session['skipped_breaks'] / session['breaks']) >= 0.2:
+ resets = session["total_resets"]
+ if (
+ session["screen_time"] > 21600
+ or (session["breaks"] and session["skipped_breaks"] / session["breaks"]) >= 0.2
+ ):
# Unhealthy behavior -> Red broken heart
- heart = '💔️'
+ heart = "💔️"
else:
# Healthy behavior -> Green heart
- heart = '💚'
+ heart = "💚"
content = [
- heart,
- f"BREAKS: {session['breaks']}",
- f"SKIPPED: {session['skipped_breaks']}",
- f"SCREEN TIME: {_format_interval(session['screen_time'])}",
+ heart,
+ f"BREAKS: {session['breaks']}",
+ f"SKIPPED: {session['skipped_breaks']}",
+ f"SCREEN TIME: {_format_interval(session['screen_time'])}",
]
if resets:
@@ -147,14 +145,14 @@ def get_widget_content(break_obj):
if resets:
content += f"\n\t[] = average of {resets} reset(s)"
if next_reset_time is None:
- content += f"\n\tSettings error in statistics reset interval: {statistics_reset_cron}"
+ content += (
+ f"\n\tSettings error in statistics reset interval: {statistics_reset_cron}"
+ )
return content
def on_start():
- """
- Track the start time.
- """
+ """Track the start time."""
global start_time
_reset_stats()
start_time = datetime.datetime.now()
@@ -167,9 +165,10 @@ def _get_next_reset_time():
try:
cron = croniter.croniter(statistics_reset_cron, datetime.datetime.now())
next_reset_time = cron.get_next(datetime.datetime)
- session['next_reset_time'] = next_reset_time.strftime("%Y-%m-%d %H:%M:%S")
- logging.debug("Health stats will be reset at " + session['next_reset_time'])
- except:
+ session["next_reset_time"] = next_reset_time.strftime("%Y-%m-%d %H:%M:%S")
+ logging.debug("Health stats will be reset at " + session["next_reset_time"])
+ except: # noqa E722
+ # TODO: consider catching Exception here instead of bare except
logging.error("Error in statistics reset expression: " + statistics_reset_cron)
next_reset_time = None
@@ -177,4 +176,4 @@ def _get_next_reset_time():
def _format_interval(seconds):
screen_time = round(seconds / 60)
hours, minutes = divmod(screen_time, 60)
- return '{:02d}:{:02d}'.format(hours, minutes)
+ return "{:02d}:{:02d}".format(hours, minutes)
diff --git a/safeeyes/plugins/limitconsecutiveskipping/plugin.py b/safeeyes/plugins/limitconsecutiveskipping/plugin.py
index 1a55183e..864b609e 100644
--- a/safeeyes/plugins/limitconsecutiveskipping/plugin.py
+++ b/safeeyes/plugins/limitconsecutiveskipping/plugin.py
@@ -16,9 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-Limit how many breaks can be skipped or postponed in a row.
-"""
+"""Limit how many breaks can be skipped or postponed in a row."""
import logging
@@ -27,76 +25,70 @@
session = None
enabled = True
+
def init(ctx, safeeyes_config, plugin_config):
- """
- Initialize the plugin.
- """
+ """Initialize the plugin."""
global enabled
global context
global session
global no_of_skipped_breaks
global no_allowed_skips
- logging.debug('Initialize Limit consecutive skipping plugin')
+ logging.debug("Initialize Limit consecutive skipping plugin")
context = ctx
- no_allowed_skips = plugin_config.get('number_of_allowed_skips_in_a_row', 2)
+ no_allowed_skips = plugin_config.get("number_of_allowed_skips_in_a_row", 2)
if session is None:
# Read the session
- session = context['session']['plugin'].get('limitconsecutiveskipping', None)
+ session = context["session"]["plugin"].get("limitconsecutiveskipping", None)
if session is None:
- session = {'no_of_skipped_breaks': 0}
- context['session']['plugin']['limitconsecutiveskipping'] = session
- no_of_skipped_breaks = session.get('no_of_skipped_breaks', 0)
+ session = {"no_of_skipped_breaks": 0}
+ context["session"]["plugin"]["limitconsecutiveskipping"] = session
+ no_of_skipped_breaks = session.get("no_of_skipped_breaks", 0)
def on_stop_break():
- """
- After the break, check if it is skipped.
- """
+ """After the break, check if it is skipped."""
# Check if the plugin is enabled
if not enabled:
return
global no_of_skipped_breaks
- if context['skipped'] or context['postponed']:
+ if context["skipped"] or context["postponed"]:
no_of_skipped_breaks += 1
- session['no_of_skipped_breaks'] = no_of_skipped_breaks
+ session["no_of_skipped_breaks"] = no_of_skipped_breaks
else:
no_of_skipped_breaks = 0
- session['no_of_skipped_breaks'] = no_of_skipped_breaks
+ session["no_of_skipped_breaks"] = no_of_skipped_breaks
def on_start_break(break_obj):
- logging.debug('Skipped / allowed = {} / {}'.format(no_of_skipped_breaks, no_allowed_skips))
+ logging.debug(
+ "Skipped / allowed = {} / {}".format(no_of_skipped_breaks, no_allowed_skips)
+ )
if no_of_skipped_breaks >= no_allowed_skips:
- context['postpone_button_disabled'] = True
- context['skip_button_disabled'] = True
+ context["postpone_button_disabled"] = True
+ context["skip_button_disabled"] = True
def get_widget_title(break_obj):
- """
- Return the widget title.
- """
+ """Return the widget title."""
# Check if the plugin is enabled
if not enabled:
return ""
- return _('Limit Consecutive Skipping')
+ return _("Limit Consecutive Skipping")
def get_widget_content(break_obj):
- """
- Return the statistics.
- """
+ """Return the statistics."""
# Check if the plugin is enabled
if not enabled:
return ""
- return _('Skipped or postponed %(num)d/%(allowed)d breaks in a row') % {
- 'num': no_of_skipped_breaks,
- 'allowed': no_allowed_skips
+ return _("Skipped or postponed %(num)d/%(allowed)d breaks in a row") % {
+ "num": no_of_skipped_breaks,
+ "allowed": no_allowed_skips,
}
-
diff --git a/safeeyes/plugins/mediacontrol/plugin.py b/safeeyes/plugins/mediacontrol/plugin.py
index 5c43ac33..ae847107 100644
--- a/safeeyes/plugins/mediacontrol/plugin.py
+++ b/safeeyes/plugins/mediacontrol/plugin.py
@@ -16,50 +16,48 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-Media Control plugin lets users to pause currently playing media player from the break screen.
+"""Media Control plugin lets users to pause currently playing media player from
+the break screen.
"""
-import logging
import os
import re
import gi
from safeeyes.model import TrayAction
-gi.require_version('Gtk', '3.0')
+
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gio
tray_icon_path = None
def __active_players():
- """
- List of all media players which are playing now.
- """
+ """List of all media players which are playing now."""
players = []
dbus_proxy = Gio.DBusProxy.new_for_bus_sync(
bus_type=Gio.BusType.SESSION,
flags=Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
info=None,
- name='org.freedesktop.DBus',
- object_path='/org/freedesktop/DBus',
- interface_name='org.freedesktop.DBus',
+ name="org.freedesktop.DBus",
+ object_path="/org/freedesktop/DBus",
+ interface_name="org.freedesktop.DBus",
cancellable=None,
)
for service in dbus_proxy.ListNames():
- if re.match('org.mpris.MediaPlayer2.', service):
+ if re.match("org.mpris.MediaPlayer2.", service):
player = Gio.DBusProxy.new_for_bus_sync(
bus_type=Gio.BusType.SESSION,
flags=Gio.DBusProxyFlags.NONE,
info=None,
name=service,
- object_path='/org/mpris/MediaPlayer2',
- interface_name='org.mpris.MediaPlayer2.Player',
+ object_path="/org/mpris/MediaPlayer2",
+ interface_name="org.mpris.MediaPlayer2.Player",
cancellable=None,
)
- status = player.get_cached_property('PlaybackStatus').unpack().lower()
+ status = player.get_cached_property("PlaybackStatus").unpack().lower()
if status == "playing":
players.append(player)
@@ -67,28 +65,24 @@ def __active_players():
def __pause_players(players):
- """
- Pause all playing media players using dbus.
- """
+ """Pause all playing media players using dbus."""
for player in players:
player.Pause()
def init(ctx, safeeyes_config, plugin_config):
- """
- Initialize the screensaver plugin.
- """
+ """Initialize the screensaver plugin."""
global tray_icon_path
- tray_icon_path = os.path.join(plugin_config['path'], "resource/pause.png")
+ tray_icon_path = os.path.join(plugin_config["path"], "resource/pause.png")
def get_tray_action(break_obj):
- """
- Return TrayAction only if there is a media player currently playing.
- """
+ """Return TrayAction only if there is a media player currently playing."""
players = __active_players()
if players:
- return TrayAction.build("Pause media",
- tray_icon_path,
- Gtk.STOCK_MEDIA_PAUSE,
- lambda: __pause_players(players))
+ return TrayAction.build(
+ "Pause media",
+ tray_icon_path,
+ Gtk.STOCK_MEDIA_PAUSE,
+ lambda: __pause_players(players),
+ )
diff --git a/safeeyes/plugins/notification/plugin.py b/safeeyes/plugins/notification/plugin.py
index b7920fbb..5dd4d51d 100644
--- a/safeeyes/plugins/notification/plugin.py
+++ b/safeeyes/plugins/notification/plugin.py
@@ -21,14 +21,14 @@
import gi
from safeeyes.model import BreakType
-gi.require_version('Notify', '0.7')
+gi.require_version("Notify", "0.7")
from gi.repository import Notify
"""
Safe Eyes Notification plugin
"""
-APPINDICATOR_ID = 'safeeyes'
+APPINDICATOR_ID = "safeeyes"
notification = None
context = None
warning_time = 10
@@ -37,42 +37,38 @@
def init(ctx, safeeyes_config, plugin_config):
- """
- Initialize the plugin.
- """
+ """Initialize the plugin."""
global context
global warning_time
- logging.debug('Initialize Notification plugin')
+ logging.debug("Initialize Notification plugin")
context = ctx
- warning_time = safeeyes_config.get('pre_break_warning_time')
+ warning_time = safeeyes_config.get("pre_break_warning_time")
def on_pre_break(break_obj):
- """
- Show the notification
- """
+ """Show the notification."""
# Construct the message based on the type of the next break
global notification
- logging.info('Show the notification')
- message = '\n'
+ logging.info("Show the notification")
+ message = "\n"
if break_obj.type == BreakType.SHORT_BREAK:
- message += (_('Ready for a short break in %s seconds') % warning_time)
+ message += _("Ready for a short break in %s seconds") % warning_time
else:
- message += (_('Ready for a long break in %s seconds') % warning_time)
+ message += _("Ready for a long break in %s seconds") % warning_time
- notification = Notify.Notification.new('Safe Eyes', message, icon='io.github.slgobinath.SafeEyes-enabled')
+ notification = Notify.Notification.new(
+ "Safe Eyes", message, icon="io.github.slgobinath.SafeEyes-enabled"
+ )
try:
notification.show()
except BaseException:
- logging.error('Failed to show the notification')
+ logging.error("Failed to show the notification")
def on_start_break(break_obj):
- """
- Close the notification.
- """
+ """Close the notification."""
global notification
- logging.info('Close pre-break notification')
+ logging.info("Close pre-break notification")
if notification:
try:
notification.close()
@@ -83,8 +79,6 @@ def on_start_break(break_obj):
def on_exit():
- """
- Uninitialize the registered notification.
- """
- logging.debug('Stop Notification plugin')
+ """Uninitialize the registered notification."""
+ logging.debug("Stop Notification plugin")
Notify.uninit()
diff --git a/safeeyes/plugins/screensaver/plugin.py b/safeeyes/plugins/screensaver/plugin.py
index e8e5e3b9..0f1240a8 100644
--- a/safeeyes/plugins/screensaver/plugin.py
+++ b/safeeyes/plugins/screensaver/plugin.py
@@ -16,8 +16,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-Screensaver plugin locks the desktop using native screensaver application, after long breaks.
+"""Screensaver plugin locks the desktop using native screensaver application,
+after long breaks.
"""
import gi
@@ -26,7 +26,8 @@
from safeeyes import utility
from safeeyes.model import TrayAction
-gi.require_version('Gtk', '3.0')
+
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
context = None
@@ -39,40 +40,69 @@
def __lock_screen_command():
- """
- Function tries to detect the screensaver command based on the current envinroment
+ """Function tries to detect the screensaver command based on the current
+ envinroment.
+
Possible results:
Gnome, Unity, Budgie: ['gnome-screensaver-command', '--lock']
Cinnamon: ['cinnamon-screensaver-command', '--lock']
Pantheon, LXDE: ['light-locker-command', '--lock']
Mate: ['mate-screensaver-command', '--lock']
- KDE: ['qdbus', 'org.freedesktop.ScreenSaver', '/ScreenSaver', 'Lock']
+ KDE: ['qdbus', 'org.freedesktop.ScreenSaver',
+ '/ScreenSaver', 'Lock']
XFCE: ['xflock4']
Otherwise: None
"""
- desktop_session = os.environ.get('DESKTOP_SESSION')
- current_desktop = os.environ.get('XDG_CURRENT_DESKTOP')
+ 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 ('xfce' in desktop_session or desktop_session.startswith('xubuntu') or (current_desktop is not None and 'xfce' in current_desktop)) and utility.command_exist('xflock4'):
- return ['xflock4']
- elif desktop_session == 'cinnamon' and utility.command_exist('cinnamon-screensaver-command'):
- return ['cinnamon-screensaver-command', '--lock']
- elif (desktop_session == 'pantheon' or desktop_session.startswith('lubuntu')) and utility.command_exist('light-locker-command'):
- return ['light-locker-command', '--lock']
- elif desktop_session == 'mate' and utility.command_exist('mate-screensaver-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') or desktop_session.startswith('gnome'):
- if utility.command_exist('gnome-screensaver-command'):
- return ['gnome-screensaver-command', '--lock']
+ if (
+ "xfce" in desktop_session
+ or desktop_session.startswith("xubuntu")
+ or (current_desktop is not None and "xfce" in current_desktop)
+ ) and utility.command_exist("xflock4"):
+ return ["xflock4"]
+ elif desktop_session == "cinnamon" and utility.command_exist(
+ "cinnamon-screensaver-command"
+ ):
+ return ["cinnamon-screensaver-command", "--lock"]
+ elif (
+ desktop_session == "pantheon" or desktop_session.startswith("lubuntu")
+ ) and utility.command_exist("light-locker-command"):
+ return ["light-locker-command", "--lock"]
+ elif desktop_session == "mate" and utility.command_exist(
+ "mate-screensaver-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")
+ or desktop_session.startswith("gnome")
+ ):
+ if utility.command_exist("gnome-screensaver-command"):
+ return ["gnome-screensaver-command", "--lock"]
# 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 'deprecated' not in os.environ.get('GNOME_DESKTOP_SESSION_ID') and utility.command_exist('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 "deprecated" not in os.environ.get(
+ "GNOME_DESKTOP_SESSION_ID"
+ ) and utility.command_exist("gnome-screensaver-command"):
# Gnome 2
- return ['gnome-screensaver-command', '--lock']
+ return ["gnome-screensaver-command", "--lock"]
return None
@@ -82,26 +112,24 @@ def __lock_screen():
def init(ctx, safeeyes_config, plugin_config):
- """
- Initialize the screensaver plugin.
- """
+ """Initialize the screensaver plugin."""
global context
global lock_screen_command
global min_seconds
global tray_icon_path
- logging.debug('Initialize Screensaver plugin')
+ logging.debug("Initialize Screensaver plugin")
context = ctx
- min_seconds = plugin_config['min_seconds']
- tray_icon_path = os.path.join(plugin_config['path'], "resource/lock.png")
- if plugin_config['command']:
- lock_screen_command = plugin_config['command'].split()
+ min_seconds = plugin_config["min_seconds"]
+ tray_icon_path = os.path.join(plugin_config["path"], "resource/lock.png")
+ if plugin_config["command"]:
+ lock_screen_command = plugin_config["command"].split()
else:
lock_screen_command = __lock_screen_command()
def on_start_break(break_obj):
- """
- Determine the break type and only if it is a long break, enable the lock_screen flag.
+ """Determine the break type and only if it is a long break, enable the
+ lock_screen flag.
"""
global lock_screen
global seconds_passed
@@ -113,23 +141,20 @@ def on_start_break(break_obj):
def on_countdown(countdown, seconds):
- """
- Keep track of seconds passed from the beginning of long break.
- """
+ """Keep track of seconds passed from the beginning of long break."""
global seconds_passed
seconds_passed = seconds
def on_stop_break():
- """
- Lock the screen after a long break if the user has not skipped within min_seconds.
+ """Lock the screen after a long break if the user has not skipped within
+ min_seconds.
"""
if user_locked_screen or (lock_screen and seconds_passed >= min_seconds):
utility.execute_command(lock_screen_command)
def get_tray_action(break_obj):
- return TrayAction.build("Lock screen",
- tray_icon_path,
- Gtk.STOCK_DIALOG_AUTHENTICATION,
- __lock_screen)
+ return TrayAction.build(
+ "Lock screen", tray_icon_path, Gtk.STOCK_DIALOG_AUTHENTICATION, __lock_screen
+ )
diff --git a/safeeyes/plugins/smartpause/plugin.py b/safeeyes/plugins/smartpause/plugin.py
index 296fe516..8c9e8663 100644
--- a/safeeyes/plugins/smartpause/plugin.py
+++ b/safeeyes/plugins/smartpause/plugin.py
@@ -21,7 +21,6 @@
import subprocess
import threading
import re
-import os
from safeeyes import utility
from safeeyes.model import State
@@ -51,33 +50,40 @@
swayidle_idle = 0
swayidle_active = 0
+
def __swayidle_running():
- return (swayidle_process is not None and
- swayidle_process.poll() is None)
+ return swayidle_process is not None and swayidle_process.poll() is None
+
def __start_swayidle_monitor():
global swayidle_process
global swayidle_start
global swayidle_idle
global swayidle_active
- logging.debug('Starting swayidle subprocess')
- swayidle_process = subprocess.Popen([
- "swayidle", "timeout", "1", "date +S%s", "resume", "date +R%s"
- ], stdout=subprocess.PIPE, bufsize=1, universal_newlines=True, encoding='utf-8')
+ logging.debug("Starting swayidle subprocess")
+ swayidle_process = subprocess.Popen(
+ ["swayidle", "timeout", "1", "date +S%s", "resume", "date +R%s"],
+ stdout=subprocess.PIPE,
+ bufsize=1,
+ universal_newlines=True,
+ encoding="utf-8",
+ )
for line in swayidle_process.stdout:
with swayidle_lock:
typ = line[0]
timestamp = int(line[1:])
- if typ == 'S':
+ if typ == "S":
swayidle_idle = timestamp
- elif typ == 'R':
+ elif typ == "R":
swayidle_active = timestamp
+
def __stop_swayidle_monitor():
if __swayidle_running():
- logging.debug('Stopping swayidle subprocess')
+ logging.debug("Stopping swayidle subprocess")
swayidle_process.terminate()
+
def __swayidle_idle_time():
with swayidle_lock:
if not __swayidle_running():
@@ -88,21 +94,25 @@ def __swayidle_idle_time():
return idle_time
return 0
+
def __gnome_wayland_idle_time():
- """
- Determine system idle time in seconds, specifically for gnome with wayland.
+ """Determine system idle time in seconds, specifically for gnome with
+ wayland.
+
If there's a failure, return 0.
https://unix.stackexchange.com/a/492328/222290
"""
try:
- output = subprocess.check_output([
- 'dbus-send',
- '--print-reply',
- '--dest=org.gnome.Mutter.IdleMonitor',
- '/org/gnome/Mutter/IdleMonitor/Core',
- 'org.gnome.Mutter.IdleMonitor.GetIdletime'
- ])
- return int(re.search(rb'\d+$', output).group(0)) / 1000
+ output = subprocess.check_output(
+ [
+ "dbus-send",
+ "--print-reply",
+ "--dest=org.gnome.Mutter.IdleMonitor",
+ "/org/gnome/Mutter/IdleMonitor/Core",
+ "org.gnome.Mutter.IdleMonitor.GetIdletime",
+ ]
+ )
+ return int(re.search(rb"\d+$", output).group(0)) / 1000
except BaseException as e:
logging.warning("Failed to get system idle time for gnome/wayland.")
logging.warning(str(e))
@@ -110,8 +120,8 @@ def __gnome_wayland_idle_time():
def __system_idle_time():
- """
- Get system idle time in minutes.
+ """Get system idle time in minutes.
+
Return the idle time if xprintidle is available, otherwise return 0.
"""
try:
@@ -120,15 +130,13 @@ def __system_idle_time():
elif use_swayidle:
return __swayidle_idle_time()
# Convert to seconds
- return int(subprocess.check_output(['xprintidle']).decode('utf-8')) / 1000
+ return int(subprocess.check_output(["xprintidle"]).decode("utf-8")) / 1000
except BaseException:
return 0
def __is_active():
- """
- Thread safe function to see if this plugin is active or not.
- """
+ """Thread safe function to see if this plugin is active or not."""
is_active = False
with lock:
is_active = active
@@ -136,18 +144,14 @@ def __is_active():
def __set_active(is_active):
- """
- Thread safe function to change the state of the plugin.
- """
+ """Thread safe function to change the state of the plugin."""
global active
with lock:
active = is_active
def init(ctx, safeeyes_config, plugin_config):
- """
- Initialize the plugin.
- """
+ """Initialize the plugin."""
global context
global enable_safeeyes
global disable_safeeyes
@@ -159,24 +163,25 @@ def init(ctx, safeeyes_config, plugin_config):
global postpone_if_active
global is_wayland_and_gnome
global use_swayidle
- logging.debug('Initialize Smart Pause plugin')
+ logging.debug("Initialize Smart Pause plugin")
context = ctx
- enable_safeeyes = context['api']['enable_safeeyes']
- disable_safeeyes = context['api']['disable_safeeyes']
- postpone = context['api']['postpone']
- idle_time = plugin_config['idle_time']
- postpone_if_active = plugin_config['postpone_if_active']
- short_break_interval = safeeyes_config.get(
- 'short_break_interval') * 60 # Convert to seconds
- long_break_duration = safeeyes_config.get('long_break_duration')
+ enable_safeeyes = context["api"]["enable_safeeyes"]
+ disable_safeeyes = context["api"]["disable_safeeyes"]
+ postpone = context["api"]["postpone"]
+ idle_time = plugin_config["idle_time"]
+ postpone_if_active = plugin_config["postpone_if_active"]
+ short_break_interval = (
+ safeeyes_config.get("short_break_interval") * 60
+ ) # Convert to seconds
+ long_break_duration = safeeyes_config.get("long_break_duration")
waiting_time = min(2, idle_time) # If idle time is 1 sec, wait only 1 sec
- is_wayland_and_gnome = context['desktop'] == 'gnome' and context['is_wayland']
- use_swayidle = context['desktop'] == 'sway'
+ is_wayland_and_gnome = context["desktop"] == "gnome" and context["is_wayland"]
+ use_swayidle = context["desktop"] == "sway"
def __start_idle_monitor():
- """
- Continuously check the system idle time and pause/resume Safe Eyes based on it.
+ """Continuously check the system idle time and pause/resume Safe Eyes based
+ on it.
"""
global smart_pause_activated
global idle_start_time
@@ -190,22 +195,29 @@ def __start_idle_monitor():
if __is_active():
# Get the system idle time
system_idle_time = __system_idle_time()
- if system_idle_time >= idle_time and context['state'] == State.WAITING:
+ if system_idle_time >= idle_time and context["state"] == State.WAITING:
smart_pause_activated = True
- idle_start_time = datetime.datetime.now() - datetime.timedelta(seconds=system_idle_time)
- logging.info('Pause Safe Eyes due to system idle')
+ idle_start_time = datetime.datetime.now() - datetime.timedelta(
+ seconds=system_idle_time
+ )
+ logging.info("Pause Safe Eyes due to system idle")
disable_safeeyes(None, True)
- elif system_idle_time < idle_time and context['state'] == State.RESTING and idle_start_time is not None:
- logging.info('Resume Safe Eyes due to user activity')
+ elif (
+ system_idle_time < idle_time
+ and context["state"] == State.RESTING
+ and idle_start_time is not None
+ ):
+ logging.info("Resume Safe Eyes due to user activity")
smart_pause_activated = False
- idle_period = (datetime.datetime.now() - idle_start_time)
+ idle_period = datetime.datetime.now() - idle_start_time
idle_seconds = idle_period.total_seconds()
- context['idle_period'] = idle_seconds
+ context["idle_period"] = idle_seconds
if idle_seconds < short_break_interval:
# Credit back the idle time
if next_break_time is not None:
# This method runs in a thread since the start.
- # It may run before next_break is initialized in the update_next_break method
+ # It may run before next_break is initialized in the
+ # update_next_break method
next_break = next_break_time + idle_period
enable_safeeyes(next_break.timestamp())
else:
@@ -216,28 +228,24 @@ def __start_idle_monitor():
def on_start():
- """
- Start a thread to continuously call xprintidle.
- """
+ """Start a thread to continuously call xprintidle."""
global active
if not __is_active():
# If SmartPause is already started, do not start it again
- logging.debug('Start Smart Pause plugin')
+ logging.debug("Start Smart Pause plugin")
__set_active(True)
utility.start_thread(__start_idle_monitor)
def on_stop():
- """
- Stop the thread from continuously calling xprintidle.
- """
+ """Stop the thread from continuously calling xprintidle."""
global active
global smart_pause_activated
if smart_pause_activated:
# Safe Eyes is stopped due to system idle
smart_pause_activated = False
return
- logging.debug('Stop Smart Pause plugin')
+ logging.debug("Stop Smart Pause plugin")
if use_swayidle:
__stop_swayidle_monitor()
__set_active(False)
@@ -247,9 +255,7 @@ def on_stop():
def update_next_break(break_obj, dateTime):
- """
- Update the next break time.
- """
+ """Update the next break time."""
global next_break_time
global next_break_duration
next_break_time = dateTime
@@ -257,9 +263,7 @@ def update_next_break(break_obj, dateTime):
def on_start_break(break_obj):
- """
- Lifecycle method executes just before the break.
- """
+ """Lifecycle method executes just before the break."""
if postpone_if_active:
# Postpone this break if the user is active
system_idle_time = __system_idle_time()
@@ -268,8 +272,6 @@ def on_start_break(break_obj):
def disable():
- """
- SmartPause plugin was active earlier but now user has disabled it.
- """
+ """SmartPause plugin was active earlier but now user has disabled it."""
# Remove the idle_period
- context.pop('idle_period', None)
+ context.pop("idle_period", None)
diff --git a/safeeyes/plugins/trayicon/dependency_checker.py b/safeeyes/plugins/trayicon/dependency_checker.py
index 574433ad..2ea5d217 100644
--- a/safeeyes/plugins/trayicon/dependency_checker.py
+++ b/safeeyes/plugins/trayicon/dependency_checker.py
@@ -20,27 +20,32 @@
from safeeyes.model import PluginDependency
import gi
-gi.require_version('Gio', '2.0')
+
+gi.require_version("Gio", "2.0")
from gi.repository import Gio
+
def validate(plugin_config, plugin_settings):
dbus_proxy = Gio.DBusProxy.new_for_bus_sync(
bus_type=Gio.BusType.SESSION,
flags=Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
info=None,
- name='org.freedesktop.DBus',
- object_path='/org/freedesktop/DBus',
- interface_name='org.freedesktop.DBus',
+ name="org.freedesktop.DBus",
+ object_path="/org/freedesktop/DBus",
+ interface_name="org.freedesktop.DBus",
cancellable=None,
)
- if dbus_proxy.NameHasOwner('(s)', 'org.kde.StatusNotifierWatcher'):
+ if dbus_proxy.NameHasOwner("(s)", "org.kde.StatusNotifierWatcher"):
return None
else:
return PluginDependency(
- message=_("Please install service providing tray icons for your desktop environment."),
+ message=_(
+ "Please install service providing tray icons for your desktop"
+ " environment."
+ ),
link="https://github.com/slgobinath/SafeEyes/wiki/How-to-install-backend-for-Safe-Eyes-tray-icon",
- retryable=True
+ retryable=True,
)
command = None
diff --git a/safeeyes/plugins/trayicon/plugin.py b/safeeyes/plugins/trayicon/plugin.py
index 42b309a5..7d0ab1cc 100644
--- a/safeeyes/plugins/trayicon/plugin.py
+++ b/safeeyes/plugins/trayicon/plugin.py
@@ -19,7 +19,8 @@
import datetime
from safeeyes.model import BreakType
import gi
-gi.require_version('Gtk', '3.0')
+
+gi.require_version("Gtk", "3.0")
from gi.repository import Gio, GLib
import logging
from safeeyes import utility
@@ -34,7 +35,8 @@
tray_icon = None
safeeyes_config = None
-SNI_NODE_INFO = Gio.DBusNodeInfo.new_for_xml("""
+SNI_NODE_INFO = Gio.DBusNodeInfo.new_for_xml(
+ """
@@ -56,9 +58,11 @@
-""").interfaces[0]
+"""
+).interfaces[0]
-MENU_NODE_INFO = Gio.DBusNodeInfo.new_for_xml("""
+MENU_NODE_INFO = Gio.DBusNodeInfo.new_for_xml(
+ """
@@ -70,15 +74,15 @@
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
@@ -86,24 +90,26 @@
-
-
-
+
+
+
-
-
-
-
+
+
+
+
-""").interfaces[0]
+"""
+).interfaces[0]
+
class DBusService:
def __init__(self, interface_info, object_path, bus):
@@ -117,7 +123,7 @@ def register(self):
object_path=self.object_path,
interface_info=self.interface_info,
method_call_closure=self.on_method_call,
- get_property_closure=self.on_get_property
+ get_property_closure=self.on_get_property,
)
if not self.registration_id:
@@ -132,7 +138,16 @@ def unregister(self):
self.bus.unregister_object(self.registration_id)
self.registration_id = None
- def on_method_call(self, _connection, _sender, _path, _interface_name, method_name, parameters, invocation):
+ def on_method_call(
+ self,
+ _connection,
+ _sender,
+ _path,
+ _interface_name,
+ method_name,
+ parameters,
+ invocation,
+ ):
method_info = self.interface_info.lookup_method(method_name)
method = getattr(self, method_name)
result = method(*parameters.unpack())
@@ -144,11 +159,13 @@ def on_method_call(self, _connection, _sender, _path, _interface_name, method_na
invocation.return_value(return_value)
- def on_get_property(self, _connection, _sender, _path, _interface_name, property_name):
+ def on_get_property(
+ self, _connection, _sender, _path, _interface_name, property_name
+ ):
property_info = self.interface_info.lookup_property(property_name)
return GLib.Variant(property_info.signature, getattr(self, property_name))
- def emit_signal(self, signal_name, args = None):
+ def emit_signal(self, signal_name, args=None):
signal_info = self.interface_info.lookup_signal(signal_name)
if len(signal_info.args) == 0:
parameters = None
@@ -160,11 +177,12 @@ def emit_signal(self, signal_name, args = None):
object_path=self.object_path,
interface_name=self.interface_info.name,
signal_name=signal_name,
- parameters=parameters
+ parameters=parameters,
)
+
class DBusMenuService(DBusService):
- DBUS_SERVICE_PATH = '/io/github/slgobinath/SafeEyes/Menu'
+ DBUS_SERVICE_PATH = "/io/github/slgobinath/SafeEyes/Menu"
revision = 0
@@ -175,7 +193,7 @@ def __init__(self, session_bus, context, items):
super().__init__(
interface_info=MENU_NODE_INFO,
object_path=self.DBUS_SERVICE_PATH,
- bus=session_bus
+ bus=session_bus,
)
self.set_items(items)
@@ -192,13 +210,13 @@ def set_items(self, items):
@staticmethod
def getItemsFlat(items, idToItems):
for item in items:
- if item.get('hidden', False) == True:
+ if item.get("hidden", False):
continue
- idToItems[item['id']] = item
+ idToItems[item["id"]] = item
- if 'children' in item:
- idToItems = DBusMenuService.getItemsFlat(item['children'], idToItems)
+ if "children" in item:
+ idToItems = DBusMenuService.getItemsFlat(item["children"], idToItems)
return idToItems
@@ -206,27 +224,27 @@ def getItemsFlat(items, idToItems):
def singleItemToDbus(item):
props = DBusMenuService.itemPropsToDbus(item)
- return (item['id'], props)
+ return (item["id"], props)
@staticmethod
def itemPropsToDbus(item):
result = {}
- string_props = ['label', 'icon-name', 'type', 'children-display']
+ string_props = ["label", "icon-name", "type", "children-display"]
for key in string_props:
if key in item:
- result[key] = GLib.Variant('s', item[key])
+ result[key] = GLib.Variant("s", item[key])
- bool_props = ['enabled']
+ bool_props = ["enabled"]
for key in bool_props:
if key in item:
- result[key] = GLib.Variant('b', item[key])
+ result[key] = GLib.Variant("b", item[key])
return result
@staticmethod
def itemToDbus(item, recursion_depth):
- if item.get('hidden', False) == True:
+ if item.get("hidden", False):
return None
props = DBusMenuService.itemPropsToDbus(item)
@@ -234,20 +252,23 @@ def itemToDbus(item, recursion_depth):
children = []
if recursion_depth > 1 or recursion_depth == -1:
if "children" in item:
- children = [DBusMenuService.itemToDbus(item, recursion_depth - 1) for item in item['children']]
+ children = [
+ DBusMenuService.itemToDbus(item, recursion_depth - 1)
+ for item in item["children"]
+ ]
children = [i for i in children if i is not None]
- return GLib.Variant("(ia{sv}av)", (item['id'], props, children))
+ return GLib.Variant("(ia{sv}av)", (item["id"], props, children))
def findItemsWithParent(self, parent_id, items):
for item in items:
- if item.get('hidden', False) == True:
+ if item.get("hidden", False):
continue
- if 'children' in item:
- if item['id'] == parent_id:
- return item['children']
+ if "children" in item:
+ if item["id"] == parent_id:
+ return item["children"]
else:
- ret = self.findItemsWithParent(parent_id, item['children'])
+ ret = self.findItemsWithParent(parent_id, item["children"])
if ret is not None:
return ret
return None
@@ -267,11 +288,7 @@ def GetLayout(self, parent_id, recursion_depth, property_names):
ret = (
self.revision,
- (
- 0,
- { 'children-display': GLib.Variant('s', 'submenu') },
- children
- )
+ (0, {"children-display": GLib.Variant("s", "submenu")}, children),
)
return ret
@@ -303,13 +320,13 @@ def Event(self, idx, event_id, data, timestamp):
if idx in self.idToItems:
item = self.idToItems[idx]
- if 'callback' in item:
- item['callback']()
+ if "callback" in item:
+ item["callback"]()
def EventGroup(self, events):
not_found = []
- for (idx, event_id, data, timestamp) in events:
+ for idx, event_id, data, timestamp in events:
if idx not in self.idToItems:
not_found.append(idx)
continue
@@ -318,8 +335,8 @@ def EventGroup(self, events):
continue
item = self.idToItems[idx]
- if 'callback' in item:
- item['callback']()
+ if "callback" in item:
+ item["callback"]()
return not_found
@@ -337,21 +354,19 @@ def AboutToShowGroup(self, ids):
return ([], not_found)
def LayoutUpdated(self, revision, parent):
- self.emit_signal(
- 'LayoutUpdated',
- (revision, parent)
- )
+ self.emit_signal("LayoutUpdated", (revision, parent))
+
class StatusNotifierItemService(DBusService):
- DBUS_SERVICE_PATH = '/org/ayatana/NotificationItem/io_github_slgobinath_SafeEyes'
-
- Category = 'ApplicationStatus'
- Id = 'io.github.slgobinath.SafeEyes'
- Title = 'Safe Eyes'
- Status = 'Active'
- IconName = 'io.github.slgobinath.SafeEyes-enabled'
- IconThemePath = ''
- ToolTip = ('', [], 'Safe Eyes', '')
+ DBUS_SERVICE_PATH = "/org/ayatana/NotificationItem/io_github_slgobinath_SafeEyes"
+
+ Category = "ApplicationStatus"
+ Id = "io.github.slgobinath.SafeEyes"
+ Title = "Safe Eyes"
+ Status = "Active"
+ IconName = "io.github.slgobinath.SafeEyes-enabled"
+ IconThemePath = ""
+ ToolTip = ("", [], "Safe Eyes", "")
XAyatanaLabel = ""
ItemIsMenu = True
Menu = None
@@ -360,7 +375,7 @@ def __init__(self, session_bus, context, menu_items):
super().__init__(
interface_info=SNI_NODE_INFO,
object_path=self.DBUS_SERVICE_PATH,
- bus=session_bus
+ bus=session_bus,
)
self.bus = session_bus
@@ -376,13 +391,13 @@ def register(self):
connection=self.bus,
flags=Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
info=None,
- name='org.kde.StatusNotifierWatcher',
- object_path='/StatusNotifierWatcher',
- interface_name='org.kde.StatusNotifierWatcher',
+ name="org.kde.StatusNotifierWatcher",
+ object_path="/StatusNotifierWatcher",
+ interface_name="org.kde.StatusNotifierWatcher",
cancellable=None,
)
- watcher.RegisterStatusNotifierItem('(s)', self.DBUS_SERVICE_PATH)
+ watcher.RegisterStatusNotifierItem("(s)", self.DBUS_SERVICE_PATH)
def unregister(self):
super().unregister()
@@ -394,67 +409,55 @@ def set_items(self, items):
def set_icon(self, icon):
self.IconName = icon
- self.emit_signal(
- 'NewIcon'
- )
+ self.emit_signal("NewIcon")
def set_tooltip(self, title, description):
- self.ToolTip = ('', [], title, description)
+ self.ToolTip = ("", [], title, description)
- self.emit_signal(
- 'NewTooltip'
- )
+ self.emit_signal("NewTooltip")
def set_xayatanalabel(self, label):
self.XAyatanaLabel = label
- self.emit_signal(
- "XAyatanaNewLabel",
- (label, "")
- )
+ self.emit_signal("XAyatanaNewLabel", (label, ""))
+
class TrayIcon:
- """
- Create and show the tray icon along with the tray menu.
- """
+ """Create and show the tray icon along with the tray menu."""
def __init__(self, context, plugin_config):
self.context = context
- self.on_show_settings = context['api']['show_settings']
- self.on_show_about = context['api']['show_about']
- self.quit = context['api']['quit']
- self.enable_safeeyes = context['api']['enable_safeeyes']
- self.disable_safeeyes = context['api']['disable_safeeyes']
- self.take_break = context['api']['take_break']
- self.has_breaks = context['api']['has_breaks']
- self.get_break_time = context['api']['get_break_time']
+ self.on_show_settings = context["api"]["show_settings"]
+ self.on_show_about = context["api"]["show_about"]
+ self.quit = context["api"]["quit"]
+ self.enable_safeeyes = context["api"]["enable_safeeyes"]
+ self.disable_safeeyes = context["api"]["disable_safeeyes"]
+ self.take_break = context["api"]["take_break"]
+ self.has_breaks = context["api"]["has_breaks"]
+ self.get_break_time = context["api"]["get_break_time"]
self.plugin_config = plugin_config
self.date_time = None
self.active = True
self.wakeup_time = None
self.idle_condition = threading.Condition()
self.lock = threading.Lock()
- self.allow_disabling = plugin_config['allow_disabling']
+ self.allow_disabling = plugin_config["allow_disabling"]
self.animate = False
self.menu_locked = False
session_bus = Gio.bus_get_sync(Gio.BusType.SESSION)
self.sni_service = StatusNotifierItemService(
- session_bus,
- context,
- menu_items = self.get_items()
+ session_bus, context, menu_items=self.get_items()
)
self.sni_service.register()
self.update_tooltip()
def initialize(self, plugin_config):
- """
- Initialize the tray icon by setting the config.
- """
+ """Initialize the tray icon by setting the config."""
self.plugin_config = plugin_config
- self.allow_disabling = plugin_config['allow_disabling']
+ self.allow_disabling = plugin_config["allow_disabling"]
self.update_menu()
self.update_tooltip()
@@ -462,7 +465,7 @@ def initialize(self, plugin_config):
def get_items(self):
breaks_found = self.has_breaks()
- info_message = _('No Breaks Available')
+ info_message = _("No Breaks Available")
if breaks_found:
if self.active:
@@ -473,139 +476,148 @@ def get_items(self):
if next_long_time:
if next_is_long:
- info_message = _('Next long break at %s') % (next_long_time)
+ info_message = _("Next long break at %s") % (next_long_time)
else:
- info_message = _('Next breaks at %(short)s/%(long)s') % {
- 'short': next_time,
- 'long': next_long_time
+ info_message = _("Next breaks at %(short)s/%(long)s") % {
+ "short": next_time,
+ "long": next_long_time,
}
else:
- info_message = _('Next break at %s') % (next_time)
+ info_message = _("Next break at %s") % (next_time)
else:
if self.wakeup_time:
- info_message = _('Disabled until %s') % utility.format_time(self.wakeup_time)
+ info_message = _("Disabled until %s") % utility.format_time(
+ self.wakeup_time
+ )
else:
- info_message = _('Disabled until restart')
+ info_message = _("Disabled until restart")
disable_items = []
if self.allow_disabling:
disable_option_dynamic_id = 13
- for disable_option in self.plugin_config['disable_options']:
- time_in_minutes = time_in_x = disable_option['time']
+ for disable_option in self.plugin_config["disable_options"]:
+ time_in_minutes = time_in_x = disable_option["time"]
# Validate time value
if not isinstance(time_in_minutes, int) or time_in_minutes <= 0:
- logging.error('Invalid time in disable option: ' + str(time_in_minutes))
+ logging.error(
+ "Invalid time in disable option: " + str(time_in_minutes)
+ )
continue
- time_unit = disable_option['unit'].lower()
- if time_unit == 'seconds' or time_unit == 'second':
+ time_unit = disable_option["unit"].lower()
+ if time_unit == "seconds" or time_unit == "second":
time_in_minutes = int(time_in_minutes / 60)
- label = self.context['locale'].ngettext(
- 'For %(num)d Second',
- 'For %(num)d Seconds',
- time_in_x
- ) % {'num': time_in_x}
- elif time_unit == 'minutes' or time_unit == 'minute':
+ label = self.context["locale"].ngettext(
+ "For %(num)d Second", "For %(num)d Seconds", time_in_x
+ ) % {"num": time_in_x}
+ elif time_unit == "minutes" or time_unit == "minute":
time_in_minutes = int(time_in_minutes * 1)
- label = self.context['locale'].ngettext(
- 'For %(num)d Minute',
- 'For %(num)d Minutes',
- time_in_x
- ) % {'num': time_in_x}
- elif time_unit == 'hours' or time_unit == 'hour':
+ label = self.context["locale"].ngettext(
+ "For %(num)d Minute", "For %(num)d Minutes", time_in_x
+ ) % {"num": time_in_x}
+ elif time_unit == "hours" or time_unit == "hour":
time_in_minutes = int(time_in_minutes * 60)
- label = self.context['locale'].ngettext(
- 'For %(num)d Hour',
- 'For %(num)d Hours',
- time_in_x
- ) % {'num': time_in_x}
+ label = self.context["locale"].ngettext(
+ "For %(num)d Hour", "For %(num)d Hours", time_in_x
+ ) % {"num": time_in_x}
else:
# Invalid unit
- logging.error('Invalid unit in disable option: ' + str(disable_option))
+ logging.error(
+ "Invalid unit in disable option: " + str(disable_option)
+ )
continue
- disable_items.append({
- 'id': disable_option_dynamic_id,
- 'label': label,
- 'callback': lambda time_in_minutes=time_in_minutes: self.on_disable_clicked(time_in_minutes),
- })
+ ttw = time_in_minutes
+ disable_items.append(
+ {
+ "id": disable_option_dynamic_id,
+ "label": label,
+ "callback": lambda ttw=ttw: self.on_disable_clicked(ttw),
+ }
+ )
disable_option_dynamic_id += 1
- disable_items.append({
- 'id': 12,
- 'label': _('Until restart'),
- 'callback': lambda: self.on_disable_clicked(-1),
- })
+ disable_items.append(
+ {
+ "id": 12,
+ "label": _("Until restart"),
+ "callback": lambda: self.on_disable_clicked(-1),
+ }
+ )
return [
{
- 'id': 1,
- 'label': info_message,
- 'icon-name': "io.github.slgobinath.SafeEyes-timer",
- 'enabled': breaks_found and self.active,
+ "id": 1,
+ "label": info_message,
+ "icon-name": "io.github.slgobinath.SafeEyes-timer",
+ "enabled": breaks_found and self.active,
},
{
- 'id': 2,
- 'type': "separator",
+ "id": 2,
+ "type": "separator",
},
{
- 'id': 3,
- 'label': _("Enable Safe Eyes"),
- 'enabled': breaks_found and not self.active,
- 'callback': self.on_enable_clicked,
- 'hidden': not self.allow_disabling,
+ "id": 3,
+ "label": _("Enable Safe Eyes"),
+ "enabled": breaks_found and not self.active,
+ "callback": self.on_enable_clicked,
+ "hidden": not self.allow_disabling,
},
{
- 'id': 4,
- 'label': _("Disable Safe Eyes"),
- 'enabled': breaks_found and self.active and not self.menu_locked,
- 'children-display': 'submenu',
- 'children': disable_items,
- 'hidden': not self.allow_disabling,
+ "id": 4,
+ "label": _("Disable Safe Eyes"),
+ "enabled": breaks_found and self.active and not self.menu_locked,
+ "children-display": "submenu",
+ "children": disable_items,
+ "hidden": not self.allow_disabling,
},
{
- 'id': 5,
- 'label': _('Take a break now'),
- 'enabled': breaks_found and self.active and not self.menu_locked,
- 'children-display': 'submenu',
- 'children': [
+ "id": 5,
+ "label": _("Take a break now"),
+ "enabled": breaks_found and self.active and not self.menu_locked,
+ "children-display": "submenu",
+ "children": [
{
- 'id': 9,
- 'label': _('Any break'),
- 'callback': lambda: self.on_manual_break_clicked(None),
+ "id": 9,
+ "label": _("Any break"),
+ "callback": lambda: self.on_manual_break_clicked(None),
},
{
- 'id': 10,
- 'label': _('Short break'),
- 'callback': lambda: self.on_manual_break_clicked(BreakType.SHORT_BREAK),
+ "id": 10,
+ "label": _("Short break"),
+ "callback": lambda: self.on_manual_break_clicked(
+ BreakType.SHORT_BREAK
+ ),
},
{
- 'id': 11,
- 'label': _('Long break'),
- 'callback': lambda: self.on_manual_break_clicked(BreakType.LONG_BREAK),
+ "id": 11,
+ "label": _("Long break"),
+ "callback": lambda: self.on_manual_break_clicked(
+ BreakType.LONG_BREAK
+ ),
},
- ]
+ ],
},
{
- 'id': 6,
- 'label': _('Settings'),
- 'enabled': not self.menu_locked,
- 'callback': self.show_settings,
+ "id": 6,
+ "label": _("Settings"),
+ "enabled": not self.menu_locked,
+ "callback": self.show_settings,
},
{
- 'id': 7,
- 'label': _('About'),
- 'callback': self.show_about,
+ "id": 7,
+ "label": _("About"),
+ "callback": self.show_about,
},
{
- 'id': 8,
- 'label': _('Quit'),
- 'enabled': not self.menu_locked,
- 'callback': self.quit_safe_eyes,
- 'hidden': not self.allow_disabling,
+ "id": 8,
+ "label": _("Quit"),
+ "enabled": not self.menu_locked,
+ "callback": self.quit_safe_eyes,
+ "hidden": not self.allow_disabling,
},
]
@@ -615,22 +627,26 @@ def update_menu(self):
def update_tooltip(self):
next_break = self.get_next_break_time()
- if next_break is not None and self.plugin_config.get('show_time_in_tray', False):
+ if next_break is not None and self.plugin_config.get(
+ "show_time_in_tray", False
+ ):
(next_time, next_long_time, _next_is_long) = next_break
- if next_long_time and self.plugin_config.get('show_long_time_in_tray', False):
+ if next_long_time and self.plugin_config.get(
+ "show_long_time_in_tray", False
+ ):
description = next_long_time
else:
description = next_time
else:
- description = ''
+ description = ""
- self.sni_service.set_tooltip('Safe Eyes', description)
+ self.sni_service.set_tooltip("Safe Eyes", description)
self.sni_service.set_xayatanalabel(description)
def quit_safe_eyes(self):
- """
- Handle Quit menu action.
+ """Handle Quit menu action.
+
This action terminates the application.
"""
with self.lock:
@@ -642,22 +658,22 @@ def quit_safe_eyes(self):
self.quit()
def show_settings(self):
- """
- Handle Settings menu action.
+ """Handle Settings menu action.
+
This action shows the Settings dialog.
"""
self.on_show_settings()
def show_about(self):
- """
- Handle About menu action.
+ """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.
+ """Update the next break time to be displayed in the menu and
+ optionally in the tray icon.
"""
logging.info("Update next break information")
self.date_time = dateTime
@@ -680,16 +696,13 @@ def get_next_break_time(self):
return (formatted_time, None, False)
-
def on_manual_break_clicked(self, break_type):
- """
- Trigger a break manually.
- """
+ """Trigger a break manually."""
self.take_break(break_type)
def on_enable_clicked(self):
- """
- Handle 'Enable Safe Eyes' menu action.
+ """Handle 'Enable Safe Eyes' menu action.
+
This action enables the application if it is currently disabled.
"""
if not self.active:
@@ -702,68 +715,66 @@ def on_enable_clicked(self):
self.idle_condition.release()
def on_disable_clicked(self, time_to_wait):
- """
- Handle the menu actions of all the sub menus of 'Disable Safe Eyes'.
+ """Handle the menu actions of all the sub menus of 'Disable Safe Eyes'.
+
This action disables the application if it is currently active.
"""
if self.active:
self.disable_ui()
if time_to_wait <= 0:
- info = _('Disabled until restart')
+ info = _("Disabled until restart")
self.disable_safeeyes(info)
self.wakeup_time = None
else:
- self.wakeup_time = datetime.datetime.now() + datetime.timedelta(minutes=time_to_wait)
- info = _('Disabled until %s') % utility.format_time(self.wakeup_time)
+ self.wakeup_time = datetime.datetime.now() + datetime.timedelta(
+ minutes=time_to_wait
+ )
+ info = _("Disabled until %s") % utility.format_time(self.wakeup_time)
self.disable_safeeyes(info)
utility.start_thread(self.__schedule_resume, time_minutes=time_to_wait)
self.update_menu()
def lock_menu(self):
- """
- This method is called by the core to prevent user from disabling Safe Eyes after the notification.
+ """This method is called by the core to prevent user from disabling
+ Safe Eyes after the notification.
"""
if self.active:
self.menu_locked = True
self.update_menu()
def unlock_menu(self):
- """
- This method is called by the core to activate the menu after the the break.
+ """This method is called by the core to activate the menu after the the
+ break.
"""
if self.active:
self.menu_locked = False
self.update_menu()
def disable_ui(self):
- """
- Change the UI to disabled state.
- """
+ """Change the UI to disabled state."""
if self.active:
- logging.info('Disable Safe Eyes')
+ logging.info("Disable Safe Eyes")
self.active = False
self.sni_service.set_icon("io.github.slgobinath.SafeEyes-disabled")
self.update_menu()
def enable_ui(self):
- """
- Change the UI to enabled state.
- """
+ """Change the UI to enabled state."""
if not self.active:
- logging.info('Enable Safe Eyes')
+ logging.info("Enable Safe Eyes")
self.active = True
self.sni_service.set_icon("io.github.slgobinath.SafeEyes-enabled")
self.update_menu()
def __schedule_resume(self, time_minutes):
- """
- Schedule a local timer to enable Safe Eyes after the given timeout.
+ """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:
@@ -773,9 +784,13 @@ def __schedule_resume(self, time_minutes):
def start_animation(self):
if not self.active or not self.animate:
return
- utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-disabled"))
+ utility.execute_main_thread(
+ lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-disabled")
+ )
time.sleep(0.5)
- utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-enabled"))
+ utility.execute_main_thread(
+ lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-enabled")
+ )
if self.animate and self.active:
time.sleep(0.5)
if self.animate and self.active:
@@ -784,18 +799,25 @@ def start_animation(self):
def stop_animation(self):
self.animate = False
if self.active:
- utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-enabled"))
+ utility.execute_main_thread(
+ lambda: self.sni_service.set_icon(
+ "io.github.slgobinath.SafeEyes-enabled"
+ )
+ )
else:
- utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-disabled"))
+ utility.execute_main_thread(
+ lambda: self.sni_service.set_icon(
+ "io.github.slgobinath.SafeEyes-disabled"
+ )
+ )
+
def init(ctx, safeeyes_cfg, plugin_config):
- """
- Initialize the tray icon.
- """
+ """Initialize the tray icon."""
global context
global tray_icon
global safeeyes_config
- logging.debug('Initialize Tray Icon plugin')
+ logging.debug("Initialize Tray Icon plugin")
context = ctx
safeeyes_config = safeeyes_cfg
if not tray_icon:
@@ -805,17 +827,13 @@ def init(ctx, safeeyes_cfg, plugin_config):
def update_next_break(break_obj, next_break_time):
- """
- Update the next break time.
- """
+ """Update the next break time."""
tray_icon.next_break_time(next_break_time)
def on_pre_break(break_obj):
- """
- Disable the menu if strict_break is enabled
- """
- if safeeyes_config.get('strict_break'):
+ """Disable the menu if strict_break is enabled."""
+ if safeeyes_config.get("strict_break"):
tray_icon.lock_menu()
tray_icon.animate = True
tray_icon.start_animation()
@@ -830,14 +848,10 @@ def on_stop_break():
def on_start():
- """
- Enable the tray icon.
- """
+ """Enable the tray icon."""
tray_icon.enable_ui()
def on_stop():
- """
- Disable the tray icon.
- """
+ """Disable the tray icon."""
tray_icon.disable_ui()
diff --git a/safeeyes/rpc.py b/safeeyes/rpc.py
index abe36295..e1398b5e 100644
--- a/safeeyes/rpc.py
+++ b/safeeyes/rpc.py
@@ -16,9 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-RPC server and client implementation.
-"""
+"""RPC server and client implementation."""
import logging
from threading import Thread
@@ -27,87 +25,75 @@
class RPCServer:
- """
- An asynchronous RPC server.
- """
+ """An asynchronous RPC server."""
+
def __init__(self, port, context):
self.__running = False
- logging.info('Setting up an RPC server on port %d', port)
- self.__server = SimpleXMLRPCServer(("localhost", port), logRequests=False, allow_none=True)
- self.__server.register_function(context['api']['show_settings'], 'show_settings')
- self.__server.register_function(context['api']['show_about'], 'show_about')
- self.__server.register_function(context['api']['enable_safeeyes'], 'enable_safeeyes')
- self.__server.register_function(context['api']['disable_safeeyes'], 'disable_safeeyes')
- self.__server.register_function(context['api']['take_break'], 'take_break')
- self.__server.register_function(context['api']['status'], 'status')
- self.__server.register_function(context['api']['quit'], 'quit')
+ logging.info("Setting up an RPC server on port %d", port)
+ self.__server = SimpleXMLRPCServer(
+ ("localhost", port), logRequests=False, allow_none=True
+ )
+ self.__server.register_function(
+ context["api"]["show_settings"], "show_settings"
+ )
+ self.__server.register_function(context["api"]["show_about"], "show_about")
+ self.__server.register_function(
+ context["api"]["enable_safeeyes"], "enable_safeeyes"
+ )
+ self.__server.register_function(
+ context["api"]["disable_safeeyes"], "disable_safeeyes"
+ )
+ self.__server.register_function(context["api"]["take_break"], "take_break")
+ self.__server.register_function(context["api"]["status"], "status")
+ self.__server.register_function(context["api"]["quit"], "quit")
def start(self):
- """
- Start the RPC server.
- """
+ """Start the RPC server."""
if not self.__running:
self.__running = True
- logging.info('Start the RPC server')
+ logging.info("Start the RPC server")
server_thread = Thread(target=self.__server.serve_forever)
server_thread.start()
def stop(self):
- """
- Stop the server.
- """
+ """Stop the server."""
if self.__running:
- logging.info('Stop the RPC server')
+ logging.info("Stop the RPC server")
self.__running = False
self.__server.shutdown()
class RPCClient:
- """
- An RPC client to communicate with the RPC server.
- """
+ """An RPC client to communicate with the RPC server."""
+
def __init__(self, port):
self.port = port
- self.proxy = ServerProxy('http://localhost:%d/' % self.port, allow_none=True)
+ self.proxy = ServerProxy("http://localhost:%d/" % self.port, allow_none=True)
def show_settings(self):
- """
- Show the settings dialog.
- """
+ """Show the settings dialog."""
self.proxy.show_settings()
def show_about(self):
- """
- Show the about dialog.
- """
+ """Show the about dialog."""
self.proxy.show_about()
def enable_safeeyes(self):
- """
- Enable Safe Eyes.
- """
+ """Enable Safe Eyes."""
self.proxy.enable_safeeyes()
def disable_safeeyes(self):
- """
- Disable Safe Eyes.
- """
+ """Disable Safe Eyes."""
self.proxy.disable_safeeyes(None)
def take_break(self):
- """
- Take a break now.
- """
+ """Take a break now."""
self.proxy.take_break()
def status(self):
- """
- Return the status of Safe Eyes
- """
+ """Return the status of Safe Eyes."""
return self.proxy.status()
def quit(self):
- """
- Quit Safe Eyes.
- """
+ """Quit Safe Eyes."""
self.proxy.quit()
diff --git a/safeeyes/safeeyes.py b/safeeyes/safeeyes.py
index 0fb9a9b2..d99c7c2d 100644
--- a/safeeyes/safeeyes.py
+++ b/safeeyes/safeeyes.py
@@ -16,13 +16,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-SafeEyes connects all the individual components and provide the complete application.
+"""SafeEyes connects all the individual components and provide the complete
+application.
"""
import atexit
import logging
-import os
from threading import Timer
import gi
@@ -36,16 +35,14 @@
from safeeyes.core import SafeEyesCore
from safeeyes.ui.settings_dialog import SettingsDialog
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gio, GLib
SAFE_EYES_VERSION = "2.2.2"
class SafeEyes(Gtk.Application):
- """
- This class represents a runnable Safe Eyes instance.
- """
+ """This class represents a runnable Safe Eyes instance."""
required_plugin_dialog_active = False
retry_errored_plugins_count = 0
@@ -53,7 +50,8 @@ class SafeEyes(Gtk.Application):
def __init__(self, system_locale, config, cli_args):
super().__init__(
application_id="io.github.slgobinath.SafeEyes",
- flags=Gio.ApplicationFlags.FLAGS_NONE ## This is necessary for compatibility with Ubuntu 22.04.
+ # This is necessary for compatibility with Ubuntu 22.04.
+ flags=Gio.ApplicationFlags.FLAGS_NONE,
)
self.active = False
self.break_screen = None
@@ -63,45 +61,51 @@ def __init__(self, system_locale, config, cli_args):
self.plugins_manager = None
self.settings_dialog_active = False
self.rpc_server = None
- self._status = ''
+ self._status = ""
self.cli_args = cli_args
self.system_locale = system_locale
def start(self):
- """
- Start Safe Eyes
- """
+ """Start Safe Eyes."""
self.run()
def do_startup(self):
Gtk.Application.do_startup(self)
- logging.info('Starting up Application')
+ logging.info("Starting up Application")
# Initialize the Safe Eyes Context
- self.context['version'] = SAFE_EYES_VERSION
- self.context['desktop'] = utility.desktop_environment()
- self.context['is_wayland'] = utility.is_wayland()
- self.context['locale'] = self.system_locale
- self.context['api'] = {}
- self.context['api']['show_settings'] = lambda: utility.execute_main_thread(
- self.show_settings)
- self.context['api']['show_about'] = lambda: utility.execute_main_thread(
- self.show_about)
- self.context['api']['enable_safeeyes'] = lambda next_break_time=-1, reset_breaks=False: \
- utility.execute_main_thread(self.enable_safeeyes, next_break_time, reset_breaks)
- self.context['api']['disable_safeeyes'] = lambda status=None, is_resting=False: utility.execute_main_thread(
- self.disable_safeeyes, status, is_resting)
- self.context['api']['status'] = self.status
- self.context['api']['quit'] = lambda: utility.execute_main_thread(
- self.quit)
- if self.config.get('persist_state'):
- self.context['session'] = utility.open_session()
+ self.context["version"] = SAFE_EYES_VERSION
+ self.context["desktop"] = utility.desktop_environment()
+ self.context["is_wayland"] = utility.is_wayland()
+ self.context["locale"] = self.system_locale
+ self.context["api"] = {}
+ self.context["api"]["show_settings"] = lambda: utility.execute_main_thread(
+ self.show_settings
+ )
+ self.context["api"]["show_about"] = lambda: utility.execute_main_thread(
+ self.show_about
+ )
+ self.context["api"]["enable_safeeyes"] = (
+ lambda next_break_time=-1, reset_breaks=False: utility.execute_main_thread(
+ self.enable_safeeyes, next_break_time, reset_breaks
+ )
+ )
+ self.context["api"]["disable_safeeyes"] = (
+ lambda status=None, is_resting=False: utility.execute_main_thread(
+ self.disable_safeeyes, status, is_resting
+ )
+ )
+ self.context["api"]["status"] = self.status
+ self.context["api"]["quit"] = lambda: utility.execute_main_thread(self.quit)
+ if self.config.get("persist_state"):
+ self.context["session"] = utility.open_session()
else:
- self.context['session'] = {'plugin': {}}
+ self.context["session"] = {"plugin": {}}
self.break_screen = BreakScreen(
- self.context, self.on_skipped, self.on_postponed, utility.STYLE_SHEET_PATH)
+ self.context, self.on_skipped, self.on_postponed, utility.STYLE_SHEET_PATH
+ )
self.break_screen.initialize(self.config)
self.plugins_manager = PluginManager()
self.safe_eyes_core = SafeEyesCore(self.context)
@@ -112,10 +116,10 @@ def do_startup(self):
self.safe_eyes_core.on_stop_break += self.stop_break
self.safe_eyes_core.on_update_next_break += self.update_next_break
self.safe_eyes_core.initialize(self.config)
- self.context['api']['take_break'] = self.take_break
- self.context['api']['has_breaks'] = self.safe_eyes_core.has_breaks
- self.context['api']['postpone'] = self.safe_eyes_core.postpone
- self.context['api']['get_break_time'] = self.safe_eyes_core.get_break_time
+ self.context["api"]["take_break"] = self.take_break
+ self.context["api"]["has_breaks"] = self.safe_eyes_core.has_breaks
+ self.context["api"]["postpone"] = self.safe_eyes_core.postpone
+ self.context["api"]["get_break_time"] = self.safe_eyes_core.get_break_time
try:
self.plugins_manager.init(self.context, self.config)
@@ -126,18 +130,22 @@ def do_startup(self):
atexit.register(self.persist_session)
- if self.config.get('use_rpc_server', True):
+ if self.config.get("use_rpc_server", True):
self.__start_rpc_server()
- if not self.plugins_manager.needs_retry() and not self.required_plugin_dialog_active and self.safe_eyes_core.has_breaks():
+ if (
+ not self.plugins_manager.needs_retry()
+ and not self.required_plugin_dialog_active
+ and self.safe_eyes_core.has_breaks()
+ ):
self.active = True
- self.context['state'] = State.START
- self.plugins_manager.start() # Call the start method of all plugins
+ self.context["state"] = State.START
+ self.plugins_manager.start() # Call the start method of all plugins
self.safe_eyes_core.start()
self.handle_system_suspend()
def do_activate(self):
- logging.info('Application activated')
+ logging.info("Application activated")
if self.plugins_manager.needs_retry():
GLib.timeout_add_seconds(1, self._retry_errored_plugins)
@@ -153,12 +161,11 @@ def do_activate(self):
elif self.cli_args.take_break:
self.take_break()
-
def _retry_errored_plugins(self):
if not self.plugins_manager.needs_retry():
return
- logging.info(f"Retry loading errored plugin")
+ logging.info("Retry loading errored plugin")
self.plugins_manager.retry_errored_plugins()
error = self.plugins_manager.get_retryable_error()
@@ -178,60 +185,58 @@ def _retry_errored_plugins(self):
GLib.timeout_add_seconds(timeout, self._retry_errored_plugins)
-
def show_settings(self):
- """
- Listen to tray icon Settings action and send the signal to Settings dialog.
+ """Listen to tray icon Settings action and send the signal to Settings
+ dialog.
"""
if not self.settings_dialog_active:
logging.info("Show Settings dialog")
self.settings_dialog_active = True
- settings_dialog = SettingsDialog(
- self.config.clone(), self.save_settings)
+ settings_dialog = SettingsDialog(self.config.clone(), self.save_settings)
settings_dialog.show()
def show_required_plugin_dialog(self, error: RequiredPluginException):
self.required_plugin_dialog_active = True
logging.info("Show RequiredPlugin dialog")
+ plugin_id = error.get_plugin_id()
+
dialog = RequiredPluginDialog(
error.get_plugin_id(),
error.get_plugin_name(),
error.get_message(),
self.quit,
- lambda: self.disable_plugin(plugin_id)
+ lambda: self.disable_plugin(plugin_id),
)
dialog.show()
def disable_plugin(self, plugin_id):
- """
- Temporarily disable plugin, and restart SafeEyes.
- """
+ """Temporarily disable plugin, and restart SafeEyes."""
config = self.config.clone()
- for plugin in config.get('plugins'):
- if plugin['id'] == plugin_id:
- plugin['enabled'] = False
+ for plugin in config.get("plugins"):
+ if plugin["id"] == plugin_id:
+ plugin["enabled"] = False
self.required_plugin_dialog_active = False
self.restart(config, set_active=True)
def show_about(self):
- """
- Listen to tray icon About action and send the signal to About dialog.
+ """Listen to tray icon About action and send the signal to About
+ dialog.
"""
logging.info("Show About dialog")
about_dialog = AboutDialog(SAFE_EYES_VERSION)
about_dialog.show()
def quit(self):
- """
- Listen to the tray menu quit action and stop the core, notification and the app itself.
+ """Listen to the tray menu quit action and stop the core, notification
+ and the app itself.
"""
logging.info("Quit Safe Eyes")
self.break_screen.close()
- self.context['state'] = State.QUIT
+ self.context["state"] = State.QUIT
self.plugins_manager.stop()
self.safe_eyes_core.stop()
self.plugins_manager.exit()
@@ -241,8 +246,9 @@ def quit(self):
super().quit()
def handle_suspend_callback(self, sleeping):
- """
- If the system goes to sleep, Safe Eyes stop the core if it is already active.
+ """If the system goes to sleep, Safe Eyes stop the core if it is
+ already active.
+
If it was active, Safe Eyes will become active after wake up.
"""
if sleeping:
@@ -262,44 +268,40 @@ def handle_suspend_signal(self, proxy, sender, signal, parameters):
if signal != "PrepareForSleep":
return
- (sleeping, ) = parameters
+ (sleeping,) = parameters
self.handle_suspend_callback(sleeping)
def handle_system_suspend(self):
- """
- Setup system suspend listener.
- """
+ """Setup system suspend listener."""
self.suspend_proxy = Gio.DBusProxy.new_for_bus_sync(
bus_type=Gio.BusType.SYSTEM,
flags=Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
info=None,
- name='org.freedesktop.login1',
- object_path='/org/freedesktop/login1',
- interface_name='org.freedesktop.login1.Manager',
+ name="org.freedesktop.login1",
+ object_path="/org/freedesktop/login1",
+ interface_name="org.freedesktop.login1.Manager",
cancellable=None,
)
- self.suspend_proxy.connect('g-signal', self.handle_suspend_signal)
+ self.suspend_proxy.connect("g-signal", self.handle_suspend_signal)
def on_skipped(self):
- """
- Listen to break screen Skip action and send the signal to core.
- """
+ """Listen to break screen Skip action and send the signal to core."""
logging.info("User skipped the break")
self.safe_eyes_core.skip()
self.plugins_manager.stop_break()
def on_postponed(self):
- """
- Listen to break screen Postpone action and send the signal to core.
+ """Listen to break screen Postpone action and send the signal to
+ core.
"""
logging.info("User postponed the break")
self.safe_eyes_core.postpone()
self.plugins_manager.stop_break()
def save_settings(self, config):
- """
- Listen to Settings dialog Save action and write to the config file.
+ """Listen to Settings dialog Save action and write to the config
+ file.
"""
self.settings_dialog_active = False
@@ -322,10 +324,10 @@ def save_settings(self, config):
def restart(self, config, set_active=False):
logging.info("Initialize SafeEyesCore with modified settings")
- if self.rpc_server is None and config.get('use_rpc_server'):
+ if self.rpc_server is None and config.get("use_rpc_server"):
# RPC server wasn't running but now enabled
self.__start_rpc_server()
- elif self.rpc_server is not None and not config.get('use_rpc_server'):
+ elif self.rpc_server is not None and not config.get("use_rpc_server"):
# RPC server was running but now disabled
self.__stop_rpc_server()
@@ -349,95 +351,76 @@ def restart(self, config, set_active=False):
self.plugins_manager.start()
def enable_safeeyes(self, scheduled_next_break_time=-1, reset_breaks=False):
- """
- Listen to tray icon enable action and send the signal to core.
- """
- if not self.required_plugin_dialog_active and not self.active and self.safe_eyes_core.has_breaks():
+ """Listen to tray icon enable action and send the signal to core."""
+ if (
+ not self.required_plugin_dialog_active
+ and not self.active
+ and self.safe_eyes_core.has_breaks()
+ ):
self.active = True
self.safe_eyes_core.start(scheduled_next_break_time, reset_breaks)
self.plugins_manager.start()
- def disable_safeeyes(self, status=None, is_resting = False):
- """
- Listen to tray icon disable action and send the signal to core.
- """
+ def disable_safeeyes(self, status=None, is_resting=False):
+ """Listen to tray icon disable action and send the signal to core."""
if self.active:
self.active = False
self.plugins_manager.stop()
self.safe_eyes_core.stop(is_resting)
if status is None:
- status = _('Disabled until restart')
+ status = _("Disabled until restart")
self._status = status
def on_start_break(self, break_obj):
- """
- Pass the break information to plugins.
- """
+ """Pass the break information to plugins."""
if not self.plugins_manager.start_break(break_obj):
return False
return True
def start_break(self, break_obj):
- """
- Pass the break information to break screen.
- """
+ """Pass the break information to break screen."""
# Get the HTML widgets content from plugins
widget = self.plugins_manager.get_break_screen_widgets(break_obj)
actions = self.plugins_manager.get_break_screen_tray_actions(break_obj)
self.break_screen.show_message(break_obj, widget, actions)
def countdown(self, countdown, seconds):
- """
- Pass the countdown to plugins and break screen.
- """
+ """Pass the countdown to plugins and break screen."""
self.break_screen.show_count_down(countdown, seconds)
self.plugins_manager.countdown(countdown, seconds)
return True
def update_next_break(self, break_obj, break_time):
- """
- Update the next break to plugins and save the session.
- """
+ """Update the next break to plugins and save the session."""
self.plugins_manager.update_next_break(break_obj, break_time)
- self._status = _('Next break at %s') % (
- utility.format_time(break_time))
- if self.config.get('persist_state'):
- utility.write_json(utility.SESSION_FILE_PATH,
- self.context['session'])
+ self._status = _("Next break at %s") % (utility.format_time(break_time))
+ if self.config.get("persist_state"):
+ utility.write_json(utility.SESSION_FILE_PATH, self.context["session"])
def stop_break(self):
- """
- Stop the current break.
- """
+ """Stop the current break."""
self.break_screen.close()
self.plugins_manager.stop_break()
return True
- def take_break(self, break_type = None):
- """
- Take a break now.
- """
+ def take_break(self, break_type=None):
+ """Take a break now."""
utility.execute_main_thread(self.safe_eyes_core.take_break, break_type)
def status(self):
- """
- Return the status of Safe Eyes.
- """
+ """Return the status of Safe Eyes."""
return self._status
def persist_session(self):
- """
- Save the session object to the session file.
- """
- if self.config.get('persist_state'):
- utility.write_json(utility.SESSION_FILE_PATH,
- self.context['session'])
+ """Save the session object to the session file."""
+ if self.config.get("persist_state"):
+ utility.write_json(utility.SESSION_FILE_PATH, self.context["session"])
else:
utility.delete(utility.SESSION_FILE_PATH)
def __start_rpc_server(self):
if self.rpc_server is None:
- self.rpc_server = RPCServer(self.config.get('rpc_port'), self.context)
+ self.rpc_server = RPCServer(self.config.get("rpc_port"), self.context)
self.rpc_server.start()
def __stop_rpc_server(self):
diff --git a/safeeyes/ui/about_dialog.py b/safeeyes/ui/about_dialog.py
index 39362b03..a766ea06 100644
--- a/safeeyes/ui/about_dialog.py
+++ b/safeeyes/ui/about_dialog.py
@@ -16,9 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-This module creates the AboutDialog which shows the version and license.
-"""
+"""This module creates the AboutDialog which shows the version and license."""
import os
@@ -28,35 +26,36 @@
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.
+ """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.
"""
def __init__(self, version):
builder = utility.create_gtk_builder(ABOUT_DIALOG_GLADE)
builder.connect_signals(self)
- self.window = builder.get_object('window_about')
- builder.get_object('lbl_decription').set_label(_("Safe Eyes protects your eyes from eye strain (asthenopia) by reminding you to take breaks while you're working long hours at the computer"))
- builder.get_object('lbl_license').set_label(_('License') + ':')
+ self.window = builder.get_object("window_about")
+ builder.get_object("lbl_decription").set_label(
+ _(
+ "Safe Eyes protects your eyes from eye strain (asthenopia) by reminding"
+ " you to take breaks while you're working long hours at the computer"
+ )
+ )
+ builder.get_object("lbl_license").set_label(_("License") + ":")
# Set the version at the runtime
- builder.get_object('lbl_app_name').set_label('Safe Eyes ' + version)
+ builder.get_object("lbl_app_name").set_label("Safe Eyes " + version)
def show(self):
- """
- Show the About dialog.
- """
+ """Show the About dialog."""
self.window.show_all()
def on_window_delete(self, *args):
- """
- Window close event handler.
- """
+ """Window close event handler."""
self.window.destroy()
def on_close_clicked(self, *args):
- """
- Close button click event handler.
- """
+ """Close button click event handler."""
self.window.destroy()
diff --git a/safeeyes/ui/break_screen.py b/safeeyes/ui/break_screen.py
index 7546fd83..bd642da6 100644
--- a/safeeyes/ui/break_screen.py
+++ b/safeeyes/ui/break_screen.py
@@ -19,7 +19,6 @@
import logging
import os
-import threading
import time
import gi
@@ -27,7 +26,7 @@
from Xlib.display import Display
from Xlib.display import X
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gdk
from gi.repository import GLib
from gi.repository import Gtk
@@ -36,9 +35,10 @@
class BreakScreen:
- """
- The fullscreen window which prevents users from using the computer.
- This class reads the break_screen.glade and build the user interface.
+ """The fullscreen window which prevents users from using the computer.
+
+ This class reads the break_screen.glade and build the user
+ interface.
"""
def __init__(self, context, on_skipped, on_postponed, style_sheet_path):
@@ -59,76 +59,67 @@ def __init__(self, context, on_skipped, on_postponed, style_sheet_path):
# 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)
+ Gtk.StyleContext.add_provider_for_screen(
+ Gdk.Screen.get_default(),
+ css_provider,
+ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION,
+ )
def initialize(self, config):
- """
- Initialize the internal properties from configuration
- """
+ """Initialize the internal properties from configuration."""
logging.info("Initialize the break screen")
- self.enable_postpone = config.get('allow_postpone', False)
- self.keycode_shortcut_postpone = config.get('shortcut_postpone', 65)
- self.keycode_shortcut_skip = config.get('shortcut_skip', 9)
- self.shortcut_disable_time = config.get('shortcut_disable_time', 2)
- self.strict_break = config.get('strict_break', False)
+ self.enable_postpone = config.get("allow_postpone", False)
+ self.keycode_shortcut_postpone = config.get("shortcut_postpone", 65)
+ self.keycode_shortcut_skip = config.get("shortcut_skip", 9)
+ self.shortcut_disable_time = config.get("shortcut_disable_time", 2)
+ self.strict_break = config.get("strict_break", False)
def skip_break(self):
- """
- Skip the break from the break screen
- """
+ """Skip the break from the break screen."""
logging.info("User skipped the break")
- # Must call on_skipped before close to lock screen before closing the break screen
+ # Must call on_skipped before close to lock screen before closing the break
+ # screen
self.on_skipped()
self.close()
def postpone_break(self):
- """
- Postpone the break from the break screen
- """
+ """Postpone the break from the break screen."""
logging.info("User postponed the break")
self.on_postponed()
self.close()
def on_window_delete(self, *args):
- """
- Window close event handler.
- """
+ """Window close event handler."""
logging.info("Closing the break screen")
self.close()
def on_skip_clicked(self, button):
- """
- Skip button press event handler.
- """
+ """Skip button press event handler."""
self.skip_break()
def on_postpone_clicked(self, button):
- """
- Postpone button press event handler.
- """
+ """Postpone button press event handler."""
self.postpone_break()
def show_count_down(self, countdown, seconds):
- """
- Show/update the count down on all screens.
- """
+ """Show/update the count down on all screens."""
self.enable_shortcut = self.shortcut_disable_time <= seconds
mins, secs = divmod(countdown, 60)
- timeformat = '{:02d}:{:02d}'.format(mins, secs)
+ timeformat = "{:02d}:{:02d}".format(mins, secs)
GLib.idle_add(lambda: self.__update_count_down(timeformat))
def show_message(self, break_obj, widget, tray_actions=[]):
- """
- Show the break screen with the given message on all displays.
- """
+ """Show the break screen with the given message on all displays."""
message = break_obj.name
image_path = break_obj.image
self.enable_shortcut = self.shortcut_disable_time <= 0
- GLib.idle_add(lambda: self.__show_break_screen(message, image_path, widget, tray_actions))
+ GLib.idle_add(
+ lambda: self.__show_break_screen(message, image_path, widget, tray_actions)
+ )
def close(self):
- """
- Hide the break screen from active window and destroy all other windows
+ """Hide the break screen from active window and destroy all other
+ windows.
"""
logging.info("Close the break screen(s)")
self.__release_keyboard()
@@ -137,17 +128,16 @@ def close(self):
GLib.idle_add(lambda: self.__destroy_all_screens())
def __tray_action(self, button, tray_action):
- """
- Tray action handler.
- Hides all toolbar buttons for this action and call the action provided by the plugin.
+ """Tray action handler.
+
+ Hides all toolbar buttons for this action and call the action
+ provided by the plugin.
"""
tray_action.reset()
tray_action.action()
def __show_break_screen(self, message, image_path, widget, tray_actions):
- """
- Show an empty break screen on all screens.
- """
+ """Show an empty break screen on all screens."""
# Lock the keyboard
utility.start_thread(self.__lock_keyboard)
@@ -156,8 +146,8 @@ def __show_break_screen(self, message, image_path, widget, tray_actions):
no_of_monitors = display.get_n_monitors()
logging.info("Show break screens in %d display(s)", no_of_monitors)
- skip_button_disabled = self.context.get('skip_button_disabled', False)
- postpone_button_disabled = self.context.get('postpone_button_disabled', False)
+ skip_button_disabled = self.context.get("skip_button_disabled", False)
+ postpone_button_disabled = self.context.get("postpone_button_disabled", False)
for monitor_num in range(no_of_monitors):
monitor = display.get_monitor(monitor_num)
@@ -181,11 +171,19 @@ def __show_break_screen(self, message, image_path, widget, tray_actions):
for tray_action in tray_actions:
toolbar_button = None
if tray_action.system_icon:
- toolbar_button = Gtk.ToolButton.new_from_stock(tray_action.get_icon())
+ toolbar_button = Gtk.ToolButton.new_from_stock(
+ tray_action.get_icon()
+ )
else:
- toolbar_button = Gtk.ToolButton.new(tray_action.get_icon(), tray_action.name)
+ toolbar_button = Gtk.ToolButton.new(
+ tray_action.get_icon(), tray_action.name
+ )
tray_action.add_toolbar_button(toolbar_button)
- toolbar_button.connect("clicked", lambda button, action: self.__tray_action(button, action), tray_action)
+ toolbar_button.connect(
+ "clicked",
+ lambda button, action: self.__tray_action(button, action),
+ tray_action,
+ )
toolbar_button.set_tooltip_text(_(tray_action.name))
toolbar.add(toolbar_button)
toolbar_button.show()
@@ -193,17 +191,17 @@ def __show_break_screen(self, message, image_path, widget, tray_actions):
# Add the buttons
if self.enable_postpone and not postpone_button_disabled:
# Add postpone button
- btn_postpone = Gtk.Button.new_with_label(_('Postpone'))
- btn_postpone.get_style_context().add_class('btn_postpone')
- btn_postpone.connect('clicked', self.on_postpone_clicked)
+ btn_postpone = Gtk.Button.new_with_label(_("Postpone"))
+ btn_postpone.get_style_context().add_class("btn_postpone")
+ btn_postpone.connect("clicked", self.on_postpone_clicked)
btn_postpone.set_visible(True)
box_buttons.pack_start(btn_postpone, True, True, 0)
if not self.strict_break and not skip_button_disabled:
# Add the skip button
- btn_skip = Gtk.Button.new_with_label(_('Skip'))
- btn_skip.get_style_context().add_class('btn_skip')
- btn_skip.connect('clicked', self.on_skip_clicked)
+ btn_skip = Gtk.Button.new_with_label(_("Skip"))
+ btn_skip.get_style_context().add_class("btn_skip")
+ btn_skip.connect("clicked", self.on_skip_clicked)
btn_skip.set_visible(True)
box_buttons.pack_start(btn_skip, True, True, 0)
@@ -218,7 +216,7 @@ def __show_break_screen(self, message, image_path, widget, tray_actions):
# 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':
+ if self.context["desktop"] == "kde":
# Fix flickering screen in KDE by setting opacity to 1
window.set_opacity(0.9)
@@ -235,15 +233,13 @@ def __show_break_screen(self, message, image_path, widget, tray_actions):
logging.info("Moved break screen to Display[%d, %d]", x, y)
def __update_count_down(self, count):
- """
- Update the countdown on all break screens.
- """
+ """Update the countdown on all break screens."""
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
+ """Lock the keyboard to prevent the user from using keyboard
+ shortcuts.
"""
logging.info("Lock the keyboard")
self.lock_keyboard = True
@@ -259,10 +255,16 @@ def __lock_keyboard(self):
# 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 and not self.strict_break:
+ if (
+ event.detail == self.keycode_shortcut_skip
+ and not self.strict_break
+ ):
self.skip_break()
break
- elif self.enable_postpone and event.detail == self.keycode_shortcut_postpone:
+ elif (
+ self.enable_postpone
+ and event.detail == self.keycode_shortcut_postpone
+ ):
self.postpone_break()
break
else:
@@ -270,18 +272,14 @@ def __lock_keyboard(self):
time.sleep(1)
def __release_keyboard(self):
- """
- Release the locked keyboard.
- """
+ """Release the locked keyboard."""
logging.info("Unlock the keyboard")
self.lock_keyboard = False
self.display.ungrab_keyboard(X.CurrentTime)
self.display.flush()
def __destroy_all_screens(self):
- """
- Close all the break screens.
- """
+ """Close all the break screens."""
for win in self.windows:
win.destroy()
del self.windows[:]
diff --git a/safeeyes/ui/required_plugin_dialog.py b/safeeyes/ui/required_plugin_dialog.py
index debe53b3..a345b7c3 100644
--- a/safeeyes/ui/required_plugin_dialog.py
+++ b/safeeyes/ui/required_plugin_dialog.py
@@ -16,8 +16,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-This module creates the RequiredPluginDialog which shows the error for a required plugin.
+"""This module creates the RequiredPluginDialog which shows the error for a
+required plugin.
"""
import os
@@ -25,12 +25,14 @@
from safeeyes import utility
from safeeyes.model import PluginDependency
-REQUIRED_PLUGIN_DIALOG_GLADE = os.path.join(utility.BIN_DIRECTORY, "glade/required_plugin_dialog.glade")
+REQUIRED_PLUGIN_DIALOG_GLADE = os.path.join(
+ utility.BIN_DIRECTORY, "glade/required_plugin_dialog.glade"
+)
class RequiredPluginDialog:
- """
- RequiredPluginDialog shows an error when a plugin has required dependencies.
+ """RequiredPluginDialog shows an error when a plugin has required
+ dependencies.
"""
def __init__(self, plugin_id, plugin_name, message, on_quit, on_disable_plugin):
@@ -38,38 +40,45 @@ def __init__(self, plugin_id, plugin_name, message, on_quit, on_disable_plugin):
self.on_disable_plugin = on_disable_plugin
builder = utility.create_gtk_builder(REQUIRED_PLUGIN_DIALOG_GLADE)
- self.window = builder.get_object('window_required_plugin')
+ self.window = builder.get_object("window_required_plugin")
self.window.connect("delete-event", self.on_window_delete)
- builder.get_object('btn_close').connect('clicked', self.on_close_clicked)
- builder.get_object('btn_disable_plugin').connect('clicked', self.on_disable_plugin_clicked)
-
- builder.get_object('lbl_header').set_label(_("The required plugin '%s' is missing dependencies!") % _(plugin_name))
-
- builder.get_object('lbl_main').set_label(_("Please install the dependencies or disable the plugin. To hide this message, you can also deactivate the plugin in the settings."))
-
- builder.get_object('btn_close').set_label(_("Quit"))
- builder.get_object('btn_disable_plugin').set_label(_("Disable plugin temporarily"))
+ builder.get_object("btn_close").connect("clicked", self.on_close_clicked)
+ builder.get_object("btn_disable_plugin").connect(
+ "clicked", self.on_disable_plugin_clicked
+ )
+
+ builder.get_object("lbl_header").set_label(
+ _("The required plugin '%s' is missing dependencies!") % _(plugin_name)
+ )
+
+ builder.get_object("lbl_main").set_label(
+ _(
+ "Please install the dependencies or disable the plugin. To hide this"
+ " message, you can also deactivate the plugin in the settings."
+ )
+ )
+
+ builder.get_object("btn_close").set_label(_("Quit"))
+ builder.get_object("btn_disable_plugin").set_label(
+ _("Disable plugin temporarily")
+ )
if isinstance(message, PluginDependency):
- builder.get_object('lbl_message').set_label(_(message.message))
- btn_extra_link = builder.get_object('btn_extra_link')
+ builder.get_object("lbl_message").set_label(_(message.message))
+ btn_extra_link = builder.get_object("btn_extra_link")
btn_extra_link.set_label(_("Click here for more information"))
btn_extra_link.set_uri(message.link)
btn_extra_link.set_visible(True)
else:
- builder.get_object('lbl_message').set_label(_(message))
+ builder.get_object("lbl_message").set_label(_(message))
def show(self):
- """
- Show the dialog.
- """
+ """Show the dialog."""
self.window.show_all()
def on_window_delete(self, *args):
- """
- Window close event handler.
- """
+ """Window close event handler."""
self.window.destroy()
self.on_quit()
diff --git a/safeeyes/ui/settings_dialog.py b/safeeyes/ui/settings_dialog.py
index 62f54f4b..8eff861e 100644
--- a/safeeyes/ui/settings_dialog.py
+++ b/safeeyes/ui/settings_dialog.py
@@ -23,33 +23,43 @@
from safeeyes import utility
from safeeyes.model import Config, PluginDependency
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import GdkPixbuf
-SETTINGS_DIALOG_GLADE = os.path.join(utility.BIN_DIRECTORY, "glade/settings_dialog.glade")
-SETTINGS_DIALOG_PLUGIN_GLADE = os.path.join(utility.BIN_DIRECTORY, "glade/settings_plugin.glade")
-SETTINGS_DIALOG_BREAK_GLADE = os.path.join(utility.BIN_DIRECTORY, "glade/settings_break.glade")
-SETTINGS_DIALOG_NEW_BREAK_GLADE = os.path.join(utility.BIN_DIRECTORY, "glade/new_break.glade")
-SETTINGS_BREAK_ITEM_GLADE = os.path.join(utility.BIN_DIRECTORY, "glade/item_break.glade")
-SETTINGS_PLUGIN_ITEM_GLADE = os.path.join(utility.BIN_DIRECTORY, "glade/item_plugin.glade")
+SETTINGS_DIALOG_GLADE = os.path.join(
+ utility.BIN_DIRECTORY, "glade/settings_dialog.glade"
+)
+SETTINGS_DIALOG_PLUGIN_GLADE = os.path.join(
+ utility.BIN_DIRECTORY, "glade/settings_plugin.glade"
+)
+SETTINGS_DIALOG_BREAK_GLADE = os.path.join(
+ utility.BIN_DIRECTORY, "glade/settings_break.glade"
+)
+SETTINGS_DIALOG_NEW_BREAK_GLADE = os.path.join(
+ utility.BIN_DIRECTORY, "glade/new_break.glade"
+)
+SETTINGS_BREAK_ITEM_GLADE = os.path.join(
+ utility.BIN_DIRECTORY, "glade/item_break.glade"
+)
+SETTINGS_PLUGIN_ITEM_GLADE = os.path.join(
+ utility.BIN_DIRECTORY, "glade/item_plugin.glade"
+)
SETTINGS_ITEM_INT_GLADE = os.path.join(utility.BIN_DIRECTORY, "glade/item_int.glade")
SETTINGS_ITEM_TEXT_GLADE = os.path.join(utility.BIN_DIRECTORY, "glade/item_text.glade")
SETTINGS_ITEM_BOOL_GLADE = os.path.join(utility.BIN_DIRECTORY, "glade/item_bool.glade")
class SettingsDialog:
- """
- Create and initialize SettingsDialog instance.
- """
+ """Create and initialize SettingsDialog instance."""
def __init__(self, config, on_save_settings):
self.config = config
self.on_save_settings = on_save_settings
self.plugin_switches = {}
self.plugin_map = {}
- self.last_short_break_interval = config.get('short_break_interval')
+ self.last_short_break_interval = config.get("short_break_interval")
self.initializing = True
self.infobar_long_break_shown = False
self.warn_bar_rpc_server_shown = False
@@ -57,24 +67,26 @@ def __init__(self, config, on_save_settings):
builder = utility.create_gtk_builder(SETTINGS_DIALOG_GLADE)
builder.connect_signals(self)
- self.window = builder.get_object('window_settings')
- self.box_short_breaks = builder.get_object('box_short_breaks')
- self.box_long_breaks = builder.get_object('box_long_breaks')
- self.box_plugins = builder.get_object('box_plugins')
- self.popover = builder.get_object('popover')
-
- self.spin_short_break_duration = builder.get_object('spin_short_break_duration')
- self.spin_long_break_duration = builder.get_object('spin_long_break_duration')
- self.spin_short_break_interval = builder.get_object('spin_short_break_interval')
- self.spin_long_break_interval = builder.get_object('spin_long_break_interval')
- self.spin_time_to_prepare = builder.get_object('spin_time_to_prepare')
- self.spin_postpone_duration = builder.get_object('spin_postpone_duration')
- self.spin_disable_keyboard_shortcut = builder.get_object('spin_disable_keyboard_shortcut')
- self.switch_strict_break = builder.get_object('switch_strict_break')
- self.switch_random_order = builder.get_object('switch_random_order')
- self.switch_postpone = builder.get_object('switch_postpone')
- self.switch_persist = builder.get_object('switch_persist')
- self.switch_rpc_server = builder.get_object('switch_rpc_server')
+ self.window = builder.get_object("window_settings")
+ self.box_short_breaks = builder.get_object("box_short_breaks")
+ self.box_long_breaks = builder.get_object("box_long_breaks")
+ self.box_plugins = builder.get_object("box_plugins")
+ self.popover = builder.get_object("popover")
+
+ self.spin_short_break_duration = builder.get_object("spin_short_break_duration")
+ self.spin_long_break_duration = builder.get_object("spin_long_break_duration")
+ self.spin_short_break_interval = builder.get_object("spin_short_break_interval")
+ self.spin_long_break_interval = builder.get_object("spin_long_break_interval")
+ self.spin_time_to_prepare = builder.get_object("spin_time_to_prepare")
+ self.spin_postpone_duration = builder.get_object("spin_postpone_duration")
+ self.spin_disable_keyboard_shortcut = builder.get_object(
+ "spin_disable_keyboard_shortcut"
+ )
+ self.switch_strict_break = builder.get_object("switch_strict_break")
+ self.switch_random_order = builder.get_object("switch_random_order")
+ self.switch_postpone = builder.get_object("switch_postpone")
+ self.switch_persist = builder.get_object("switch_persist")
+ self.switch_rpc_server = builder.get_object("switch_rpc_server")
self.info_bar_long_break = builder.get_object("info_bar_long_break")
self.warn_bar_rpc_server = builder.get_object("warn_bar_rpc_server")
self.info_bar_long_break.hide()
@@ -87,69 +99,79 @@ def __init__(self, config, on_save_settings):
# GtkSwitch state-set signal is available only from 3.14
if Gtk.get_minor_version() >= 14:
# Add event listener to postpone switch
- self.switch_postpone.connect('state-set', self.on_switch_postpone_activate)
- self.on_switch_postpone_activate(self.switch_postpone, self.switch_postpone.get_active())
+ self.switch_postpone.connect("state-set", self.on_switch_postpone_activate)
+ self.on_switch_postpone_activate(
+ self.switch_postpone, self.switch_postpone.get_active()
+ )
# Add event listener to RPC server switch
- self.switch_rpc_server.connect('state-set', self.on_switch_rpc_server_activate)
- self.on_switch_rpc_server_activate(self.switch_rpc_server, self.switch_rpc_server.get_active())
+ self.switch_rpc_server.connect(
+ "state-set", self.on_switch_rpc_server_activate
+ )
+ self.on_switch_rpc_server_activate(
+ self.switch_rpc_server, self.switch_rpc_server.get_active()
+ )
self.initializing = False
def __initialize(self, config):
# Don't show infobar for changes made internally
self.infobar_long_break_shown = True
- for short_break in config.get('short_breaks'):
+ for short_break in config.get("short_breaks"):
self.__create_break_item(short_break, True)
- for long_break in config.get('long_breaks'):
+ for long_break in config.get("long_breaks"):
self.__create_break_item(long_break, False)
for plugin_config in utility.load_plugins_config(config):
- self.box_plugins.pack_start(self.__create_plugin_item(plugin_config), False, False, 0)
-
- self.spin_short_break_duration.set_value(config.get('short_break_duration'))
- self.spin_long_break_duration.set_value(config.get('long_break_duration'))
- self.spin_short_break_interval.set_value(config.get('short_break_interval'))
- self.spin_long_break_interval.set_value(config.get('long_break_interval'))
- self.spin_time_to_prepare.set_value(config.get('pre_break_warning_time'))
- self.spin_postpone_duration.set_value(config.get('postpone_duration'))
- self.spin_disable_keyboard_shortcut.set_value(config.get('shortcut_disable_time'))
- self.switch_strict_break.set_active(config.get('strict_break'))
- self.switch_random_order.set_active(config.get('random_order'))
- self.switch_postpone.set_active(config.get('allow_postpone'))
- self.switch_persist.set_active(config.get('persist_state'))
- self.switch_rpc_server.set_active(config.get('use_rpc_server'))
+ self.box_plugins.pack_start(
+ self.__create_plugin_item(plugin_config), False, False, 0
+ )
+
+ self.spin_short_break_duration.set_value(config.get("short_break_duration"))
+ self.spin_long_break_duration.set_value(config.get("long_break_duration"))
+ self.spin_short_break_interval.set_value(config.get("short_break_interval"))
+ self.spin_long_break_interval.set_value(config.get("long_break_interval"))
+ self.spin_time_to_prepare.set_value(config.get("pre_break_warning_time"))
+ self.spin_postpone_duration.set_value(config.get("postpone_duration"))
+ self.spin_disable_keyboard_shortcut.set_value(
+ config.get("shortcut_disable_time")
+ )
+ self.switch_strict_break.set_active(config.get("strict_break"))
+ self.switch_random_order.set_active(config.get("random_order"))
+ self.switch_postpone.set_active(config.get("allow_postpone"))
+ self.switch_persist.set_active(config.get("persist_state"))
+ self.switch_rpc_server.set_active(config.get("use_rpc_server"))
self.infobar_long_break_shown = False
def __create_break_item(self, break_config, is_short):
- """
- Create an entry for break to be listed in the break tab.
- """
+ """Create an entry for break to be listed in the break tab."""
parent_box = self.box_long_breaks
if is_short:
parent_box = self.box_short_breaks
builder = utility.create_gtk_builder(SETTINGS_BREAK_ITEM_GLADE)
- box = builder.get_object('box')
- lbl_name = builder.get_object('lbl_name')
- lbl_name.set_label(_(break_config['name']))
- btn_properties = builder.get_object('btn_properties')
+ box = builder.get_object("box")
+ lbl_name = builder.get_object("lbl_name")
+ lbl_name.set_label(_(break_config["name"]))
+ btn_properties = builder.get_object("btn_properties")
btn_properties.connect(
- 'clicked',
+ "clicked",
lambda button: self.__show_break_properties_dialog(
break_config,
is_short,
self.config,
- lambda cfg: lbl_name.set_label(_(cfg['name'])),
- lambda is_short, break_config: self.__create_break_item(break_config, is_short),
- lambda: parent_box.remove(box)
- )
+ lambda cfg: lbl_name.set_label(_(cfg["name"])),
+ lambda is_short, break_config: self.__create_break_item(
+ break_config, is_short
+ ),
+ lambda: parent_box.remove(box),
+ ),
)
- btn_delete = builder.get_object('btn_delete')
+ btn_delete = builder.get_object("btn_delete")
btn_delete.connect(
- 'clicked',
+ "clicked",
lambda button: self.__delete_break(
break_config,
is_short,
lambda: parent_box.remove(box),
- )
+ ),
)
box.set_visible(True)
parent_box.pack_start(box, False, False, 0)
@@ -157,15 +179,22 @@ def __create_break_item(self, break_config, is_short):
def on_reset_menu_clicked(self, button):
self.popover.hide()
+
def __confirmation_dialog_response(widget, response_id):
if response_id == Gtk.ResponseType.OK:
utility.reset_config()
self.config = Config()
# Remove breaks from the container
- self.box_short_breaks.foreach(lambda element: self.box_short_breaks.remove(element))
- self.box_long_breaks.foreach(lambda element: self.box_long_breaks.remove(element))
+ self.box_short_breaks.foreach(
+ lambda element: self.box_short_breaks.remove(element)
+ )
+ self.box_long_breaks.foreach(
+ lambda element: self.box_long_breaks.remove(element)
+ )
# Remove plugins from the container
- self.box_plugins.foreach(lambda element: self.box_plugins.remove(element))
+ self.box_plugins.foreach(
+ lambda element: self.box_plugins.remove(element)
+ )
# Initialize again
self.__initialize(self.config)
widget.destroy()
@@ -173,57 +202,57 @@ def __confirmation_dialog_response(widget, response_id):
messagedialog = Gtk.MessageDialog()
messagedialog.set_modal(True)
messagedialog.set_transient_for(self.window)
- messagedialog.set_property('message_type', Gtk.MessageType.WARNING)
- messagedialog.set_property('text', _("Are you sure you want to reset all settings to default?"))
- messagedialog.set_property('secondary-text', _("You can't undo this action."))
- messagedialog.add_button('_Cancel', Gtk.ResponseType.CANCEL)
+ messagedialog.set_property("message_type", Gtk.MessageType.WARNING)
+ messagedialog.set_property(
+ "text", _("Are you sure you want to reset all settings to default?")
+ )
+ messagedialog.set_property("secondary-text", _("You can't undo this action."))
+ messagedialog.add_button("_Cancel", Gtk.ResponseType.CANCEL)
messagedialog.add_button(_("Reset"), Gtk.ResponseType.OK)
messagedialog.connect("response", __confirmation_dialog_response)
messagedialog.show()
def __delete_break(self, break_config, is_short, on_remove):
- """
- Remove the break after a confirmation.
- """
+ """Remove the break after a confirmation."""
def __confirmation_dialog_response(widget, response_id):
if response_id == Gtk.ResponseType.OK:
if is_short:
- self.config.get('short_breaks').remove(break_config)
+ self.config.get("short_breaks").remove(break_config)
else:
- self.config.get('long_breaks').remove(break_config)
+ self.config.get("long_breaks").remove(break_config)
on_remove()
widget.destroy()
messagedialog = Gtk.MessageDialog()
messagedialog.set_modal(True)
messagedialog.set_transient_for(self.window)
- messagedialog.set_property('message_type', Gtk.MessageType.WARNING)
- messagedialog.set_property('text', _("Are you sure you want to delete this break?"))
- messagedialog.set_property('secondary-text', _("You can't undo this action."))
- messagedialog.add_button('_Cancel', Gtk.ResponseType.CANCEL)
+ messagedialog.set_property("message_type", Gtk.MessageType.WARNING)
+ messagedialog.set_property(
+ "text", _("Are you sure you want to delete this break?")
+ )
+ messagedialog.set_property("secondary-text", _("You can't undo this action."))
+ messagedialog.add_button("_Cancel", Gtk.ResponseType.CANCEL)
messagedialog.add_button(_("Delete"), Gtk.ResponseType.OK)
messagedialog.connect("response", __confirmation_dialog_response)
messagedialog.show()
def __create_plugin_item(self, plugin_config):
- """
- Create an entry for plugin to be listed in the plugin tab.
- """
+ """Create an entry for plugin to be listed in the plugin tab."""
builder = utility.create_gtk_builder(SETTINGS_PLUGIN_ITEM_GLADE)
- lbl_plugin_name = builder.get_object('lbl_plugin_name')
- lbl_plugin_description = builder.get_object('lbl_plugin_description')
- switch_enable = builder.get_object('switch_enable')
- btn_properties = builder.get_object('btn_properties')
- lbl_plugin_name.set_label(_(plugin_config['meta']['name']))
- switch_enable.set_active(plugin_config['enabled'])
- if plugin_config['error']:
- message = plugin_config['meta']['dependency_description']
+ lbl_plugin_name = builder.get_object("lbl_plugin_name")
+ lbl_plugin_description = builder.get_object("lbl_plugin_description")
+ switch_enable = builder.get_object("switch_enable")
+ btn_properties = builder.get_object("btn_properties")
+ lbl_plugin_name.set_label(_(plugin_config["meta"]["name"]))
+ switch_enable.set_active(plugin_config["enabled"])
+ if plugin_config["error"]:
+ message = plugin_config["meta"]["dependency_description"]
if isinstance(message, PluginDependency):
lbl_plugin_description.set_label(_(message.message))
- btn_plugin_extra_link = builder.get_object('btn_plugin_extra_link')
+ btn_plugin_extra_link = builder.get_object("btn_plugin_extra_link")
btn_plugin_extra_link.set_label(_("Click here for more information"))
btn_plugin_extra_link.set_uri(message.link)
btn_plugin_extra_link.set_visible(True)
@@ -233,93 +262,96 @@ def __create_plugin_item(self, plugin_config):
lbl_plugin_description.set_sensitive(False)
switch_enable.set_sensitive(False)
btn_properties.set_sensitive(False)
- if plugin_config['enabled']:
- btn_disable_errored = builder.get_object('btn_disable_errored')
+ if plugin_config["enabled"]:
+ btn_disable_errored = builder.get_object("btn_disable_errored")
btn_disable_errored.set_visible(True)
- btn_disable_errored.connect('clicked', lambda button: self.__disable_errored_plugin(button, plugin_config))
+ btn_disable_errored.connect(
+ "clicked",
+ lambda button: self.__disable_errored_plugin(button, plugin_config),
+ )
else:
- lbl_plugin_description.set_label(_(plugin_config['meta']['description']))
- if plugin_config['settings']:
+ lbl_plugin_description.set_label(_(plugin_config["meta"]["description"]))
+ if plugin_config["settings"]:
btn_properties.set_sensitive(True)
- btn_properties.connect('clicked', lambda button: self.__show_plugins_properties_dialog(plugin_config))
+ btn_properties.connect(
+ "clicked",
+ lambda button: self.__show_plugins_properties_dialog(plugin_config),
+ )
else:
btn_properties.set_sensitive(False)
- self.plugin_switches[plugin_config['id']] = switch_enable
- if plugin_config.get('break_override_allowed', False):
- self.plugin_map[plugin_config['id']] = plugin_config['meta']['name']
- if plugin_config['icon']:
- builder.get_object('img_plugin_icon').set_from_file(plugin_config['icon'])
- box = builder.get_object('box')
+ self.plugin_switches[plugin_config["id"]] = switch_enable
+ if plugin_config.get("break_override_allowed", False):
+ self.plugin_map[plugin_config["id"]] = plugin_config["meta"]["name"]
+ if plugin_config["icon"]:
+ builder.get_object("img_plugin_icon").set_from_file(plugin_config["icon"])
+ box = builder.get_object("box")
box.set_visible(True)
return box
def __show_plugins_properties_dialog(self, plugin_config):
- """
- Show the PluginProperties dialog
- """
+ """Show the PluginProperties dialog."""
dialog = PluginSettingsDialog(plugin_config)
dialog.show()
def __disable_errored_plugin(self, button, plugin_config):
- """
- Permanently disable errored plugin
- """
+ """Permanently disable errored plugin."""
button.set_sensitive(False)
- self.plugin_switches[plugin_config['id']].set_active(False)
-
- def __show_break_properties_dialog(self, break_config, is_short, parent, on_close, on_add, on_remove):
- """
- Show the BreakProperties dialog
- """
- dialog = BreakSettingsDialog(break_config, is_short, parent, self.plugin_map, on_close, on_add, on_remove)
+ self.plugin_switches[plugin_config["id"]].set_active(False)
+
+ def __show_break_properties_dialog(
+ self, break_config, is_short, parent, on_close, on_add, on_remove
+ ):
+ """Show the BreakProperties dialog."""
+ dialog = BreakSettingsDialog(
+ break_config, is_short, parent, self.plugin_map, on_close, on_add, on_remove
+ )
dialog.show()
def show(self):
- """
- Show the SettingsDialog.
- """
+ """Show the SettingsDialog."""
self.window.show()
def on_switch_postpone_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.
+ """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.
"""
self.spin_postpone_duration.set_sensitive(self.switch_postpone.get_active())
def on_spin_short_break_interval_change(self, spin_button, *value):
- """
- Event handler for value change of short break interval.
- """
+ """Event handler for value change of short break interval."""
short_break_interval = self.spin_short_break_interval.get_value_as_int()
long_break_interval = self.spin_long_break_interval.get_value_as_int()
self.spin_long_break_interval.set_range(short_break_interval * 2, 120)
- self.spin_long_break_interval.set_increments(short_break_interval, short_break_interval * 2)
- self.spin_long_break_interval.set_value(short_break_interval * math.ceil(long_break_interval / self.last_short_break_interval))
+ self.spin_long_break_interval.set_increments(
+ short_break_interval, short_break_interval * 2
+ )
+ self.spin_long_break_interval.set_value(
+ short_break_interval
+ * math.ceil(long_break_interval / self.last_short_break_interval)
+ )
self.last_short_break_interval = short_break_interval
if not self.initializing and not self.infobar_long_break_shown:
self.infobar_long_break_shown = True
self.info_bar_long_break.show()
def on_spin_long_break_interval_change(self, spin_button, *value):
- """
- Event handler for value change of long break interval.
- """
+ """Event handler for value change of long break interval."""
if not self.initializing and not self.infobar_long_break_shown:
self.infobar_long_break_shown = True
self.info_bar_long_break.show()
def on_info_bar_long_break_close(self, infobar, *user_data):
- """
- Event handler for info bar close action.
- """
+ """Event handler for info bar close action."""
self.info_bar_long_break.hide()
def on_switch_rpc_server_activate(self, switch, enabled):
- """
- Event handler to the state change of the rpc server switch.
- Show or hide the self.warn_bar_rpc_server based on the state of the rpc server.
+ """Event handler to the state change of the rpc server switch.
+
+ Show or hide the self.warn_bar_rpc_server based on the state of
+ the rpc server.
"""
if not self.initializing and not enabled and not self.warn_bar_rpc_server_shown:
self.warn_bar_rpc_server_shown = True
@@ -328,46 +360,58 @@ def on_switch_rpc_server_activate(self, switch, enabled):
self.warn_bar_rpc_server.hide()
def on_warn_bar_rpc_server_close(self, warnbar, *user_data):
- """
- Event handler for warning bar close action.
- """
+ """Event handler for warning bar close action."""
self.warn_bar_rpc_server.hide()
def add_break(self, button):
- """
- Event handler for add break button.
- """
- dialog = NewBreakDialog(self.config, lambda is_short, break_config: self.__create_break_item(break_config, is_short))
+ """Event handler for add break button."""
+ dialog = NewBreakDialog(
+ self.config,
+ lambda is_short, break_config: self.__create_break_item(
+ break_config, is_short
+ ),
+ )
dialog.show()
def on_window_delete(self, *args):
- """
- Event handler for Settings dialog close action.
- """
- self.config.set('short_break_duration', self.spin_short_break_duration.get_value_as_int())
- self.config.set('long_break_duration', self.spin_long_break_duration.get_value_as_int())
- self.config.set('short_break_interval', self.spin_short_break_interval.get_value_as_int())
- self.config.set('long_break_interval', self.spin_long_break_interval.get_value_as_int())
- self.config.set('pre_break_warning_time', self.spin_time_to_prepare.get_value_as_int())
- self.config.set('postpone_duration', self.spin_postpone_duration.get_value_as_int())
- self.config.set('shortcut_disable_time', self.spin_disable_keyboard_shortcut.get_value_as_int())
- self.config.set('strict_break', self.switch_strict_break.get_active())
- self.config.set('random_order', self.switch_random_order.get_active())
- self.config.set('allow_postpone', self.switch_postpone.get_active())
- self.config.set('persist_state', self.switch_persist.get_active())
- self.config.set('use_rpc_server', self.switch_rpc_server.get_active())
- for plugin in self.config.get('plugins'):
- if plugin['id'] in self.plugin_switches:
- plugin['enabled'] = self.plugin_switches[plugin['id']].get_active()
-
- self.on_save_settings(self.config) # Call the provided save method
+ """Event handler for Settings dialog close action."""
+ self.config.set(
+ "short_break_duration", self.spin_short_break_duration.get_value_as_int()
+ )
+ self.config.set(
+ "long_break_duration", self.spin_long_break_duration.get_value_as_int()
+ )
+ self.config.set(
+ "short_break_interval", self.spin_short_break_interval.get_value_as_int()
+ )
+ self.config.set(
+ "long_break_interval", self.spin_long_break_interval.get_value_as_int()
+ )
+ self.config.set(
+ "pre_break_warning_time", self.spin_time_to_prepare.get_value_as_int()
+ )
+ self.config.set(
+ "postpone_duration", self.spin_postpone_duration.get_value_as_int()
+ )
+ self.config.set(
+ "shortcut_disable_time",
+ self.spin_disable_keyboard_shortcut.get_value_as_int(),
+ )
+ self.config.set("strict_break", self.switch_strict_break.get_active())
+ self.config.set("random_order", self.switch_random_order.get_active())
+ self.config.set("allow_postpone", self.switch_postpone.get_active())
+ self.config.set("persist_state", self.switch_persist.get_active())
+ self.config.set("use_rpc_server", self.switch_rpc_server.get_active())
+ for plugin in self.config.get("plugins"):
+ if plugin["id"] in self.plugin_switches:
+ plugin["enabled"] = self.plugin_switches[plugin["id"]].get_active()
+
+ self.on_save_settings(self.config) # Call the provided save method
self.window.destroy()
class PluginSettingsDialog:
- """
- Builds a settings dialog based on the configuration of a plugin.
- """
+ """Builds a settings dialog based on the configuration of a plugin."""
def __init__(self, config):
self.config = config
@@ -375,78 +419,108 @@ def __init__(self, config):
builder = utility.create_gtk_builder(SETTINGS_DIALOG_PLUGIN_GLADE)
builder.connect_signals(self)
- self.window = builder.get_object('dialog_settings_plugin')
- box_settings = builder.get_object('box_settings')
- self.window.set_title(_('Plugin Settings'))
- for setting in config.get('settings'):
- if setting['type'].upper() == 'INT':
- box_settings.pack_start(self.__load_int_item(setting['label'], setting['id'], setting['safeeyes_config'], setting.get('min', 0), setting.get('max', 120)), False, False, 0)
- elif setting['type'].upper() == 'TEXT':
- box_settings.pack_start(self.__load_text_item(setting['label'], setting['id'], setting['safeeyes_config']), False, False, 0)
- elif setting['type'].upper() == 'BOOL':
- box_settings.pack_start(self.__load_bool_item(setting['label'], setting['id'], setting['safeeyes_config']), False, False, 0)
+ self.window = builder.get_object("dialog_settings_plugin")
+ box_settings = builder.get_object("box_settings")
+ self.window.set_title(_("Plugin Settings"))
+ for setting in config.get("settings"):
+ if setting["type"].upper() == "INT":
+ box_settings.pack_start(
+ self.__load_int_item(
+ setting["label"],
+ setting["id"],
+ setting["safeeyes_config"],
+ setting.get("min", 0),
+ setting.get("max", 120),
+ ),
+ False,
+ False,
+ 0,
+ )
+ elif setting["type"].upper() == "TEXT":
+ box_settings.pack_start(
+ self.__load_text_item(
+ setting["label"], setting["id"], setting["safeeyes_config"]
+ ),
+ False,
+ False,
+ 0,
+ )
+ elif setting["type"].upper() == "BOOL":
+ box_settings.pack_start(
+ self.__load_bool_item(
+ setting["label"], setting["id"], setting["safeeyes_config"]
+ ),
+ False,
+ False,
+ 0,
+ )
def __load_int_item(self, name, key, settings, min_value, max_value):
- """
- Load the UI control for int property.
- """
+ """Load the UI control for int property."""
builder = utility.create_gtk_builder(SETTINGS_ITEM_INT_GLADE)
- builder.get_object('lbl_name').set_label(_(name))
- spin_value = builder.get_object('spin_value')
+ builder.get_object("lbl_name").set_label(_(name))
+ spin_value = builder.get_object("spin_value")
spin_value.set_range(min_value, max_value)
spin_value.set_value(settings[key])
- box = builder.get_object('box')
+ box = builder.get_object("box")
box.set_visible(True)
- self.property_controls.append({'key': key, 'settings': settings, 'value': spin_value.get_value})
+ self.property_controls.append(
+ {"key": key, "settings": settings, "value": spin_value.get_value}
+ )
return box
def __load_text_item(self, name, key, settings):
- """
- Load the UI control for text property.
- """
+ """Load the UI control for text property."""
builder = utility.create_gtk_builder(SETTINGS_ITEM_TEXT_GLADE)
- builder.get_object('lbl_name').set_label(_(name))
- txt_value = builder.get_object('txt_value')
+ builder.get_object("lbl_name").set_label(_(name))
+ txt_value = builder.get_object("txt_value")
txt_value.set_text(settings[key])
- box = builder.get_object('box')
+ box = builder.get_object("box")
box.set_visible(True)
- self.property_controls.append({'key': key, 'settings': settings, 'value': txt_value.get_text})
+ self.property_controls.append(
+ {"key": key, "settings": settings, "value": txt_value.get_text}
+ )
return box
def __load_bool_item(self, name, key, settings):
- """
- Load the UI control for boolean property.
- """
+ """Load the UI control for boolean property."""
builder = utility.create_gtk_builder(SETTINGS_ITEM_BOOL_GLADE)
- builder.get_object('lbl_name').set_label(_(name))
- switch_value = builder.get_object('switch_value')
+ builder.get_object("lbl_name").set_label(_(name))
+ switch_value = builder.get_object("switch_value")
switch_value.set_active(settings[key])
- box = builder.get_object('box')
+ box = builder.get_object("box")
box.set_visible(True)
- self.property_controls.append({'key': key, 'settings': settings, 'value': switch_value.get_active})
+ self.property_controls.append(
+ {"key": key, "settings": settings, "value": switch_value.get_active}
+ )
return box
def on_window_delete(self, *args):
- """
- Event handler for Properties dialog close action.
- """
+ """Event handler for Properties dialog close action."""
for property_control in self.property_controls:
- property_control['settings'][property_control['key']] = property_control['value']()
+ property_control["settings"][property_control["key"]] = property_control[
+ "value"
+ ]()
self.window.destroy()
def show(self):
- """
- Show the Properties dialog.
- """
+ """Show the Properties dialog."""
self.window.show_all()
class BreakSettingsDialog:
- """
- Builds a settings dialog based on the configuration of a plugin.
- """
-
- def __init__(self, break_config, is_short, parent_config, plugin_map, on_close, on_add, on_remove):
+ """Builds a settings dialog based on the configuration of a plugin."""
+
+ def __init__(
+ self,
+ break_config,
+ is_short,
+ parent_config,
+ plugin_map,
+ on_close,
+ on_add,
+ on_remove,
+ ):
self.break_config = break_config
self.parent_config = parent_config
self.plugin_check_buttons = {}
@@ -457,26 +531,26 @@ def __init__(self, break_config, is_short, parent_config, plugin_map, on_close,
builder = utility.create_gtk_builder(SETTINGS_DIALOG_BREAK_GLADE)
builder.connect_signals(self)
- self.window = builder.get_object('dialog_settings_break')
- self.txt_break = builder.get_object('txt_break')
- self.switch_override_interval = builder.get_object('switch_override_interval')
- self.switch_override_duration = builder.get_object('switch_override_duration')
- self.switch_override_plugins = builder.get_object('switch_override_plugins')
- self.spin_interval = builder.get_object('spin_interval')
- self.spin_duration = builder.get_object('spin_duration')
- self.img_break = builder.get_object('img_break')
- self.cmb_type = builder.get_object('cmb_type')
-
- grid_plugins = builder.get_object('grid_plugins')
- list_types = builder.get_object('lst_break_types')
-
- interval_overriden = break_config.get('interval', None) is not None
- duration_overriden = break_config.get('duration', None) is not None
- plugins_overriden = break_config.get('plugins', None) is not None
+ self.window = builder.get_object("dialog_settings_break")
+ self.txt_break = builder.get_object("txt_break")
+ self.switch_override_interval = builder.get_object("switch_override_interval")
+ self.switch_override_duration = builder.get_object("switch_override_duration")
+ self.switch_override_plugins = builder.get_object("switch_override_plugins")
+ self.spin_interval = builder.get_object("spin_interval")
+ self.spin_duration = builder.get_object("spin_duration")
+ self.img_break = builder.get_object("img_break")
+ self.cmb_type = builder.get_object("cmb_type")
+
+ grid_plugins = builder.get_object("grid_plugins")
+ list_types = builder.get_object("lst_break_types")
+
+ interval_overriden = break_config.get("interval", None) is not None
+ duration_overriden = break_config.get("duration", None) is not None
+ plugins_overriden = break_config.get("plugins", None) is not None
# Set the values
- self.window.set_title(_('Break Settings'))
- self.txt_break.set_text(_(break_config['name']))
+ self.window.set_title(_("Break Settings"))
+ self.txt_break.set_text(_(break_config["name"]))
self.switch_override_interval.set_active(interval_overriden)
self.switch_override_duration.set_active(duration_overriden)
self.switch_override_plugins.set_active(plugins_overriden)
@@ -485,20 +559,20 @@ def __init__(self, break_config, is_short, parent_config, plugin_map, on_close,
list_types[1][0] = _(list_types[1][0])
if interval_overriden:
- self.spin_interval.set_value(break_config['interval'])
+ self.spin_interval.set_value(break_config["interval"])
else:
if is_short:
- self.spin_interval.set_value(parent_config.get('short_break_interval'))
+ self.spin_interval.set_value(parent_config.get("short_break_interval"))
else:
- self.spin_interval.set_value(parent_config.get('long_break_interval'))
+ self.spin_interval.set_value(parent_config.get("long_break_interval"))
if duration_overriden:
- self.spin_duration.set_value(break_config['duration'])
+ self.spin_duration.set_value(break_config["duration"])
else:
if is_short:
- self.spin_duration.set_value(parent_config.get('short_break_duration'))
+ self.spin_duration.set_value(parent_config.get("short_break_duration"))
else:
- self.spin_duration.set_value(parent_config.get('long_break_duration'))
+ self.spin_duration.set_value(parent_config.get("long_break_duration"))
row = 0
col = 0
for plugin_id in plugin_map.keys():
@@ -506,7 +580,7 @@ def __init__(self, break_config, is_short, parent_config, plugin_map, on_close,
self.plugin_check_buttons[plugin_id] = chk_button
grid_plugins.attach(chk_button, row, col, 1, 1)
if plugins_overriden:
- chk_button.set_active(plugin_id in break_config['plugins'])
+ chk_button.set_active(plugin_id in break_config["plugins"])
else:
chk_button.set_active(True)
row += 1
@@ -515,37 +589,53 @@ def __init__(self, break_config, is_short, parent_config, plugin_map, on_close,
row = 0
# GtkSwitch state-set signal is available only from 3.14
if Gtk.get_minor_version() >= 14:
- self.switch_override_interval.connect('state-set', self.on_switch_override_interval_activate)
- self.switch_override_duration.connect('state-set', self.on_switch_override_duration_activate)
- self.switch_override_plugins.connect('state-set', self.on_switch_override_plugins_activate)
- self.on_switch_override_interval_activate(self.switch_override_interval, self.switch_override_interval.get_active())
- self.on_switch_override_duration_activate(self.switch_override_duration, self.switch_override_duration.get_active())
- self.on_switch_override_plugins_activate(self.switch_override_plugins, self.switch_override_plugins.get_active())
+ self.switch_override_interval.connect(
+ "state-set", self.on_switch_override_interval_activate
+ )
+ self.switch_override_duration.connect(
+ "state-set", self.on_switch_override_duration_activate
+ )
+ self.switch_override_plugins.connect(
+ "state-set", self.on_switch_override_plugins_activate
+ )
+ self.on_switch_override_interval_activate(
+ self.switch_override_interval,
+ self.switch_override_interval.get_active(),
+ )
+ self.on_switch_override_duration_activate(
+ self.switch_override_duration,
+ self.switch_override_duration.get_active(),
+ )
+ self.on_switch_override_plugins_activate(
+ self.switch_override_plugins, self.switch_override_plugins.get_active()
+ )
def on_switch_override_interval_activate(self, switch_button, state):
- """
- switch_override_interval state change event handler.
- """
+ """switch_override_interval state change event handler."""
self.spin_interval.set_sensitive(state)
def on_switch_override_duration_activate(self, switch_button, state):
- """
- switch_override_duration state change event handler.
- """
+ """switch_override_duration state change event handler."""
self.spin_duration.set_sensitive(state)
def on_switch_override_plugins_activate(self, switch_button, state):
- """
- switch_override_plugins state change event handler.
- """
+ """switch_override_plugins state change event handler."""
for chk_box in self.plugin_check_buttons.values():
chk_box.set_sensitive(state)
def select_image(self, button):
- """
- Show a file chooser dialog and let the user to select an image.
- """
- dialog = Gtk.FileChooserDialog(_('Please select an image'), self.window, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
+ """Show a file chooser dialog and let the user to select an image."""
+ dialog = Gtk.FileChooserDialog(
+ _("Please select an image"),
+ self.window,
+ Gtk.FileChooserAction.OPEN,
+ (
+ Gtk.STOCK_CANCEL,
+ Gtk.ResponseType.CANCEL,
+ Gtk.STOCK_OPEN,
+ Gtk.ResponseType.OK,
+ ),
+ )
png_filter = Gtk.FileFilter()
png_filter.set_name("PNG files")
@@ -555,49 +645,49 @@ def select_image(self, button):
response = dialog.run()
if response == Gtk.ResponseType.OK:
- self.break_config['image'] = dialog.get_filename()
- pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(self.break_config['image'], 16, 16, True)
+ self.break_config["image"] = dialog.get_filename()
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
+ self.break_config["image"], 16, 16, True
+ )
self.img_break.set_from_pixbuf(pixbuf)
elif response == Gtk.ResponseType.CANCEL:
- self.break_config.pop('image', None)
- self.img_break.set_from_stock('gtk-missing-image', Gtk.IconSize.BUTTON)
+ self.break_config.pop("image", None)
+ self.img_break.set_from_stock("gtk-missing-image", Gtk.IconSize.BUTTON)
dialog.destroy()
def on_window_delete(self, *args):
- """
- Event handler for Properties dialog close action.
- """
+ """Event handler for Properties dialog close action."""
break_name = self.txt_break.get_text().strip()
if break_name:
- self.break_config['name'] = break_name
+ self.break_config["name"] = break_name
if self.switch_override_interval.get_active():
- self.break_config['interval'] = int(self.spin_interval.get_value())
+ self.break_config["interval"] = int(self.spin_interval.get_value())
else:
- self.break_config.pop('interval', None)
+ self.break_config.pop("interval", None)
if self.switch_override_duration.get_active():
- self.break_config['duration'] = int(self.spin_duration.get_value())
+ self.break_config["duration"] = int(self.spin_duration.get_value())
else:
- self.break_config.pop('duration', None)
+ self.break_config.pop("duration", None)
if self.switch_override_plugins.get_active():
selected_plugins = []
for plugin_id in self.plugin_check_buttons:
if self.plugin_check_buttons[plugin_id].get_active():
selected_plugins.append(plugin_id)
- self.break_config['plugins'] = selected_plugins
+ self.break_config["plugins"] = selected_plugins
else:
- self.break_config.pop('plugins', None)
+ self.break_config.pop("plugins", None)
if self.is_short and self.cmb_type.get_active() == 1:
# Changed from short to long
- self.parent_config.get('short_breaks').remove(self.break_config)
- self.parent_config.get('long_breaks').append(self.break_config)
+ self.parent_config.get("short_breaks").remove(self.break_config)
+ self.parent_config.get("long_breaks").append(self.break_config)
self.on_remove()
self.on_add(not self.is_short, self.break_config)
elif not self.is_short and self.cmb_type.get_active() == 0:
# Changed from long to short
- self.parent_config.get('long_breaks').remove(self.break_config)
- self.parent_config.get('short_breaks').append(self.break_config)
+ self.parent_config.get("long_breaks").remove(self.break_config)
+ self.parent_config.get("short_breaks").append(self.break_config)
self.on_remove()
self.on_add(not self.is_short, self.break_config)
else:
@@ -605,16 +695,12 @@ def on_window_delete(self, *args):
self.window.destroy()
def show(self):
- """
- Show the Properties dialog.
- """
+ """Show the Properties dialog."""
self.window.show_all()
class NewBreakDialog:
- """
- Builds a new break dialog.
- """
+ """Builds a new break dialog."""
def __init__(self, parent_config, on_add):
self.parent_config = parent_config
@@ -622,45 +708,37 @@ def __init__(self, parent_config, on_add):
builder = utility.create_gtk_builder(SETTINGS_DIALOG_NEW_BREAK_GLADE)
builder.connect_signals(self)
- self.window = builder.get_object('dialog_new_break')
- self.txt_break = builder.get_object('txt_break')
- self.cmb_type = builder.get_object('cmb_type')
- list_types = builder.get_object('lst_break_types')
+ self.window = builder.get_object("dialog_new_break")
+ self.txt_break = builder.get_object("txt_break")
+ self.cmb_type = builder.get_object("cmb_type")
+ list_types = builder.get_object("lst_break_types")
list_types[0][0] = _(list_types[0][0])
list_types[1][0] = _(list_types[1][0])
# Set the values
- self.window.set_title(_('New Break'))
+ self.window.set_title(_("New Break"))
def discard(self, button):
- """
- Close the dialog.
- """
+ """Close the dialog."""
self.window.destroy()
def save(self, button):
- """
- Event handler for Properties dialog close action.
- """
- break_config = {'name': self.txt_break.get_text().strip()}
+ """Event handler for Properties dialog close action."""
+ break_config = {"name": self.txt_break.get_text().strip()}
if self.cmb_type.get_active() == 0:
- self.parent_config.get('short_breaks').append(break_config)
+ self.parent_config.get("short_breaks").append(break_config)
self.on_add(True, break_config)
else:
- self.parent_config.get('long_breaks').append(break_config)
+ self.parent_config.get("long_breaks").append(break_config)
self.on_add(False, break_config)
self.window.destroy()
def on_window_delete(self, *args):
- """
- Event handler for dialog close action.
- """
+ """Event handler for dialog close action."""
self.window.destroy()
def show(self):
- """
- Show the Properties dialog.
- """
+ """Show the Properties dialog."""
self.window.show_all()
diff --git a/safeeyes/utility.py b/safeeyes/utility.py
index bcb0fc26..a1d477b8 100644
--- a/safeeyes/utility.py
+++ b/safeeyes/utility.py
@@ -16,9 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-This module contains utility functions for Safe Eyes and its plugins.
-"""
+"""This module contains utility functions for Safe Eyes and its plugins."""
import errno
import inspect
@@ -38,47 +36,52 @@
import babel.core
import babel.dates
import gi
-gi.require_version('Gtk', '3.0')
+
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import GdkPixbuf
from packaging.version import parse
-gi.require_version('Gdk', '3.0')
+gi.require_version("Gdk", "3.0")
BIN_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
-HOME_DIRECTORY = os.environ.get('HOME') or os.path.expanduser('~')
-CONFIG_DIRECTORY = os.path.join(os.environ.get(
- 'XDG_CONFIG_HOME') or os.path.join(HOME_DIRECTORY, '.config'), 'safeeyes')
-STYLE_SHEET_DIRECTORY = os.path.join(CONFIG_DIRECTORY, 'style')
-CONFIG_FILE_PATH = os.path.join(CONFIG_DIRECTORY, 'safeeyes.json')
-CONFIG_RESOURCE = os.path.join(CONFIG_DIRECTORY, 'resource')
-SESSION_FILE_PATH = os.path.join(CONFIG_DIRECTORY, 'session.json')
-STYLE_SHEET_PATH = os.path.join(STYLE_SHEET_DIRECTORY, 'safeeyes_style.css')
+HOME_DIRECTORY = os.environ.get("HOME") or os.path.expanduser("~")
+CONFIG_DIRECTORY = os.path.join(
+ os.environ.get("XDG_CONFIG_HOME") or os.path.join(HOME_DIRECTORY, ".config"),
+ "safeeyes",
+)
+STYLE_SHEET_DIRECTORY = os.path.join(CONFIG_DIRECTORY, "style")
+CONFIG_FILE_PATH = os.path.join(CONFIG_DIRECTORY, "safeeyes.json")
+CONFIG_RESOURCE = os.path.join(CONFIG_DIRECTORY, "resource")
+SESSION_FILE_PATH = os.path.join(CONFIG_DIRECTORY, "session.json")
+STYLE_SHEET_PATH = os.path.join(STYLE_SHEET_DIRECTORY, "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(HOME_DIRECTORY, 'safeeyes.log')
-SYSTEM_PLUGINS_DIR = os.path.join(BIN_DIRECTORY, 'plugins')
-USER_PLUGINS_DIR = os.path.join(CONFIG_DIRECTORY, 'plugins')
-LOCALE_PATH = os.path.join(BIN_DIRECTORY, 'config/locale')
-SYSTEM_DESKTOP_FILE = os.path.join(BIN_DIRECTORY, "platform/io.github.slgobinath.SafeEyes.desktop")
+SYSTEM_STYLE_SHEET_PATH = os.path.join(BIN_DIRECTORY, "config/style/safeeyes_style.css")
+LOG_FILE_PATH = os.path.join(HOME_DIRECTORY, "safeeyes.log")
+SYSTEM_PLUGINS_DIR = os.path.join(BIN_DIRECTORY, "plugins")
+USER_PLUGINS_DIR = os.path.join(CONFIG_DIRECTORY, "plugins")
+LOCALE_PATH = os.path.join(BIN_DIRECTORY, "config/locale")
+SYSTEM_DESKTOP_FILE = os.path.join(
+ BIN_DIRECTORY, "platform/io.github.slgobinath.SafeEyes.desktop"
+)
SYSTEM_ICONS = os.path.join(BIN_DIRECTORY, "platform/icons")
DESKTOP_ENVIRONMENT = None
IS_WAYLAND = False
def get_resource_path(resource_name):
- """
- Return the user-defined resource if a system resource is overridden by the user.
- Otherwise, return the system resource. Return None if the specified resource does not exist.
+ """Return the user-defined resource if a system resource is overridden by
+ the user.
+
+ Otherwise, return the system resource. Return None if the specified
+ resource does not exist.
"""
if resource_name is None:
return None
resource_location = os.path.join(CONFIG_RESOURCE, resource_name)
if not os.path.isfile(resource_location):
- resource_location = os.path.join(
- BIN_DIRECTORY, 'resource', resource_name)
+ resource_location = os.path.join(BIN_DIRECTORY, "resource", resource_name)
if not os.path.isfile(resource_location):
# Resource not found
resource_location = None
@@ -87,70 +90,59 @@ def get_resource_path(resource_name):
def start_thread(target_function, **args):
- """
- Execute the function in a separate thread.
- """
- thread = threading.Thread(target=target_function, name="WorkThread", daemon=False, kwargs=args)
+ """Execute the function in a separate thread."""
+ thread = threading.Thread(
+ target=target_function, name="WorkThread", daemon=False, kwargs=args
+ )
thread.start()
-# def execute_main_thread(target_function, *args, **kwargs):
-# """
-# Execute the given function in main thread, forwarding positional and keyword arguments.
-# """
-
def execute_main_thread(target_function, *args, **kwargs):
- """
- Execute the given function in main thread.
- """
+ """Execute the given function in main thread."""
GLib.idle_add(lambda: target_function(*args, **kwargs))
+
def system_locale(category=locale.LC_MESSAGES):
- """
- Return the system locale. If not available, return en_US.UTF-8.
+ """Return the system locale.
+
+ If not available, return en_US.UTF-8.
"""
try:
- locale.setlocale(locale.LC_ALL, '')
+ locale.setlocale(locale.LC_ALL, "")
sys_locale = locale.getlocale(category)[0]
if not sys_locale:
- sys_locale = 'en_US.UTF-8'
+ sys_locale = "en_US.UTF-8"
return sys_locale
except BaseException:
# Some systems does not return proper locale
- return 'en_US.UTF-8'
+ return "en_US.UTF-8"
def format_time(time):
- """
- Format time based on the system time.
- """
+ """Format time based on the system time."""
sys_locale = system_locale(locale.LC_TIME)
try:
- return babel.dates.format_time(time, format='short', locale=sys_locale)
+ return babel.dates.format_time(time, format="short", locale=sys_locale)
except babel.core.UnknownLocaleError:
# Some locale types are not supported by the babel library.
# Use 'en' locale format if the system locale is not supported.
- return babel.dates.format_time(time, format='short', locale='en')
+ return babel.dates.format_time(time, format="short", locale="en")
def mkdir(path):
- """
- Create directory if not exists.
- """
+ """Create directory if not exists."""
try:
os.makedirs(path)
except OSError as exc:
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
- logging.error('Error while creating ' + str(path))
+ logging.error("Error while creating " + str(path))
raise
def load_json(json_path):
- """
- Load the JSON file from the given path.
- """
+ """Load the JSON file from the given path."""
json_obj = None
if os.path.isfile(json_path):
try:
@@ -162,20 +154,16 @@ def load_json(json_path):
def write_json(json_path, json_obj):
- """
- Write the JSON object at the given path
- """
+ """Write the JSON object at the given path."""
try:
- with open(json_path, 'w') as json_file:
+ with open(json_path, "w") as json_file:
json.dump(json_obj, json_file, indent=4, sort_keys=True)
except BaseException:
pass
def delete(file_path):
- """
- Delete the given file or directory
- """
+ """Delete the given file or directory."""
try:
os.remove(file_path)
except OSError:
@@ -183,33 +171,44 @@ def delete(file_path):
def check_plugin_dependencies(plugin_id, plugin_config, plugin_settings, plugin_path):
- """
- Check the plugin dependencies.
- """
+ """Check the plugin dependencies."""
# Check the desktop environment
- if plugin_config['dependencies']['desktop_environments']:
+ if plugin_config["dependencies"]["desktop_environments"]:
# Plugin has restrictions on desktop environments
- if DESKTOP_ENVIRONMENT not in plugin_config['dependencies']['desktop_environments']:
- return _('Plugin does not support %s desktop environment') % DESKTOP_ENVIRONMENT
+ if (
+ DESKTOP_ENVIRONMENT
+ not in plugin_config["dependencies"]["desktop_environments"]
+ ):
+ return (
+ _("Plugin does not support %s desktop environment")
+ % DESKTOP_ENVIRONMENT
+ )
# Check the Python modules
- for module in plugin_config['dependencies']['python_modules']:
+ for module in plugin_config["dependencies"]["python_modules"]:
if not module_exist(module):
return _("Please install the Python module '%s'") % module
# Check the shell commands
- for command in plugin_config['dependencies']['shell_commands']:
+ for command in plugin_config["dependencies"]["shell_commands"]:
if not command_exist(command):
return _("Please install the command-line tool '%s'") % command
# Check the resources
- for resource in plugin_config['dependencies']['resources']:
+ for resource in plugin_config["dependencies"]["resources"]:
if get_resource_path(resource) is None:
- return _('Please add the resource %(resource)s to %(config_resource)s directory') % {'resource': resource, 'config_resource': CONFIG_RESOURCE}
-
- plugin_dependency_checker = os.path.join(plugin_path, 'dependency_checker.py')
+ return _(
+ "Please add the resource %(resource)s to %(config_resource)s directory"
+ ) % {
+ "resource": resource,
+ "config_resource": CONFIG_RESOURCE,
+ }
+
+ plugin_dependency_checker = os.path.join(plugin_path, "dependency_checker.py")
if os.path.isfile(plugin_dependency_checker):
- dependency_checker = importlib.import_module((plugin_id + '.dependency_checker'))
+ dependency_checker = importlib.import_module(
+ (plugin_id + ".dependency_checker")
+ )
if dependency_checker and hasattr(dependency_checker, "validate"):
return dependency_checker.validate(plugin_config, plugin_settings)
@@ -217,75 +216,99 @@ def check_plugin_dependencies(plugin_id, plugin_config, plugin_settings, plugin_
def load_plugins_config(safeeyes_config):
- """
- Load all the plugins from the given directory.
- """
+ """Load all the plugins from the given directory."""
configs = []
- for plugin in safeeyes_config.get('plugins'):
- plugin_path = os.path.join(SYSTEM_PLUGINS_DIR, plugin['id'])
+ for plugin in safeeyes_config.get("plugins"):
+ plugin_path = os.path.join(SYSTEM_PLUGINS_DIR, plugin["id"])
if not os.path.isdir(plugin_path):
# User plugin
- plugin_path = os.path.join(USER_PLUGINS_DIR, plugin['id'])
- plugin_config_path = os.path.join(plugin_path, 'config.json')
- plugin_icon_path = os.path.join(plugin_path, 'icon.png')
- plugin_module_path = os.path.join(plugin_path, 'plugin.py')
+ plugin_path = os.path.join(USER_PLUGINS_DIR, plugin["id"])
+ plugin_config_path = os.path.join(plugin_path, "config.json")
+ plugin_icon_path = os.path.join(plugin_path, "icon.png")
+ plugin_module_path = os.path.join(plugin_path, "plugin.py")
if not os.path.isfile(plugin_module_path):
return
icon = None
if os.path.isfile(plugin_icon_path):
icon = plugin_icon_path
else:
- icon = get_resource_path('ic_plugin.png')
+ icon = get_resource_path("ic_plugin.png")
config = load_json(plugin_config_path)
if config is None:
continue
- dependency_description = check_plugin_dependencies(plugin['id'], config, plugin.get('settings', {}), plugin_path)
+ dependency_description = check_plugin_dependencies(
+ plugin["id"], config, plugin.get("settings", {}), plugin_path
+ )
if dependency_description:
- config['error'] = True
- config['meta']['dependency_description'] = dependency_description
- icon = get_resource_path('ic_warning.png')
+ config["error"] = True
+ config["meta"]["dependency_description"] = dependency_description
+ icon = get_resource_path("ic_warning.png")
else:
- config['error'] = False
- config['id'] = plugin['id']
- config['icon'] = icon
- config['enabled'] = plugin['enabled']
- for setting in config['settings']:
- setting['safeeyes_config'] = plugin['settings']
+ config["error"] = False
+ config["id"] = plugin["id"]
+ config["icon"] = icon
+ config["enabled"] = plugin["enabled"]
+ for setting in config["settings"]:
+ setting["safeeyes_config"] = plugin["settings"]
configs.append(config)
return configs
def desktop_environment():
- """
- Detect the desktop environment.
- """
+ """Detect the desktop environment."""
global DESKTOP_ENVIRONMENT
- desktop_session = os.environ.get('DESKTOP_SESSION')
- current_desktop = os.environ.get('XDG_CURRENT_DESKTOP')
- env = 'unknown'
+ desktop_session = os.environ.get("DESKTOP_SESSION")
+ current_desktop = os.environ.get("XDG_CURRENT_DESKTOP")
+ env = "unknown"
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",
+ ]:
env = desktop_session
- elif desktop_session.startswith('xubuntu') or (current_desktop is not None and 'xfce' in current_desktop):
- env = 'xfce'
- elif desktop_session.startswith('lubuntu'):
- env = 'lxde'
- elif 'plasma' in desktop_session or desktop_session.startswith('kubuntu') or os.environ.get('KDE_FULL_SESSION') == 'true':
- env = 'kde'
- elif os.environ.get('GNOME_DESKTOP_SESSION_ID') or desktop_session.startswith('gnome'):
- env = 'gnome'
- elif desktop_session.startswith('ubuntu'):
- env = 'unity'
+ elif desktop_session.startswith("xubuntu") or (
+ current_desktop is not None and "xfce" in current_desktop
+ ):
+ env = "xfce"
+ elif desktop_session.startswith("lubuntu"):
+ env = "lxde"
+ elif (
+ "plasma" in desktop_session
+ or desktop_session.startswith("kubuntu")
+ or os.environ.get("KDE_FULL_SESSION") == "true"
+ ):
+ env = "kde"
+ elif os.environ.get("GNOME_DESKTOP_SESSION_ID") or desktop_session.startswith(
+ "gnome"
+ ):
+ env = "gnome"
+ elif desktop_session.startswith("ubuntu"):
+ env = "unity"
elif current_desktop is not None:
- if current_desktop.startswith('sway'):
- env = 'sway'
+ if current_desktop.startswith("sway"):
+ env = "sway"
DESKTOP_ENVIRONMENT = env
return env
+
def is_wayland():
- """
- Determine if Wayland is running
+ """Determine if Wayland is running.
+
https://unix.stackexchange.com/a/325972/222290
"""
global IS_WAYLAND
@@ -297,22 +320,20 @@ def is_wayland():
return IS_WAYLAND
try:
- session_id = subprocess.check_output(['loginctl']).split(b'\n')[1].split()[0]
+ session_id = subprocess.check_output(["loginctl"]).split(b"\n")[1].split()[0]
output = subprocess.check_output(
- ['loginctl', 'show-session', session_id, '-p', 'Type']
+ ["loginctl", "show-session", session_id, "-p", "Type"]
)
except BaseException:
- logging.warning('Unable to determine if wayland is running. Assuming no.')
+ logging.warning("Unable to determine if wayland is running. Assuming no.")
IS_WAYLAND = False
else:
- IS_WAYLAND = bool(re.search(b'wayland', output, re.IGNORECASE))
+ IS_WAYLAND = bool(re.search(b"wayland", output, re.IGNORECASE))
return IS_WAYLAND
def execute_command(command, args=[]):
- """
- Execute the shell command without waiting for its response.
- """
+ """Execute the shell command without waiting for its response."""
if command:
command_to_execute = []
if isinstance(command, str):
@@ -324,22 +345,18 @@ def execute_command(command, args=[]):
try:
subprocess.Popen(command_to_execute)
except BaseException:
- logging.error('Error in executing the command ' + str(command))
+ logging.error("Error in executing the command " + str(command))
def command_exist(command):
- """
- Check whether the given command exist in the system or not.
- """
+ """Check whether the given command exist in the system or not."""
if shutil.which(command):
return True
return False
def module_exist(module):
- """
- Check wther the given Python module exists or not.
- """
+ """Check wther the given Python module exists or not."""
try:
importlib.util.find_spec(module)
return True
@@ -348,20 +365,18 @@ def module_exist(module):
def merge_configs(new_config, old_config):
- """
- Merge the values of old_config into the new_config.
- """
+ """Merge the values of old_config into the new_config."""
new_config = new_config.copy()
new_config.update(old_config)
return new_config
def initialize_safeeyes():
+ """Create the config file and style sheet in XDG_CONFIG_HOME(or
+ ~/.config)/safeeyes directory.
"""
- Create the config file and style sheet in XDG_CONFIG_HOME(or ~/.config)/safeeyes directory.
- """
- logging.info('Copy the config files to XDG_CONFIG_HOME(or ~/.config)/safeeyes')
-
+ logging.info("Copy the config files to XDG_CONFIG_HOME(or ~/.config)/safeeyes")
+
# Remove the ~/.config/safeeyes/safeeyes.json file
delete(CONFIG_FILE_PATH)
@@ -374,9 +389,13 @@ def initialize_safeeyes():
create_user_stylesheet_if_missing()
- # initialize_safeeyes gets called when the configuration file is not present, which happens just after installation or manual deletion of .config/safeeyes/safeeyes.json file. In these cases, we want to force the creation of a startup entry
+ # initialize_safeeyes gets called when the configuration file is not present, which
+ # happens just after installation or manual deletion of
+ # .config/safeeyes/safeeyes.json file. In these cases, we want to force the creation
+ # of a startup entry
create_startup_entry(force=True)
+
def create_user_stylesheet_if_missing():
# Create the XDG_CONFIG_HOME(or ~/.config)/safeeyes/style directory
if not os.path.isdir(STYLE_SHEET_DIRECTORY):
@@ -387,15 +406,16 @@ def create_user_stylesheet_if_missing():
shutil.copy2(SYSTEM_STYLE_SHEET_PATH, STYLE_SHEET_PATH)
os.chmod(STYLE_SHEET_PATH, 0o666)
+
def create_startup_entry(force=False):
- """
- Create start up entry.
- """
- startup_dir_path = os.path.join(HOME_DIRECTORY, '.config/autostart')
- startup_entry = os.path.join(startup_dir_path, 'io.github.slgobinath.SafeEyes.desktop')
+ """Create start up entry."""
+ startup_dir_path = os.path.join(HOME_DIRECTORY, ".config/autostart")
+ startup_entry = os.path.join(
+ startup_dir_path, "io.github.slgobinath.SafeEyes.desktop"
+ )
# until SafeEyes 2.1.5 the startup entry had another name
- # https://github.com/slgobinath/SafeEyes/commit/684d16265a48794bb3fd670da67283fe4e2f591b#diff-0863348c2143a4928518a4d3661f150ba86d042bf5320b462ea2e960c36ed275L398
- obsolete_entry = os.path.join(startup_dir_path, 'safeeyes.desktop')
+ # https://github.com/slgobinath/SafeEyes/commit/684d16265a48794bb3fd670da67283fe4e2f591b#diff-0863348c2143a4928518a4d3661f150ba86d042bf5320b462ea2e960c36ed275L398
+ obsolete_entry = os.path.join(startup_dir_path, "safeeyes.desktop")
create_link = False
@@ -403,14 +423,16 @@ def create_startup_entry(force=False):
# if force is True, just create the link
create_link = True
else:
- # if force is False, we want to avoid creating the startup symlink if it was manually deleted by the user, we want to create it only if a broken one is found
+ # if force is False, we want to avoid creating the startup symlink if it was
+ # manually deleted by the user, we want to create it only if a broken one is
+ # found
if os.path.islink(startup_entry):
# if the link exists, check if it is broken
try:
os.stat(startup_entry)
except FileNotFoundError:
# a FileNotFoundError will get thrown if the startup symlink is broken
- create_link = True
+ create_link = True
if os.path.islink(obsolete_entry):
# if a link with the old naming exists, delete it and create a new one
@@ -432,23 +454,27 @@ def create_startup_entry(force=False):
def initialize_platform():
- """
- Copy icons and generate desktop entries.
- """
+ """Copy icons and generate desktop entries."""
logging.debug("Initialize the platform")
- applications_dir_path = os.path.join(HOME_DIRECTORY, '.local/share/applications')
- icons_dir_path = os.path.join(HOME_DIRECTORY, '.local/share/icons')
- desktop_entry = os.path.join(applications_dir_path, 'io.github.slgobinath.SafeEyes.desktop')
+ applications_dir_path = os.path.join(HOME_DIRECTORY, ".local/share/applications")
+ icons_dir_path = os.path.join(HOME_DIRECTORY, ".local/share/icons")
+ desktop_entry = os.path.join(
+ applications_dir_path, "io.github.slgobinath.SafeEyes.desktop"
+ )
# Create the folder if not exist
mkdir(icons_dir_path)
# Create a desktop entry
- if not os.path.exists(os.path.join(sys.prefix, "share/applications/io.github.slgobinath.SafeEyes.desktop")):
+ if not os.path.exists(
+ os.path.join(
+ sys.prefix, "share/applications/io.github.slgobinath.SafeEyes.desktop"
+ )
+ ):
# Create the folder if not exist
mkdir(applications_dir_path)
-
+
# Remove existing file
delete(desktop_entry)
@@ -459,17 +485,21 @@ def initialize_platform():
logging.error("Failed to create desktop entry at %s" % desktop_entry)
# Add links for all icons
- for (path, _, filenames) in os.walk(SYSTEM_ICONS):
+ for path, _, filenames in os.walk(SYSTEM_ICONS):
for filename in filenames:
system_icon = os.path.join(path, filename)
- local_icon = os.path.join(icons_dir_path, os.path.relpath(system_icon, SYSTEM_ICONS))
- global_icon = os.path.join(sys.prefix, "share/icons", os.path.relpath(system_icon, SYSTEM_ICONS))
+ local_icon = os.path.join(
+ icons_dir_path, os.path.relpath(system_icon, SYSTEM_ICONS)
+ )
+ global_icon = os.path.join(
+ sys.prefix, "share/icons", os.path.relpath(system_icon, SYSTEM_ICONS)
+ )
parent_dir = str(Path(local_icon).parent)
-
+
if os.path.exists(global_icon):
# This icon is already added to the /usr/share/icons/hicolor folder
continue
-
+
# Create the directory if not exists
mkdir(parent_dir)
@@ -491,7 +521,7 @@ def reset_config():
# Copy the safeeyes.json and safeeyes_style.css
shutil.copy2(SYSTEM_CONFIG_FILE_PATH, CONFIG_FILE_PATH)
shutil.copy2(SYSTEM_STYLE_SHEET_PATH, STYLE_SHEET_PATH)
-
+
# Add write permission (e.g. if original file was stored in /nix/store)
os.chmod(CONFIG_FILE_PATH, 0o666)
os.chmod(STYLE_SHEET_PATH, 0o666)
@@ -500,28 +530,28 @@ def reset_config():
def replace_style_sheet():
- """
- Replace the user style sheet by system style sheet.
- """
+ """Replace the user style sheet by system style sheet."""
delete(STYLE_SHEET_PATH)
shutil.copy2(SYSTEM_STYLE_SHEET_PATH, STYLE_SHEET_PATH)
os.chmod(STYLE_SHEET_PATH, 0o666)
def initialize_logging(debug):
- """
- Initialize the logging framework using the Safe Eyes specific configurations.
+ """Initialize the logging framework using the Safe Eyes specific
+ configurations.
"""
# Configure logging.
root_logger = logging.getLogger()
log_formatter = logging.Formatter(
- '%(asctime)s [%(levelname)s]:[%(threadName)s] %(message)s')
+ "%(asctime)s [%(levelname)s]:[%(threadName)s] %(message)s"
+ )
# Append the logs and overwrite once reached 1MB
if debug:
# Log to file
file_handler = RotatingFileHandler(
- LOG_FILE_PATH, maxBytes=1024 * 1024, backupCount=5, encoding=None, delay=0)
+ LOG_FILE_PATH, maxBytes=1024 * 1024, backupCount=5, encoding=None, delay=0
+ )
file_handler.setFormatter(log_formatter)
# Log to console
console_handler = logging.StreamHandler()
@@ -535,11 +565,9 @@ def initialize_logging(debug):
def __open_plugin_config(plugins_dir, plugin_id):
- """
- Open the given plugin's configuration.
- """
- plugin_config_path = os.path.join(plugins_dir, plugin_id, 'config.json')
- plugin_module_path = os.path.join(plugins_dir, plugin_id, 'plugin.py')
+ """Open the given plugin's configuration."""
+ plugin_config_path = os.path.join(plugins_dir, plugin_id, "config.json")
+ plugin_module_path = os.path.join(plugins_dir, plugin_id, "plugin.py")
if not os.path.isfile(plugin_config_path) or not os.path.isfile(plugin_module_path):
# Either the config.json or plugin.py is not available
return None
@@ -547,52 +575,48 @@ def __open_plugin_config(plugins_dir, plugin_id):
def __update_plugin_config(plugin, plugin_config, config):
- """
- Update the plugin configuration.
- """
+ """Update the plugin configuration."""
if plugin_config is None:
- config['plugins'].remove(plugin)
+ config["plugins"].remove(plugin)
else:
- if parse(plugin.get('version', '0.0.0')) != parse(plugin_config['meta']['version']):
+ if parse(plugin.get("version", "0.0.0")) != parse(
+ plugin_config["meta"]["version"]
+ ):
# Update the configuration
- plugin['version'] = plugin_config['meta']['version']
+ plugin["version"] = plugin_config["meta"]["version"]
setting_ids = []
# Add the new settings
- for setting in plugin_config['settings']:
- setting_ids.append(setting['id'])
- if 'settings' not in plugin:
- plugin['settings'] = {}
- if plugin['settings'].get(setting['id'], None) is None:
- plugin['settings'][setting['id']] = setting['default']
+ for setting in plugin_config["settings"]:
+ setting_ids.append(setting["id"])
+ if "settings" not in plugin:
+ plugin["settings"] = {}
+ if plugin["settings"].get(setting["id"], None) is None:
+ plugin["settings"][setting["id"]] = setting["default"]
# Remove the removed ids
keys_to_remove = []
- for key in plugin.get('settings', []):
+ for key in plugin.get("settings", []):
if key not in setting_ids:
keys_to_remove.append(key)
for key in keys_to_remove:
- del plugin['settings'][key]
+ del plugin["settings"][key]
def __add_plugin_config(plugin_id, plugin_config, safe_eyes_config):
- """
- """
if plugin_config is None:
return
config = {}
- config['id'] = plugin_id
- config['enabled'] = False # By default plugins are disabled
- config['version'] = plugin_config['meta']['version']
- if plugin_config['settings']:
- config['settings'] = {}
- for setting in plugin_config['settings']:
- config['settings'][setting['id']] = setting['default']
- safe_eyes_config['plugins'].append(config)
+ config["id"] = plugin_id
+ config["enabled"] = False # By default plugins are disabled
+ config["version"] = plugin_config["meta"]["version"]
+ if plugin_config["settings"]:
+ config["settings"] = {}
+ for setting in plugin_config["settings"]:
+ config["settings"][setting["id"]] = setting["default"]
+ safe_eyes_config["plugins"].append(config)
def merge_plugins(config):
- """
- Merge plugin configurations with Safe Eyes configuration.
- """
+ """Merge plugin configurations with Safe Eyes configuration."""
system_plugins = None
user_plugins = None
@@ -609,8 +633,8 @@ def merge_plugins(config):
user_plugins = []
# Create a list of existing plugins
- for plugin in config['plugins']:
- plugin_id = plugin['id']
+ for plugin in config["plugins"]:
+ plugin_id = plugin["id"]
if plugin_id in system_plugins:
plugin_config = __open_plugin_config(SYSTEM_PLUGINS_DIR, plugin_id)
__update_plugin_config(plugin, plugin_config, config)
@@ -620,7 +644,7 @@ def merge_plugins(config):
__update_plugin_config(plugin, plugin_config, config)
user_plugins.remove(plugin_id)
else:
- config['plugins'].remove(plugin)
+ config["plugins"].remove(plugin)
# Add all system plugins
for plugin_id in system_plugins:
@@ -634,23 +658,19 @@ def merge_plugins(config):
def open_session():
- """
- Open the last session.
- """
- logging.info('Reading the session file')
+ """Open the last session."""
+ logging.info("Reading the session file")
session = load_json(SESSION_FILE_PATH)
if session is None:
- session = {'plugin': {}}
+ session = {"plugin": {}}
return session
def create_gtk_builder(glade_file):
- """
- Create a Gtk builder and load the glade file.
- """
+ """Create a Gtk builder and load the glade file."""
builder = Gtk.Builder()
- builder.set_translation_domain('safeeyes')
+ builder.set_translation_domain("safeeyes")
builder.add_from_file(glade_file)
# Tranlslate all sub components
for obj in builder.get_objects():
@@ -669,18 +689,14 @@ def load_and_scale_image(path, width, height):
if not os.path.isfile(path):
return None
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
- filename=path,
- width=width,
- height=height,
- preserve_aspect_ratio=True)
+ filename=path, width=width, height=height, preserve_aspect_ratio=True
+ )
image = Gtk.Image.new_from_pixbuf(pixbuf)
return image
def has_method(module, method_name, no_of_args=0):
- """
- Check whether the given function is defined in the module or not.
- """
+ """Check whether the given function is defined in the module or not."""
if hasattr(module, method_name):
if len(inspect.getfullargspec(getattr(module, method_name)).args) == no_of_args:
return True
@@ -688,8 +704,6 @@ def has_method(module, method_name, no_of_args=0):
def remove_if_exists(list_of_items, item):
- """
- Remove the item from the list_of_items it it exists.
- """
+ """Remove the item from the list_of_items it it exists."""
if item in list_of_items:
list_of_items.remove(item)
diff --git a/setup.py b/setup.py
index 480c3ab4..0d0faba3 100644
--- a/setup.py
+++ b/setup.py
@@ -1,86 +1,136 @@
-import os, sys, site
+import os
+import sys
import subprocess
import setuptools
-requires = [
- 'babel',
- 'psutil',
- 'croniter',
- 'PyGObject',
- 'packaging',
- 'python-xlib'
-]
+requires = ["babel", "psutil", "croniter", "PyGObject", "packaging", "python-xlib"]
_ROOT = os.path.abspath(os.path.dirname(__file__))
-with open(os.path.join(_ROOT, 'README.md')) as f:
+with open(os.path.join(_ROOT, "README.md")) as f:
long_description = f.read()
def __compile_po_files():
- """
- Compile the *.po trainslation files.
- """
- localedir = 'safeeyes/config/locale'
- po_dirs = [localedir + '/' + l + '/LC_MESSAGES/'
- for l in next(os.walk(localedir))[1]]
+ """Compile the *.po trainslation files."""
+ localedir = "safeeyes/config/locale"
+ po_dirs = [
+ localedir + "/" + d + "/LC_MESSAGES/" for d in next(os.walk(localedir))[1]
+ ]
for po_dir in po_dirs:
- po_files = [f
- for f in next(os.walk(po_dir))[2]
- if os.path.splitext(f)[1] == '.po']
+ po_files = [
+ f for f in next(os.walk(po_dir))[2] if os.path.splitext(f)[1] == ".po"
+ ]
for po_file in po_files:
filename, _ = os.path.splitext(po_file)
- mo_file = filename + '.mo'
- msgfmt_cmd = 'msgfmt {} -o {}'.format(
- po_dir + po_file, po_dir + mo_file)
+ mo_file = filename + ".mo"
+ msgfmt_cmd = "msgfmt {} -o {}".format(po_dir + po_file, po_dir + mo_file)
subprocess.call(msgfmt_cmd, shell=True)
def __data_files():
- """
- Collect the data files.
- """
+ """Collect the data files."""
root_dir = sys.prefix
- return [(os.path.join(root_dir, "share/applications"), ["safeeyes/platform/io.github.slgobinath.SafeEyes.desktop"]),
- (os.path.join(root_dir, "share/icons/hicolor/24x24/status"), ["safeeyes/platform/icons/hicolor/24x24/status/io.github.slgobinath.SafeEyes-disabled.png", "safeeyes/platform/icons/hicolor/24x24/status/io.github.slgobinath.SafeEyes-enabled.png", "safeeyes/platform/icons/hicolor/24x24/status/io.github.slgobinath.SafeEyes-timer.png"]),
- (os.path.join(root_dir, "share/icons/hicolor/24x24/apps"), ["safeeyes/platform/icons/hicolor/24x24/apps/io.github.slgobinath.SafeEyes.png"]),
- (os.path.join(root_dir, "share/icons/hicolor/16x16/status"), ["safeeyes/platform/icons/hicolor/16x16/status/io.github.slgobinath.SafeEyes-disabled.png", "safeeyes/platform/icons/hicolor/16x16/status/io.github.slgobinath.SafeEyes-enabled.png", "safeeyes/platform/icons/hicolor/16x16/status/io.github.slgobinath.SafeEyes-timer.png"]),
- (os.path.join(root_dir, "share/icons/hicolor/16x16/apps"), ["safeeyes/platform/icons/hicolor/16x16/apps/io.github.slgobinath.SafeEyes.png"]),
- (os.path.join(root_dir, "share/icons/hicolor/32x32/status"), ["safeeyes/platform/icons/hicolor/32x32/status/io.github.slgobinath.SafeEyes-disabled.png", "safeeyes/platform/icons/hicolor/32x32/status/io.github.slgobinath.SafeEyes-enabled.png"]),
- (os.path.join(root_dir, "share/icons/hicolor/32x32/apps"), ["safeeyes/platform/icons/hicolor/32x32/apps/io.github.slgobinath.SafeEyes.png"]),
- (os.path.join(root_dir, "share/icons/hicolor/64x64/apps"), ["safeeyes/platform/icons/hicolor/64x64/apps/io.github.slgobinath.SafeEyes.png"]),
- (os.path.join(root_dir, "share/icons/hicolor/128x128/apps"), ["safeeyes/platform/icons/hicolor/128x128/apps/io.github.slgobinath.SafeEyes.png"]),
- (os.path.join(root_dir, "share/icons/hicolor/48x48/status"), ["safeeyes/platform/icons/hicolor/48x48/status/io.github.slgobinath.SafeEyes-disabled.png", "safeeyes/platform/icons/hicolor/48x48/status/io.github.slgobinath.SafeEyes-enabled.png"]),
- (os.path.join(root_dir, "share/icons/hicolor/48x48/apps"), ["safeeyes/platform/icons/hicolor/48x48/apps/io.github.slgobinath.SafeEyes.png"]),]
+ return [
+ (
+ os.path.join(root_dir, "share/applications"),
+ ["safeeyes/platform/io.github.slgobinath.SafeEyes.desktop"],
+ ),
+ (
+ os.path.join(root_dir, "share/icons/hicolor/24x24/status"),
+ [
+ "safeeyes/platform/icons/hicolor/24x24/status/io.github.slgobinath.SafeEyes-disabled.png",
+ "safeeyes/platform/icons/hicolor/24x24/status/io.github.slgobinath.SafeEyes-enabled.png",
+ "safeeyes/platform/icons/hicolor/24x24/status/io.github.slgobinath.SafeEyes-timer.png",
+ ],
+ ),
+ (
+ os.path.join(root_dir, "share/icons/hicolor/24x24/apps"),
+ [
+ "safeeyes/platform/icons/hicolor/24x24/apps/io.github.slgobinath.SafeEyes.png"
+ ],
+ ),
+ (
+ os.path.join(root_dir, "share/icons/hicolor/16x16/status"),
+ [
+ "safeeyes/platform/icons/hicolor/16x16/status/io.github.slgobinath.SafeEyes-disabled.png",
+ "safeeyes/platform/icons/hicolor/16x16/status/io.github.slgobinath.SafeEyes-enabled.png",
+ "safeeyes/platform/icons/hicolor/16x16/status/io.github.slgobinath.SafeEyes-timer.png",
+ ],
+ ),
+ (
+ os.path.join(root_dir, "share/icons/hicolor/16x16/apps"),
+ [
+ "safeeyes/platform/icons/hicolor/16x16/apps/io.github.slgobinath.SafeEyes.png"
+ ],
+ ),
+ (
+ os.path.join(root_dir, "share/icons/hicolor/32x32/status"),
+ [
+ "safeeyes/platform/icons/hicolor/32x32/status/io.github.slgobinath.SafeEyes-disabled.png",
+ "safeeyes/platform/icons/hicolor/32x32/status/io.github.slgobinath.SafeEyes-enabled.png",
+ ],
+ ),
+ (
+ os.path.join(root_dir, "share/icons/hicolor/32x32/apps"),
+ [
+ "safeeyes/platform/icons/hicolor/32x32/apps/io.github.slgobinath.SafeEyes.png"
+ ],
+ ),
+ (
+ os.path.join(root_dir, "share/icons/hicolor/64x64/apps"),
+ [
+ "safeeyes/platform/icons/hicolor/64x64/apps/io.github.slgobinath.SafeEyes.png"
+ ],
+ ),
+ (
+ os.path.join(root_dir, "share/icons/hicolor/128x128/apps"),
+ [
+ "safeeyes/platform/icons/hicolor/128x128/apps/io.github.slgobinath.SafeEyes.png"
+ ],
+ ),
+ (
+ os.path.join(root_dir, "share/icons/hicolor/48x48/status"),
+ [
+ "safeeyes/platform/icons/hicolor/48x48/status/io.github.slgobinath.SafeEyes-disabled.png",
+ "safeeyes/platform/icons/hicolor/48x48/status/io.github.slgobinath.SafeEyes-enabled.png",
+ ],
+ ),
+ (
+ os.path.join(root_dir, "share/icons/hicolor/48x48/apps"),
+ [
+ "safeeyes/platform/icons/hicolor/48x48/apps/io.github.slgobinath.SafeEyes.png"
+ ],
+ ),
+ ]
def __package_files(directory):
- """
- Collect the package files.
- """
+ """Collect the package files."""
paths = []
- for (path, _, filenames) in os.walk(directory):
+ for path, _, filenames in os.walk(directory):
for filename in filenames:
- paths.append(os.path.join('..', path, filename))
+ paths.append(os.path.join("..", path, filename))
return paths
def __package_data():
- """
- Return a list of package data.
- """
+ """Return a list of package data."""
__compile_po_files()
- data = ['glade/*.glade', 'resource/*']
- data.extend(__package_files('safeeyes/config'))
- data.extend(__package_files('safeeyes/plugins'))
- data.extend(__package_files('safeeyes/platform'))
+ data = ["glade/*.glade", "resource/*"]
+ data.extend(__package_files("safeeyes/config"))
+ data.extend(__package_files("safeeyes/plugins"))
+ data.extend(__package_files("safeeyes/platform"))
return data
+
setuptools.setup(
name="safeeyes",
version="2.2.2",
- description="Protect your eyes from eye strain using this continuous breaks reminder.",
+ description=(
+ "Protect your eyes from eye strain using this continuous breaks reminder."
+ ),
long_description=long_description,
long_description_content_type="text/markdown",
author="Gobinath Loganathan",
@@ -88,16 +138,21 @@ def __package_data():
url="https://github.com/slgobinath/SafeEyes",
download_url="https://github.com/slgobinath/SafeEyes/archive/v2.2.2.tar.gz",
packages=setuptools.find_packages(),
- package_data={'safeeyes': __package_data()},
+ package_data={"safeeyes": __package_data()},
data_files=__data_files(),
install_requires=requires,
- entry_points={'console_scripts': ['safeeyes = safeeyes.__main__:main']},
- keywords='linux utility health eye-strain safe-eyes',
+ entry_points={"console_scripts": ["safeeyes = safeeyes.__main__:main"]},
+ keywords="linux utility health eye-strain safe-eyes",
classifiers=[
"Operating System :: POSIX :: Linux",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Development Status :: 5 - Production/Stable",
"Environment :: X11 Applications :: GTK",
"Intended Audience :: End Users/Desktop",
- "Topic :: Utilities"] + [('Programming Language :: Python :: %s' % x) for x in '3 3.5 3.6 3.7 3.8 3.9'.split()]
+ "Topic :: Utilities",
+ ]
+ + [
+ "Programming Language :: Python :: %s" % x
+ for x in "3 3.5 3.6 3.7 3.8 3.9".split()
+ ],
)