Add image to break screen

This commit is contained in:
Gobinath 2017-04-07 16:20:23 -04:00
parent 6c5faca8ad
commit af8e9d51ab
16 changed files with 265 additions and 133 deletions

View File

@ -97,8 +97,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):
GLib.idle_add(lambda: self.__show_break_screen(message, image_path))
"""
@ -115,7 +115,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):
# Lock the keyboard
thread = threading.Thread(target=self.__lock_keyboard)
thread.start()
@ -138,12 +138,11 @@ class BreakScreen:
lbl_count = builder.get_object("lbl_count")
btn_skip = builder.get_object("btn_skip")
btn_postpone = builder.get_object("btn_postpone")
lbl_left = builder.get_object("lbl_left")
lbl_right = builder.get_object("lbl_right")
img_break = builder.get_object("img_break")
lbl_message.set_label(message)
btn_skip.set_label(self.skip_button_text)
btn_skip.set_visible(not self.strict_break)
btn_postpone.set_label(self.postpone_button_text)
btn_postpone.set_visible(not self.strict_break)
window.move(x, y)
self.windows.append(window)
self.count_labels.append(lbl_count)
@ -151,12 +150,26 @@ class BreakScreen:
# Set visual to apply css theme. It should be called before show method.
window.set_visual(window.get_screen().get_rgba_visual())
window.move(x, y)
window.stick()
window.set_keep_above(True)
window.present()
window.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
window.resize(monitor_gemoetry.width, monitor_gemoetry.height)
window.fullscreen()
# 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_postpone.set_label(self.postpone_button_text)
# Set the visibility of buttons
btn_postpone.set_visible(not self.strict_break)
btn_skip.set_visible(not self.strict_break)
window.present()
"""
Update the countdown on all break screens.

View File

@ -78,12 +78,14 @@ class SafeEyesCore:
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, audible_alert])
self.short_break_exercises.append([name, break_time, audible_alert, image])
for long_break_config in config['long_breaks']:
exercise_name = long_break_config['name']
@ -96,13 +98,14 @@ class SafeEyesCore:
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])
self.long_break_exercises.append([name, break_time, audible_alert, image])
"""
@ -270,6 +273,7 @@ class SafeEyesCore:
# User can disable SafeEyes during notification
if self.__is_running():
message = ""
image = None
seconds = 0
audible_alert = None
if self.__is_long_break():
@ -278,15 +282,17 @@ class SafeEyesCore:
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]
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]
# 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 and not self.postponed:

View File

@ -25,9 +25,10 @@ import 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')
"""
Play the alert.mp3
Play the alert.wav
"""
def play_notification():
logging.info("Playing audible alert")
@ -35,7 +36,9 @@ def play_notification():
try:
# Open the sound file
path = os.path.join(bin_directory, 'resource','alert.wav')
path = get_resource_path('alert.wav')
if path is None:
return
sound = wave.open(path, 'rb')
# Create a sound stream
@ -61,6 +64,23 @@ def play_notification():
logging.warning('Unable to play audible alert')
logging.exception(e)
"""
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.
"""
def get_resource_path(resource_name):
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
"""
Get system idle time in minutes.
Return the idle time if xprintidle is available, otherwise return 0.
@ -97,33 +117,38 @@ def execute_main_thread(target_function, args=None):
def is_active_window_skipped(skip_break_window_classes, take_break_window_classes, unfullscreen_allowed=False):
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, '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:
screen.get_active_window().unfullscreen()
except:
logging.error('Error in unfullscreen the window ' + process)
pass
return False
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']
return is_fullscreen
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
"""

View File

@ -43,7 +43,7 @@ system_style_sheet_path = os.path.join(Utility.bin_directory, "config/style/safe
is_active = True
CONFIGURATION_VERSION = 4
SAFE_EYES_VERSION = "1.1.8"
SAFE_EYES_VERSION = "1.2.0"
"""
Listen to tray icon Settings action and send the signal to Settings dialog.
@ -72,10 +72,10 @@ def show_notification():
"""
Receive the break signal from core and pass it to the break screen.
"""
def show_alert(message):
def show_alert(message, image_name):
logging.info("Show the break screen")
notification.close()
break_screen.show_message(message)
break_screen.show_message(message, Utility.get_resource_path(image_name))
if config['strict_break'] and is_active:
Utility.execute_main_thread(tray_icon.unlock_menu)
@ -258,7 +258,7 @@ def running():
try:
# Check if safeeyes is in process arguments
cmd_line = proc.cmdline()
if 'python2' == cmd_line[0] and 'safeeyes' in cmd_line[1]:
if 'python3' == cmd_line[0] and 'safeeyes' in cmd_line[1]:
process_count += 1
if process_count > 1:
return True

View File

@ -40,33 +40,42 @@
],
"short_breaks": [
{
"name": "short_break_close_eyes"
"name": "short_break_close_eyes",
"image": "short_break_close_eyes.png"
},
{
"name": "short_break_roll_eyes"
"name": "short_break_roll_eyes",
"image": "short_break_roll_eyes.png"
},
{
"name": "short_break_rotate_clockwise"
"name": "short_break_rotate_clockwise",
"image": "short_break_rotate_clockwise.png"
},
{
"name": "short_break_rotate_counter_clockwise"
"name": "short_break_rotate_counter_clockwise",
"image": "short_break_rotate_counter_clockwise.png"
},
{
"name": "short_break_blink"
"name": "short_break_blink",
"image": "short_break_blink.png"
},
{
"name": "short_break_focus_far_distance"
"name": "short_break_focus_far_distance",
"image": "short_break_focus_far_distance.png"
},
{
"name": "short_break_drink_water"
"name": "short_break_drink_water",
"image": "short_break_drink_water.png"
}
],
"long_breaks": [
{
"name": "long_break_walk"
"name": "long_break_walk",
"image": "long_break_walk.png"
},
{
"name": "long_break_lean_back"
"name": "long_break_lean_back",
"image": "long_break_lean_back.png"
}
],
"custom_exercises": {

View File

@ -71,3 +71,9 @@
font-size: 12pt;
color: white;
}
.panel_left {
}
.panel_right {
}

View File

@ -1,6 +1,5 @@
<?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.
@ -20,7 +19,6 @@
~ 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">
@ -33,120 +31,195 @@
<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="GtkGrid" id="grid_parent">
<object class="GtkBox" id="box1">
<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">
<object class="GtkLabel" id="lbl_left">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Hello World</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="lbl_message"/>
<class name="panel_left"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">3</property>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</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="GtkBox" id="box_buttons">
<object class="GtkGrid" id="grid1">
<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>
<property name="valign">center</property>
<property name="row_spacing">10</property>
<child>
<object class="GtkButton" id="btn_postpone">
<property name="label" translatable="yes">Postpone</property>
<object class="GtkGrid" id="grid_parent">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<signal name="clicked" handler="on_postpone_clicked" swapped="no"/>
<style>
<class name="btn_postpone"/>
</style>
<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="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>
<object class="GtkButton" id="btn_postpone">
<property name="label" translatable="yes">Postpone</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="valign">center</property>
<signal name="clicked" handler="on_postpone_clicked" swapped="no"/>
<style>
<class name="btn_postpone"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</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="valign">center</property>
<signal name="clicked" handler="on_skip_clicked" swapped="no"/>
<style>
<class name="btn_skip"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</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="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="height">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="btn_skip">
<property name="label" translatable="yes">Skip</property>
<object class="GtkImage" id="img_break">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="valign">center</property>
<signal name="clicked" handler="on_skip_clicked" swapped="no"/>
<style>
<class name="btn_skip"/>
</style>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
<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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 975 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB