Resolved the conflicts with the Turkish Language file.
Merge remote-tracking branch 'upstream/master'
|
@ -0,0 +1,94 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# dotenv
|
||||
.env
|
||||
|
||||
# virtualenv
|
||||
.venv
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
|
@ -0,0 +1,2 @@
|
|||
include LICENSE
|
||||
include README.md
|
219
README.md
|
@ -1,177 +1,92 @@
|
|||
# Safe Eyes
|
||||
Protect your eyes from eye strain using this continuous breaks reminder. A Free and Open Source Linux alternative for EyeLeo.
|
||||
Protect your eyes from eye strain using this simple and beautiful, yet extensible break reminder. A Free and Open Source Linux alternative to EyeLeo.
|
||||
|
||||
For more details: [SafeEyes Protects You From Eye Strain When Working On The Computer](http://www.webupd8.org/2016/10/safeeyes-protects-you-from-eye-strain.html)
|
||||
Visit to the official site: http://slgobinath.github.io/SafeEyes/ for more details.
|
||||
|
||||
## Installation
|
||||
## Installation guide
|
||||
Safe Eyes is available in Ubuntu PPA, Arch AUR and Python PyPI. You can choose any installation source and install on any Linux system with Python 3. To see how to install Safe Eyes, visit [Getting Started](http://slgobinath.github.io/SafeEyes/#introduction)
|
||||
|
||||
### Ubuntu:
|
||||
1: Add the PPA: `sudo add-apt-repository ppa:slgobinath/safeeyes`
|
||||
### Compile from source
|
||||
Ensure to meet the following dependencies when compiling from source:
|
||||
|
||||
2: Download the package list: `sudo apt update`
|
||||
- gir1.2-appindicator3-0.1
|
||||
- gir1.2-notify-0.7
|
||||
- libappindicator-gtk3
|
||||
- python3-pyaudio
|
||||
- python3-psutil
|
||||
- xprintidle (optional)
|
||||
|
||||
3: Install Safe Eyes: `sudo apt install safeeyes`
|
||||
## Customizing options
|
||||
One of the key advantage of Safe Eyes over other similar products is its highly customizable design. You can change almost everything in Safe Eyes. A detailed documentation is available in the official site: [Customize Safe Eyes](http://slgobinath.github.io/SafeEyes/#customize)
|
||||
|
||||
4: Start Safe Eyes from start menu.
|
||||
|
||||
### Arch:
|
||||
Install SafeEyes via [AUR](https://aur.archlinux.org/packages/safeeyes/). Credits to [Yamakaky](https://github.com/Yamakaky)
|
||||
|
||||
### Other Linux:
|
||||
1: Install the dependencies:
|
||||
|
||||
* Arch: `hicolor-icon-theme`, `libappindicator-gtk3`, `xorg-xprop`, `python2-xlib`, `python2-gobject`, `python2-dbus`, `python2-babel`, `xprintidle` and `mpg123`
|
||||
|
||||
* Debian: `gir1.2-appindicator3-0.1`, `python-xlib`, `python-gobject`, `python-gi`, `python-dbus`, `gir1.2-notify-0.7`, `python-gtk2`, `python-babel`, `xprintidle` and `mpg123`
|
||||
|
||||
* Fedora 24: `libappindicator-gtk3`, `python-xlib`, `python-gobject`, `xorg-x11-utils`, `python-dbus`, `python-babel`, `xprintidle` and `mpg123`
|
||||
|
||||
2: Download and extract [safeeyes.tar.gz](https://github.com/slgobinath/SafeEyes/releases/download/v1.1.4/safeeyes.tar.gz) into `/`: `sudo tar -xzvf safeeyes.tar.gz -C /`
|
||||
|
||||
4: Start Safe Eyes using this command: `/opt/safeeyes/safeeyes`
|
||||
|
||||
Once started, Safe Eyes will copy the desktop file to `~/.config/autostart` and the configurations to `~/.config/safeeyes`. Therefore, from next time onwards, it should start with the system.
|
||||
|
||||
## Configuring Safe Eyes
|
||||
Just install and forget; Safe Eyes will take care of your eyes. To customize the preferences, go to Settings from Safe Eyes tray icon.
|
||||
You can change the look and feel of the break screen in `~/.config/safeeyes/style/safeeyes_style.css`.
|
||||
|
||||
## Uninstalling Safe Eyes
|
||||
Use the following commands to uninstall SafeEyes from your system.
|
||||
```
|
||||
sudo apt remove safeeyes
|
||||
rm -r ~/.config/safeeyes
|
||||
rm ~/.config/autostart/safeeyes.desktop
|
||||
```
|
||||
## Contribute
|
||||
I started this project for my own use and later released it as an open source alternative to EyeLeo and progressively reached to the current state with the great support of open source community. Most of the creative ideas were suggested and implemented by users. You can always add more to Safe Eyes. I have listed some possible ways here: [How to contribute](http://slgobinath.github.io/SafeEyes/#contribute)
|
||||
|
||||
## Features
|
||||
|
||||
General Features:
|
||||
|
||||
- Short breaks with eye exercises
|
||||
- Long breaks to change physical position and to warm up
|
||||
- Strict break for those who are addicted to computer
|
||||
- Highly customizable
|
||||
- Do not disturb when working with fullscreen applications( Eg: Watching movies)
|
||||
- Disable the keyboard during break
|
||||
- Notifications before every break
|
||||
- Smart pause and resume based on system idle time
|
||||
- Optional audible alert at the end of break
|
||||
- Do not disturb when working with full-screen applications( Eg: Watching movies)
|
||||
- Smart pause and resume based on system idle time (Require `xprintidle`)
|
||||
- Multi-monitor support
|
||||
- Elegant and customizable design
|
||||
- Multi-language support
|
||||
- Elegant and customizable design
|
||||
|
||||
## Contributing
|
||||
**Are you a user?**
|
||||
Optional Features:
|
||||
|
||||
Please test Safe Eyes on your system and report any issues [here](https://github.com/slgobinath/SafeEyes/issues)
|
||||
- Strict break for those who are addicted to computer
|
||||
- Postpone break
|
||||
- Skip or take break based on active windows (Regardless of full-screen-mode)
|
||||
- Customize individual break time
|
||||
- Define your own custom exercise
|
||||
- Audible alert at the end of break
|
||||
- Turn on/off audible alert for individual breaks
|
||||
- Customize disable time period
|
||||
- Lock screen after long breaks
|
||||
- Add images to breaks
|
||||
- Plug-in support to extend Safe Eyes
|
||||
|
||||
**Are you a developer?**
|
||||
For more details: [SafeEyes Features](http://slgobinath.github.io/SafeEyes/#features)
|
||||
|
||||
1. Fork it!
|
||||
2. Create your feature branch: `git checkout -b my-new-feature`
|
||||
3. Commit your changes: `git commit -am 'Add some feature'`
|
||||
4. Push to the branch: `git push origin my-new-feature`
|
||||
5. Submit a pull request
|
||||
## Currently available translations
|
||||
* [Čeština](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/cs.json)
|
||||
* [Deutsch](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/de.json)
|
||||
* [English](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/en.json)
|
||||
* [Español](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/es.json)
|
||||
* [Français](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/fr.json)
|
||||
* [ქართული](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/ge.json)
|
||||
* [हिंदी](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/hi.json)
|
||||
* [Magyar](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/hu.json)
|
||||
* [Bahasa Indonesia](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/id.json)
|
||||
* [Polski](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/pl.json)
|
||||
* [Português](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/pt.json)
|
||||
* [Русский](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/ru.json)
|
||||
* [Slovenský](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/sk.json)
|
||||
* [தமிழ்](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/ta.json)
|
||||
* [Türkçe](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/config/lang/tr.json)
|
||||
|
||||
**Are you using a different Linux system?**
|
||||
|
||||
Please test Safe Eyes and create installers for your operating system
|
||||
|
||||
**Found a bug?**
|
||||
|
||||
Please report them [here](https://github.com/slgobinath/SafeEyes/issues)
|
||||
|
||||
|
||||
**Can you translate English to your mother tongue (or whatever the language)?**
|
||||
|
||||
Show your support by translating Safe Eyes to a new language or by improving the existing translations.
|
||||
|
||||
**How else can you show your support?**
|
||||
|
||||
- Vote for Safe Eyes in [alternativeto.net](http://alternativeto.net/software/eyeleo/?platform=linux).
|
||||
- Suggest any improvements.
|
||||
- Share with your friends.
|
||||
|
||||
## Translating Safe Eyes
|
||||
From version 1.1.0, Safe Eyes supports translation. Translation files for each langauges must be placed in `/opt/safeeyes/config/lang` directory. The language file name must follow [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) language code standard. For example, the language file of English must be `en.json`. Follow these steps to translate Safe Eyes to your language.
|
||||
|
||||
1. Copy `/opt/safeeyes/config/lang/en.json` to `/opt/safeeyes/config/lang/<iso-639-1-language-code>.json`
|
||||
|
||||
2. Provide `language_name` in the language itself and `language_name_en` in English.
|
||||
|
||||
3. Translate other property values to the selected language.
|
||||
|
||||
4. Translate the comment in [safeeyes.desktop](https://github.com/slgobinath/SafeEyes/blob/master/safeeyes/share/applications/safeeyes.desktop) file.
|
||||
|
||||
**Note 1:** The `{}` used in property values will be replaced by runtime variables related to those commands. For example the `{}` in `Next break at {}` will be replaced by time at the runtime.
|
||||
|
||||
**Note 2:** Use Unicode when translating Safe Eyes.
|
||||
|
||||
**Note 3:** To change the language of Safe Eyes, change the `language` property in `~/.config/safeeyes/safeeyes.json` to the ISO 639-1 code of your language and restart the Safe Eyes.
|
||||
|
||||
For more details, have a look at existing language files: [lang](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/safeeyes/config/lang)
|
||||
|
||||
### Currently available translations
|
||||
* [Čeština](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/safeeyes/config/lang/cz.json)
|
||||
* [Deutsch](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/safeeyes/config/lang/de.json)
|
||||
* [English](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/safeeyes/config/lang/en.json)
|
||||
* [Español](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/safeeyes/config/lang/es.json)
|
||||
* [Français](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/safeeyes/config/lang/fr.json)
|
||||
* [Magyar](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/safeeyes/config/lang/hu.json)
|
||||
* [Português](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/safeeyes/config/lang/pt.json)
|
||||
* [Русский](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/safeeyes/config/lang/ru.json)
|
||||
* [Slovenský](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/safeeyes/config/lang/sk.json)
|
||||
* [தமிழ்](https://github.com/slgobinath/SafeEyes/tree/master/safeeyes/safeeyes/config/lang/ta.json)
|
||||
|
||||
|
||||
|
||||
|
||||
## History
|
||||
Version 1.1.3:
|
||||
* Bug fix for no audible alert
|
||||
|
||||
Version 1.1.3:
|
||||
* Optional audible alert after breaks
|
||||
* Pause Safe Eyes if the system is idle for a given time. (Resume when user is active)
|
||||
* Bug fix for no break after fullscreen apps found
|
||||
* Dependency fix for Kubuntu
|
||||
|
||||
Version 1.1.2:
|
||||
* Bug fix for no break
|
||||
|
||||
Version 1.1.1:
|
||||
* About dialog
|
||||
* UI control to select the language
|
||||
* Fixed bug in disable option after suspend
|
||||
|
||||
Version 1.1.0:
|
||||
* Multi-language support
|
||||
* Fixed bug in multi-screen support
|
||||
* Fixed bug in break screen transparency
|
||||
* Next break information in tray menu
|
||||
|
||||
Version 1.0.9:
|
||||
* Multi-screen support
|
||||
* Handling system suspend (Stop and restart during system suspend)
|
||||
|
||||
Version 1.0.8:
|
||||
* Bug fix for Ubuntu Mate
|
||||
|
||||
Version 1.0.7:
|
||||
* Removed python-apscheduler dependency
|
||||
* Installation directory is restructured
|
||||
* Bug fixes:
|
||||
* Supporting Ubuntu 16.10
|
||||
* Symlink for autostart instead of copying the desktop file
|
||||
|
||||
Version 1.0.6:
|
||||
* Latest stable release
|
||||
Do you want to see your language here? Please translate Safe Eyes to whatever the languages you know. Visit to **Translate Safe Eyes** in [Customize Safe Eyes](http://slgobinath.github.io/SafeEyes/#customize) to see how to translate.
|
||||
|
||||
## Tested Environments
|
||||
* Ubuntu 14.04
|
||||
* Ubuntu 16.04
|
||||
* Ubuntu 16.10
|
||||
* Linux Mint 18
|
||||
* Ubuntu Mate 16.04
|
||||
* Kubuntu 16.10
|
||||
|
||||
Core functionalities of Safe Eyes are tested by the developer in the following environments:
|
||||
|
||||
* Antergos 17.4
|
||||
* Elementary OS Loki
|
||||
* Fedora 25
|
||||
* Kubuntu 17.04
|
||||
* Linux Mint 18.1
|
||||
* Manjaro 16.10.3
|
||||
* Ubuntu 14.04
|
||||
* Ubuntu 16.04
|
||||
* Ubuntu 16.10
|
||||
* Ubuntu Budgie 17.04
|
||||
* Ubuntu Mate 16.04
|
||||
* Xubuntu 16.10
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -1,4 +1,32 @@
|
|||
safeeyes (1.1.4-1) xenial; urgency=medium
|
||||
safeeyes (1.2.1-1) xenial; urgency=low
|
||||
|
||||
* Support postponing the break
|
||||
|
||||
* Handle configuration update efficiently
|
||||
|
||||
* Move to Python 3
|
||||
|
||||
* Add plugin support
|
||||
|
||||
* Add custom breaks
|
||||
|
||||
* Add optional break image feature
|
||||
|
||||
* Add lock screen support
|
||||
|
||||
* Prevent disabling Safe Eyes after notification
|
||||
|
||||
* Fix random crash
|
||||
|
||||
* Use system language if available
|
||||
|
||||
* Fix disable menu issue in Elementary OS
|
||||
|
||||
* Fix long breaks in hours
|
||||
|
||||
* Fix locale time format issue
|
||||
|
||||
* Advanced configurations and disable for given time
|
||||
|
||||
* Optional audible alert and pause Safe Eyes if system is idle
|
||||
|
||||
|
@ -11,23 +39,23 @@ safeeyes (1.1.4-1) xenial; urgency=medium
|
|||
* Add next break information to tray menu
|
||||
|
||||
* Support translation
|
||||
|
||||
|
||||
* Adding multiscreen support & handling system suspend
|
||||
|
||||
|
||||
* Fixing bug in Ubuntu MATE environment
|
||||
|
||||
|
||||
* Removing apscheduler dependency
|
||||
|
||||
* Fixing seconds instead of minutes bug
|
||||
|
||||
|
||||
* Bug fixes for Ubuntu 14.04 and keyboard lock during break
|
||||
|
||||
|
||||
* Reducing minimal Python requirement
|
||||
|
||||
|
||||
* Fixing appindicator version mismatch
|
||||
|
||||
|
||||
* Fixing apscheduler version mismatch
|
||||
|
||||
* Initial release
|
||||
|
||||
-- Loganathan Gobinath <slgobinath@gmail.com> Sat, 15 Oct 2016 06:28:40 +0530
|
||||
-- Gobinath Loganathan <slgobinath@gmail.com> Sat, 15 Oct 2016 06:28:40 +0530
|
|
@ -1,14 +1,15 @@
|
|||
Source: safeeyes
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Maintainer: Loganathan Gobinath <slgobinath@gmail.com>
|
||||
Build-Depends: debhelper (>= 9)
|
||||
Maintainer: Gobinath Loganathan <slgobinath@gmail.com>
|
||||
Build-Depends: debhelper (>= 9), python3, python3-setuptools
|
||||
Standards-Version: 3.9.6
|
||||
X-Python3-Version: >= 3.4
|
||||
Homepage: https://github.com/slgobinath/SafeEyes/
|
||||
|
||||
Package: safeeyes
|
||||
Architecture: any
|
||||
Depends: gir1.2-appindicator3-0.1, python (>= 2.7.0), python-xlib, python-gi, python-dbus, gir1.2-notify-0.7, python-gtk2, python-babel, xprintidle, mpg123
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, ${python3:Depends}, gir1.2-appindicator3-0.1, python3 (>= 3.4.0), python3-xlib, python3-dbus, gir1.2-notify-0.7, python3-babel, x11-utils, xprintidle, python3-pyaudio, python3-psutil
|
||||
Description: Safe Eyes
|
||||
Safe Eyes is a simple tool to remind you to take periodic breaks for your eyes. This is essential for anyone spending more time on the computer to avoid eye strain and other physical problems.
|
||||
.
|
||||
|
@ -16,10 +17,11 @@ Description: Safe Eyes
|
|||
- Short breaks with eye exercises
|
||||
- Long breaks to change physical position and to warm up
|
||||
- Strict break for those who are addicted to computer
|
||||
- Highly customizable
|
||||
- Do not disturb when working with fullscreen applications( Eg: Watching movies)
|
||||
- Do not disturb when working with full-screen applications( Eg: Watching movies)
|
||||
- Notifications before every break
|
||||
- Optional audible alert at the end of break
|
||||
- Option to lock screen after long breaks
|
||||
- Smart pause and resume based on system idle time
|
||||
- Multi-monitor support
|
||||
- Plugins to utilize Safe Eyes
|
||||
- Elegant and customizable design
|
|
@ -3,7 +3,7 @@ Upstream-Name: uget-chrome-wrapper
|
|||
Source: https://github.com/slgobinath/SafeEyes/
|
||||
|
||||
Files: *
|
||||
Copyright: 2016 Loganathan Gobinath <slgobinath@gmail.com>
|
||||
Copyright: 2016 Gobinath Loganathan <slgobinath@gmail.com>
|
||||
License: GPL-3.0+
|
||||
|
||||
License: GPL-3.0+
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/make -f
|
||||
%:
|
||||
dh $@ --with python3 --buildsystem=pybuild
|
|
@ -30,12 +30,15 @@ class AboutDialog:
|
|||
"""
|
||||
Read the about_dialog.glade and build the user interface.
|
||||
"""
|
||||
def __init__(self, glade_file, version):
|
||||
def __init__(self, glade_file, version, language):
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(glade_file)
|
||||
builder.connect_signals(self)
|
||||
self.window = builder.get_object("window_about")
|
||||
|
||||
builder.get_object('lbl_decription').set_label(language['app_info']['description'])
|
||||
builder.get_object('lbl_license').set_label(str(language['ui_controls']['license']) + ':')
|
||||
builder.get_object('btn_close').set_label(language['ui_controls']['close'])
|
||||
|
||||
# Set the version at the runtime
|
||||
builder.get_object("lbl_app_name").set_label("Safe Eyes " + version)
|
||||
|
|
@ -16,11 +16,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import gi
|
||||
import signal
|
||||
import sys
|
||||
import threading
|
||||
import logging
|
||||
import gi, signal, sys, threading, logging
|
||||
from Xlib import Xatom, Xutil
|
||||
from Xlib.display import Display, X
|
||||
gi.require_version('Gtk', '3.0')
|
||||
|
@ -35,8 +31,9 @@ class BreakScreen:
|
|||
"""
|
||||
Read the break_screen.glade and build the user interface.
|
||||
"""
|
||||
def __init__(self, on_skip, glade_file, style_sheet_path):
|
||||
def __init__(self, on_skip, on_postpone, glade_file, style_sheet_path):
|
||||
self.on_skip = on_skip
|
||||
self.on_postpone = on_postpone
|
||||
self.is_pretified = False
|
||||
self.key_lock_condition = threading.Condition()
|
||||
self.windows = []
|
||||
|
@ -55,7 +52,9 @@ class BreakScreen:
|
|||
def initialize(self, config, language):
|
||||
logging.info("Initialize the break screen")
|
||||
self.skip_button_text = language['ui_controls']['skip']
|
||||
self.strict_break = config['strict_break']
|
||||
self.postpone_button_text = language['ui_controls']['postpone']
|
||||
self.strict_break = config.get('strict_break', False)
|
||||
self.enable_postpone = config.get('allow_postpone', False)
|
||||
|
||||
|
||||
"""
|
||||
|
@ -72,10 +71,20 @@ class BreakScreen:
|
|||
"""
|
||||
def on_skip_clicked(self, button):
|
||||
logging.info("User skipped the break")
|
||||
# Must call on_skip before close to lock screen before closing the break screen
|
||||
self.on_skip()
|
||||
self.close()
|
||||
|
||||
|
||||
"""
|
||||
Postpone button press event handler.
|
||||
"""
|
||||
def on_postpone_clicked(self, button):
|
||||
logging.info("User postponed the break")
|
||||
self.on_postpone()
|
||||
self.close()
|
||||
|
||||
|
||||
"""
|
||||
Show/update the count down on all screens.
|
||||
"""
|
||||
|
@ -86,8 +95,8 @@ class BreakScreen:
|
|||
"""
|
||||
Show the break screen with the given message on all displays.
|
||||
"""
|
||||
def show_message(self, message):
|
||||
GLib.idle_add(lambda: self.__show_break_screen(message))
|
||||
def show_message(self, message, image_path, plugins_data):
|
||||
GLib.idle_add(lambda: self.__show_break_screen(message, image_path, plugins_data))
|
||||
|
||||
|
||||
"""
|
||||
|
@ -104,7 +113,7 @@ class BreakScreen:
|
|||
"""
|
||||
Show an empty break screen on all screens.
|
||||
"""
|
||||
def __show_break_screen(self, message):
|
||||
def __show_break_screen(self, message, image_path, plugins_data):
|
||||
# Lock the keyboard
|
||||
thread = threading.Thread(target=self.__lock_keyboard)
|
||||
thread.start()
|
||||
|
@ -125,11 +134,36 @@ class BreakScreen:
|
|||
window = builder.get_object("window_main")
|
||||
lbl_message = builder.get_object("lbl_message")
|
||||
lbl_count = builder.get_object("lbl_count")
|
||||
btn_skip = builder.get_object("btn_skip")
|
||||
lbl_left = builder.get_object("lbl_left")
|
||||
lbl_right = builder.get_object("lbl_right")
|
||||
img_break = builder.get_object("img_break")
|
||||
box_buttons = builder.get_object("box_buttons")
|
||||
|
||||
# Add the buttons
|
||||
if not self.strict_break:
|
||||
# Add postpone button
|
||||
if self.enable_postpone:
|
||||
btn_postpone = Gtk.Button(self.postpone_button_text)
|
||||
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)
|
||||
|
||||
# Add the skip button
|
||||
btn_skip = Gtk.Button(self.skip_button_text)
|
||||
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)
|
||||
|
||||
|
||||
|
||||
# Set values
|
||||
if image_path:
|
||||
img_break.set_from_file(image_path)
|
||||
lbl_message.set_label(message)
|
||||
btn_skip.set_label(self.skip_button_text)
|
||||
btn_skip.set_visible(not self.strict_break)
|
||||
lbl_left.set_markup(plugins_data['left']);
|
||||
lbl_right.set_markup(plugins_data['right']);
|
||||
|
||||
self.windows.append(window)
|
||||
self.count_labels.append(lbl_count)
|
||||
|
@ -167,7 +201,7 @@ class BreakScreen:
|
|||
while self.lock_keyboard:
|
||||
self.key_lock_condition.wait()
|
||||
self.key_lock_condition.release()
|
||||
|
||||
|
||||
# Ungrap the keyboard
|
||||
logging.info("Unlock the keyboard")
|
||||
display.ungrab_keyboard(X.CurrentTime)
|
|
@ -16,46 +16,46 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import gi
|
||||
import logging
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('AppIndicator3', '0.1')
|
||||
import gi, logging
|
||||
gi.require_version('Notify', '0.7')
|
||||
from gi.repository import Gtk, Gdk, GLib, GdkX11
|
||||
from gi.repository import AppIndicator3 as appindicator
|
||||
from gi.repository import Notify
|
||||
from safeeyes import Utility
|
||||
|
||||
|
||||
APPINDICATOR_ID = 'safeeyes'
|
||||
|
||||
"""
|
||||
This class is responsible for the notification to the user before the break.
|
||||
"""
|
||||
class Notification:
|
||||
"""
|
||||
This class is responsible for the notification to the user before the break.
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
Initialize the notification.
|
||||
"""
|
||||
def __init__(self, language):
|
||||
logging.info("Initialize the notification")
|
||||
"""
|
||||
Initialize the notification.
|
||||
"""
|
||||
logging.info('Initialize the notification')
|
||||
Notify.init(APPINDICATOR_ID)
|
||||
self.language = language
|
||||
|
||||
|
||||
"""
|
||||
Show the notification"
|
||||
"""
|
||||
def show(self, warning_time):
|
||||
logging.info("Show pre-break notification")
|
||||
self.notification = Notify.Notification.new("Safe Eyes", "\n" + self.language['messages']['ready_for_a_break'].format(warning_time), icon="safeeyes_enabled")
|
||||
self.notification.show()
|
||||
"""
|
||||
Show the notification
|
||||
"""
|
||||
logging.info('Show pre-break notification')
|
||||
self.notification = Notify.Notification.new('Safe Eyes', '\n' + self.language['messages']['ready_for_a_break'].format(warning_time), icon='safeeyes_enabled')
|
||||
try:
|
||||
self.notification.show()
|
||||
except Exception as e:
|
||||
logging.exception('Error in showing notification', e)
|
||||
|
||||
|
||||
"""
|
||||
Close the notification if it is not closed by the system already.
|
||||
"""
|
||||
def close(self):
|
||||
logging.info("Close pre-break notification")
|
||||
"""
|
||||
Close the notification if it is not closed by the system already.
|
||||
"""
|
||||
logging.info('Close pre-break notification')
|
||||
try:
|
||||
self.notification.close()
|
||||
except:
|
||||
|
@ -63,9 +63,9 @@ class Notification:
|
|||
pass
|
||||
|
||||
|
||||
"""
|
||||
Uninitialize the notification. Call this method when closing the application.
|
||||
"""
|
||||
def quite(self):
|
||||
logging.info("Uninitialize Safe Eyes notification")
|
||||
GLib.idle_add(lambda: Notify.uninit())
|
||||
"""
|
||||
Uninitialize the notification. Call this method when closing the application.
|
||||
"""
|
||||
logging.info('Uninitialize Safe Eyes notification')
|
||||
Utility.execute_main_thread(Notify.uninit)
|
|
@ -0,0 +1,150 @@
|
|||
# Safe Eyes is a utility to remind you to take break frequently
|
||||
# to protect your eyes from eye strain.
|
||||
|
||||
# Copyright (C) 2017 Gobinath
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import logging, importlib, os, sys, inspect, copy
|
||||
from multiprocessing.pool import ThreadPool
|
||||
from safeeyes import Utility
|
||||
|
||||
plugins_directory = os.path.join(Utility.config_directory, 'plugins')
|
||||
sys.path.append(os.path.abspath(plugins_directory))
|
||||
|
||||
class Plugins:
|
||||
"""
|
||||
This class manages imports the plugins and calls the methods defined in those plugins.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, config):
|
||||
"""
|
||||
Load the plugins.
|
||||
"""
|
||||
logging.info('Load all the plugins')
|
||||
self.__plugins = []
|
||||
|
||||
for plugin in config['plugins']:
|
||||
if plugin['location'].lower() in ['left', 'right']:
|
||||
if os.path.isfile(os.path.join(plugins_directory, plugin['name'] + '.py')):
|
||||
module = importlib.import_module(plugin['name'])
|
||||
if self.__has_method(module, 'start') and self.__has_method(module, 'pre_notification') and self.__has_method(module, 'pre_break') and self.__has_method(module, 'post_break') and self.__has_method(module, 'exit'):
|
||||
self.__plugins.append({'name': plugin['name'], 'module': module, 'location': plugin['location'].lower()})
|
||||
else:
|
||||
logging.warning('Ignoring the plugin ' + str(plugin['name']) + ' due to invalid method signature')
|
||||
else:
|
||||
logging.warning('Plugin file ' + str(plugin['name']) + '.py not found')
|
||||
else:
|
||||
logging.warning('Ignoring the plugin ' + str(plugin['name']) + ' due to invalid location value: ' + plugin['location'])
|
||||
|
||||
if self.__plugins:
|
||||
self.__thread_pool = ThreadPool(min([4, len(self.__plugins)]))
|
||||
|
||||
|
||||
def start(self, context):
|
||||
"""
|
||||
Call the start function of all the plugins in separate thread.
|
||||
"""
|
||||
if self.__plugins:
|
||||
context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes
|
||||
for plugin in self.__plugins:
|
||||
try:
|
||||
self.__thread_pool.apply_async(plugin['module'].start, (context,))
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
def pre_notification(self, context):
|
||||
"""
|
||||
Call the pre_notification function of all the plugins in separate thread.
|
||||
"""
|
||||
if self.__plugins:
|
||||
context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes
|
||||
for plugin in self.__plugins:
|
||||
try:
|
||||
self.__thread_pool.apply_async(plugin['module'].pre_notification, (context,))
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
def pre_break(self, context):
|
||||
"""
|
||||
Call the pre_break function of all the plugins and provide maximum 1 second to return the result.
|
||||
If they return the reault within 1 sec, append it to the output.
|
||||
|
||||
Returns: {'left': 'Markup of plugins to be aligned on left', 'right': 'Markup of plugins to be aligned on right' }
|
||||
"""
|
||||
output = {'left': ' \n', 'right': ' \n'}
|
||||
if self.__plugins:
|
||||
context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes
|
||||
multiple_results = [self.__thread_pool.apply_async(plugin['module'].pre_break, (context,)) for plugin in self.__plugins]
|
||||
for i in range(len(multiple_results)):
|
||||
try:
|
||||
result = multiple_results[i].get(timeout=1)
|
||||
if result:
|
||||
# Limit the line length to 50 characters
|
||||
large_lines = list(filter(lambda x: len(x) > 50, Utility.html_to_text(result).splitlines()))
|
||||
if large_lines:
|
||||
logging.warning('Ignoring lengthy result from the plugin ' + self.__plugins[i]['name'])
|
||||
continue
|
||||
output[self.__plugins[i]['location']] += (result + '\n\n')
|
||||
except Exception:
|
||||
# Something went wrong in the plugin
|
||||
logging.warning('Error when executing the plugin ' + self.__plugins[i]['name'])
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def post_break(self, context):
|
||||
"""
|
||||
Call the post_break function of all the plugins in separate thread.
|
||||
"""
|
||||
if self.__plugins:
|
||||
context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes
|
||||
for plugin in self.__plugins:
|
||||
try:
|
||||
self.__thread_pool.apply_async(plugin['module'].post_break, (context,))
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
def exit(self, context):
|
||||
"""
|
||||
Call the exit function of all the plugins in separate thread.
|
||||
"""
|
||||
if self.__plugins:
|
||||
context = copy.deepcopy(context) # If plugins change the context, it should not affect Safe Eyes
|
||||
|
||||
# Give maximum 1 sec for all plugins before terminating the thread pool
|
||||
multiple_results = [self.__thread_pool.apply_async(plugin['module'].exit, (context,)) for plugin in self.__plugins]
|
||||
for i in range(len(multiple_results)):
|
||||
try:
|
||||
multiple_results[i].get(timeout=1)
|
||||
except Exception:
|
||||
# Something went wrong in the plugin
|
||||
pass
|
||||
|
||||
try:
|
||||
self.__thread_pool.terminate() # Shutdown the pool
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
def __has_method(self, module, method_name, no_of_args = 1):
|
||||
"""
|
||||
Check whether the given function is defined in the module or not.
|
||||
"""
|
||||
if hasattr(module, method_name):
|
||||
if len(inspect.getargspec(getattr(module, method_name)).args) == no_of_args:
|
||||
return True
|
||||
return False
|
|
@ -17,7 +17,8 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import time, datetime, threading, sys, subprocess, logging, Utility
|
||||
import time, datetime, threading, sys, subprocess, logging
|
||||
from safeeyes import Utility
|
||||
|
||||
|
||||
"""
|
||||
|
@ -28,12 +29,11 @@ class SafeEyesCore:
|
|||
"""
|
||||
Initialize the internal variables of the core.
|
||||
"""
|
||||
def __init__(self, show_notification, start_break, end_break, on_countdown, update_next_break_info):
|
||||
def __init__(self, context, show_notification, start_break, end_break, on_countdown, update_next_break_info):
|
||||
# Initialize the variables
|
||||
self.break_count = -1
|
||||
self.long_break_message_index = -1
|
||||
self.short_break_message_index = -1
|
||||
self.skipped = False
|
||||
self.active = False
|
||||
self.running = False
|
||||
self.show_notification = show_notification
|
||||
|
@ -44,6 +44,9 @@ class SafeEyesCore:
|
|||
self.notification_condition = threading.Condition()
|
||||
self.idle_condition = threading.Condition()
|
||||
self.lock = threading.Lock()
|
||||
self.context = context
|
||||
self.context['skipped'] = False
|
||||
self.context['postponed'] = False
|
||||
|
||||
|
||||
"""
|
||||
|
@ -60,31 +63,53 @@ class SafeEyesCore:
|
|||
self.short_break_duration = config['short_break_duration']
|
||||
self.break_interval = config['break_interval']
|
||||
self.idle_time = config['idle_time']
|
||||
self.postpone_duration = config['postpone_duration']
|
||||
self.skip_break_window_classes = [x.lower() for x in config['active_window_class']['skip_break']]
|
||||
self.take_break_window_classes = [x.lower() for x in config['active_window_class']['take_break']]
|
||||
self.custom_exercises = config['custom_exercises']
|
||||
# Enable idle time pause only if xprintidle is available
|
||||
self.context['idle_pause_enabled'] = Utility.command_exist('xprintidle')
|
||||
|
||||
exercises = language['exercises']
|
||||
for short_break_config in config['short_breaks']:
|
||||
name = language['exercises'][short_break_config['name']]
|
||||
# break_time = short_break_config['time']
|
||||
exercise_name = short_break_config['name']
|
||||
name = None
|
||||
|
||||
if exercise_name in self.custom_exercises:
|
||||
name = self.custom_exercises[exercise_name]
|
||||
else:
|
||||
name = exercises[exercise_name]
|
||||
|
||||
break_time = short_break_config.get('time', self.short_break_duration)
|
||||
audible_alert = short_break_config.get('audible_alert', config['audible_alert'])
|
||||
image = short_break_config.get('image')
|
||||
|
||||
# Validate time value
|
||||
if not isinstance(break_time, int) or break_time <= 0:
|
||||
logging.error('Invalid time in short break: ' + str(short_break_config))
|
||||
continue
|
||||
|
||||
self.short_break_exercises.append([name, break_time])
|
||||
|
||||
self.short_break_exercises.append([name, break_time, audible_alert, image])
|
||||
|
||||
for long_break_config in config['long_breaks']:
|
||||
name = language['exercises'][long_break_config['name']]
|
||||
break_time = long_break_config.get('time', self.short_break_duration)
|
||||
# Validate time value
|
||||
if not break_time:
|
||||
break_time = self.short_break_duration
|
||||
elif not isinstance(break_time, int) or break_time <= 0:
|
||||
logging.error('Invalid time in short break: ' + str(long_break_config))
|
||||
continue
|
||||
exercise_name = long_break_config['name']
|
||||
name = None
|
||||
|
||||
if exercise_name in self.custom_exercises:
|
||||
name = self.custom_exercises[exercise_name]
|
||||
else:
|
||||
break_time = break_time * 60 # Convert to seconds
|
||||
|
||||
self.long_break_exercises.append([name, break_time])
|
||||
name = exercises[exercise_name]
|
||||
|
||||
break_time = long_break_config.get('time', self.long_break_duration)
|
||||
audible_alert = long_break_config.get('audible_alert', config['audible_alert'])
|
||||
image = long_break_config.get('image')
|
||||
|
||||
# Validate time value
|
||||
if not isinstance(break_time, int) or break_time <= 0:
|
||||
logging.error('Invalid time in long break: ' + str(long_break_config))
|
||||
continue
|
||||
|
||||
self.long_break_exercises.append([name, break_time, audible_alert, image])
|
||||
|
||||
|
||||
"""
|
||||
|
@ -97,7 +122,8 @@ class SafeEyesCore:
|
|||
self.active = True
|
||||
self.running = True
|
||||
Utility.start_thread(self.__scheduler_job)
|
||||
Utility.start_thread(self.__start_idle_monitor)
|
||||
if self.context['idle_pause_enabled']:
|
||||
Utility.start_thread(self.__start_idle_monitor)
|
||||
|
||||
|
||||
"""
|
||||
|
@ -151,7 +177,13 @@ class SafeEyesCore:
|
|||
User skipped the break using Skip button
|
||||
"""
|
||||
def skip_break(self):
|
||||
self.skipped = True
|
||||
self.context['skipped'] = True
|
||||
|
||||
"""
|
||||
User postponed the break using Postpone button
|
||||
"""
|
||||
def postpone_break(self):
|
||||
self.context['postponed'] = True
|
||||
|
||||
|
||||
"""
|
||||
|
@ -161,14 +193,31 @@ class SafeEyesCore:
|
|||
if not self.__is_running():
|
||||
return
|
||||
|
||||
next_break_time = datetime.datetime.now() + datetime.timedelta(minutes=self.break_interval)
|
||||
time_to_wait = self.break_interval # In minutes
|
||||
|
||||
if self.context['postponed']:
|
||||
# Reduce the break count by 1 to show the same break again
|
||||
if self.break_count == 0:
|
||||
self.break_count = -1
|
||||
else:
|
||||
self.break_count = ((self.break_count - 1) % self.no_of_short_breaks_per_long_break)
|
||||
if self.__is_long_break():
|
||||
self.long_break_message_index = (self.long_break_message_index - 1) % len(self.long_break_exercises)
|
||||
else:
|
||||
self.short_break_message_index = (self.short_break_message_index - 1) % len(self.short_break_exercises)
|
||||
|
||||
# Wait until the postpone time
|
||||
time_to_wait = self.postpone_duration
|
||||
self.context['postponed'] = False
|
||||
|
||||
next_break_time = datetime.datetime.now() + datetime.timedelta(minutes=time_to_wait)
|
||||
self.update_next_break_info(next_break_time)
|
||||
|
||||
|
||||
# Wait for the pre break warning period
|
||||
logging.info("Pre-break waiting for {} minutes".format(self.break_interval))
|
||||
logging.info("Pre-break waiting for {} minutes".format(time_to_wait))
|
||||
self.notification_condition.acquire()
|
||||
self.notification_condition.wait(self.break_interval * 60) # In minutes
|
||||
self.notification_condition.wait(time_to_wait * 60) # Convert to seconds
|
||||
self.notification_condition.release()
|
||||
|
||||
logging.info("Pre-break waiting is over")
|
||||
|
@ -178,29 +227,17 @@ class SafeEyesCore:
|
|||
|
||||
logging.info("Ready to show the break")
|
||||
|
||||
Utility.execute_main_thread(self.__process_job)
|
||||
|
||||
"""
|
||||
Used to process the job in default thread because __is_full_screen_app_found must be run by default thread
|
||||
"""
|
||||
def __process_job(self):
|
||||
if Utility.is_full_screen_app_found():
|
||||
# If full screen app found, do not show break screen
|
||||
logging.info("Found a full-screen application. Skip the break")
|
||||
if self.__is_running():
|
||||
# Schedule the break again
|
||||
Utility.start_thread(self.__scheduler_job)
|
||||
return
|
||||
|
||||
self.break_count = ((self.break_count + 1) % self.no_of_short_breaks_per_long_break)
|
||||
|
||||
Utility.start_thread(self.__notify_and_start_break)
|
||||
self.is_before_break = False
|
||||
Utility.execute_main_thread(self.__check_active_window)
|
||||
|
||||
|
||||
"""
|
||||
Show notification and start the break after given number of seconds
|
||||
Show the notification and start the break after the notification.
|
||||
"""
|
||||
def __notify_and_start_break(self):
|
||||
# Show a notification
|
||||
def __show_notification(self):
|
||||
# Show the notification
|
||||
self.show_notification()
|
||||
|
||||
logging.info("Wait for {} seconds which is the time to prepare".format(self.pre_break_warning_time))
|
||||
|
@ -209,26 +246,68 @@ class SafeEyesCore:
|
|||
self.notification_condition.wait(self.pre_break_warning_time)
|
||||
self.notification_condition.release()
|
||||
|
||||
self.is_before_break = True
|
||||
Utility.execute_main_thread(self.__check_active_window)
|
||||
|
||||
|
||||
"""
|
||||
Check the active window for full-screen and user defined exceptions.
|
||||
"""
|
||||
def __check_active_window(self):
|
||||
# Check the active window again. (User might changed the window)
|
||||
if self.__is_running() and Utility.is_active_window_skipped(self.skip_break_window_classes, self.take_break_window_classes, self.is_before_break):
|
||||
# If full screen app found, do not show break screen
|
||||
logging.info("Found a skip_break or full-screen window. Skip the break")
|
||||
if self.__is_running():
|
||||
# Schedule the break again
|
||||
Utility.start_thread(self.__scheduler_job)
|
||||
return
|
||||
|
||||
# Execute the post-operation
|
||||
if self.is_before_break:
|
||||
Utility.start_thread(self.__start_break)
|
||||
else:
|
||||
Utility.start_thread(self.__show_notification)
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Start the break screen.
|
||||
"""
|
||||
def __start_break(self):
|
||||
# User can disable SafeEyes during notification
|
||||
if self.__is_running():
|
||||
message = ""
|
||||
image = None
|
||||
seconds = 0
|
||||
audible_alert = None
|
||||
if self.__is_long_break():
|
||||
logging.info("Count is {}; get a long beak message".format(self.break_count))
|
||||
self.long_break_message_index = (self.long_break_message_index + 1) % len(self.long_break_exercises)
|
||||
message = self.long_break_exercises[self.long_break_message_index][0]
|
||||
seconds = self.long_break_exercises[self.long_break_message_index][1]
|
||||
audible_alert = self.long_break_exercises[self.long_break_message_index][2]
|
||||
image = self.long_break_exercises[self.long_break_message_index][3]
|
||||
self.context['break_type'] = 'long'
|
||||
else:
|
||||
logging.info("Count is {}; get a short beak message".format(self.break_count))
|
||||
self.short_break_message_index = (self.short_break_message_index + 1) % len(self.short_break_exercises)
|
||||
message = self.short_break_exercises[self.short_break_message_index][0]
|
||||
seconds = self.short_break_exercises[self.short_break_message_index][1]
|
||||
|
||||
audible_alert = self.short_break_exercises[self.short_break_message_index][2]
|
||||
image = self.short_break_exercises[self.short_break_message_index][3]
|
||||
self.context['break_type'] = 'short'
|
||||
|
||||
self.context['break_length'] = seconds
|
||||
self.context['audible_alert'] = audible_alert
|
||||
total_break_time = seconds
|
||||
|
||||
# Show the break screen
|
||||
self.start_break(message)
|
||||
self.start_break(message, image)
|
||||
|
||||
# Use self.active instead of self.__is_running to avoid idle pause interrupting the break
|
||||
while seconds and self.active and not self.skipped:
|
||||
while seconds and self.active and not self.context['skipped'] and not self.context['postponed']:
|
||||
self.context['count_down'] = total_break_time - seconds
|
||||
mins, secs = divmod(seconds, 60)
|
||||
timeformat = '{:02d}:{:02d}'.format(mins, secs)
|
||||
self.on_countdown(timeformat)
|
||||
|
@ -236,16 +315,18 @@ class SafeEyesCore:
|
|||
seconds -= 1
|
||||
|
||||
# Loop terminated because of timeout (not skipped) -> Close the break alert
|
||||
if not self.skipped:
|
||||
logging.info("Break wasn't skipped. Automatically terminating the break")
|
||||
self.end_break()
|
||||
if not self.context['skipped'] and not self.context['postponed']:
|
||||
logging.info("Break is terminated automatically")
|
||||
self.end_break(audible_alert)
|
||||
|
||||
# Reset the skipped flag
|
||||
self.context['skipped'] = False
|
||||
|
||||
# Resume
|
||||
if self.__is_running():
|
||||
# Schedule the break again
|
||||
Utility.start_thread(self.__scheduler_job)
|
||||
|
||||
self.skipped = False
|
||||
|
||||
|
||||
"""
|
|
@ -19,12 +19,13 @@
|
|||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, Gdk, GdkX11, GObject
|
||||
from safeeyes import Utility
|
||||
|
||||
class SettingsDialog:
|
||||
"""
|
||||
Create and initialize SettingsDialog instance.
|
||||
"""
|
||||
def __init__(self, config, language, languages, on_save_settings, glade_file):
|
||||
def __init__(self, config, language, languages, able_to_lock_screen, on_save_settings, glade_file):
|
||||
self.config = config
|
||||
self.on_save_settings = on_save_settings
|
||||
self.languages = []
|
||||
|
@ -33,6 +34,7 @@ class SettingsDialog:
|
|||
builder.add_from_file(glade_file)
|
||||
builder.connect_signals(self)
|
||||
|
||||
# Get the UI components
|
||||
self.window = builder.get_object('window_settings')
|
||||
self.spin_short_break_duration = builder.get_object('spin_short_break_duration')
|
||||
self.spin_long_break_duration = builder.get_object('spin_long_break_duration')
|
||||
|
@ -40,68 +42,154 @@ class SettingsDialog:
|
|||
self.spin_short_between_long = builder.get_object('spin_short_between_long')
|
||||
self.spin_time_to_prepare = builder.get_object('spin_time_to_prepare')
|
||||
self.spin_idle_time_to_pause = builder.get_object('spin_idle_time_to_pause')
|
||||
self.spin_postpone_duration = builder.get_object('spin_postpone_duration')
|
||||
self.switch_show_time_in_tray = builder.get_object('switch_show_time_in_tray')
|
||||
self.switch_strict_break = builder.get_object('switch_strict_break')
|
||||
self.switch_postpone = builder.get_object('switch_postpone')
|
||||
self.switch_audible_alert = builder.get_object('switch_audible_alert')
|
||||
self.cmb_language = builder.get_object('cmb_language')
|
||||
self.switch_screen_lock = builder.get_object('switch_screen_lock')
|
||||
self.spin_time_to_screen_lock = builder.get_object('spin_time_to_screen_lock')
|
||||
|
||||
# Translate the UI labels
|
||||
builder.get_object('lbl_short_break').set_label(language['ui_controls']['short_break_duration'])
|
||||
builder.get_object('lbl_long_break').set_label(language['ui_controls']['long_break_duration'])
|
||||
builder.get_object('lbl_interval_bettween_breaks').set_label(language['ui_controls']['interval_between_two_breaks'])
|
||||
builder.get_object('lbl_short_per_long').set_label(language['ui_controls']['no_of_short_breaks_between_two_long_breaks'])
|
||||
builder.get_object('lbl_time_to_prepare').set_label(language['ui_controls']['time_to_prepare_for_break'])
|
||||
builder.get_object('lbl_idle_time_to_pause').set_label(language['ui_controls']['idle_time'])
|
||||
builder.get_object('lbl_postpone_duration').set_label(language['ui_controls']['postpone_duration'])
|
||||
builder.get_object('lbl_allow_postpone').set_label(language['ui_controls']['allow_postpone'])
|
||||
builder.get_object('lbl_show_time_in_tray').set_label(language['ui_controls']['show_time_in_tray'])
|
||||
builder.get_object('lbl_strict_break').set_label(language['ui_controls']['strict_break'])
|
||||
builder.get_object('lbl_audible_alert').set_label(language['ui_controls']['audible_alert'])
|
||||
builder.get_object('lbl_language').set_label(language['ui_controls']['language'])
|
||||
builder.get_object('lbl_enable_screen_lock').set_label(language['ui_controls']['enable_screen_lock'])
|
||||
builder.get_object('lbl_lock_screen_after').set_label(language['ui_controls']['time_to_screen_lock'])
|
||||
builder.get_object('btn_cancel').set_label(language['ui_controls']['cancel'])
|
||||
builder.get_object('btn_save').set_label(language['ui_controls']['save'])
|
||||
|
||||
# Set the current values of input fields
|
||||
self.spin_short_break_duration.set_value(config['short_break_duration'])
|
||||
self.spin_long_break_duration.set_value(config['long_break_duration'])
|
||||
self.spin_interval_between_two_breaks.set_value(config['break_interval'])
|
||||
self.spin_short_between_long.set_value(config['no_of_short_breaks_per_long_break'])
|
||||
self.spin_time_to_prepare.set_value(config['pre_break_warning_time'])
|
||||
self.spin_idle_time_to_pause.set_value(config['idle_time'])
|
||||
self.spin_postpone_duration.set_value(config['postpone_duration'])
|
||||
self.switch_show_time_in_tray.set_active(config['show_time_in_tray'])
|
||||
self.switch_strict_break.set_active(config['strict_break'])
|
||||
self.switch_audible_alert.set_active(config['audible_alert'])
|
||||
self.spin_time_to_screen_lock.set_value(config['time_to_screen_lock'])
|
||||
|
||||
# Enable idle_time_to_pause only if xprintidle is available
|
||||
self.spin_idle_time_to_pause.set_sensitive(Utility.command_exist('xprintidle'))
|
||||
|
||||
self.switch_screen_lock.set_sensitive(able_to_lock_screen)
|
||||
self.switch_screen_lock.set_active(able_to_lock_screen and config['enable_screen_lock'])
|
||||
self.switch_postpone.set_active(config['allow_postpone'] and not config['strict_break'])
|
||||
|
||||
# Update relative states
|
||||
# GtkSwitch state-set signal is available only from 3.14
|
||||
if Gtk.get_minor_version() >= 14:
|
||||
self.switch_strict_break.connect('state-set', self.on_switch_strict_break_activate)
|
||||
self.switch_screen_lock.connect('state-set', self.on_switch_screen_lock_activate)
|
||||
self.switch_postpone.connect('state-set', self.on_switch_postpone_activate)
|
||||
self.on_switch_strict_break_activate(self.switch_strict_break, self.switch_strict_break.get_active())
|
||||
self.on_switch_screen_lock_activate(self.switch_screen_lock, self.switch_screen_lock.get_active())
|
||||
self.on_switch_postpone_activate(self.switch_postpone, self.switch_postpone.get_active())
|
||||
|
||||
# Initialize the language combobox
|
||||
language_list_store = Gtk.ListStore(GObject.TYPE_STRING)
|
||||
language_index = 0
|
||||
language_index = 2
|
||||
lang_code = config['language']
|
||||
|
||||
# Add 'System Language' as the first option
|
||||
language_list_store.append([language['ui_controls']['system_language']])
|
||||
language_list_store.append(['-'])
|
||||
self.languages.append('system')
|
||||
self.languages.append('system') # Dummy record for row separator
|
||||
if 'system' == lang_code:
|
||||
self.cmb_language.set_active(0)
|
||||
|
||||
for key in sorted(languages.keys()):
|
||||
language_list_store.append([languages[key]])
|
||||
self.languages.append(key)
|
||||
if key == config['language']:
|
||||
if key == lang_code:
|
||||
self.cmb_language.set_active(language_index)
|
||||
language_index += 1
|
||||
|
||||
self.cmb_language.set_model(language_list_store)
|
||||
self.cmb_language.set_row_separator_func(lambda m,i: m.get_value(i, 0) == '-')
|
||||
cell = Gtk.CellRendererText()
|
||||
self.cmb_language.pack_start(cell, True)
|
||||
self.cmb_language.add_attribute(cell, 'text', 0)
|
||||
|
||||
"""
|
||||
Show the SettingsDialog.
|
||||
"""
|
||||
|
||||
def show(self):
|
||||
"""
|
||||
Show the SettingsDialog.
|
||||
"""
|
||||
self.window.show_all()
|
||||
|
||||
|
||||
def on_switch_screen_lock_activate(self, switch, state):
|
||||
"""
|
||||
Event handler to the state change of the screen_lock switch.
|
||||
Enable or disable the self.spin_time_to_screen_lock based on the state of the screen_lock switch.
|
||||
"""
|
||||
self.spin_time_to_screen_lock.set_sensitive(self.switch_screen_lock.get_active())
|
||||
|
||||
|
||||
def on_switch_strict_break_activate(self, switch, state):
|
||||
"""
|
||||
Event handler to the state change of the postpone switch.
|
||||
Enable or disable the self.spin_postpone_duration based on the state of the postpone switch.
|
||||
"""
|
||||
strict_break_enable = state #self.switch_strict_break.get_active()
|
||||
self.switch_postpone.set_sensitive(not strict_break_enable)
|
||||
if strict_break_enable:
|
||||
self.switch_postpone.set_active(False)
|
||||
|
||||
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.
|
||||
"""
|
||||
self.spin_postpone_duration.set_sensitive(self.switch_postpone.get_active())
|
||||
|
||||
def on_window_delete(self, *args):
|
||||
"""
|
||||
Event handler for Settings dialog close action.
|
||||
"""
|
||||
self.window.destroy()
|
||||
|
||||
|
||||
def on_save_clicked(self, button):
|
||||
"""
|
||||
Event handler for Save button click.
|
||||
"""
|
||||
self.config['short_break_duration'] = self.spin_short_break_duration.get_value_as_int()
|
||||
self.config['long_break_duration'] = self.spin_long_break_duration.get_value_as_int()
|
||||
self.config['break_interval'] = self.spin_interval_between_two_breaks.get_value_as_int()
|
||||
self.config['no_of_short_breaks_per_long_break'] = self.spin_short_between_long.get_value_as_int()
|
||||
self.config['pre_break_warning_time'] = self.spin_time_to_prepare.get_value_as_int()
|
||||
self.config['idle_time'] = self.spin_idle_time_to_pause.get_value_as_int()
|
||||
self.config['postpone_duration'] = self.spin_postpone_duration.get_value_as_int()
|
||||
self.config['show_time_in_tray'] = self.switch_show_time_in_tray.get_active()
|
||||
self.config['strict_break'] = self.switch_strict_break.get_active()
|
||||
self.config['audible_alert'] = self.switch_audible_alert.get_active()
|
||||
self.config['language'] = self.languages[self.cmb_language.get_active()]
|
||||
self.config['time_to_screen_lock'] = self.spin_time_to_screen_lock.get_value_as_int()
|
||||
self.config['enable_screen_lock'] = self.switch_screen_lock.get_active()
|
||||
self.config['allow_postpone'] = self.switch_postpone.get_active()
|
||||
|
||||
self.on_save_settings(self.config) # Call the provided save method
|
||||
self.window.destroy() # Close the settings window
|
||||
|
||||
|
||||
def on_cancel_clicked(self, button):
|
||||
"""
|
||||
Event handler for Cancel button click.
|
||||
"""
|
||||
self.window.destroy()
|
|
@ -0,0 +1,262 @@
|
|||
# Safe Eyes is a utility to remind you to take break frequently
|
||||
# to protect your eyes from eye strain.
|
||||
|
||||
# Copyright (C) 2016 Gobinath
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import gi, logging, threading, datetime
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('AppIndicator3', '0.1')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import AppIndicator3 as appindicator
|
||||
from safeeyes import Utility
|
||||
|
||||
# Global variables
|
||||
APPINDICATOR_ID = 'safeeyes'
|
||||
|
||||
|
||||
class TrayIcon:
|
||||
|
||||
def __init__(self, config, language, on_show_settings, on_show_about, on_enable, on_disable, on_quite):
|
||||
logging.info("Initialize the tray icon")
|
||||
self.on_show_settings = on_show_settings
|
||||
self.on_show_about = on_show_about
|
||||
self.on_quite = on_quite
|
||||
self.on_enable = on_enable
|
||||
self.on_disable = on_disable
|
||||
self.language = language
|
||||
self.dateTime = None
|
||||
self.active = True
|
||||
self.wakeup_time = None
|
||||
self.idle_condition = threading.Condition()
|
||||
self.lock = threading.Lock()
|
||||
|
||||
# Construct the tray icon
|
||||
self.indicator = appindicator.Indicator.new(
|
||||
APPINDICATOR_ID, "safeeyes_enabled", appindicator.IndicatorCategory.APPLICATION_STATUS)
|
||||
self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
|
||||
|
||||
self.initialize(config)
|
||||
|
||||
# Construct the context menu
|
||||
self.menu = Gtk.Menu()
|
||||
|
||||
# Next break info menu item
|
||||
self.item_info = Gtk.ImageMenuItem()
|
||||
img_timer = Gtk.Image()
|
||||
img_timer.set_from_icon_name("safeeyes_timer", 16)
|
||||
self.item_info.set_image(img_timer)
|
||||
|
||||
self.item_separator = Gtk.SeparatorMenuItem()
|
||||
|
||||
self.item_enable = Gtk.MenuItem()
|
||||
self.item_enable.connect('activate', self.on_enable_clicked)
|
||||
|
||||
self.item_disable = Gtk.MenuItem()
|
||||
self.item_disable.connect('activate', self.on_disable_clicked)
|
||||
|
||||
self.sub_menu_disable = Gtk.Menu()
|
||||
self.sub_menu_items = []
|
||||
|
||||
# Read disable options and build the sub menu
|
||||
for disable_option in config['disable_options']:
|
||||
time_in_minutes = disable_option['time']
|
||||
# Validate time value
|
||||
if not isinstance(time_in_minutes, int) or time_in_minutes <= 0:
|
||||
logging.error('Invalid time in disable option: ' + str(time_in_minutes))
|
||||
continue
|
||||
time_unit = disable_option['unit'].lower()
|
||||
if time_unit == 'seconds' or time_unit == 'second':
|
||||
time_in_minutes = int(time_in_minutes / 60)
|
||||
elif time_unit == 'minutes' or time_unit == 'minute':
|
||||
time_in_minutes = int(time_in_minutes * 1)
|
||||
elif time_unit == 'hours' or time_unit == 'hour':
|
||||
time_in_minutes = int(time_in_minutes * 60)
|
||||
else:
|
||||
# Invalid unit
|
||||
logging.error('Invalid unit in disable option: ' + str(disable_option))
|
||||
continue
|
||||
|
||||
# Create submenu
|
||||
sub_menu_item = Gtk.MenuItem()
|
||||
sub_menu_item.connect('activate', self.on_disable_clicked, time_in_minutes)
|
||||
self.sub_menu_items.append([sub_menu_item, disable_option['label'], disable_option['time']])
|
||||
self.sub_menu_disable.append(sub_menu_item)
|
||||
|
||||
# Disable until restart submenu
|
||||
self.sub_menu_item_until_restart = Gtk.MenuItem()
|
||||
self.sub_menu_item_until_restart.connect('activate', self.on_disable_clicked, -1)
|
||||
self.sub_menu_disable.append(self.sub_menu_item_until_restart)
|
||||
|
||||
# Add the sub menu to the enable/disable menu
|
||||
self.item_disable.set_submenu(self.sub_menu_disable)
|
||||
|
||||
# Settings menu item
|
||||
self.item_settings = Gtk.MenuItem()
|
||||
self.item_settings.connect('activate', self.show_settings)
|
||||
|
||||
# About menu item
|
||||
self.item_about = Gtk.MenuItem()
|
||||
self.item_about.connect('activate', self.show_about)
|
||||
|
||||
# Quit menu item
|
||||
self.item_quit = Gtk.MenuItem()
|
||||
self.item_quit.connect('activate', self.quit_safe_eyes)
|
||||
|
||||
self.set_labels(language)
|
||||
|
||||
# At startup, no need for activate menu
|
||||
self.item_enable.set_sensitive(False)
|
||||
|
||||
# Append all menu items and show the menu
|
||||
self.menu.append(self.item_info)
|
||||
self.menu.append(self.item_separator)
|
||||
self.menu.append(self.item_enable)
|
||||
self.menu.append(self.item_disable)
|
||||
self.menu.append(self.item_settings)
|
||||
self.menu.append(self.item_about)
|
||||
self.menu.append(self.item_quit)
|
||||
self.menu.show_all()
|
||||
|
||||
self.indicator.set_menu(self.menu)
|
||||
|
||||
def initialize(self, config):
|
||||
self.config = config
|
||||
|
||||
def set_labels(self, language):
|
||||
self.language = language
|
||||
for entry in self.sub_menu_items:
|
||||
entry[0].set_label(self.language['ui_controls'][entry[1]].format(entry[2]))
|
||||
|
||||
self.sub_menu_item_until_restart.set_label(self.language['ui_controls']['until_restart'])
|
||||
self.item_enable.set_label(self.language['ui_controls']['enable'])
|
||||
self.item_disable.set_label(self.language['ui_controls']['disable'])
|
||||
|
||||
if self.active:
|
||||
if self.dateTime:
|
||||
self.__set_next_break_info()
|
||||
else:
|
||||
if self.wakeup_time:
|
||||
self.item_info.set_label(self.language['messages']['disabled_until_x'].format(Utility.format_time(self.wakeup_time)))
|
||||
else:
|
||||
self.item_info.set_label(self.language['messages']['disabled_until_restart'])
|
||||
|
||||
self.item_settings.set_label(self.language['ui_controls']['settings'])
|
||||
self.item_about.set_label(self.language['ui_controls']['about'])
|
||||
self.item_quit.set_label(self.language['ui_controls']['quit'])
|
||||
|
||||
def show_icon(self):
|
||||
Utility.execute_main_thread(self.indicator.set_status, appindicator.IndicatorStatus.ACTIVE)
|
||||
|
||||
def hide_icon(self):
|
||||
Utility.execute_main_thread(self.indicator.set_status, appindicator.IndicatorStatus.PASSIVE)
|
||||
|
||||
def quit_safe_eyes(self, *args):
|
||||
self.on_quite()
|
||||
with self.lock:
|
||||
self.active = True
|
||||
# Notify all schedulers
|
||||
self.idle_condition.acquire()
|
||||
self.idle_condition.notify_all()
|
||||
self.idle_condition.release()
|
||||
|
||||
def show_settings(self, *args):
|
||||
self.on_show_settings()
|
||||
|
||||
def show_about(self, *args):
|
||||
self.on_show_about()
|
||||
|
||||
def next_break_time(self, dateTime):
|
||||
logging.info("Update next break information")
|
||||
self.dateTime = dateTime
|
||||
self.__set_next_break_info()
|
||||
|
||||
def __set_next_break_info(self):
|
||||
formatted_time = Utility.format_time(self.dateTime)
|
||||
message = self.language['messages']['next_break_at'].format(formatted_time)
|
||||
# Update the tray icon label
|
||||
if self.config.get('show_time_in_tray', False):
|
||||
self.indicator.set_label(formatted_time, '')
|
||||
else:
|
||||
self.indicator.set_label('', '')
|
||||
# Update the menu item label
|
||||
Utility.execute_main_thread(self.item_info.set_label, message)
|
||||
|
||||
def on_enable_clicked(self, *args):
|
||||
# active = self.item_enable.get_active()
|
||||
if not self.active:
|
||||
with self.lock:
|
||||
logging.info('Enable Safe Eyes')
|
||||
self.active = True
|
||||
self.indicator.set_icon("safeeyes_enabled")
|
||||
self.item_info.set_sensitive(True)
|
||||
self.item_enable.set_sensitive(False)
|
||||
self.item_disable.set_sensitive(True)
|
||||
self.on_enable()
|
||||
# Notify all schedulers
|
||||
self.idle_condition.acquire()
|
||||
self.idle_condition.notify_all()
|
||||
self.idle_condition.release()
|
||||
|
||||
def on_disable_clicked(self, *args):
|
||||
# active = self.item_enable.get_active()
|
||||
if self.active and len(args) > 1:
|
||||
logging.info('Disable Safe Eyes')
|
||||
self.active = False
|
||||
self.indicator.set_icon("safeeyes_disabled")
|
||||
self.indicator.set_label('', '')
|
||||
self.item_info.set_sensitive(False)
|
||||
self.item_enable.set_sensitive(True)
|
||||
self.item_disable.set_sensitive(False)
|
||||
self.on_disable()
|
||||
|
||||
time_to_wait = args[1]
|
||||
if time_to_wait <= 0:
|
||||
self.wakeup_time = None
|
||||
self.item_info.set_label(self.language['messages']['disabled_until_restart'])
|
||||
else:
|
||||
self.wakeup_time = datetime.datetime.now() + datetime.timedelta(minutes=time_to_wait)
|
||||
Utility.start_thread(self.__schedule_resume, time_minutes=time_to_wait)
|
||||
self.item_info.set_label(self.language['messages']['disabled_until_x'].format(Utility.format_time(self.wakeup_time)))
|
||||
|
||||
|
||||
"""
|
||||
This method is called by the core to prevent user from disabling Safe Eyes after the notification.
|
||||
"""
|
||||
def lock_menu(self):
|
||||
if self.active:
|
||||
# self.item_disable.set_sensitive(False)
|
||||
# self.item_settings.set_sensitive(False)
|
||||
# self.item_quit.set_sensitive(False)
|
||||
self.menu.set_sensitive(False)
|
||||
|
||||
"""
|
||||
This method is called by the core to activate the disable menu after the the break.
|
||||
"""
|
||||
def unlock_menu(self):
|
||||
if self.active:
|
||||
# self.item_disable.set_sensitive(True)
|
||||
# self.item_settings.set_sensitive(True)
|
||||
# self.item_quit.set_sensitive(True)
|
||||
self.menu.set_sensitive(True)
|
||||
|
||||
def __schedule_resume(self, time_minutes):
|
||||
self.idle_condition.acquire()
|
||||
self.idle_condition.wait(time_minutes * 60) # Convert to seconds
|
||||
self.idle_condition.release()
|
||||
|
||||
with self.lock:
|
||||
if not self.active:
|
||||
Utility.execute_main_thread(self.item_enable.activate)
|
|
@ -0,0 +1,455 @@
|
|||
# Safe Eyes is a utility to remind you to take break frequently
|
||||
# to protect your eyes from eye strain.
|
||||
|
||||
# Copyright (C) 2017 Gobinath
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import gi
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gdk, GLib
|
||||
from html.parser import HTMLParser
|
||||
from distutils.version import LooseVersion
|
||||
from logging.handlers import RotatingFileHandler
|
||||
import babel.dates, os, errno, re, subprocess, threading, logging, locale, json, shutil, pyaudio, wave
|
||||
|
||||
bin_directory = os.path.dirname(os.path.realpath(__file__))
|
||||
home_directory = os.path.expanduser('~')
|
||||
system_language_directory = os.path.join(bin_directory, 'config/lang')
|
||||
config_directory = os.path.join(home_directory, '.config/safeeyes')
|
||||
config_file_path = os.path.join(config_directory, 'safeeyes.json')
|
||||
style_sheet_path = os.path.join(config_directory, 'style/safeeyes_style.css')
|
||||
system_config_file_path = os.path.join(bin_directory, "config/safeeyes.json")
|
||||
system_style_sheet_path = os.path.join(bin_directory, "config/style/safeeyes_style.css")
|
||||
log_file_path = os.path.join(config_directory, 'safeeyes.log')
|
||||
|
||||
def play_notification():
|
||||
"""
|
||||
Play the alert.wav
|
||||
"""
|
||||
logging.info('Playing audible alert')
|
||||
CHUNK = 1024
|
||||
|
||||
try:
|
||||
# Open the sound file
|
||||
path = get_resource_path('alert.wav')
|
||||
if path is None:
|
||||
return
|
||||
sound = wave.open(path, 'rb')
|
||||
|
||||
# Create a sound stream
|
||||
wrapper = pyaudio.PyAudio()
|
||||
stream = wrapper.open(format=wrapper.get_format_from_width(sound.getsampwidth()),
|
||||
channels=sound.getnchannels(),
|
||||
rate=sound.getframerate(),
|
||||
output=True)
|
||||
|
||||
# Write file data into the sound stream
|
||||
data = sound.readframes(CHUNK)
|
||||
while data != b'':
|
||||
stream.write(data)
|
||||
data = sound.readframes(CHUNK)
|
||||
|
||||
# Close steam
|
||||
stream.stop_stream()
|
||||
stream.close()
|
||||
sound.close()
|
||||
wrapper.terminate()
|
||||
|
||||
except Exception as e:
|
||||
logging.warning('Unable to play audible alert')
|
||||
logging.exception(e)
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
if resource_name is None:
|
||||
return None
|
||||
resource_location = os.path.join(config_directory, 'resource', resource_name)
|
||||
if not os.path.isfile(resource_location):
|
||||
resource_location = os.path.join(bin_directory, 'resource', resource_name)
|
||||
if not os.path.isfile(resource_location):
|
||||
logging.error('Resource not found: ' + resource_name)
|
||||
resource_location = None
|
||||
|
||||
return resource_location
|
||||
|
||||
|
||||
def system_idle_time():
|
||||
"""
|
||||
Get system idle time in minutes.
|
||||
Return the idle time if xprintidle is available, otherwise return 0.
|
||||
"""
|
||||
try:
|
||||
return int(subprocess.check_output(['xprintidle']).decode('utf-8')) / 60000 # Convert to minutes
|
||||
except:
|
||||
return 0
|
||||
|
||||
|
||||
def start_thread(target_function, **args):
|
||||
"""
|
||||
Execute the function in a separate thread.
|
||||
"""
|
||||
thread = threading.Thread(target=target_function, kwargs=args)
|
||||
thread.start()
|
||||
|
||||
|
||||
def execute_main_thread(target_function, args=None):
|
||||
"""
|
||||
Execute the given function in main thread.
|
||||
"""
|
||||
if args:
|
||||
GLib.idle_add(lambda: target_function(args))
|
||||
else:
|
||||
GLib.idle_add(lambda: target_function())
|
||||
|
||||
|
||||
def is_active_window_skipped(skip_break_window_classes, take_break_window_classes, unfullscreen_allowed=False):
|
||||
"""
|
||||
Check for full-screen applications.
|
||||
This method must be executed by the main thread. If not, it will cause to random failure.
|
||||
"""
|
||||
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']
|
||||
|
||||
try:
|
||||
stdout = subprocess.check_output(cmdlist).decode('utf-8')
|
||||
except subprocess.CalledProcessError:
|
||||
logging.warning('Error in finding full-screen application')
|
||||
pass
|
||||
else:
|
||||
if stdout:
|
||||
is_fullscreen = 'FULLSCREEN' in stdout
|
||||
# Extract the process name
|
||||
process_names = re.findall('"(.+?)"', stdout)
|
||||
if process_names:
|
||||
process = process_names[1].lower()
|
||||
if process in skip_break_window_classes:
|
||||
return True
|
||||
elif process in take_break_window_classes:
|
||||
if is_fullscreen and unfullscreen_allowed:
|
||||
try:
|
||||
active_window.unfullscreen()
|
||||
except:
|
||||
logging.error('Error in unfullscreen the window ' + process)
|
||||
pass
|
||||
return False
|
||||
|
||||
return is_fullscreen
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def __system_locale():
|
||||
"""
|
||||
Return the system locale. If not available, return en_US.UTF-8.
|
||||
"""
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
system_locale = locale.getlocale(locale.LC_TIME)[0]
|
||||
if not system_locale:
|
||||
system_locale = 'en_US.UTF-8'
|
||||
return system_locale
|
||||
|
||||
|
||||
def format_time(time):
|
||||
"""
|
||||
Format time based on the system time.
|
||||
"""
|
||||
system_locale = __system_locale()
|
||||
return babel.dates.format_time(time, format='short', locale=system_locale)
|
||||
|
||||
|
||||
def mkdir(path):
|
||||
"""
|
||||
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))
|
||||
raise
|
||||
|
||||
def parse_language_code(lang_code):
|
||||
"""
|
||||
Convert the user defined language code to a valid one.
|
||||
This includes converting to lower case and finding system locale language,
|
||||
if the given lang_code code is 'system'.
|
||||
"""
|
||||
# Convert to lower case
|
||||
lang_code = str(lang_code).lower()
|
||||
|
||||
# If it is system, use the system language
|
||||
if lang_code == 'system':
|
||||
logging.info('Use system language for Safe Eyes')
|
||||
system_locale = __system_locale()
|
||||
lang_code = system_locale[0:2].lower()
|
||||
|
||||
# Check whether translation is available for this language.
|
||||
# If not available, use English by default.
|
||||
language_file_path = os.path.join(system_language_directory, lang_code + '.json')
|
||||
if not os.path.exists(language_file_path):
|
||||
logging.warn('The language {} does not exist. Use English instead'.format(lang_code))
|
||||
lang_code = 'en'
|
||||
|
||||
return lang_code
|
||||
|
||||
|
||||
def load_language(lang_code):
|
||||
"""
|
||||
Load the desired language from the available list based on the preference.
|
||||
"""
|
||||
# Convert the user defined language code to a valid one
|
||||
lang_code = parse_language_code(lang_code)
|
||||
|
||||
# Construct the translation file path
|
||||
language_file_path = os.path.join(system_language_directory, lang_code + '.json')
|
||||
|
||||
language = None
|
||||
# Read the language file and construct the json object
|
||||
with open(language_file_path) as language_file:
|
||||
language = json.load(language_file)
|
||||
|
||||
return language
|
||||
|
||||
|
||||
def read_lang_files():
|
||||
"""
|
||||
Read all the language translations and build a key-value mapping of language names
|
||||
in English and ISO 639-1 (Filename without extension).
|
||||
"""
|
||||
languages = {}
|
||||
for lang_file_name in os.listdir(system_language_directory):
|
||||
lang_file_path = os.path.join(system_language_directory, lang_file_name)
|
||||
if os.path.isfile(lang_file_path):
|
||||
with open(lang_file_path) as lang_file:
|
||||
lang = json.load(lang_file)
|
||||
languages[lang_file_name.lower().replace('.json', '')] = lang['meta_info']['language_name']
|
||||
|
||||
return languages
|
||||
|
||||
def lock_screen_command():
|
||||
"""
|
||||
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']
|
||||
XFCE: ['xflock4']
|
||||
Otherwise: None
|
||||
"""
|
||||
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 command_exist('xflock4'):
|
||||
return ['xflock4']
|
||||
elif desktop_session == 'cinnamon' and command_exist('cinnamon-screensaver-command'):
|
||||
return ['cinnamon-screensaver-command', '--lock']
|
||||
elif (desktop_session == 'pantheon' or desktop_session.startswith('lubuntu')) and command_exist('light-locker-command'):
|
||||
return ['light-locker-command', '--lock']
|
||||
elif desktop_session == 'mate' and 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'):
|
||||
if command_exist('gnome-screensaver-command'):
|
||||
return ['gnome-screensaver-command', '--lock']
|
||||
else:
|
||||
# From Gnome 3.8 no gnome-screensaver-command
|
||||
return ['dbus-send', '--type=method_call', '--dest=org.gnome.ScreenSaver', '/org/gnome/ScreenSaver', 'org.gnome.ScreenSaver.Lock']
|
||||
elif os.environ.get('GNOME_DESKTOP_SESSION_ID'):
|
||||
if not 'deprecated' in os.environ.get('GNOME_DESKTOP_SESSION_ID') and command_exist('gnome-screensaver-command'):
|
||||
# Gnome 2
|
||||
return ['gnome-screensaver-command', '--lock']
|
||||
return None
|
||||
|
||||
|
||||
def lock_desktop(command):
|
||||
"""
|
||||
Lock the screen using the predefined commands
|
||||
"""
|
||||
if command:
|
||||
try:
|
||||
subprocess.Popen(command)
|
||||
except Exception as e:
|
||||
logging.error('Error in executing the commad' + str(command) + ' to lock screen')
|
||||
|
||||
|
||||
def html_to_text(html):
|
||||
"""
|
||||
Convert HTML to plain text
|
||||
"""
|
||||
extractor = __HTMLTextExtractor()
|
||||
extractor.feed(html)
|
||||
return extractor.get_data()
|
||||
|
||||
|
||||
def command_exist(command):
|
||||
"""
|
||||
Check whether the given command exist in the system or not.
|
||||
"""
|
||||
if shutil.which(command):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def merge_configs(new_config, old_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 ~/.config/safeeyes directory.
|
||||
"""
|
||||
logging.info('Copy the config files to ~/.config/safeeyes')
|
||||
|
||||
style_dir_path = os.path.join(home_directory, '.config/safeeyes/style')
|
||||
startup_dir_path = os.path.join(home_directory, '.config/autostart')
|
||||
|
||||
# Remove the ~/.config/safeeyes directory
|
||||
shutil.rmtree(config_directory, ignore_errors=True)
|
||||
|
||||
# Remove the startup file
|
||||
try:
|
||||
os.remove(os.path.join(home_directory, os.path.join(startup_dir_path, 'safeeyes.desktop')))
|
||||
except:
|
||||
pass
|
||||
|
||||
# Create the ~/.config/safeeyes/style directory
|
||||
mkdir(style_dir_path)
|
||||
mkdir(startup_dir_path)
|
||||
|
||||
# Copy the safeeyes.json
|
||||
shutil.copy2(system_config_file_path, config_file_path)
|
||||
|
||||
# Copy the new startup file
|
||||
try:
|
||||
os.symlink("/usr/share/applications/safeeyes.desktop", os.path.join(startup_dir_path, 'safeeyes.desktop'))
|
||||
except OSError as exc:
|
||||
pass
|
||||
|
||||
# Copy the new style sheet
|
||||
if not os.path.isfile(style_sheet_path):
|
||||
shutil.copy2(system_style_sheet_path, style_sheet_path)
|
||||
|
||||
|
||||
def intialize_logging():
|
||||
"""
|
||||
Initialize the logging framework using the Safe Eyes specific configurations.
|
||||
"""
|
||||
# Create the directory to store log file if not exist
|
||||
if not os.path.exists(config_directory):
|
||||
try:
|
||||
os.makedirs(config_directory)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Configure logging.
|
||||
log_formatter = logging.Formatter('%(asctime)s [%(levelname)s]:[%(threadName)s] %(message)s')
|
||||
|
||||
# Apped the logs and overwrite once reached 5MB
|
||||
handler = RotatingFileHandler(log_file_path, mode='a', maxBytes=5*1024*1024, backupCount=2, encoding=None, delay=0)
|
||||
handler.setFormatter(log_formatter)
|
||||
handler.setLevel(logging.INFO)
|
||||
|
||||
root_logger = logging.getLogger()
|
||||
root_logger.setLevel(logging.INFO)
|
||||
root_logger.addHandler(handler)
|
||||
|
||||
|
||||
def read_config():
|
||||
"""
|
||||
Read the configuration from the config directory.
|
||||
If does not exist or outdated by major version, copy the system config and
|
||||
startup script to user directory.
|
||||
If the user config is outdated by minor version, update the config by the new values.
|
||||
"""
|
||||
logging.info('Reading the configuration file')
|
||||
|
||||
if not os.path.isfile(config_file_path):
|
||||
logging.info('Safe Eyes configuration file not found')
|
||||
__initialize_safeeyes()
|
||||
|
||||
# Read the configurations
|
||||
with open(config_file_path) as config_file:
|
||||
user_config = json.load(config_file)
|
||||
|
||||
with open(system_config_file_path) as config_file:
|
||||
system_config = json.load(config_file)
|
||||
|
||||
user_config_version = str(user_config['meta']['config_version'])
|
||||
system_config_version = str(system_config['meta']['config_version'])
|
||||
|
||||
if LooseVersion(user_config_version) < LooseVersion(system_config_version):
|
||||
# Outdated user config
|
||||
logging.info('Update the old config version {} with new config version {}'.format(user_config_version, system_config_version))
|
||||
user_config_major_version = user_config_version.split('.')[0]
|
||||
system_config_major_version = system_config_version.split('.')[0]
|
||||
|
||||
if LooseVersion(user_config_major_version) < LooseVersion(system_config_major_version):
|
||||
# Major version change
|
||||
__initialize_safeeyes()
|
||||
# Update the user_config
|
||||
user_config = system_config
|
||||
else:
|
||||
# Minor version change
|
||||
new_config = system_config.copy()
|
||||
new_config.update(user_config)
|
||||
# Update the version
|
||||
new_config['meta']['config_version'] = system_config_version
|
||||
|
||||
# Write the configuration to file
|
||||
with open(config_file_path, 'w') as config_file:
|
||||
json.dump(new_config, config_file, indent=4, sort_keys=True)
|
||||
|
||||
# Update the user_config
|
||||
user_config = new_config
|
||||
|
||||
return user_config
|
||||
|
||||
|
||||
class __HTMLTextExtractor(HTMLParser):
|
||||
"""
|
||||
Helper class to convert HTML to text
|
||||
"""
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
self.strict = False
|
||||
self.convert_charrefs= True
|
||||
self.fed = []
|
||||
|
||||
def handle_data(self, d):
|
||||
self.fed.append(d)
|
||||
|
||||
def get_data(self):
|
||||
return ''.join(self.fed)
|
|
@ -0,0 +1,278 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Safe Eyes is a utility to remind you to take break frequently
|
||||
# to protect your eyes from eye strain.
|
||||
|
||||
# Copyright (C) 2016 Gobinath
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os, gi, json, dbus, logging, operator, psutil, sys
|
||||
from threading import Timer
|
||||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from safeeyes.AboutDialog import AboutDialog
|
||||
from safeeyes.BreakScreen import BreakScreen
|
||||
from safeeyes.Notification import Notification
|
||||
from safeeyes.Plugins import Plugins
|
||||
from safeeyes.SafeEyesCore import SafeEyesCore
|
||||
from safeeyes.SettingsDialog import SettingsDialog
|
||||
from safeeyes.TrayIcon import TrayIcon
|
||||
from safeeyes import Utility
|
||||
|
||||
# Define necessary paths
|
||||
break_screen_glade = os.path.join(Utility.bin_directory, "glade/break_screen.glade")
|
||||
settings_dialog_glade = os.path.join(Utility.bin_directory, "glade/settings_dialog.glade")
|
||||
about_dialog_glade = os.path.join(Utility.bin_directory, "glade/about_dialog.glade")
|
||||
|
||||
|
||||
is_active = True
|
||||
SAFE_EYES_VERSION = "1.2.1"
|
||||
|
||||
"""
|
||||
Listen to tray icon Settings action and send the signal to Settings dialog.
|
||||
"""
|
||||
def show_settings():
|
||||
logging.info("Show Settings dialog")
|
||||
able_to_lock_screen = False
|
||||
if system_lock_command:
|
||||
able_to_lock_screen = True
|
||||
settings_dialog = SettingsDialog(config, language, Utility.read_lang_files(), able_to_lock_screen, save_settings, settings_dialog_glade)
|
||||
settings_dialog.show()
|
||||
|
||||
"""
|
||||
Listen to tray icon About action and send the signal to About dialog.
|
||||
"""
|
||||
def show_about():
|
||||
logging.info("Show About dialog")
|
||||
about_dialog = AboutDialog(about_dialog_glade, SAFE_EYES_VERSION, language)
|
||||
about_dialog.show()
|
||||
|
||||
"""
|
||||
Receive the signal from core and pass it to the Notification.
|
||||
"""
|
||||
def show_notification():
|
||||
if config['strict_break']:
|
||||
Utility.execute_main_thread(tray_icon.lock_menu)
|
||||
plugins.pre_notification(context)
|
||||
notification.show(config['pre_break_warning_time'])
|
||||
|
||||
"""
|
||||
Receive the break signal from core and pass it to the break screen.
|
||||
"""
|
||||
def show_alert(message, image_name):
|
||||
logging.info("Show the break screen")
|
||||
notification.close()
|
||||
plugins_data = plugins.pre_break(context)
|
||||
break_screen.show_message(message, Utility.get_resource_path(image_name), plugins_data)
|
||||
if config['strict_break'] and is_active:
|
||||
Utility.execute_main_thread(tray_icon.unlock_menu)
|
||||
|
||||
"""
|
||||
Receive the stop break signal from core and pass it to the break screen.
|
||||
"""
|
||||
def close_alert(audible_alert_on):
|
||||
logging.info("Close the break screen")
|
||||
if config['enable_screen_lock'] and context['break_type'] == 'long':
|
||||
# Lock the screen before closing the break screen
|
||||
Utility.lock_desktop(system_lock_command)
|
||||
break_screen.close()
|
||||
if audible_alert_on:
|
||||
Utility.play_notification()
|
||||
plugins.post_break(context)
|
||||
|
||||
|
||||
"""
|
||||
Listen to the tray menu quit action and stop the core, notification and the app itself.
|
||||
"""
|
||||
def on_quit():
|
||||
logging.info("Quit Safe Eyes")
|
||||
plugins.exit(context)
|
||||
core.stop()
|
||||
notification.quite();
|
||||
Gtk.main_quit()
|
||||
|
||||
"""
|
||||
If the system goes to sleep, Safe Eyes stop the core if it is already active.
|
||||
If it was active, Safe Eyes will become active after wake up.
|
||||
"""
|
||||
def handle_suspend_callback(sleeping):
|
||||
if sleeping:
|
||||
# Sleeping / suspending
|
||||
if is_active:
|
||||
core.stop()
|
||||
logging.info("Stopped Safe Eyes due to system suspend")
|
||||
else:
|
||||
# Resume from sleep
|
||||
if is_active:
|
||||
core.start()
|
||||
logging.info("Resumed Safe Eyes after system wakeup")
|
||||
|
||||
"""
|
||||
Setup system suspend listener.
|
||||
"""
|
||||
def handle_system_suspend():
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
bus = dbus.SystemBus()
|
||||
bus.add_signal_receiver(handle_suspend_callback, 'PrepareForSleep', 'org.freedesktop.login1.Manager', 'org.freedesktop.login1')
|
||||
|
||||
"""
|
||||
Listen to break screen Skip action and send the signal to core.
|
||||
"""
|
||||
def on_skipped():
|
||||
logging.info("User skipped the break")
|
||||
if config['enable_screen_lock'] and context['break_type'] == 'long' and context.get('count_down', 0) >= config['time_to_screen_lock']:
|
||||
# Lock the screen before closing the break screen
|
||||
Utility.lock_desktop(system_lock_command)
|
||||
core.skip_break()
|
||||
plugins.post_break(context)
|
||||
|
||||
"""
|
||||
Listen to break screen Postpone action and send the signal to core.
|
||||
"""
|
||||
def on_postponed():
|
||||
logging.info("User postponed the break")
|
||||
if config['enable_screen_lock'] and context['break_type'] == 'long' and context.get('count_down', 0) >= config['time_to_screen_lock']:
|
||||
# Lock the screen before closing the break screen
|
||||
Utility.lock_desktop(system_lock_command)
|
||||
core.postpone_break()
|
||||
|
||||
"""
|
||||
Listen to Settings dialog Save action and write to the config file.
|
||||
"""
|
||||
def save_settings(config):
|
||||
global language
|
||||
|
||||
logging.info("Saving settings to safeeyes.json")
|
||||
|
||||
# Stop the Safe Eyes core
|
||||
if is_active:
|
||||
core.stop()
|
||||
|
||||
# Write the configuration to file
|
||||
with open(Utility.config_file_path, 'w') as config_file:
|
||||
json.dump(config, config_file, indent=4, sort_keys=True)
|
||||
|
||||
# Reload the language translation
|
||||
language = Utility.load_language(config['language'])
|
||||
tray_icon.initialize(config)
|
||||
tray_icon.set_labels(language)
|
||||
|
||||
logging.info("Initialize SafeEyesCore with modified settings")
|
||||
|
||||
# Restart the core and intialize the components
|
||||
core.initialize(config, language)
|
||||
break_screen.initialize(config, language)
|
||||
if is_active:
|
||||
# 1 sec delay is required to give enough time for core to be stopped
|
||||
Timer(1.0, core.start).start()
|
||||
|
||||
"""
|
||||
Listen to tray icon enable action and send the signal to core.
|
||||
"""
|
||||
def enable_safeeyes():
|
||||
global is_active
|
||||
is_active = True
|
||||
core.start()
|
||||
|
||||
"""
|
||||
Listen to tray icon disable action and send the signal to core.
|
||||
"""
|
||||
def disable_safeeyes():
|
||||
global is_active
|
||||
is_active = False
|
||||
core.stop()
|
||||
|
||||
|
||||
def running():
|
||||
"""
|
||||
Check if SafeEyes is already running.
|
||||
"""
|
||||
process_count = 0
|
||||
for proc in psutil.process_iter():
|
||||
if not proc.cmdline: continue
|
||||
try:
|
||||
# Check if safeeyes is in process arguments
|
||||
if callable(proc.cmdline):
|
||||
# Latest psutil has cmdline function
|
||||
cmd_line = proc.cmdline()
|
||||
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):
|
||||
process_count += 1
|
||||
if process_count > 1:
|
||||
return True
|
||||
|
||||
# Ignore if process does not exist or does not have command line args
|
||||
except (IndexError, psutil.NoSuchProcess):
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Start the Safe Eyes.
|
||||
"""
|
||||
# Initialize the logging
|
||||
Utility.intialize_logging()
|
||||
|
||||
logging.info("Starting Safe Eyes")
|
||||
|
||||
if not running():
|
||||
|
||||
global break_screen
|
||||
global core
|
||||
global config
|
||||
global notification
|
||||
global tray_icon
|
||||
global language
|
||||
global context
|
||||
global plugins
|
||||
global system_lock_command
|
||||
|
||||
config = Utility.read_config()
|
||||
|
||||
context = {}
|
||||
language = Utility.load_language(config['language'])
|
||||
# Get the lock command only one time
|
||||
if config['lock_screen_command']:
|
||||
system_lock_command = config['lock_screen_command']
|
||||
else:
|
||||
system_lock_command = Utility.lock_screen_command()
|
||||
|
||||
|
||||
# Initialize the Safe Eyes Context
|
||||
context['version'] = SAFE_EYES_VERSION
|
||||
|
||||
tray_icon = TrayIcon(config, language, show_settings, show_about, enable_safeeyes, disable_safeeyes, on_quit)
|
||||
break_screen = BreakScreen(on_skipped, on_postponed, break_screen_glade, Utility.style_sheet_path)
|
||||
break_screen.initialize(config, language)
|
||||
notification = Notification(language)
|
||||
plugins = Plugins(config)
|
||||
core = SafeEyesCore(context, show_notification, show_alert, close_alert, break_screen.show_count_down, tray_icon.next_break_time)
|
||||
core.initialize(config, language)
|
||||
plugins.start(context) # Call the start method of all plugins
|
||||
core.start()
|
||||
|
||||
handle_system_suspend()
|
||||
|
||||
Gtk.main()
|
||||
else:
|
||||
logging.info('Another instance of safeeyes is already running')
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"meta_info": {
|
||||
"language_name": "Čeština",
|
||||
"language_name_en": "Czech"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "Safe Eyes protects your eyes from eye strain (asthenopia) by reminding you to take breaks while you're working long hours at the computer."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Zavřete oči",
|
||||
"short_break_roll_eyes": "Kroužete očima",
|
||||
"short_break_rotate_clockwise": "Kroužete očima ve směru hodinových ručiček",
|
||||
"short_break_rotate_counter_clockwise": "Kroužete očima proti směru hodinových ručiček",
|
||||
"short_break_blink": "Mrkejte",
|
||||
"short_break_focus_far_distance": "Soustřeďte se na bod v dálce",
|
||||
"short_break_drink_water": "Dejte si nějakou vodu",
|
||||
"long_break_walk": "Na chvíli se projděte",
|
||||
"long_break_lean_back": "Opřete se do židle a relaxujte"
|
||||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "Připravte se na přestávku za {} sekund",
|
||||
"disabled_until_restart": "Pozastaveno do restartu",
|
||||
"disabled_until_x": "Pozastaveno na {}",
|
||||
"next_break_at": "Příští přestávka v {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "O aplikaci",
|
||||
"allow_postpone": "Allow postponing the breaks",
|
||||
"audible_alert": "Zvukové upozornění na konec přestávky",
|
||||
"cancel": "Zrušit",
|
||||
"close": "Close",
|
||||
"disable": "Pozastavit Safe Eyes",
|
||||
"enable": "Zapnout Safe Eyes",
|
||||
"enable_screen_lock": "Po každé dlouhé přestávce uzamknout obrazovku",
|
||||
"for_x_hour": "Na {} hodinu",
|
||||
"for_x_hours": "Na {} hodiny",
|
||||
"for_x_minutes": "Na {} minut",
|
||||
"idle_time": "Pozastavit při nečinnosti delší než (minut)",
|
||||
"interval_between_two_breaks": "Interval mezi dvěma přestávkami",
|
||||
"language": "Jazyk",
|
||||
"license": "License",
|
||||
"long_break_duration": "Trvání dlouhé přestávky (v sekundách)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Počet krátkých přestávek mezi dvěma dlouhými",
|
||||
"postpone": "Odložit",
|
||||
"postpone_duration": "O kolik odložit (v minutách)",
|
||||
"quit": "Ukončit",
|
||||
"save": "Uložit",
|
||||
"settings": "Nastavení",
|
||||
"short_break_duration": "Trvání krátké přestávky (v sekundách)",
|
||||
"show_time_in_tray": "Show the next break time in system tray",
|
||||
"skip": "Přeskočit",
|
||||
"strict_break": "Povinná přestávka (skrýt tlačítko pro přeskočení)",
|
||||
"system_language": "Systémový jazyk",
|
||||
"time_to_prepare_for_break": "Čas k přípravě na přestávku (v sekundách)",
|
||||
"time_to_screen_lock": "O kolik nejvýše přeskočit, obcházející zámek obrazovky (v sekundách)",
|
||||
"until_restart": "Do restartu"
|
||||
}
|
||||
}
|
|
@ -3,44 +3,56 @@
|
|||
"language_name": "Deutsch",
|
||||
"language_name_en": "German"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "Safe Eyes schützt Ihre Augen vor Überlastung (Asthenopie), indem es Sie bei längerer Arbeit am Computer an regelmäßige Pausen erinnert."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Schließen Sie die Augen",
|
||||
"short_break_roll_eyes": "Rollen Sie mit den Augen",
|
||||
"short_break_rotate_clockwise": "Rotieren Sie mit den Augen im Uhrzeigersinn",
|
||||
"short_break_rotate_counter_clockwise": "Rotieren Sie mit den Augen gegen den Uhrzeigersinn",
|
||||
"short_break_blink": "Zwinkern Sie",
|
||||
"short_break_focus_far_distance": "Fokussieren Sie sich auf einen weit entfernten Punkt",
|
||||
"short_break_blink": "Blinzeln Sie",
|
||||
"short_break_focus_far_distance": "Fokussieren Sie sich auf einen Punkt in weiter Ferne",
|
||||
"short_break_drink_water": "Trinken Sie ein wenig Wasser",
|
||||
"long_break_walk": "Gehen Sie ein wenig",
|
||||
"long_break_lean_back": "Lehnen Sie sich zurück und entspannen Sie sich"
|
||||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "Nächste Pause in {} Sekunden",
|
||||
"disabled_until_restart": "Disabled until restart",
|
||||
"disabled_until_x": "Disabled until {}",
|
||||
"disabled_until_restart": "Deaktiviert bis zum Neustart",
|
||||
"disabled_until_x": "Deaktiviert bis {}",
|
||||
"next_break_at": "Nächste Pause um {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"skip": "Überspringen",
|
||||
"short_break_duration": "Kleine Pause Intervall (in Sekunden)",
|
||||
"long_break_duration": "Lange Pause Intervall (in Sekunden)",
|
||||
"interval_between_two_breaks": "Intervall zwischen zwei Pausen",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Anzahl von kleinen Pausen zwischen zwei langen Pausen",
|
||||
"time_to_prepare_for_break": "Zeit zur Vorbereitung für die Pause (in Sekunden)",
|
||||
"idle_time": "Minimum idle time to pause (in minutes)",
|
||||
"strict_break": "Strikte Pause (Überspringen nicht möglich)",
|
||||
"about": "Über",
|
||||
"allow_postpone": "Erlaube Verschieben von Pausen",
|
||||
"audible_alert": "Akustisches Signal am Ende der Pause",
|
||||
"cancel": "Abbrechen",
|
||||
"close": "Schließen",
|
||||
"disable": "Safe Eyes deaktivieren",
|
||||
"enable": "Safe Eyes aktivieren",
|
||||
"enable_screen_lock": "Sperrt den Bildschirm nach einer langen Pause",
|
||||
"for_x_hour": "Für {} Stunde",
|
||||
"for_x_hours": "Für {} Stunden",
|
||||
"for_x_minutes": "Für {} Minuten",
|
||||
"idle_time": "Minimale Leerlaufzeit zum Pausieren (in Minuten)",
|
||||
"interval_between_two_breaks": "Intervall zwischen zwei Pausen",
|
||||
"language": "Sprache",
|
||||
"enable": "Safe Eyes Aktivieren",
|
||||
"disable": "Disable Safe Eyes",
|
||||
"for_x_minutes": "For {} Minutes",
|
||||
"for_x_hour": "For {} Hour",
|
||||
"for_x_hours": "For {} Hours",
|
||||
"until_restart": "Until restart",
|
||||
"settings": "Einstellungen",
|
||||
"about": "About",
|
||||
"license": "Lizenz",
|
||||
"long_break_duration": "Lange-Pause-Intervall (in Sekunden)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Anzahl von kleinen Pausen zwischen zwei langen Pausen",
|
||||
"postpone": "Verzögernn",
|
||||
"postpone_duration": "Verzögerungsdauer (in Minuten)",
|
||||
"quit": "Schließen",
|
||||
"save": "Speichern",
|
||||
"cancel": "Abbrechen"
|
||||
"settings": "Einstellungen",
|
||||
"short_break_duration": "Kleine-Pause-Intervall (in Sekunden)",
|
||||
"show_time_in_tray": "Show the next break time in system tray",
|
||||
"skip": "Überspringen",
|
||||
"strict_break": "Strikte Pause (Überspringen nicht möglich)",
|
||||
"system_language": "Systemsprache",
|
||||
"time_to_prepare_for_break": "Zeit zur Vorbereitung für die Pause (in Sekunden)",
|
||||
"time_to_screen_lock": "Maximale Zeit zum Überspringen der Bildschirmsperre (in Sekunden)",
|
||||
"until_restart": "Bis zum Neustart"
|
||||
}
|
||||
}
|
|
@ -3,9 +3,12 @@
|
|||
"language_name": "English",
|
||||
"language_name_en": "English"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "Safe Eyes protects your eyes from eye strain (asthenopia) by reminding you to take breaks while you're working long hours at the computer."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Tightly close your eyes",
|
||||
"short_break_roll_eyes": "Roll your eyes",
|
||||
"short_break_roll_eyes": "Roll your eyes a few times to each side",
|
||||
"short_break_rotate_clockwise": "Rotate your eyes in clockwise direction",
|
||||
"short_break_rotate_counter_clockwise": "Rotate your eyes in counterclockwise direction",
|
||||
"short_break_blink": "Blink your eyes",
|
||||
|
@ -21,26 +24,35 @@
|
|||
"next_break_at": "Next break at {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"skip": "Skip",
|
||||
"short_break_duration": "Short break duration (in seconds)",
|
||||
"long_break_duration": "Long break duration (in seconds)",
|
||||
"interval_between_two_breaks": "Interval between two breaks (in minutes)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Number of short breaks between two long breaks",
|
||||
"time_to_prepare_for_break": "Time to prepare for break (in seconds)",
|
||||
"idle_time": "Minimum idle time to pause (in minutes)",
|
||||
"strict_break": "Strict break (Hide skip button)",
|
||||
"about": "About",
|
||||
"allow_postpone": "Allow postponing the breaks",
|
||||
"audible_alert": "Audible alert at the end of break",
|
||||
"language": "Language",
|
||||
"enable": "Enable Safe Eyes",
|
||||
"cancel": "Cancel",
|
||||
"close": "Close",
|
||||
"disable": "Disable Safe Eyes",
|
||||
"for_x_minutes": "For {} Minutes",
|
||||
"enable": "Enable Safe Eyes",
|
||||
"enable_screen_lock": "Lock the screen after every long break",
|
||||
"for_x_hour": "For {} Hour",
|
||||
"for_x_hours": "For {} Hours",
|
||||
"until_restart": "Until restart",
|
||||
"settings": "Settings",
|
||||
"about": "About",
|
||||
"for_x_minutes": "For {} Minutes",
|
||||
"idle_time": "Minimum idle time to pause (in minutes)",
|
||||
"interval_between_two_breaks": "Interval between two breaks (in minutes)",
|
||||
"language": "Language",
|
||||
"license": "License",
|
||||
"long_break_duration": "Long break duration (in seconds)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Number of short breaks between two long breaks",
|
||||
"postpone": "Postpone",
|
||||
"postpone_duration": "Postpone duration (in minutes)",
|
||||
"quit": "Quit",
|
||||
"save": "Save",
|
||||
"cancel": "Cancel"
|
||||
"settings": "Settings",
|
||||
"short_break_duration": "Short break duration (in seconds)",
|
||||
"show_time_in_tray": "Show the next break time in system tray",
|
||||
"skip": "Skip",
|
||||
"strict_break": "Strict break (Hide skip button)",
|
||||
"system_language": "System Language",
|
||||
"time_to_prepare_for_break": "Time to prepare for break (in seconds)",
|
||||
"time_to_screen_lock": "Maximum time to skip, bypassing the lock screen (in seconds)",
|
||||
"until_restart": "Until restart"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"meta_info": {
|
||||
"language_name": "Español",
|
||||
"language_name_en": "Spanish"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "Safe Eyes protege tus ojos de la fatiga visual (astenopía), recordándote tomar descansos cuando estás trabajando muchas horas en el ordenador."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Cierra fuertemente tus ojos",
|
||||
"short_break_roll_eyes": "Pon los ojos en blanco hacia cada lado",
|
||||
"short_break_rotate_clockwise": "Mueve tus ojos en círculos en sentido horario",
|
||||
"short_break_rotate_counter_clockwise": "Mueve tus ojos en círculos en sentido antihorario",
|
||||
"short_break_blink": "Parpadea tus ojos",
|
||||
"short_break_focus_far_distance": "Enfoca un punto lejano",
|
||||
"short_break_drink_water": "Bebe un poco de agua",
|
||||
"long_break_walk": "Vete a andar un rato",
|
||||
"long_break_lean_back": "Reclínate sobre tu silla y relájate"
|
||||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "Listo para una pausa en {} segundos",
|
||||
"disabled_until_restart": "Deshabilitado hasta reinicio",
|
||||
"disabled_until_x": "Deshabilitado hasta {}",
|
||||
"next_break_at": "Próxima pausa a las {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "Acerca de",
|
||||
"allow_postpone": "Permitir posponer las pausas",
|
||||
"audible_alert": "Alerta sonora al final de cada pausa",
|
||||
"cancel": "Cancelar",
|
||||
"close": "Close",
|
||||
"disable": "Desactivar Safe Eyes",
|
||||
"enable": "Activar Safe Eyes",
|
||||
"enable_screen_lock": "Bloquear la pantalla despues de cada pausa larga",
|
||||
"for_x_hour": "Durante {} hora",
|
||||
"for_x_hours": "Durante {} horas",
|
||||
"for_x_minutes": "Durante {} minutos",
|
||||
"idle_time": "Tiempo mínimo (en minutos) sin usar el ordenador para que Safe Eyes se desactive automáticamente",
|
||||
"interval_between_two_breaks": "Tiempo entre dos pausas",
|
||||
"language": "Idioma",
|
||||
"license": "Licencia",
|
||||
"long_break_duration": "Duración de una pausa larga (en segundos)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Número de pausas cortas entre dos pausas largas",
|
||||
"postpone": "Posponer",
|
||||
"postpone_duration": "Duración de cada posposición (en minutos)",
|
||||
"quit": "Salir",
|
||||
"save": "Guardar",
|
||||
"settings": "Preferencias",
|
||||
"short_break_duration": "Duración de una pausa corta (en segundos)",
|
||||
"show_time_in_tray": "Show the next break time in system tray",
|
||||
"skip": "Saltar",
|
||||
"strict_break": "Pausa estricta (No hay botón Saltar)",
|
||||
"system_language": "Idioma del sistema",
|
||||
"time_to_prepare_for_break": "Tiempo para prepararse para una pausa (en segundos)",
|
||||
"time_to_screen_lock": "Máximo tiempo para saltarse una pausa, evitando la pantalla de bloqueo (en segundos)",
|
||||
"until_restart": "Hasta reinicio"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"meta_info": {
|
||||
"language_name": "Français",
|
||||
"language_name_en": "French"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "Safe Eyes protège vos yeux contre la fatigue de l'œil (asthénopie) en vous rappelant de prendre des pauses lors de vos longues heures de travail sur un ordinateur."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Fermez bien vos yeux",
|
||||
"short_break_roll_eyes": "Regardez de droite à gauche en alternance",
|
||||
"short_break_rotate_clockwise": "Faites rouler vos yeux dans le sens horaire",
|
||||
"short_break_rotate_counter_clockwise": "Faites rouler vos yeux dans le sens antihoraire",
|
||||
"short_break_blink": "Clignez des yeux",
|
||||
"short_break_focus_far_distance": "Regardez un point au loin",
|
||||
"short_break_drink_water": "Buvez de l'eau",
|
||||
"long_break_walk": "Marchez un peu",
|
||||
"long_break_lean_back": "Adossez-vous à votre siège et relaxez-vous"
|
||||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "Préparez-vous à une pause dans {} secondes",
|
||||
"disabled_until_restart": "Désactivé jusqu'au redémarrage",
|
||||
"disabled_until_x": "Désactivé jusqu'à {}",
|
||||
"next_break_at": "Prochaine pause à {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "À propos",
|
||||
"allow_postpone": "Permettre le report des pauses",
|
||||
"audible_alert": "Alerte sonore en fin d'une pause",
|
||||
"cancel": "Annuler",
|
||||
"close": "Fermer",
|
||||
"disable": "Désactiver Safe Eyes",
|
||||
"enable": "Activer Safe Eyes",
|
||||
"enable_screen_lock": "Verrouiller l'écran après chaque pause longue",
|
||||
"for_x_hour": "Pendant {} heure",
|
||||
"for_x_hours": "Pendant {} heures",
|
||||
"for_x_minutes": "Pendant {} minutes",
|
||||
"idle_time": "Durée minimale d'une pause (en minutes)",
|
||||
"interval_between_two_breaks": "Intervalle entre deux pauses (en minutes)",
|
||||
"language": "Langue",
|
||||
"license": "Licence",
|
||||
"long_break_duration": "Durée d'une pause longue (en secondes)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Nombre de pauses courtes entre deux pauses longues",
|
||||
"postpone": "Reporter",
|
||||
"postpone_duration": "Durée de report (en minutes)",
|
||||
"quit": "Quitter",
|
||||
"save": "Enregistrer",
|
||||
"settings": "Paramètres",
|
||||
"short_break_duration": "Durée d'une pause courte (en secondes)",
|
||||
"show_time_in_tray": "Show the next break time in system tray",
|
||||
"skip": "Ignorer",
|
||||
"strict_break": "Pause stricte (cacher le bouton Ignorer)",
|
||||
"system_language": "Langue du système ",
|
||||
"time_to_prepare_for_break": "Temps de préparation à une pause (en secondes)",
|
||||
"time_to_screen_lock": "Temps maximal pour ignorer, en passant outre le verrouillage (en secondes)",
|
||||
"until_restart": "Jusqu'au redémarrage"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"meta_info": {
|
||||
"language_name": "ქართული",
|
||||
"language_name_en": "Georgian"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "Safe Eyes protects your eyes from eye strain (asthenopia) by reminding you to take breaks while you're working long hours at the computer."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "ძლიერად დახუჭე თვალები",
|
||||
"short_break_roll_eyes": "ფართოდ გაახილეთ თვალები",
|
||||
"short_break_rotate_clockwise": "დაატრიალეთ თვალები საათის მოძრაობის მიმართულებით",
|
||||
"short_break_rotate_counter_clockwise": "დაატრიალეთ თვალები საათის მოძრაობის საწინააღმდეგოდ",
|
||||
"short_break_blink": "თვალები დაახამხამეთ",
|
||||
"short_break_focus_far_distance": "ფოკუსირება გააკეთეთ შორ ობიექტზე",
|
||||
"short_break_drink_water": "დალიეთ წყალი",
|
||||
"long_break_walk": "ცოტა გაიარეთ",
|
||||
"long_break_lean_back": "სავარძლის საზურგეზე გადაწექით და ცოტა დაისვენეთ"
|
||||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "მოემზადეთ შესვენებისთვის {} წამში",
|
||||
"disabled_until_restart": "გავაუქმოთ შემდეგ რესტარტამდე",
|
||||
"disabled_until_x": "გაუქმებულია {} მდე",
|
||||
"next_break_at": "შემდეგი შესვენება {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "პროგრამის შესახებ",
|
||||
"allow_postpone": "Allow postponing the breaks",
|
||||
"audible_alert": "ხმოვანი შეტყობინება შესვენების დამთავრებისას",
|
||||
"cancel": "უარყოფა",
|
||||
"close": "Close",
|
||||
"disable": "Safe Eyes გამორთვა",
|
||||
"enable": "Safe Eyes ჩართვა",
|
||||
"enable_screen_lock": "Lock the screen after every long break",
|
||||
"for_x_hour": "{} საათით",
|
||||
"for_x_hours": "{} საათით",
|
||||
"for_x_minutes": "{} საათით",
|
||||
"idle_time": "მინიმუმ idle დრო პაუზის ასაღებად (წუთებში)",
|
||||
"interval_between_two_breaks": "ინტერვალი შესვენებებს შორის",
|
||||
"language": "ენა",
|
||||
"license": "License",
|
||||
"long_break_duration": "დიდი შესვენების ხანგრძლივობა (წამებში)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "მცირე შესვენებების რაოდენობა ორ დიდ შესვენებას შორის",
|
||||
"postpone": "Postpone",
|
||||
"postpone_duration": "Postpone duration (in minutes)",
|
||||
"quit": "გასვლა",
|
||||
"save": "დამახსოვრება",
|
||||
"settings": "პარამეტრები",
|
||||
"short_break_duration": "მცირე შესვენების ხანგრძლივობა (წამებში)",
|
||||
"show_time_in_tray": "Show the next break time in system tray",
|
||||
"skip": "გამოტოვება",
|
||||
"strict_break": "აუცილებელი შესვენება (დავმალოთ ღილაკი 'გამოტოვება')",
|
||||
"system_language": "System Language",
|
||||
"time_to_prepare_for_break": "შესვენებისთვის მოსამზადებელი დრო (წამებში)",
|
||||
"time_to_screen_lock": "Maximum time to skip, bypassing the lock screen (in seconds)",
|
||||
"until_restart": "შემდეგ რესტარტამდე"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"meta_info": {
|
||||
"language_name": "हिंदी",
|
||||
"language_name_en": "Hindi"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "Safe Eyes protects your eyes from eye strain (asthenopia) by reminding you to take breaks while you're working long hours at the computer."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "आँखों को ज़ोर से बंद करें",
|
||||
"short_break_roll_eyes": "आँखों को घुमाएं",
|
||||
"short_break_rotate_clockwise": "आँखों को घड़ी की दिशा में घुमाएं",
|
||||
"short_break_rotate_counter_clockwise": "आँखों को घड़ी से उल्टी दिशा में घुमाएं",
|
||||
"short_break_blink": "ऑंखें झपकायें",
|
||||
"short_break_focus_far_distance": "आँखों को दूर किसी वस्तु पे टिकाएं",
|
||||
"short_break_drink_water": "पानी पियें",
|
||||
"long_break_walk": "थोड़ा टहलें",
|
||||
"long_break_lean_back": "पीछे हट कर आराम करें"
|
||||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "{} पलों में आराम",
|
||||
"disabled_until_restart": "अगले आरंभ तक बंद",
|
||||
"disabled_until_x": "{} तक बंद",
|
||||
"next_break_at": "अगला आराम {} में"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "हमारे बारे में",
|
||||
"allow_postpone": "Allow postponing the breaks",
|
||||
"audible_alert": "आराम के ख़तम के ख़तम होने पर आवाज़",
|
||||
"cancel": "रखना नहीं",
|
||||
"close": "Close",
|
||||
"disable": "सेफ आईज बंद",
|
||||
"enable": "सेफ आईज शुरू",
|
||||
"enable_screen_lock": "Lock the screen after every long break",
|
||||
"for_x_hour": "{} घंटे के लिए",
|
||||
"for_x_hours": "{} घंटों के लिए",
|
||||
"for_x_minutes": "{} मिन्टों के लिए",
|
||||
"idle_time": "बीच रोकने पर कितने मिन्टों का अंतर",
|
||||
"interval_between_two_breaks": "दो आराम अवधियों में अंतर (मिन्टों में)",
|
||||
"language": "भाषा",
|
||||
"license": "License",
|
||||
"long_break_duration": "लंबे आराम की अवधि (पलों में)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "दो बड़े आराम के बीच कितने छोटे आराम",
|
||||
"postpone": "Postpone",
|
||||
"postpone_duration": "Postpone duration (in minutes)",
|
||||
"quit": "बंद",
|
||||
"save": "रखें",
|
||||
"settings": "सेटिंग्स",
|
||||
"short_break_duration": "छोटे आराम की अवधि (पलों में)",
|
||||
"show_time_in_tray": "Show the next break time in system tray",
|
||||
"skip": "अभी नहीं",
|
||||
"strict_break": "जरूरी आराम (रोक नहीं सकते)",
|
||||
"system_language": "System Language",
|
||||
"time_to_prepare_for_break": "कितने पलों पहले बताएं",
|
||||
"time_to_screen_lock": "Maximum time to skip, bypassing the lock screen (in seconds)",
|
||||
"until_restart": "अगले आरम्भ तक"
|
||||
}
|
||||
}
|
|
@ -3,6 +3,9 @@
|
|||
"language_name": "Magyar",
|
||||
"language_name_en": "Hungarian"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "Safe Eyes protects your eyes from eye strain (asthenopia) by reminding you to take breaks while you're working long hours at the computer."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Csukja be hosszabb időre a szemét!",
|
||||
"short_break_roll_eyes": "Roll your eyes",
|
||||
|
@ -21,26 +24,35 @@
|
|||
"next_break_at": "A következő szünet {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"skip": "Átugrás",
|
||||
"short_break_duration": "Rövid szünet hossza (másodpercekben)",
|
||||
"long_break_duration": "Hosszú szünet hossza (másodpercekben)",
|
||||
"interval_between_two_breaks": "Két szünet közötti idő",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Hány rövid szünet legyen a hosszabbak között?",
|
||||
"time_to_prepare_for_break": "Szünet előtti figyelmeztetés (másodperc)",
|
||||
"idle_time": "Minimum idle time to pause (in minutes)",
|
||||
"strict_break": "Kötelezők a szünetek? (nincs átugrás gomb)",
|
||||
"about": "Ról ről",
|
||||
"allow_postpone": "Allow postponing the breaks",
|
||||
"audible_alert": "Audible alert at the end of break",
|
||||
"language": "Nyelv",
|
||||
"enable": "Safe Eyes Bekapcsolása",
|
||||
"cancel": "Mégse",
|
||||
"close": "Close",
|
||||
"disable": "Disable Safe Eyes",
|
||||
"for_x_minutes": "For {} Minutes",
|
||||
"enable": "Safe Eyes Bekapcsolása",
|
||||
"enable_screen_lock": "Lock the screen after every long break",
|
||||
"for_x_hour": "For {} Hour",
|
||||
"for_x_hours": "For {} Hours",
|
||||
"until_restart": "Until restart",
|
||||
"settings": "Beállítások",
|
||||
"about": "Ról ről",
|
||||
"for_x_minutes": "For {} Minutes",
|
||||
"idle_time": "Minimum idle time to pause (in minutes)",
|
||||
"interval_between_two_breaks": "Két szünet közötti idő",
|
||||
"language": "Nyelv",
|
||||
"license": "License",
|
||||
"long_break_duration": "Hosszú szünet hossza (másodpercekben)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Hány rövid szünet legyen a hosszabbak között?",
|
||||
"postpone": "Postpone",
|
||||
"postpone_duration": "Postpone duration (in minutes)",
|
||||
"quit": "Kilépés",
|
||||
"save": "Mentés",
|
||||
"cancel": "Mégse"
|
||||
"settings": "Beállítások",
|
||||
"short_break_duration": "Rövid szünet hossza (másodpercekben)",
|
||||
"show_time_in_tray": "Show the next break time in system tray",
|
||||
"skip": "Átugrás",
|
||||
"strict_break": "Kötelezők a szünetek? (nincs átugrás gomb)",
|
||||
"system_language": "System Language",
|
||||
"time_to_prepare_for_break": "Szünet előtti figyelmeztetés (másodperc)",
|
||||
"time_to_screen_lock": "Maximum time to skip, bypassing the lock screen (in seconds)",
|
||||
"until_restart": "Until restart"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"meta_info": {
|
||||
"language_name": "Bahasa Indonesia",
|
||||
"language_name_en": "Indonesian"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "Safe Eyes protects your eyes from eye strain (asthenopia) by reminding you to take breaks while you're working long hours at the computer."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Tutup rapat mata Anda",
|
||||
"short_break_roll_eyes": "Putar mata Anda",
|
||||
"short_break_rotate_clockwise": "Putar mata Anda searah jarum jam",
|
||||
"short_break_rotate_counter_clockwise": "Putar mata Anda melawan arah jarum jam",
|
||||
"short_break_blink": "Kedipkan mata Anda",
|
||||
"short_break_focus_far_distance": "Fokus ke titik di kejauhan",
|
||||
"short_break_drink_water": "Silakan meminum air",
|
||||
"long_break_walk": "Silakan berjalan sebentar",
|
||||
"long_break_lean_back": "Silakan bersandar ke kursi dan bersantai"
|
||||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "Bersiap beristirahat dalam {} detik",
|
||||
"disabled_until_restart": "Dimatikan hingga dijalankan ulang",
|
||||
"disabled_until_x": "Dimatikan hingga {}",
|
||||
"next_break_at": "Istirahat selanjutnya pada {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "Tentang",
|
||||
"allow_postpone": "Allow postponing the breaks",
|
||||
"audible_alert": "Peringatan bersuara saat istirahat berakhir",
|
||||
"cancel": "Batal",
|
||||
"close": "Close",
|
||||
"disable": "Matikan Safe Eyes",
|
||||
"enable": "Hidupkan Safe Eyes",
|
||||
"enable_screen_lock": "Lock the screen after every long break",
|
||||
"for_x_hour": "Selama {} Jam",
|
||||
"for_x_hours": "Selama {} Jam",
|
||||
"for_x_minutes": "Selama {} Menit",
|
||||
"idle_time": "Waktu diam jeda minimal (dalam menit)",
|
||||
"interval_between_two_breaks": "Jarak antara dua istirahat (dalam menit)",
|
||||
"language": "Bahasa",
|
||||
"license": "License",
|
||||
"long_break_duration": "Durasi istirahat panjang (dalam detik)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Jumlah istirahat singkat di antara dua istirahat panjang",
|
||||
"postpone": "Postpone",
|
||||
"postpone_duration": "Postpone duration (in minutes)",
|
||||
"quit": "Keluar",
|
||||
"save": "Simpan",
|
||||
"settings": "Pengaturan",
|
||||
"short_break_duration": "Durasi istirahat singkat (dalam detik)",
|
||||
"show_time_in_tray": "Show the next break time in system tray",
|
||||
"skip": "Lewati",
|
||||
"strict_break": "Paksa istirahat (sembunyikan tombol Lewati)",
|
||||
"system_language": "System Language",
|
||||
"time_to_prepare_for_break": "Waktu persiapan istirahat (dalam detik)",
|
||||
"time_to_screen_lock": "Maximum time to skip, bypassing the lock screen (in seconds)",
|
||||
"until_restart": "Hingga dijalankan ulang"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"meta_info": {
|
||||
"language_name": "Македонски",
|
||||
"language_name_en": "Macedonian"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "Safe Eyes ве штити од замор на очите (asthenophobia) така што ве потсетува да правите паузи додека работите долги часови на компјутер."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Цврсто затворете ги очите",
|
||||
"short_break_roll_eyes": "Превртете ги очите неколку пати на сите страни",
|
||||
"short_break_rotate_clockwise": "Превртете ги очите во насока на стрелките на часовникот",
|
||||
"short_break_rotate_counter_clockwise": "Превртете ги очите обратно од стрелките на часовникот",
|
||||
"short_break_blink": "Трепнете неколкупати",
|
||||
"short_break_focus_far_distance": "Фокусирајте се на точка далеку од вас",
|
||||
"short_break_drink_water": "Напијте се малку вода",
|
||||
"long_break_walk": "Пешачете малку",
|
||||
"long_break_lean_back": "Наслонете се на столот и одморете"
|
||||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "Пауза за {} секунди",
|
||||
"disabled_until_restart": "Оневозможено до рестарт",
|
||||
"disabled_until_x": "Оневозможено до {}",
|
||||
"next_break_at": "Следна пауза во {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "За",
|
||||
"allow_postpone": "Овозможи одложување на паузи",
|
||||
"audible_alert": "Звучен оглас на крај на пауза",
|
||||
"cancel": "Откажи",
|
||||
"close": "Затвори",
|
||||
"disable": "Оневозможете го Safe Eyes",
|
||||
"enable": "Овозможете го Safe Eyes",
|
||||
"enable_screen_lock": "Заклучување на екранот по секоја долга пауза",
|
||||
"for_x_hour": "За {} час",
|
||||
"for_x_hours": "За {} часа",
|
||||
"for_x_minutes": "За {} минути",
|
||||
"idle_time": "Минимум време на мирување до пауза (Во минути)",
|
||||
"interval_between_two_breaks": "Време помеѓу две паузи (Во минути)",
|
||||
"language": "Јазик",
|
||||
"license": "Лиценца",
|
||||
"long_break_duration": "Траење на долгите паузи (Во секунди)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Број на кратки паузи помеѓу две долги",
|
||||
"postpone": "Одложи",
|
||||
"postpone_duration": "Траење на одложувањето (Во секунди)",
|
||||
"quit": "Исклучи",
|
||||
"save": "Зачувај",
|
||||
"settings": "Подесувања",
|
||||
"short_break_duration": "Траење на кратките паузи (Во секунди)",
|
||||
"show_time_in_tray": "Show the next break time in system tray",
|
||||
"skip": "Skip",
|
||||
"strict_break": "Строга пауза (Сокриј го „Прескокни“ копчето)",
|
||||
"system_language": "Системски Јазик",
|
||||
"time_to_prepare_for_break": "Време за подготовка за пауза (Во секунди)",
|
||||
"time_to_screen_lock": "Максимално време од прескокнување на паузата без да се заклучи екранот (Во секунди)",
|
||||
"until_restart": "До рестарт"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"meta_info": {
|
||||
"language_name": "Polski",
|
||||
"language_name_en": "Polish"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "Safe Eyes chroni Twoje oczy przed przemęczeniem i astenopią przypominając Ci o robieniu przerw podczas długotrwałej pracy przy komputerze."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Mocno zaciśnij powieki",
|
||||
"short_break_roll_eyes": "Ruszaj oczami z boku na bok",
|
||||
"short_break_rotate_clockwise": "Wykonuj krążenia oczami zgodnie z ruchem wskazówek zegara",
|
||||
"short_break_rotate_counter_clockwise": "Wykonuj krążenia oczami przeciwnie do ruchu wskazówek zegara",
|
||||
"short_break_blink": "Pomrugaj oczami",
|
||||
"short_break_focus_far_distance": "Skup wzrok na odległym punkcie",
|
||||
"short_break_drink_water": "Napij się wody",
|
||||
"long_break_walk": "Przejdź się chwilę",
|
||||
"long_break_lean_back": "Oprzyj się wygodnie na krześle i zrelaksuj"
|
||||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "Przygotuj się! Przerwa za {} sekund(y).",
|
||||
"disabled_until_restart": "Wyłączony do ponownego uruchomienia",
|
||||
"disabled_until_x": "Wyłączony do {}",
|
||||
"next_break_at": "Następna przerwa o {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "O programie",
|
||||
"allow_postpone": "Pozwól na odroczenie przerwy",
|
||||
"audible_alert": "Powiadomienie dźwiękowe na koniec przerwy",
|
||||
"cancel": "Anuluj",
|
||||
"close": "Zamknij",
|
||||
"disable": "Zatrzymaj Safe Eyes",
|
||||
"enable": "Uruchom Safe Eyes",
|
||||
"enable_screen_lock": "Zablokuj ekran po każdej długiej przerwie",
|
||||
"for_x_hour": "Na {} godzinę",
|
||||
"for_x_hours": "Na {} godzin(y)",
|
||||
"for_x_minutes": "Na {} minut(y)",
|
||||
"idle_time": "Najkrótszy czas bez aktywności do wstrzymania odliczania (w minutach)",
|
||||
"interval_between_two_breaks": "Czas między kolejnymi przerwami (w minutach)",
|
||||
"language": "Język",
|
||||
"license": "Licencja użytkownika",
|
||||
"long_break_duration": "Czas trwania długiej przerwy (w sekundach)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Liczba krótkich przerw między długimi przerwami",
|
||||
"postpone": "Odrocz",
|
||||
"postpone_duration": "Odroczenie przerwy (w minutach)",
|
||||
"quit": "Wyjdź",
|
||||
"save": "Zapisz",
|
||||
"settings": "Ustawienia",
|
||||
"short_break_duration": "Czas trwania krótkiej przerwy (w sekundach)",
|
||||
"show_time_in_tray": "Show the next break time in system tray",
|
||||
"skip": "Pomiń",
|
||||
"strict_break": "Bezwzględna przerwa (ukryj przycisk pominięcia)",
|
||||
"system_language": "Język systemu",
|
||||
"time_to_prepare_for_break": "Czas na przygotowanie się do przerwy (w sekundach)",
|
||||
"time_to_screen_lock": "Czas na pominięcie przerwy bez blokowania ekranu (w sekundach)",
|
||||
"until_restart": "Do ponownego uruchomienia"
|
||||
}
|
||||
}
|
|
@ -3,6 +3,9 @@
|
|||
"language_name": "Português",
|
||||
"language_name_en": "Portuguese"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "Safe Eyes protege seus olhos da fadiga ocular (astenopia) ao lembrá-lo de dar uma pausa enquando você trabalha por horas no computador."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Feche bem os olhos",
|
||||
"short_break_roll_eyes": "Mexa os olhos",
|
||||
|
@ -16,31 +19,40 @@
|
|||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "Pronto para uma pausa em {} segundos",
|
||||
"disabled_until_restart": "Disabled until restart",
|
||||
"disabled_until_x": "Disabled until {}",
|
||||
"disabled_until_restart": "Desativado até reiniciar",
|
||||
"disabled_until_x": "Desativado até {}",
|
||||
"next_break_at": "Próxima pausa em {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"skip": "Pular",
|
||||
"short_break_duration": "Duração de uma pausa curta (em segundos)",
|
||||
"long_break_duration": "Duração de uma pausa longa (em segundos)",
|
||||
"interval_between_two_breaks": "Intervalo entre duas pausas",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Número de pausas curtas entre duas pausas longas",
|
||||
"time_to_prepare_for_break": "Tempo para se preparar para a pausa (em segundos)",
|
||||
"idle_time": "Tempo mínimo de inatividade para pausar (em minutos)",
|
||||
"strict_break": "Pausa rigorosa (Esconder botão pular)",
|
||||
"audible_alert": "Alerta sonoro no fim da pausa",
|
||||
"language": "Idioma",
|
||||
"enable": "Habilitar Safe Eyes",
|
||||
"disable": "Disable Safe Eyes",
|
||||
"for_x_minutes": "For {} Minutes",
|
||||
"for_x_hour": "For {} Hour",
|
||||
"for_x_hours": "For {} Hours",
|
||||
"until_restart": "Until restart",
|
||||
"settings": "Configuração",
|
||||
"about": "Sobre",
|
||||
"allow_postpone": "Permite o adiamento da pausa",
|
||||
"audible_alert": "Alerta sonoro no fim da pausa",
|
||||
"cancel": "Cancelar",
|
||||
"close": "Fechar",
|
||||
"disable": "Desativar Safe Eyes",
|
||||
"enable": "Habilitar Safe Eyes",
|
||||
"enable_screen_lock": "Bloqueie a tela após cada pausa longa",
|
||||
"for_x_hour": "Por {} Hora",
|
||||
"for_x_hours": "Por {} Horas",
|
||||
"for_x_minutes": "Por {} Minutos",
|
||||
"idle_time": "Tempo mínimo de inatividade para pausar (em minutos)",
|
||||
"interval_between_two_breaks": "Intervalo entre duas pausas",
|
||||
"language": "Idioma",
|
||||
"license": "Licença",
|
||||
"long_break_duration": "Duração de uma pausa longa (em segundos)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Número de pausas curtas entre duas pausas longas",
|
||||
"postpone": "Adiar",
|
||||
"postpone_duration": "Duração do adiamento (em minutos)",
|
||||
"quit": "Sair",
|
||||
"save": "Salvar",
|
||||
"cancel": "Cancelar"
|
||||
"settings": "Configuração",
|
||||
"short_break_duration": "Duração de uma pausa curta (em segundos)",
|
||||
"show_time_in_tray": "Show the next break time in system tray",
|
||||
"skip": "Pular",
|
||||
"strict_break": "Pausa rigorosa (Esconder botão pular)",
|
||||
"system_language": "Linguagem do Sistema",
|
||||
"time_to_prepare_for_break": "Tempo para se preparar para a pausa (em segundos)",
|
||||
"time_to_screen_lock": "Tempo máximo para saltar, ignorando a tela de bloqueio (em segundos)",
|
||||
"until_restart": "Até reiniciar"
|
||||
}
|
||||
}
|
|
@ -3,6 +3,9 @@
|
|||
"language_name": "Русский",
|
||||
"language_name_en": "Russian"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "Safe Eyes protects your eyes from eye strain (asthenopia) by reminding you to take breaks while you're working long hours at the computer."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Сильно зажмурьтесь",
|
||||
"short_break_roll_eyes": "Закатите глаза",
|
||||
|
@ -16,31 +19,40 @@
|
|||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "Приготовьтесь к перерыву через {} секунд",
|
||||
"disabled_until_restart": "Disabled until restart",
|
||||
"disabled_until_x": "Disabled until {}",
|
||||
"disabled_until_restart": "Отключено до перезагрузки",
|
||||
"disabled_until_x": "Отключено до {}",
|
||||
"next_break_at": "Следующий перерыв в {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"skip": "Пропустить",
|
||||
"short_break_duration": "Продолжительность короткого перерыва (в секундах)",
|
||||
"long_break_duration": "Продолжительность длинного перерыва (в секундах)",
|
||||
"interval_between_two_breaks": "Интервал между двумя перерывами",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Количество коротких перерывов между двумя длинными",
|
||||
"time_to_prepare_for_break": "Время подготовки в перерыву (в секундах)",
|
||||
"idle_time": "Minimum idle time to pause (in minutes)",
|
||||
"strict_break": "Обязательный перерыв (Скрыть кнопку 'Пропустить')",
|
||||
"audible_alert": "Audible alert at the end of break",
|
||||
"language": "Язык",
|
||||
"enable": "Активировать Safe Eyes",
|
||||
"disable": "Disable Safe Eyes",
|
||||
"for_x_minutes": "For {} Minutes",
|
||||
"for_x_hour": "For {} Hour",
|
||||
"for_x_hours": "For {} Hours",
|
||||
"until_restart": "Until restart",
|
||||
"settings": "Настройки",
|
||||
"about": "О программе",
|
||||
"allow_postpone": "Allow postponing the breaks",
|
||||
"audible_alert": "Звуковой сигнал в конце перерыва",
|
||||
"cancel": "Отменить",
|
||||
"close": "Close",
|
||||
"disable": "Отключить Safe Eyes",
|
||||
"enable": "Активировать Safe Eyes",
|
||||
"enable_screen_lock": "Включить блокировку экрана",
|
||||
"for_x_hour": "На {} час",
|
||||
"for_x_hours": "На {} часов",
|
||||
"for_x_minutes": "На {} минут",
|
||||
"idle_time": "Минимальное время простоя для паузы (в минутах)",
|
||||
"interval_between_two_breaks": "Интервал между двумя перерывами",
|
||||
"language": "Язык",
|
||||
"license": "License",
|
||||
"long_break_duration": "Продолжительность длинного перерыва (в секундах)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Количество коротких перерывов между двумя длинными",
|
||||
"postpone": "Отложить",
|
||||
"postpone_duration": "Отложить на время (в минутах)",
|
||||
"quit": "Выйти",
|
||||
"save": "Сохранить",
|
||||
"cancel": "Отменить"
|
||||
"settings": "Настройки",
|
||||
"short_break_duration": "Продолжительность короткого перерыва (в секундах)",
|
||||
"show_time_in_tray": "Show the next break time in system tray",
|
||||
"skip": "Пропустить",
|
||||
"strict_break": "Обязательный перерыв (Скрыть кнопку 'Пропустить')",
|
||||
"system_language": "System Language",
|
||||
"time_to_prepare_for_break": "Время подготовки в перерыву (в секундах)",
|
||||
"time_to_screen_lock": "Заблокировать экран если перерыв дольше(в секундах)",
|
||||
"until_restart": "До перезагрузки"
|
||||
}
|
||||
}
|
|
@ -3,44 +3,56 @@
|
|||
"language_name": "Slovenský",
|
||||
"language_name_en": "Slovak"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "Safe Eyes protects your eyes from eye strain (asthenopia) by reminding you to take breaks while you're working long hours at the computer."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Pevne zavri oči",
|
||||
"short_break_roll_eyes": "Gúľaj očami",
|
||||
"short_break_rotate_clockwise": "Otáčaj oči v smere hodinových ručičiek",
|
||||
"short_break_rotate_counter_clockwise": "Otáčaj oči v protismere hodinových ručičiek",
|
||||
"short_break_blink": "Žmurkaj očami",
|
||||
"short_break_focus_far_distance": "Focus on a point in the far distance",
|
||||
"short_break_focus_far_distance": "Zameraj sa na bod v diaľke",
|
||||
"short_break_drink_water": "Daj si trochu vody",
|
||||
"long_break_walk": "Pobehaj si chvíľku",
|
||||
"long_break_lean_back": "Opri sa o kreslo a relaxuj"
|
||||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "Priprav sa na prestávku o {} sekúnd",
|
||||
"disabled_until_restart": "Disabled until restart",
|
||||
"disabled_until_x": "Disabled until {}",
|
||||
"disabled_until_restart": "Zakázať do reštartu",
|
||||
"disabled_until_x": "Zakázať do {}",
|
||||
"next_break_at": "Ďalšia prestávka o {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"skip": "Preskočiť",
|
||||
"short_break_duration": "Trvanie krátkej prestávky (v sekundách)",
|
||||
"long_break_duration": "Trvanie dlhej prestávky (v sekundách)",
|
||||
"interval_between_two_breaks": "Interval medzi dvomi prestávkami",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Počet krátkych prestávok medzi dvomi dlhými prestávkami",
|
||||
"time_to_prepare_for_break": "Čas na prípravu na prestávku (v sekundách)",
|
||||
"idle_time": "Minimum idle time to pause (in minutes)",
|
||||
"strict_break": "Povinná prestávka (Skryje tlačitko Preskočiť)",
|
||||
"audible_alert": "Audible alert at the end of break",
|
||||
"language": "Jazyk",
|
||||
"enable": "Povoliť Safe Eyes",
|
||||
"disable": "Disable Safe Eyes",
|
||||
"for_x_minutes": "For {} Minutes",
|
||||
"for_x_hour": "For {} Hour",
|
||||
"for_x_hours": "For {} Hours",
|
||||
"until_restart": "Until restart",
|
||||
"settings": "Nastavenia",
|
||||
"about": "Ohľadom",
|
||||
"allow_postpone": "Allow postponing the breaks",
|
||||
"audible_alert": "Zvukový signál na konci prestávky",
|
||||
"cancel": "Zrušiť",
|
||||
"close": "Close",
|
||||
"disable": "Zakázať Safe Eyes",
|
||||
"enable": "Povoliť Safe Eyes",
|
||||
"enable_screen_lock": "Lock the screen after every long break",
|
||||
"for_x_hour": "Počas {} hodiny",
|
||||
"for_x_hours": "Počas {} hodín",
|
||||
"for_x_minutes": "Počas {} minút",
|
||||
"idle_time": "Pozastaviť pri nečinnosti dlhšej ako (v minútach)",
|
||||
"interval_between_two_breaks": "Interval medzi dvomi prestávkami",
|
||||
"language": "Jazyk",
|
||||
"license": "License",
|
||||
"long_break_duration": "Trvanie dlhej prestávky (v sekundách)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Počet krátkych prestávok medzi dvomi dlhými prestávkami",
|
||||
"postpone": "Postpone",
|
||||
"postpone_duration": "Postpone duration (in minutes)",
|
||||
"quit": "Koniec",
|
||||
"save": "Uložiť",
|
||||
"cancel": "Zrušiť"
|
||||
"settings": "Nastavenia",
|
||||
"short_break_duration": "Trvanie krátkej prestávky (v sekundách)",
|
||||
"show_time_in_tray": "Show the next break time in system tray",
|
||||
"skip": "Preskočiť",
|
||||
"strict_break": "Povinná prestávka (Skryje tlačitko Preskočiť)",
|
||||
"system_language": "System Language",
|
||||
"time_to_prepare_for_break": "Čas na prípravu na prestávku (v sekundách)",
|
||||
"time_to_screen_lock": "Maximum time to skip, bypassing the lock screen (in seconds)",
|
||||
"until_restart": "Do reštartu"
|
||||
}
|
||||
}
|
|
@ -3,6 +3,9 @@
|
|||
"language_name": "தமிழ்",
|
||||
"language_name_en": "Tamil"
|
||||
},
|
||||
"app_info": {
|
||||
"description": "அடிக்கடி ஓய்வெடுக்க அறிவுறுத்துவதன் மூலம் Safe Eyes கணனியில் நீண்ட நேரம் பணியாற்றுவதால் ஏற்படும் கண் சோர்வை தடுக்கிறது."
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "உங்கள் கண்களை இறுக்கமாக மூடுங்கள்",
|
||||
"short_break_roll_eyes": "உங்கள் கண்களை இடம் வலமாக உருட்டுங்கள்",
|
||||
|
@ -21,26 +24,35 @@
|
|||
"next_break_at": "அடுத்த இடைவேளை {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"about": "Safe Eyes குறித்த தகவல்கள்",
|
||||
"allow_postpone": "ஒத்திவைக்க அனுமதிக்கவும்",
|
||||
"audible_alert": "இடைவேளையின் இறுதியில் ஒலி சமிக்கை",
|
||||
"cancel": "ரத்து",
|
||||
"enable": "Safe Eyes ஐ செயல்படுத்துக",
|
||||
"close": "மூடு",
|
||||
"disable": "Safe Eyes ஐ நிறுத்துக",
|
||||
"for_x_minutes": "{} நிமிடங்களுக்கு",
|
||||
"enable": "Safe Eyes ஐ செயல்படுத்துக",
|
||||
"enable_screen_lock": "நீண்ட கால இடைவேளைகளின் பின்னர் திரையை பூட்டுக",
|
||||
"for_x_hour": "{} மணித்தியாலத்திற்கு",
|
||||
"for_x_hours": "{} மணித்தியாலங்களுக்கு",
|
||||
"until_restart": "மீள ஆரம்பிக்கும் வரைை",
|
||||
"interval_between_two_breaks": "இரண்டு இடைவேளைகளுக்கிடையிலான இடைவெளி (நிமிடங்களில்)",
|
||||
"long_break_duration": "நீண்ட கால இடைவேளை (விநாடிகளில்)",
|
||||
"for_x_minutes": "{} நிமிடங்களுக்கு",
|
||||
"idle_time": "இடைநிறுத்துவதற்கான குறைந்தபட்ச செயலற்ற நேரம் (நிமிடங்களில்)",
|
||||
"interval_between_two_breaks": "இரண்டு இடைவேளைகளுக்கிடையிலான இடைவெளி (நிமிடங்களில்)",
|
||||
"language": "மொழி",
|
||||
"license": "மென்பொருள் உரிமம்",
|
||||
"long_break_duration": "நீண்ட கால இடைவேளை (விநாடிகளில்)",
|
||||
"no_of_short_breaks_between_two_long_breaks": "இரண்டு நீண்ட இடைவேளைகளுக்கிடையிலான குறுகிய இடைவேளைகள்",
|
||||
"postpone": "ஒத்தி வை",
|
||||
"postpone_duration": "இடைவேளையை ஒத்தி வைக்கும் காலம் (நிமிடங்களில்)",
|
||||
"quit": "நிறுத்து",
|
||||
"about": "Safe Eyes குறித்த தகவல்கள்",
|
||||
"save": "சேமி",
|
||||
"settings": "அமைப்பு",
|
||||
"short_break_duration": "குறுகிய கால இடைவேளை (விநாடிகளில்)",
|
||||
"show_time_in_tray": "அடுத்த இடைவேளை நேரத்தை Safe Eyes சின்னத்திற்கு அருகில் காண்பிக்கவும்",
|
||||
"skip": "தவிர்",
|
||||
"strict_break": "கட்டாய இடைவேளை (தவிர்க்கும் பொத்தான் காண்பிக்கப்பட மாட்டாது)",
|
||||
"audible_alert": "இடைவேளையின் இறுதியில் ஒலி சமிக்கை",
|
||||
"language": "மொழி",
|
||||
"time_to_prepare_for_break": "இடைவேளைக்கு தயாராக தேவைப்படும் நேரம் (விநாடிகளில்)"
|
||||
"system_language": "இயங்குதள மொழி",
|
||||
"time_to_prepare_for_break": "இடைவேளைக்கு தயாராக தேவைப்படும் நேரம் (விநாடிகளில்)",
|
||||
"time_to_screen_lock": "திரையினை பூட்டாமல் இடைவேளையை தவிர்ப்பதற்கான அதிகபட்ச நேரம் (விநாடிகளில்)",
|
||||
"until_restart": "மீள ஆரம்பிக்கும் வரைை"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
{
|
||||
"meta": {
|
||||
"config_version": "5.0.1"
|
||||
},
|
||||
"allow_postpone": false,
|
||||
"break_interval": 15,
|
||||
"enable_screen_lock": false,
|
||||
"long_break_duration": 60,
|
||||
"no_of_short_breaks_per_long_break": 5,
|
||||
"pre_break_warning_time": 10,
|
||||
"short_break_duration": 15,
|
||||
"idle_time": 5,
|
||||
"postpone_duration": 5,
|
||||
"show_time_in_tray": false,
|
||||
"strict_break": false,
|
||||
"audible_alert": false,
|
||||
"language": "system",
|
||||
"time_to_screen_lock": 5,
|
||||
"lock_screen_command": [],
|
||||
"active_window_class": {
|
||||
"skip_break": [],
|
||||
"take_break": []
|
||||
},
|
||||
"disable_options": [
|
||||
{
|
||||
"label": "for_x_minutes",
|
||||
"time": 30,
|
||||
"unit": "minute"
|
||||
},
|
||||
{
|
||||
"label": "for_x_hour",
|
||||
"time": 1,
|
||||
"unit": "hour"
|
||||
},
|
||||
{
|
||||
"label": "for_x_hours",
|
||||
"time": 2,
|
||||
"unit": "hour"
|
||||
},
|
||||
{
|
||||
"label": "for_x_hours",
|
||||
"time": 3,
|
||||
"unit": "hour"
|
||||
}
|
||||
],
|
||||
"short_breaks": [
|
||||
{
|
||||
"name": "short_break_close_eyes"
|
||||
},
|
||||
{
|
||||
"name": "short_break_roll_eyes"
|
||||
},
|
||||
{
|
||||
"name": "short_break_rotate_clockwise"
|
||||
},
|
||||
{
|
||||
"name": "short_break_rotate_counter_clockwise"
|
||||
},
|
||||
{
|
||||
"name": "short_break_blink"
|
||||
},
|
||||
{
|
||||
"name": "short_break_focus_far_distance"
|
||||
},
|
||||
{
|
||||
"name": "short_break_drink_water"
|
||||
}
|
||||
],
|
||||
"long_breaks": [
|
||||
{
|
||||
"name": "long_break_walk"
|
||||
},
|
||||
{
|
||||
"name": "long_break_lean_back"
|
||||
}
|
||||
],
|
||||
"custom_exercises": {
|
||||
|
||||
},
|
||||
"plugins": []
|
||||
}
|
|
@ -25,7 +25,8 @@
|
|||
|
||||
.btn_skip {
|
||||
color: white;
|
||||
border-radius: 20px;
|
||||
font-size: 10pt;
|
||||
border-radius: 25px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 25px;
|
||||
|
@ -41,13 +42,38 @@
|
|||
color: black;
|
||||
}
|
||||
|
||||
.btn_postpone{
|
||||
color: white;
|
||||
font-size: 10pt;
|
||||
border-radius: 25px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 25px;
|
||||
padding-right: 25px;
|
||||
border-color: white;
|
||||
background: transparent;
|
||||
border-width: 2px;
|
||||
border-image: none;
|
||||
}
|
||||
|
||||
.btn_postpone:hover {
|
||||
background: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.lbl_message {
|
||||
font-size: 16pt;
|
||||
font-size: 22pt;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.lbl_count {
|
||||
font-size: 10pt;
|
||||
font-size: 12pt;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.panel_left {
|
||||
}
|
||||
|
||||
.panel_right {
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
safeeyes /opt
|
||||
share /usr
|
|
@ -1,3 +0,0 @@
|
|||
#!/usr/bin/make -f
|
||||
%:
|
||||
dh $@
|
|
@ -41,7 +41,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.</pro
|
|||
<property name="title" translatable="yes">Safe Eyes</property>
|
||||
<property name="resizable">False</property>
|
||||
<property name="window_position">center</property>
|
||||
<property name="default_width">400</property>
|
||||
<property name="default_width">350</property>
|
||||
<property name="default_height">200</property>
|
||||
<property name="icon_name">safeeyes</property>
|
||||
<signal name="delete-event" handler="on_window_delete" swapped="no"/>
|
||||
|
@ -88,10 +88,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.</pro
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">4</property>
|
||||
<property name="label" translatable="yes">Safe Eyes is an open source Linux alternative for Eye Leo to protect your eyes
|
||||
from eye strain (asthenopia) by reminding you to take breaks while you're
|
||||
working long hours at the computer.</property>
|
||||
<property name="label" translatable="yes">Safe Eyes protects your eyes from eye strain (asthenopia) by reminding you to take breaks while you're working long hours at the computer.</property>
|
||||
<property name="justify">fill</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="max_width_chars">60</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -118,9 +118,9 @@ working long hours at the computer.</property>
|
|||
<object class="GtkTextView" id="txt_license">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="resize_mode">queue</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="wrap_mode">word</property>
|
||||
<property name="justification">fill</property>
|
||||
<property name="buffer">text_buffer_license</property>
|
||||
<property name="accepts_tab">False</property>
|
||||
</object>
|
||||
|
@ -132,14 +132,14 @@ working long hours at the computer.</property>
|
|||
</child>
|
||||
<child>
|
||||
<object class="GtkLinkButton" id="btn_url">
|
||||
<property name="label" translatable="yes">https://github.com/slgobinath/SafeEyes</property>
|
||||
<property name="label" translatable="yes">http://slgobinath.github.io/SafeEyes</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="uri">https://github.com/slgobinath/SafeEyes</property>
|
||||
<property name="uri">http://slgobinath.github.io/SafeEyes</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
|
@ -0,0 +1,201 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.18.3 -->
|
||||
<!--
|
||||
~ Safe Eyes is a utility to remind you to take break frequently
|
||||
~ to protect your eyes from eye strain.
|
||||
|
||||
~ Copyright (C) 2016 Gobinath
|
||||
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.10"/>
|
||||
<object class="GtkWindow" id="window_main">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="window_position">center</property>
|
||||
<property name="hide_titlebar_when_maximized">True</property>
|
||||
<property name="icon_name">safeeyes</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="urgency_hint">True</property>
|
||||
<property name="focus_on_map">False</property>
|
||||
<property name="decorated">False</property>
|
||||
<property name="deletable">False</property>
|
||||
<property name="gravity">center</property>
|
||||
<signal name="delete-event" handler="on_window_delete" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_left">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">start</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<style>
|
||||
<class name="panel_left"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="row_spacing">10</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid_parent">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="row_spacing">15</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_message">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Hello World</property>
|
||||
<property name="justify">center</property>
|
||||
<style>
|
||||
<class name="lbl_message"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="yscale">0.20000000298023224</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_count">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">00</property>
|
||||
<style>
|
||||
<class name="lbl_count"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box_buttons">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="spacing">50</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="height">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage" id="img_break">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_right">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">start</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<style>
|
||||
<class name="panel_right"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
<class name="window_main"/>
|
||||
</style>
|
||||
</object>
|
||||
</interface>
|
|
@ -21,6 +21,12 @@
|
|||
-->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.10"/>
|
||||
<object class="GtkAdjustment" id="adjust_idle_time_to_pause">
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">60</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">5</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjust_interval_between_breaks">
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">60</property>
|
||||
|
@ -40,19 +46,25 @@
|
|||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">5</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjust_postpone_duration">
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">15</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">5</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjust_short_break_duration">
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">60</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">5</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjust_time_to_prepare">
|
||||
<property name="lower">10</property>
|
||||
<object class="GtkAdjustment" id="adjust_time_to_lock_screen">
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">60</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">5</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjust_idle_time_to_pause">
|
||||
<object class="GtkAdjustment" id="adjust_time_to_prepare">
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">60</property>
|
||||
<property name="step_increment">1</property>
|
||||
|
@ -79,7 +91,7 @@
|
|||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="layoutt_grid">
|
||||
<object class="GtkGrid" id="layout_grid">
|
||||
<property name="visible">True</property>
|
||||
<property name="app_paintable">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -167,6 +179,32 @@
|
|||
<property name="top_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_postpone_duration">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="label" translatable="yes">Postpone duration (in minutes)</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_show_time_in_tray">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="label" translatable="yes">Show the next break time in system tray icon</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_strict_break">
|
||||
<property name="visible">True</property>
|
||||
|
@ -175,37 +213,11 @@
|
|||
<property name="valign">center</property>
|
||||
<property name="label" translatable="yes">Strict break (hide skip button)</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_audible_alert">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="label" translatable="yes">Audible alert at the end of break</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_language">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="label" translatable="yes">Language</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spin_short_break_duration">
|
||||
<property name="visible">True</property>
|
||||
|
@ -319,9 +331,30 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="switch_strict_break">
|
||||
<object class="GtkSpinButton" id="spin_postpone_duration">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="text" translatable="yes">1</property>
|
||||
<property name="input_purpose">number</property>
|
||||
<property name="adjustment">adjust_postpone_duration</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="numeric">True</property>
|
||||
<property name="update_policy">if-valid</property>
|
||||
<property name="value">5</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="switch_show_time_in_tray">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
|
@ -329,20 +362,140 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="switch_audible_alert">
|
||||
<object class="GtkSwitch" id="switch_strict_break">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="switch_audible_alert">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_audible_alert">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="label" translatable="yes">Audible alert at the end of break</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="cmb_language">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">13</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_language">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="label" translatable="yes">Language</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">13</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_enable_screen_lock">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="label" translatable="yes">Lock the screen after every long break</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">11</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="switch_screen_lock">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">11</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_lock_screen_after">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="label" translatable="yes">Maximum time to skip, bypassing the lock screen (in seconds)</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">12</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spin_time_to_screen_lock">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="text" translatable="yes">1</property>
|
||||
<property name="input_purpose">number</property>
|
||||
<property name="adjustment">adjust_time_to_lock_screen</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="numeric">True</property>
|
||||
<property name="update_policy">if-valid</property>
|
||||
<property name="value">1</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">12</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_allow_postpone">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="label" translatable="yes">Allow postponing the breaks</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="switch_postpone">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">8</property>
|
|
@ -1,209 +0,0 @@
|
|||
# Safe Eyes is a utility to remind you to take break frequently
|
||||
# to protect your eyes from eye strain.
|
||||
|
||||
# Copyright (C) 2016 Gobinath
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import gi, logging, threading, datetime, Utility
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('AppIndicator3', '0.1')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import AppIndicator3 as appindicator
|
||||
|
||||
# Global variables
|
||||
APPINDICATOR_ID = 'safeeyes'
|
||||
|
||||
|
||||
class TrayIcon:
|
||||
|
||||
def __init__(self, config, language, on_show_settings, on_show_about, on_enable, on_disable, on_quite):
|
||||
logging.info("Initialize the tray icon")
|
||||
self.on_show_settings = on_show_settings
|
||||
self.on_show_about = on_show_about
|
||||
self.on_quite = on_quite
|
||||
self.on_enable = on_enable
|
||||
self.on_disable = on_disable
|
||||
self.language = language
|
||||
self.dateTime = None
|
||||
self.active = True
|
||||
self.idle_condition = threading.Condition()
|
||||
self.lock = threading.Lock()
|
||||
|
||||
# Construct the tray icon
|
||||
self.indicator = appindicator.Indicator.new(
|
||||
APPINDICATOR_ID, "safeeyes_enabled", appindicator.IndicatorCategory.APPLICATION_STATUS)
|
||||
self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
|
||||
|
||||
# Construct the context menu
|
||||
self.menu = Gtk.Menu()
|
||||
|
||||
# Next break info menu item
|
||||
self.item_info = Gtk.ImageMenuItem()
|
||||
img_timer = Gtk.Image()
|
||||
img_timer.set_from_icon_name("safeeyes_timer", 16)
|
||||
self.item_info.set_image(img_timer)
|
||||
|
||||
self.item_separator = Gtk.SeparatorMenuItem()
|
||||
|
||||
# Enable/Disable menu item
|
||||
self.item_enable = Gtk.MenuItem()
|
||||
self.item_enable.connect('activate', self.on_toogle_enable)
|
||||
|
||||
self.sub_menu_disable = Gtk.Menu()
|
||||
self.sub_menu_items = []
|
||||
|
||||
# Read disable options and build the sub menu
|
||||
for disable_option in config['disable_options']:
|
||||
time_in_minutes = disable_option['time']
|
||||
# Validate time value
|
||||
if not isinstance(time_in_minutes, int) or time_in_minutes <= 0:
|
||||
logging.error('Invalid time in disable option: ' + str(time_in_minutes))
|
||||
continue
|
||||
time_unit = disable_option['unit'].lower()
|
||||
if time_unit == 'seconds' or time_unit == 'second':
|
||||
time_in_minutes = int(time_in_minutes / 60)
|
||||
elif time_unit == 'minutes' or time_unit == 'minute':
|
||||
time_in_minutes = int(time_in_minutes * 1)
|
||||
elif time_unit == 'hours' or time_unit == 'hour':
|
||||
time_in_minutes = int(time_in_minutes * 60)
|
||||
else:
|
||||
# Invalid unit
|
||||
logging.error('Invalid unit in disable option: ' + str(disable_option))
|
||||
continue
|
||||
|
||||
# Create submenu
|
||||
sub_menu_item = Gtk.MenuItem()
|
||||
sub_menu_item.connect('activate', self.on_toogle_enable, time_in_minutes)
|
||||
self.sub_menu_items.append([sub_menu_item, disable_option['label'], disable_option['time']])
|
||||
self.sub_menu_disable.append(sub_menu_item)
|
||||
|
||||
# Disable until restart submenu
|
||||
self.sub_menu_item_until_restart = Gtk.MenuItem()
|
||||
self.sub_menu_item_until_restart.connect('activate', self.on_toogle_enable, -1)
|
||||
self.sub_menu_disable.append(self.sub_menu_item_until_restart)
|
||||
|
||||
# Add the sub menu to the enable/disable menu
|
||||
self.item_enable.set_submenu(self.sub_menu_disable)
|
||||
|
||||
# Settings menu item
|
||||
self.item_settings = Gtk.MenuItem()
|
||||
self.item_settings.connect('activate', self.show_settings)
|
||||
|
||||
# About menu item
|
||||
self.item_about = Gtk.MenuItem()
|
||||
self.item_about.connect('activate', self.show_about)
|
||||
|
||||
# Quit menu item
|
||||
self.item_quit = Gtk.MenuItem()
|
||||
self.item_quit.connect('activate', self.quit_safe_eyes)
|
||||
|
||||
self.set_labels(language)
|
||||
|
||||
# Append all menu items and show the menu
|
||||
self.menu.append(self.item_info)
|
||||
self.menu.append(self.item_separator)
|
||||
self.menu.append(self.item_enable)
|
||||
self.menu.append(self.item_settings)
|
||||
self.menu.append(self.item_about)
|
||||
self.menu.append(self.item_quit)
|
||||
self.menu.show_all()
|
||||
|
||||
self.indicator.set_menu(self.menu)
|
||||
|
||||
def set_labels(self, language):
|
||||
self.language = language
|
||||
for entry in self.sub_menu_items:
|
||||
entry[0].set_label(self.language['ui_controls'][entry[1]].format(entry[2]))
|
||||
self.sub_menu_item_until_restart.set_label(self.language['ui_controls']['until_restart'])
|
||||
self.item_info.set_label(self.language['messages']['disabled_until_restart'])
|
||||
self.item_enable.set_label(self.language['ui_controls']['disable'])
|
||||
self.item_settings.set_label(self.language['ui_controls']['settings'])
|
||||
self.item_about.set_label(self.language['ui_controls']['about'])
|
||||
self.item_quit.set_label(self.language['ui_controls']['quit'])
|
||||
|
||||
def show_icon(self):
|
||||
Utility.execute_main_thread(self.indicator.set_status, appindicator.IndicatorStatus.ACTIVE)
|
||||
|
||||
def hide_icon(self):
|
||||
Utility.execute_main_thread(self.indicator.set_status, appindicator.IndicatorStatus.PASSIVE)
|
||||
|
||||
def quit_safe_eyes(self, *args):
|
||||
self.on_quite()
|
||||
with self.lock:
|
||||
self.active = True
|
||||
# Notify all schedulers
|
||||
self.idle_condition.acquire()
|
||||
self.idle_condition.notify_all()
|
||||
self.idle_condition.release()
|
||||
|
||||
def show_settings(self, *args):
|
||||
self.on_show_settings()
|
||||
|
||||
def show_about(self, *args):
|
||||
self.on_show_about()
|
||||
|
||||
def next_break_time(self, dateTime):
|
||||
logging.info("Update next break information")
|
||||
self.dateTime = dateTime
|
||||
self.set_next_break_info(self.dateTime)
|
||||
|
||||
def set_next_break_info(self, dateTime):
|
||||
formatted_time = Utility.format_time(dateTime)
|
||||
message = self.language['messages'][
|
||||
'next_break_at'].format(formatted_time)
|
||||
|
||||
Utility.execute_main_thread(self.item_info.set_label, message)
|
||||
|
||||
def on_toogle_enable(self, *args):
|
||||
# active = self.item_enable.get_active()
|
||||
if args[0] == self.item_enable and not self.active:
|
||||
with self.lock:
|
||||
logging.info('Enable Safe Eyes')
|
||||
self.active = True
|
||||
self.indicator.set_icon("safeeyes_enabled")
|
||||
self.item_info.set_sensitive(True)
|
||||
self.on_enable()
|
||||
self.item_enable.set_label(self.language['ui_controls']['disable'])
|
||||
self.item_enable.set_submenu(self.sub_menu_disable)
|
||||
# Notify all schedulers
|
||||
self.idle_condition.acquire()
|
||||
self.idle_condition.notify_all()
|
||||
self.idle_condition.release()
|
||||
elif args[0] != self.item_enable and self.active:
|
||||
logging.info('Disable Safe Eyes')
|
||||
self.active = False
|
||||
self.indicator.set_icon("safeeyes_disabled")
|
||||
self.item_info.set_sensitive(False)
|
||||
self.on_disable()
|
||||
self.item_enable.set_label(self.language['ui_controls']['enable'])
|
||||
self.item_enable.set_submenu(None)
|
||||
|
||||
time_to_wait = args[1]
|
||||
if time_to_wait <= 0:
|
||||
self.item_info.set_label(self.language['messages']['disabled_until_restart'])
|
||||
else:
|
||||
wakeup_time = datetime.datetime.now() + datetime.timedelta(minutes=time_to_wait)
|
||||
Utility.start_thread(self.__schedule_resume, args={'time_minutes': time_to_wait})
|
||||
self.item_info.set_label(self.language['messages']['disabled_until_x'].format(Utility.format_time(wakeup_time)))
|
||||
|
||||
|
||||
def __schedule_resume(self, time_minutes):
|
||||
self.idle_condition.acquire()
|
||||
self.idle_condition.wait(time_minutes * 60) # Convert to seconds
|
||||
self.idle_condition.release()
|
||||
|
||||
with self.lock:
|
||||
if not self.active:
|
||||
Utility.execute_main_thread(self.item_enable.activate)
|
|
@ -1,103 +0,0 @@
|
|||
# Safe Eyes is a utility to remind you to take break frequently
|
||||
# to protect your eyes from eye strain.
|
||||
|
||||
# Copyright (C) 2017 Gobinath
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import gi
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gdk, GLib
|
||||
import babel.dates, os, errno, subprocess, threading, logging, locale
|
||||
|
||||
"""
|
||||
Play the alert.mp3
|
||||
"""
|
||||
def play_notification():
|
||||
logging.info("Playing audible alert")
|
||||
try:
|
||||
subprocess.Popen(['mpg123', '-q', os.path.join(os.path.dirname(os.path.realpath(__file__)), 'resource/alert.mp3')])
|
||||
except:
|
||||
pass
|
||||
|
||||
"""
|
||||
Get system idle time in minutes.
|
||||
Return the idle time if xprintidle is available, otherwise return 0.
|
||||
"""
|
||||
def system_idle_time():
|
||||
try:
|
||||
return int(subprocess.check_output(['xprintidle']).decode('utf-8')) / 60000 # Convert to minutes
|
||||
except:
|
||||
return 0
|
||||
|
||||
|
||||
"""
|
||||
Execute the function in a separate thread.
|
||||
"""
|
||||
def start_thread(target_function, args=None):
|
||||
thread = threading.Thread(target=target_function, kwargs=args)
|
||||
thread.start()
|
||||
|
||||
|
||||
"""
|
||||
Execute the given function in main thread.
|
||||
"""
|
||||
def execute_main_thread(target_function, args=None):
|
||||
if args:
|
||||
GLib.idle_add(lambda: target_function(args))
|
||||
else:
|
||||
GLib.idle_add(lambda: target_function())
|
||||
|
||||
|
||||
"""
|
||||
Check for full-screen applications.
|
||||
"""
|
||||
def is_full_screen_app_found():
|
||||
logging.info("Searching for full-screen application")
|
||||
screen = Gdk.Screen.get_default()
|
||||
active_xid = str(screen.get_active_window().get_xid())
|
||||
cmdlist = ['xprop', '-root', '-notype','-id',active_xid, '_NET_WM_STATE']
|
||||
|
||||
try:
|
||||
stdout = subprocess.check_output(cmdlist)
|
||||
except subprocess.CalledProcessError:
|
||||
logging.warning("Error in finding full-screen application")
|
||||
pass
|
||||
else:
|
||||
if stdout:
|
||||
return 'FULLSCREEN' in stdout
|
||||
|
||||
|
||||
"""
|
||||
Format time based on the system time.
|
||||
"""
|
||||
def format_time(time):
|
||||
system_locale = locale.setlocale(locale.LC_ALL, '')
|
||||
if not system_locale:
|
||||
system_locale = 'en_US.UTF-8'
|
||||
return babel.dates.format_time(time, format='short', locale=system_locale)
|
||||
|
||||
|
||||
"""
|
||||
Create directory if not exists.
|
||||
"""
|
||||
def mkdir(path):
|
||||
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))
|
||||
raise
|
|
@ -1,46 +0,0 @@
|
|||
{
|
||||
"meta_info": {
|
||||
"language_name": "Čeština",
|
||||
"language_name_en": "Czech"
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Zavřete oči",
|
||||
"short_break_roll_eyes": "Koulejte očima",
|
||||
"short_break_rotate_clockwise": "Kružte očima ve směru hodinových ručiček",
|
||||
"short_break_rotate_counter_clockwise": "Kružte očima proti směru hodinových ručiček",
|
||||
"short_break_blink": "Mrkejte",
|
||||
"short_break_focus_far_distance": "Soustřeďte se na bod v dálce",
|
||||
"short_break_drink_water": "Dejte si nějakou vodu",
|
||||
"long_break_walk": "Na chvíli se projděte",
|
||||
"long_break_lean_back": "Opřete se do křesla a relaxujte"
|
||||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "Připravte se na přestávku za {} sekund",
|
||||
"disabled_until_restart": "Disabled until restart",
|
||||
"disabled_until_x": "Disabled until {}",
|
||||
"next_break_at": "Příští přestávka v {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"skip": "Přeskočit",
|
||||
"short_break_duration": "Trvání krátké přestávky (v sekundách)",
|
||||
"long_break_duration": "Trvání dlouhé přestávky (v sekundách)",
|
||||
"interval_between_two_breaks": "Interval mezi dvěma přestávkami",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Počet krátkých přestávek mezi dvěma dlouhými přestávkami",
|
||||
"time_to_prepare_for_break": "Čas k přípravě na přestávku (v sekundách)",
|
||||
"idle_time": "Pozastavit při nečinnosti delší než (v minutách)",
|
||||
"strict_break": "Povinná přestávka (Skrýt tlačítko pro přeskočení)",
|
||||
"audible_alert": "Zvukové upozornění na konec přestávky",
|
||||
"language": "Jazyk",
|
||||
"enable": "Povolit Safe Eyes",
|
||||
"disable": "Disable Safe Eyes",
|
||||
"for_x_minutes": "For {} Minutes",
|
||||
"for_x_hour": "For {} Hour",
|
||||
"for_x_hours": "For {} Hours",
|
||||
"until_restart": "Until restart",
|
||||
"settings": "Nastavení",
|
||||
"about": "O programu",
|
||||
"quit": "Ukončit",
|
||||
"save": "Uložit",
|
||||
"cancel": "Zrušit"
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
{
|
||||
"meta_info": {
|
||||
"language_name": "Español",
|
||||
"language_name_en": "Spanish"
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Cierra bien tus ojos",
|
||||
"short_break_roll_eyes": "Mueve tus ojos en círculos",
|
||||
"short_break_rotate_clockwise": "Mueve tus ojos en círculos en el sentido horario",
|
||||
"short_break_rotate_counter_clockwise": "Mueve tus ojos en círculos en el sentido antihorario",
|
||||
"short_break_blink": "Parpadea tus ojos",
|
||||
"short_break_focus_far_distance": "Fijarse en un punto lejano",
|
||||
"short_break_drink_water": "Bebe un poco de agua",
|
||||
"long_break_walk": "Vete a andar un poco",
|
||||
"long_break_lean_back": "Reclínate sobre tu silla y relájate"
|
||||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "Listo para una pausa en {} segundos",
|
||||
"disabled_until_restart": "Disabled until restart",
|
||||
"disabled_until_x": "Disabled until {}",
|
||||
"next_break_at": "Próxima pausa a las {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"skip": "Cancelar",
|
||||
"short_break_duration": "Duración de una pausa corta (en segundos)",
|
||||
"long_break_duration": "Duración de una pausa larga (en segundos)",
|
||||
"interval_between_two_breaks": "Tiempo entre dos pausas",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Número de pausas cortas entre dos pausas largas",
|
||||
"time_to_prepare_for_break": "Tiempo para prepararse para una pausa (en segundos)",
|
||||
"idle_time": "Minimum idle time to pause (in minutes)",
|
||||
"strict_break": "Pausa estricta (Sin botón Cancelar)",
|
||||
"audible_alert": "Audible alert at the end of break",
|
||||
"language": "Idioma",
|
||||
"enable": "Activar Safe Eyes",
|
||||
"disable": "Disable Safe Eyes",
|
||||
"for_x_minutes": "For {} Minutes",
|
||||
"for_x_hour": "For {} Hour",
|
||||
"for_x_hours": "For {} Hours",
|
||||
"until_restart": "Until restart",
|
||||
"settings": "Preferencias",
|
||||
"about": "Acerca de",
|
||||
"quit": "Salir",
|
||||
"save": "Guardar",
|
||||
"cancel": "Cancelar"
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
{
|
||||
"meta_info": {
|
||||
"language_name": "Français",
|
||||
"language_name_en": "French"
|
||||
},
|
||||
"exercises": {
|
||||
"short_break_close_eyes": "Fermez bien vos yeux",
|
||||
"short_break_roll_eyes": "Roulez vos yeux",
|
||||
"short_break_rotate_clockwise": "Faites rouler vos yeux dans le sens des aiguilles d'une montre",
|
||||
"short_break_rotate_counter_clockwise": "Faites rouler vos yeux dans le sens contraire des aiguilles d'une montre",
|
||||
"short_break_blink": "Clignez des yeux",
|
||||
"short_break_focus_far_distance": "Focus sur un point dans la distance lointaine",
|
||||
"short_break_drink_water": "Buvez de l'eau",
|
||||
"long_break_walk": "Allez marcher un peu",
|
||||
"long_break_lean_back": "Reposez-vous un moment dans votre siège"
|
||||
},
|
||||
"messages": {
|
||||
"ready_for_a_break": "Prêt pour une pause dans {} secondes",
|
||||
"disabled_until_restart": "Disabled until restart",
|
||||
"disabled_until_x": "Disabled until {}",
|
||||
"next_break_at": "Prochaine pause à {}"
|
||||
},
|
||||
"ui_controls": {
|
||||
"skip": "Annuler",
|
||||
"short_break_duration": "Durée d'une pause courte (en secondes)",
|
||||
"long_break_duration": "Durée d'une pause longue (en secondes)",
|
||||
"interval_between_two_breaks": "Intervalle entre deux pauses",
|
||||
"no_of_short_breaks_between_two_long_breaks": "Nombre de pauses courtes entre deux pauses longues",
|
||||
"time_to_prepare_for_break": "Temps pour se préparer avant une pause (en secondes)",
|
||||
"idle_time": "Minimum idle time to pause (in minutes)",
|
||||
"strict_break": "Pause non annulable (Cacher le bouton Annuler)",
|
||||
"audible_alert": "Audible alert at the end of break",
|
||||
"language": "La language",
|
||||
"enable": "Activer Safe Eyes",
|
||||
"disable": "Disable Safe Eyes",
|
||||
"for_x_minutes": "For {} Minutes",
|
||||
"for_x_hour": "For {} Hour",
|
||||
"for_x_hours": "For {} Hours",
|
||||
"until_restart": "Until restart",
|
||||
"settings": "Réglages",
|
||||
"about": "À propos",
|
||||
"quit": "Quitter",
|
||||
"save": "Enregistrer",
|
||||
"cancel": "Annuler"
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
{
|
||||
"meta": {
|
||||
"config_version": 3
|
||||
},
|
||||
"break_interval": 15,
|
||||
"long_break_duration": 60,
|
||||
"no_of_short_breaks_per_long_break": 5,
|
||||
"pre_break_warning_time": 10,
|
||||
"short_break_duration": 15,
|
||||
"idle_time": 5,
|
||||
"strict_break": false,
|
||||
"audible_alert": false,
|
||||
"language": "en",
|
||||
"disable_options": [
|
||||
{
|
||||
"label": "for_x_minutes",
|
||||
"time": 30,
|
||||
"unit": "minute"
|
||||
},
|
||||
{
|
||||
"label": "for_x_hour",
|
||||
"time": 1,
|
||||
"unit": "hour"
|
||||
},
|
||||
{
|
||||
"label": "for_x_hours",
|
||||
"time": 2,
|
||||
"unit": "hour"
|
||||
},
|
||||
{
|
||||
"label": "for_x_hours",
|
||||
"time": 3,
|
||||
"unit": "hour"
|
||||
}
|
||||
],
|
||||
"short_breaks": [
|
||||
{
|
||||
"name": "short_break_close_eyes"
|
||||
},
|
||||
{
|
||||
"name": "short_break_roll_eyes"
|
||||
},
|
||||
{
|
||||
"name": "short_break_rotate_clockwise"
|
||||
},
|
||||
{
|
||||
"name": "short_break_rotate_counter_clockwise"
|
||||
},
|
||||
{
|
||||
"name": "short_break_blink"
|
||||
},
|
||||
{
|
||||
"name": "short_break_focus_far_distance"
|
||||
},
|
||||
{
|
||||
"name": "short_break_drink_water"
|
||||
}
|
||||
],
|
||||
"long_breaks": [
|
||||
{
|
||||
"name": "long_break_walk"
|
||||
},
|
||||
{
|
||||
"name": "long_break_lean_back"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.18.3 -->
|
||||
|
||||
<!--
|
||||
~ Safe Eyes is a utility to remind you to take break frequently
|
||||
~ to protect your eyes from eye strain.
|
||||
|
||||
~ Copyright (C) 2016 Gobinath
|
||||
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.10"/>
|
||||
<object class="GtkWindow" id="window_main">
|
||||
<property name="can_focus">False</property>
|
||||
<!-- <property name="type">popup</property> -->
|
||||
<property name="window_position">center</property>
|
||||
<property name="hide_titlebar_when_maximized">True</property>
|
||||
<property name="icon_name">safeeyes</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="urgency_hint">True</property>
|
||||
<property name="focus_on_map">False</property>
|
||||
<property name="decorated">False</property>
|
||||
<property name="deletable">False</property>
|
||||
<signal name="delete-event" handler="on_window_delete" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid_parent">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="row_spacing">5</property>
|
||||
<property name="row_homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_message">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Hello World</property>
|
||||
<style>
|
||||
<class name="lbl_message"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="yscale">0.20000000298023224</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lbl_count">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">00</property>
|
||||
<style>
|
||||
<class name="lbl_count"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="btn_skip">
|
||||
<property name="label" translatable="yes">Skip</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="clicked" handler="on_skip_clicked" swapped="no"/>
|
||||
<style>
|
||||
<class name="btn_skip"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
<class name="window_main"/>
|
||||
</style>
|
||||
</object>
|
||||
</interface>
|
|
@ -1,289 +0,0 @@
|
|||
#!/usr/bin/env python2
|
||||
|
||||
# Safe Eyes is a utility to remind you to take break frequently
|
||||
# to protect your eyes from eye strain.
|
||||
|
||||
# Copyright (C) 2016 Gobinath
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os, gi, json, shutil, dbus, logging, operator, Utility
|
||||
from threading import Timer
|
||||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
from BreakScreen import BreakScreen
|
||||
from TrayIcon import TrayIcon
|
||||
from SettingsDialog import SettingsDialog
|
||||
from AboutDialog import AboutDialog
|
||||
from SafeEyesCore import SafeEyesCore
|
||||
from Notification import Notification
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Define necessary paths
|
||||
bin_directory = os.path.dirname(os.path.realpath(__file__))
|
||||
config_file_path = os.path.join(os.path.expanduser('~'), '.config/safeeyes/safeeyes.json')
|
||||
style_sheet_path = os.path.join(os.path.expanduser('~'), '.config/safeeyes/style/safeeyes_style.css')
|
||||
log_file_path = os.path.join(os.path.expanduser('~'), '.config/safeeyes/safeeyes.log')
|
||||
break_screen_glade = os.path.join(bin_directory, "glade/break_screen.glade")
|
||||
settings_dialog_glade = os.path.join(bin_directory, "glade/settings_dialog.glade")
|
||||
about_dialog_glade = os.path.join(bin_directory, "glade/about_dialog.glade")
|
||||
system_config_file_path = os.path.join(bin_directory, "config/safeeyes.json")
|
||||
system_style_sheet_path = os.path.join(bin_directory, "config/style/safeeyes_style.css")
|
||||
system_language_directory = os.path.join(bin_directory, "config/lang")
|
||||
|
||||
is_active = True
|
||||
CONFIGURATION_VERSION = 3
|
||||
SAFE_EYES_VERSION = "1.1.4"
|
||||
|
||||
"""
|
||||
Listen to tray icon Settings action and send the signal to Settings dialog.
|
||||
"""
|
||||
def show_settings():
|
||||
logging.info("Show Settings dialog")
|
||||
settings_dialog = SettingsDialog(config, language, read_lang_files(), save_settings, settings_dialog_glade)
|
||||
settings_dialog.show()
|
||||
|
||||
"""
|
||||
Listen to tray icon About action and send the signal to About dialog.
|
||||
"""
|
||||
def show_about():
|
||||
logging.info("Show About dialog")
|
||||
about_dialog = AboutDialog(about_dialog_glade, SAFE_EYES_VERSION)
|
||||
about_dialog.show()
|
||||
|
||||
"""
|
||||
Receive the signal from core and pass it to the Notification.
|
||||
"""
|
||||
def show_notification():
|
||||
notification.show(config['pre_break_warning_time'])
|
||||
|
||||
"""
|
||||
Receive the break signal from core and pass it to the break screen.
|
||||
"""
|
||||
def show_alert(message):
|
||||
logging.info("Show the break screen")
|
||||
notification.close()
|
||||
break_screen.show_message(message)
|
||||
|
||||
"""
|
||||
Receive the stop break signal from core and pass it to the break screen.
|
||||
"""
|
||||
def close_alert():
|
||||
logging.info("Close the break screen")
|
||||
break_screen.close()
|
||||
if config['audible_alert']:
|
||||
Utility.play_notification()
|
||||
|
||||
"""
|
||||
Receive the count from core and pass it to the break screen.
|
||||
"""
|
||||
def on_countdown(count):
|
||||
break_screen.show_count_down(count)
|
||||
|
||||
"""
|
||||
Listen to the tray menu quit action and stop the core, notification and the app itself.
|
||||
"""
|
||||
def on_quit():
|
||||
logging.info("Quit Safe Eyes")
|
||||
core.stop()
|
||||
notification.quite();
|
||||
Gtk.main_quit()
|
||||
|
||||
"""
|
||||
If the system goes to sleep, Safe Eyes stop the core if it is already active.
|
||||
If it was active, Safe Eyes will become active after wake up.
|
||||
"""
|
||||
def handle_suspend_callback(sleeping):
|
||||
if sleeping:
|
||||
# Sleeping / suspending
|
||||
if is_active:
|
||||
core.stop()
|
||||
logging.info("Stopped Safe Eyes due to system suspend")
|
||||
else:
|
||||
# Resume from sleep
|
||||
if is_active:
|
||||
core.start()
|
||||
logging.info("Resumed Safe Eyes after system wakeup")
|
||||
|
||||
"""
|
||||
Setup system suspend listener.
|
||||
"""
|
||||
def handle_system_suspend():
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
bus = dbus.SystemBus()
|
||||
bus.add_signal_receiver(handle_suspend_callback, 'PrepareForSleep', 'org.freedesktop.login1.Manager', 'org.freedesktop.login1')
|
||||
|
||||
"""
|
||||
Listen to break screen Skip action and send the signal to core.
|
||||
"""
|
||||
def on_skipped():
|
||||
logging.info("User skipped the break")
|
||||
core.skip_break()
|
||||
|
||||
"""
|
||||
Listen to Settings dialog Save action and write to the config file.
|
||||
"""
|
||||
def save_settings(config):
|
||||
global language
|
||||
|
||||
logging.info("Saving settings to safeeyes.json")
|
||||
|
||||
# Stop the Safe Eyes core
|
||||
if is_active:
|
||||
core.stop()
|
||||
|
||||
# Write the configuration to file
|
||||
with open(config_file_path, 'w') as config_file:
|
||||
json.dump(config, config_file, indent=4, sort_keys=True)
|
||||
|
||||
# Reload the language translation
|
||||
language_file_path = os.path.join(system_language_directory, str(config['language']) + '.json')
|
||||
with open(language_file_path) as language_file:
|
||||
language = json.load(language_file)
|
||||
|
||||
tray_icon.set_labels(language)
|
||||
|
||||
logging.info("Initialize SafeEyesCore with modified settings")
|
||||
|
||||
# Restart the core and intialize the components
|
||||
core.initialize(config, language)
|
||||
break_screen.initialize(config, language)
|
||||
if is_active:
|
||||
# 1 sec delay is required to give enough time for core to be stopped
|
||||
Timer(1.0, core.start).start()
|
||||
|
||||
"""
|
||||
Listen to tray icon enable action and send the signal to core.
|
||||
"""
|
||||
def enable_safeeyes():
|
||||
global is_active
|
||||
is_active = True
|
||||
core.start()
|
||||
|
||||
"""
|
||||
Listen to tray icon disable action and send the signal to core.
|
||||
"""
|
||||
def disable_safeeyes():
|
||||
global is_active
|
||||
is_active = False
|
||||
core.stop()
|
||||
|
||||
"""
|
||||
Initialize the configuration directory and copy the files to ~/.config directory.
|
||||
"""
|
||||
def initialize_config():
|
||||
global config
|
||||
config_dir_path = os.path.join(os.path.expanduser('~'), '.config/safeeyes/style')
|
||||
startup_dir_path = os.path.join(os.path.expanduser('~'), '.config/autostart')
|
||||
|
||||
Utility.mkdir(config_dir_path)
|
||||
|
||||
if not os.path.isfile(config_file_path):
|
||||
shutil.copy2(system_config_file_path, config_file_path)
|
||||
|
||||
Utility.mkdir(startup_dir_path)
|
||||
|
||||
# Add to startup for the first time only
|
||||
try:
|
||||
os.symlink("/usr/share/applications/safeeyes.desktop", os.path.join(startup_dir_path, "safeeyes.desktop"))
|
||||
except OSError as exc:
|
||||
pass
|
||||
|
||||
if not os.path.isfile(style_sheet_path):
|
||||
shutil.copy2(system_style_sheet_path, style_sheet_path)
|
||||
|
||||
# Read the configuration
|
||||
with open(config_file_path) as config_file:
|
||||
config = json.load(config_file)
|
||||
|
||||
"""
|
||||
Configuration file has a version config_version.
|
||||
It is used to overwrite the exsiting config file if there is an update.
|
||||
Earlier versions did not have this attribute so the following method
|
||||
checks the version and if it mismatches, it will overwrite the exsiting
|
||||
config files. If the version property is not available, the file is
|
||||
considered as an older one and replaced by the new configuration file.
|
||||
"""
|
||||
def validate_config():
|
||||
version_mismatch = False
|
||||
try:
|
||||
# Check the config version
|
||||
config_version = config['meta']['config_version']
|
||||
version_mismatch = config_version is not CONFIGURATION_VERSION
|
||||
except:
|
||||
version_mismatch = True
|
||||
|
||||
if version_mismatch:
|
||||
# Remove ~/.config/safeeyes directory
|
||||
try:
|
||||
shutil.rmtree(os.path.join(os.path.expanduser('~'), '.config/safeeyes'), ignore_errors=False)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Remove startup script
|
||||
try:
|
||||
os.remove(os.path.join(os.path.expanduser('~'), '.config/autostart/safeeyes.desktop'))
|
||||
except:
|
||||
pass
|
||||
|
||||
# Create config files again
|
||||
initialize_config()
|
||||
|
||||
"""
|
||||
Read all the language translations and build a key-value mapping of language names
|
||||
in English and ISO 639-1 (Filename without extension).
|
||||
"""
|
||||
def read_lang_files():
|
||||
languages = {}
|
||||
for lang_file_name in os.listdir(system_language_directory):
|
||||
lang_file_path = os.path.join(system_language_directory, lang_file_name)
|
||||
if os.path.isfile(lang_file_path):
|
||||
with open(lang_file_path) as lang_file:
|
||||
lang = json.load(lang_file)
|
||||
languages[lang_file_name.lower().replace(".json", "")] = lang['meta_info']['language_name']
|
||||
|
||||
return languages
|
||||
|
||||
def main():
|
||||
initialize_config()
|
||||
|
||||
# Configure logging. Reset with every restart
|
||||
logging.basicConfig(format='%(asctime)s [%(levelname)s] %(message)s', filename=log_file_path, filemode='w', level=logging.INFO)
|
||||
logging.info("Starting Safe Eyes")
|
||||
|
||||
validate_config()
|
||||
|
||||
global break_screen
|
||||
global core
|
||||
global notification
|
||||
global tray_icon
|
||||
global language
|
||||
|
||||
language_file_path = os.path.join(system_language_directory, str(config['language']) + '.json')
|
||||
with open(language_file_path) as language_file:
|
||||
language = json.load(language_file)
|
||||
|
||||
tray_icon = TrayIcon(config, language, show_settings, show_about, enable_safeeyes, disable_safeeyes, on_quit)
|
||||
break_screen = BreakScreen(on_skipped, break_screen_glade, style_sheet_path)
|
||||
break_screen.initialize(config, language)
|
||||
core = SafeEyesCore(show_notification, show_alert, close_alert, on_countdown, tray_icon.next_break_time)
|
||||
core.initialize(config, language)
|
||||
core.start()
|
||||
notification = Notification(language)
|
||||
|
||||
handle_system_suspend()
|
||||
|
||||
Gtk.main()
|
||||
|
||||
main()
|
|
@ -1,16 +0,0 @@
|
|||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Name=Safe Eyes
|
||||
Comment=Protect your eyes from eye strain
|
||||
Comment[de]=Schützt die Augen vor Überanstrengung
|
||||
Comment[cs]=Chraňte své oči před únavou
|
||||
Comment[fr]=Protégez vos yeux de la fatigue
|
||||
Comment[ta]=உங்கள் கண்களை சோர்வடையாது பாதுகாத்திடுங்கள்
|
||||
Comment[pt]=Proteja seus olhos da tensão ocular
|
||||
Comment[tr]=Gözünüzü yorgunluğa karşı koruyun
|
||||
Exec=/opt/safeeyes/safeeyes
|
||||
Icon=safeeyes
|
||||
Version=1.1.4
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;
|
|
@ -0,0 +1,63 @@
|
|||
import os
|
||||
|
||||
import setuptools
|
||||
|
||||
|
||||
requires = [
|
||||
'python-xlib',
|
||||
'pyaudio',
|
||||
'psutil',
|
||||
'babel']
|
||||
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
with open(os.path.join(here, 'README.md')) as f:
|
||||
long_description = '\n' + f.read()
|
||||
|
||||
def _data_files(path):
|
||||
for root, dirs, files in os.walk(path):
|
||||
if not files:
|
||||
continue
|
||||
yield (os.path.join('/usr', root), [os.path.join(root, f) for f in files])
|
||||
|
||||
setuptools.setup(
|
||||
name="safeeyes",
|
||||
version="1.2.1",
|
||||
description="Protect your eyes from eye strain using this continuous breaks reminder.",
|
||||
long_description=long_description,
|
||||
author="Gobinath Loganathan",
|
||||
author_email="slgobinath@gmail.com",
|
||||
url="https://github.com/slgobinath/SafeEyes",
|
||||
download_url="https://github.com/slgobinath/SafeEyes/archive/v1.2.1.tar.gz",
|
||||
packages=setuptools.find_packages(),
|
||||
package_data={'safeeyes': ['config/*.json',
|
||||
'config/style/*.css',
|
||||
'config/lang/*.json',
|
||||
'glade/*.glade',
|
||||
'resource/*']},
|
||||
data_files=[('/usr/share/applications', ['share/applications/safeeyes.desktop']),
|
||||
('/usr/share/icons/hicolor/16x16/apps', ['share/icons/hicolor/16x16/apps/safeeyes.png']),
|
||||
('/usr/share/icons/hicolor/24x24/apps', ['share/icons/hicolor/24x24/apps/safeeyes.png']),
|
||||
('/usr/share/icons/hicolor/48x48/apps', ['share/icons/hicolor/48x48/apps/safeeyes.png']),
|
||||
('/usr/share/icons/hicolor/32x32/apps', ['share/icons/hicolor/32x32/apps/safeeyes.png']),
|
||||
('/usr/share/icons/hicolor/64x64/apps', ['share/icons/hicolor/64x64/apps/safeeyes.png']),
|
||||
('/usr/share/icons/hicolor/128x128/apps', ['share/icons/hicolor/128x128/apps/safeeyes.png']),
|
||||
('/usr/share/icons/hicolor/48x48/status', ['share/icons/hicolor/48x48/status/safeeyes_enabled.png', 'share/icons/hicolor/48x48/status/safeeyes_disabled.png']),
|
||||
('/usr/share/icons/hicolor/32x32/status', ['share/icons/hicolor/32x32/status/safeeyes_enabled.png', 'share/icons/hicolor/32x32/status/safeeyes_disabled.png']),
|
||||
('/usr/share/icons/hicolor/24x24/status', ['share/icons/hicolor/24x24/status/safeeyes_enabled.png', 'share/icons/hicolor/24x24/status/safeeyes_disabled.png', 'share/icons/hicolor/24x24/status/safeeyes_timer.png']),
|
||||
('/usr/share/icons/hicolor/16x16/status', ['share/icons/hicolor/16x16/status/safeeyes_enabled.png', 'share/icons/hicolor/16x16/status/safeeyes_disabled.png', 'share/icons/hicolor/16x16/status/safeeyes_timer.png'])
|
||||
],
|
||||
install_requires=requires,
|
||||
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 :: 3 - Alpha",
|
||||
"Environment :: X11 Applications :: GTK",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"Topic :: Utilities"] + [
|
||||
('Programming Language :: Python :: %s' % x) for x in
|
||||
'3 3.4 3.5 3.6'.split()]
|
||||
)
|
|
@ -0,0 +1,21 @@
|
|||
[Desktop Entry]
|
||||
Name=Safe Eyes
|
||||
Comment=Protect your eyes from eye strain
|
||||
Comment[ge]=დაიცავით თქვენი თვალები დაღლილობისაგან
|
||||
Comment[de]=Schützt die Augen vor Überanstrengung
|
||||
Comment[cs]=Chraňte své oči před únavou
|
||||
Comment[fr]=Protégez vos yeux de la fatigue
|
||||
Comment[id]=Melindungi mata Anda dari kelelahan
|
||||
Comment[ta]=உங்கள் கண்களை சோர்வடையாது பாதுகாத்திடுங்கள்
|
||||
Comment[pt]=Proteja seus olhos da tensão ocular
|
||||
Comment[tr]=Gözünüzü yorgunluğa karşı koruyun
|
||||
Comment[hi]=तनाव से आंखों की रक्षा
|
||||
Comment[es]=Protege tus ojos de la fatiga ocular
|
||||
Comment[ru]=Защитите свои глаза от зрительного перенапряжения
|
||||
Comment[pl]=Chroń oczy przed zmęczeniem
|
||||
Comment[mk]=Заштитете се од замор на очите
|
||||
Exec=env GDK_BACKEND=x11 safeeyes
|
||||
Icon=safeeyes
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 649 B After Width: | Height: | Size: 649 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 640 B After Width: | Height: | Size: 640 B |
After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 902 B After Width: | Height: | Size: 902 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 894 B After Width: | Height: | Size: 894 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |