summaryrefslogtreecommitdiffstats
path: root/python/openbox-shutdown.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/openbox-shutdown.py')
-rwxr-xr-xpython/openbox-shutdown.py190
1 files changed, 190 insertions, 0 deletions
diff --git a/python/openbox-shutdown.py b/python/openbox-shutdown.py
new file mode 100755
index 0000000..cfdd67b
--- /dev/null
+++ b/python/openbox-shutdown.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python
+"""
+
+shutdown script for Openbox with configurable buttons and actions
+needs pygtk for gui
+"""
+
+# standard stuff
+import os
+import re
+import sys
+
+# gui modules
+try:
+ import gtk
+ import pygtk
+except ImportError:
+ sys.stderr.write('pygtk is needed for using this script')
+ sys.exit(1)
+
+if sys.version_info < (2, 5, 0):
+ sys.stderr.write('Python 2.5.0 or higher is needed')
+ sys.exit(1)
+
+# a simple parser for the configuration
+class ConfigParser(dict):
+ """
+ ConfigParser(file) -> dict
+
+ reads file and creates dict object containing corresponding
+ configuration
+ """
+
+ def __init__(self, filename):
+ self._section_match_regexp = re.compile(r'\[(\w+)\]')
+ self._key_val_match_regexp = re.compile(r'\s*=\s*')
+ self._comment_remov_regexp = re.compile(r'#.*')
+ try:
+ config_handle = open(filename, 'r')
+ except IOError:
+ sys.stderr.write('Could not read %s. Aborting ...\n' % filename)
+ sys.exit(1)
+ linecount = 0
+ for line in config_handle.readlines():
+ linecount += 1
+ line = self._comment_remov_regexp.sub('', line.strip())
+ if len(line) == 0:
+ continue
+ if self._section_match_regexp.match(line):
+ section = self._section_match_regexp.match(line).groups()[0]
+ self[section] = {}
+ continue
+ try:
+ (key, value) = self._key_val_match_regexp.split(line)
+ except ValueError:
+ sys.stderr.write('Syntax error in line %d of %s.' % (linecount,
+ filename))
+ sys.exit(1)
+ if key == 'command' and not key in self[section]:
+ self[section][key] = [value]
+ continue
+ if key in self[section] and key == 'command':
+ self[section][key].append(value)
+ else:
+ self[section][key] = value
+
+ def check(self):
+ """
+ checks if configuration is complete
+ """
+ for section in self:
+ for needed_key in ['command', 'image', 'info_text', 'label']:
+ if not needed_key in self[section]:
+ sys.stderr.write('Missing option %s in %s.' % (needed_key, section))
+ sys.exit(1)
+
+# the main class for this script
+class OpenBoxShutdown(object):
+ """
+ main class for this script
+
+ reads configuration and displays buttons as requested
+
+ on click, the specified actions are made
+ """
+
+ def __init__(self, basedir):
+ self._config = ConfigParser(os.path.join(basedir, 'openbox-shutdown.conf'))
+ self._config.check()
+ # button box on top for the action buttons
+ button_box = gtk.HButtonBox()
+ for key in self._config:
+ button_box.pack_start(self._create_button(key,
+ self._config[key]['label'],
+ self._config[key]['info_text'],
+ self._config[key]['image']))
+ button_box.show()
+ # create cancel button
+ cancel_button = gtk.Button(stock = gtk.STOCK_CANCEL)
+ cancel_button.connect('clicked', self._click_callback, 'cancel')
+ cancel_button.show()
+ # label widget for small hints
+ self._label = gtk.Label()
+ self._label.show()
+ # frame widget to contain label
+ frame = gtk.Frame()
+ frame.set_shadow_type(gtk.SHADOW_NONE)
+ frame.add(self._label)
+ frame.show()
+ # area containing hints and cancel button
+ action_area = gtk.HBox()
+ action_area.pack_end(cancel_button, expand=False)
+ action_area.pack_end(frame)
+ action_area.show()
+ # box containing button box and action area
+ vbox = gtk.VBox()
+ vbox.pack_start(button_box)
+ vbox.pack_end(action_area)
+ vbox.show()
+ # the main window with all widgets
+ # always on top and on all desktops
+ window = gtk.Window()
+ window.set_position('center')
+ window.set_decorated(False)
+ window.stick()
+ window.set_keep_above(True)
+ window.set_skip_pager_hint(True)
+ window.set_skip_taskbar_hint(True)
+ window.add(vbox)
+ window.show()
+ # give control to gtk
+ gtk.main()
+
+ def _click_callback(self, event, action):
+ """
+ _click_callback(event, action)
+
+ executes commands on button click
+ """
+ gtk.main_quit()
+ if action == 'cancel':
+ sys.exit(0)
+ for command in self._config[action]['command']:
+ os.system(command)
+
+ def _create_button(self, key, label_text, info_text, image_file):
+ """
+ _create_button(key, label, info, image) -> button
+
+ creates a button widget with key (used for handling actions),
+ a label for the button, a small text as hint and an image
+ """
+ button = gtk.Button()
+ if label_text != 'None':
+ button.set_label(label_text)
+ if image_file != 'None':
+ button.set_image(self._create_image(image_file))
+ button.set_image_position(gtk.POS_TOP)
+ button.connect('clicked', self._click_callback, key)
+ button.connect('leave', lambda event: self._label.set_text(''))
+ button.connect('enter', self._set_label, info_text)
+ button.show()
+ return button
+
+ def _create_image(self, image_file):
+ """
+ _create_image(image) -> image
+
+ creates an image widget from the specified file
+ """
+ image = gtk.Image()
+ image.set_from_file(image_file)
+ return image
+
+ def _set_label(self, event, info_text):
+ """
+ _set_label(event, info_text)
+
+ set label on entering the widget
+ """
+ if info_text != 'None':
+ self._label.set_markup('<span size="smaller">%s</span>' % info_text)
+
+# start the script if called standalone
+if __name__ == '__main__':
+ # get the real path for the configuration file
+ conf_path = os.path.realpath(os.path.dirname(os.readlink(__file__))) \
+ if os.path.islink(__file__) \
+ else os.path.realpath(os.path.dirname(__file__))
+ shutdown = OpenBoxShutdown(conf_path)