#!/usr/bin/env python #---------------------------------------------------- # Author: ZaB|SHC|@irc.freenode.net # # License: Beerware #---------------------------------------------------- """ 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('%s' % 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)