You are not logged in.
Very nice indeed, Merlin. Thanks
Why not create a pull request for it on github
Offline
Will do that, first, have a look at the code:
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
from __future__ import print_function
import os
display = os.environ.get('DISPLAY') is not None
import sys
import getpass
import subprocess
import dbus
__me__ = 'bl-exit'
__version__ = '2.0.0'
# Translate command-line option to method - command line only
actionToMethod = dict(
logout='Logout', l='Logout',
suspend='Suspend', s='Suspend',
hybridsleep='HybridSleep', y='HybridSleep',
hibernate='Hibernate', i='Hibernate',
reboot='Reboot', b='Reboot',
poweroff='PowerOff', p='PowerOff'
)
class CanDoItError(Exception):
pass
class BlexitBase(object):
def __init__(self):
self.dbus_iface = None
def setup_dbus_connection(self):
try:
bus = dbus.SystemBus()
dbus_object = bus.get_object('org.freedesktop.login1',
'/org/freedesktop/login1')
self.dbus_iface = dbus.Interface(dbus_object,
'org.freedesktop.login1.Manager')
except bus.DBusException as e:
self.on_error(str(e))
def can_do_action(self, action):
# There is no 'CanLogout' method
if action == "Logout":
return "yes"
actionMethod = "Can{}".format(action)
response = self.send_dbus(actionMethod)
return str(response)
def do_action(self, action):
print("do_action: {}".format(action), file=sys.stderr)
self.send_dbus(action)
def send_dbus(self, method):
try:
if self.dbus_iface is None:
self.setup_dbus_connection()
if method[:3] == "Can":
command = "self.dbus_iface.{}()".format(method)
else:
command = "self.dbus_iface.{}(['True'])".format(method)
response = eval(command)
return str(response)
except dbus.DBusException as e:
self.on_error(str(e))
def on_error(self, string):
print ("{}".format(string), file=sys.stderr)
sys.exit(1)
def openbox_exit(self):
subprocess.check_output(["openbox", "--exit"])
def logout(self):
try:
self.openbox_exit()
except subprocess.CalledProcessError as e:
self.on_error(e.output)
def action_from_command_line(self, action):
try:
self.do_action(action)
except (subprocess.CalledProcessError, CanDoItError, KeyError) as e:
self.on_error(str(e))
def main(self):
opts = get_options()
if opts.logout:
self.logout()
else:
if opts.suspend:
action = "suspend"
elif opts.hibernate:
action = "hibernate"
elif opts.hybridsleep:
action = "hybridsleep"
elif opts.reboot:
action = "reboot"
elif opts.poweroff:
action = "poweroff"
self.setup_dbus_connection()
self.action_from_command_line(actionToMethod[action])
if display:
"""Testing for display here because we want to be able to run the script
in a non-graphical environment as well. Importing gtk.Window on the console
spits out some errors and crashes the application."""
import gtk
import pygtk
pygtk.require('2.0')
import ConfigParser
from time import sleep
class BlexitWindow(BlexitBase, gtk.Window):
"""A dialog offering the user to log out, suspend, reboot or shut down.
"""
def __init__(self):
BlexitBase.__init__(self)
gtk.Window.__init__(self)
def configure(self):
"""Determine config directory: first try the environment variable
XDG_CONFIG_HOME according to XDG specification and as a fallback
use ~/.config/bl-exit. Use /etc/bl-exit/bl-exitrc as a last
resort."""
config_dirs = []
xdg_config_dir = os.getenv('XDG_CONFIG_HOME')
if xdg_config_dir:
config_dirs.append(xdg_config_dir)
user_config_dir = os.path.expanduser('~/.config')
try:
if not (xdg_config_dir and os.path.samefile(user_config_dir,
xdg_config_dir)):
config_dirs.append(user_config_dir)
except OSError as e:
print ("{}: {}".format(__me__, str(e)), file=sys.stderr)
pass
config_dirs.append('/etc')
config_file = None
for config_dir in config_dirs:
config_dir = config_dir + '/bl-exit'
if os.path.isdir(config_dir):
maybe_config_file = config_dir + '/bl-exitrc'
if os.path.isfile(maybe_config_file):
config_file = maybe_config_file
self.saved_config_dir = config_dir
break
if config_file:
try:
self.config = ConfigParser.RawConfigParser()
self.config.read(config_file)
except ConfigParser.ParsingError as e:
print ("{}: {}".format(__me__, str(e)), file=sys.stderr)
self.config = None
sys.exit(1)
else:
self.config = None
self.dialogHeight = 120
self.dialogBackgroundColor = '#222222'
self.buttonColorStateNormal = '#222222'
self.buttonColorStateActive = '#222222'
self.buttonColorStatePrelight= '#222222'
self.buttonSpacing = 10
self.sleepDelay = 0.003
try:
try:
print( 'Loading '+self.config.get('theme', 'name')+' by '+ self.config.get('theme', 'author'))
self.dialogHeight = int(self.config.get('theme', 'dialogHeight'))
self.windowBackgroundColor = self.config.get('theme', 'windowBackgroundColor')
self.buttonColorStateNormal = self.config.get('theme', 'buttonColorStateNormal')
self.buttonColorStateActive = self.config.get('theme', 'buttonColorStateActive')
self.buttonColorStatePrelight = self.config.get('theme', 'buttonColorStatePrelight')
self.buttonSpacing = int(self.config.get('theme', 'buttonSpacing'))
self.sleepDelay = float( self.config.get('theme', 'sleepDelay') )
except (ConfigParser.NoOptionError) as e:
print(e)
except (ConfigParser.NoSectionError) as e:
print(e)
def construct_ui(self):
self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.dialogBackgroundColor))
self.set_resizable(False)
self.set_keep_above(True)
self.stick
self.set_decorated(False)
self.set_position(gtk.WIN_POS_CENTER)
self.connect("delete_event", gtk.main_quit)
self.set_size_request(gtk.gdk.screen_width(), self.dialogHeight)
# Cancel key (Escape)
accelgroup = gtk.AccelGroup()
key, mod = gtk.accelerator_parse('Escape')
accelgroup.connect_group(key, mod, gtk.ACCEL_VISIBLE, gtk.main_quit)
self.add_accel_group(accelgroup)
self.button_box = gtk.HBox()
self.button_box.set_size_request(-1, self.dialogHeight)
self.button_box.set_spacing(self.buttonSpacing)
self.add_button(1, self.cancel_action, stock='cancel')
self.build_button_visibility_array()
for button in self.bva:
(action, label, actionfunc, method, show, onError) = button
if not show == 0:
self.add_button(show, actionfunc, label=label, btype=action)
self.status = gtk.Label()
vbox = gtk.VBox()
vbox.pack_start(self.button_box)
self.add(vbox)
self.set_opacity(0)
self.show_all()
for o in range(1,100):
sleep(self.sleepDelay)
while gtk.events_pending():
gtk.main_iteration(False)
self.set_opacity(float(o)/100)
#Don't use anything else!
self.set_keep_above(True)
def build_button_visibility_array(self):
"""Determine button visibily using bl-exit configuration file.
Build self.bva, an array of tuples, one entry per button,
containing (action, label, actionfunction, actionMethod, show,
onerror)
"""
self.bva = []
bva = [('logout', '_Log out', self.logout_action),
('suspend', '_Suspend', self.suspend_action),
('hibernate', 'H_ibernate', self.hibernate_action),
('hybridsleep', 'H_ybrid sleep', self.hybridsleep_action),
('reboot', 'Re_boot', self.reboot_action),
('poweroff', '_Power off', self.shutdown_action)]
show_values = dict(never=0, always=1, maybe=2)
"""Values that the 'show' keyword can take in the configuration
file."""
onerror_values = dict(novisual=0, visual=1)
"""Values that the 'onerror' keyword can take in the configuration
file."""
# Per button default settings
per_button_defaults = dict(
logout='always',
suspend='always',
hibernate='never',
hybridsleep='never',
reboot='always',
poweroff='always'
)
for (action, label, actionfunction) in bva:
# Defaults.
show = show_values[per_button_defaults[action]]
onError = onerror_values['novisual']
if self.config:
for section in ['default', action]:
try:
try:
getshow = self.config.get(section, 'show')
if getshow in show_values:
show = show_values[getshow]
if show == 2:
candoit = self.can_do_action(
actionToMethod[action])
if not candoit == 'yes':
show = 3
except ConfigParser.NoOptionError as e:
pass
try:
getonerror = self.config.get(section,
'onerror')
if getonerror in onerror_values:
onError = onerror_values[getonerror]
except ConfigParser.NoOptionError as e:
pass
except ConfigParser.NoSectionError as e:
pass
self.bva.append(tuple([action, label, actionfunction,
actionToMethod[action], show,
onError]))
def main(self):
self.configure()
self.construct_ui()
gtk.main()
def add_button(self, show, action, label=None, stock=None, btype=None):
if stock is not None:
btype = stock
button = gtk.ColorButton(gtk.gdk.color_parse(self.dialogBackgroundColor))
#button.set_size_request(100, 40)
button.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.buttonColorStateNormal))
button.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse(self.buttonColorStateActive))
button.modify_bg(gtk.STATE_PRELIGHT, gtk.gdk.color_parse(self.buttonColorStatePrelight))
#No borders
button.set_relief(gtk.RELIEF_NONE)
image = gtk.Image()
image.set_from_file(self.saved_config_dir+"/"+str(btype)+".png")
button.set_image(image)
if show == 3:
button.set_sensitive(False)
button.set_border_width(0)
button.connect("clicked", action)
self.button_box.pack_start(button)
def disable_buttons(self):
self.button_box.foreach(lambda button:
button.set_sensitive(False))
def cancel_action(self, button):
self.disable_buttons()
gtk.main_quit()
def get_onerror(self):
for item in self.bva:
(action, label, actionfunction, actionMethod, show,
onerror) = item
if action == self.selected_action:
return onerror
def on_error(self, e):
onerror = self.get_onerror()
if onerror == 0:
print ("{}: {}".format(__me__, str(e)), file=sys.stderr)
sys.exit(1)
else:
emDialog = gtk.MessageDialog(parent=None, flags=0,
type=gtk.MESSAGE_INFO,
buttons=gtk.BUTTONS_OK,
message_format=None)
if emDialog:
emDialog.set_markup("{}".format(e))
emDialog.run()
def logout_action(self, button):
self.disable_buttons()
self.selected_action = 'logout'
self.status.set_label("Exiting Openbox, please standby...")
self.openbox_exit()
gtk.main_quit()
def suspend_action(self, button):
self.disable_buttons()
self.selected_action = 'suspend'
self.status.set_label("Suspending, please standby...")
self.do_action("Suspend")
gtk.main_quit()
def hibernate_action(self, button):
self.disable_buttons()
self.selected_action = 'hibernate'
self.status.set_label("Hibernating, please standby...")
self.do_action("Hibernate")
gtk.main_quit()
def hybridsleep_action(self, button):
self.disable_buttons()
self.selected_action = 'hybridsleep'
self.status.set_label("Hibernating + Sleeping, please standby...")
self.do_action("HybridSleep")
gtk.main_quit()
def reboot_action(self, button):
self.disable_buttons()
self.selected_action = 'reboot'
self.status.set_label("Rebooting, please standby...")
self.do_action("Reboot")
gtk.main_quit()
def shutdown_action(self, button):
self.disable_buttons()
self.selected_action = 'poweroff'
self.status.set_label("Shutting down, please standby...")
self.do_action("PowerOff")
gtk.main_quit()
def get_options():
result = None
import argparse
parser = argparse.ArgumentParser(description="Bunsenlabs exit")
if display:
parser.add_argument("-l", "--logout", help="Log out",
action="store_true")
parser.add_argument("-s", "--suspend", help="Suspend",
action="store_true")
parser.add_argument("-i", "--hibernate", help="Hibernate",
action="store_true")
parser.add_argument("-y", "--hybridsleep", help="Hybrid sleep",
action="store_true")
parser.add_argument("-b", "--reboot", help="Reboot",
action="store_true")
parser.add_argument("-p", "--poweroff", help="Power off",
action="store_true")
parser.parse_args(sys.argv[1:])
"""No check if more than one option was specified. Take the first option and
discard the other"""
result = parser.parse_args()
return result
def main():
'''
The script works both in a graphical and a non-graphical environment.
In a graphical environment, the BlExitWindow instance is only shown when
the script is launched without arguments. The user selects the action she
wants by clicking the right button.
WHen the script is launched In a graphical environment the requested
action should be one of the accepted arguments and the action is executed
without asking for confirmation - as if the script was launched from the
command line.
In a non-graphical environment, one of the accepted actions must be
specified as an argument.
'''
if display and len(sys.argv[1:]) == 0:
blexit = BlexitWindow()
else:
blexit = BlexitBase()
blexit.main()
if __name__ == "__main__":
sys.exit(main())
...still dirty, but not too quick anymore
And the theme section of the rc file:
[theme]
#This is printed to the console... just to satisfy your ego :P
name=Dark Theme
author=MerlinElMago
#Overall height of the dialog.
dialogHeight=120
#Delay for the fade in counter
sleepDelay=0.003
#Background of the Dialog Window
windowBackgroundColor=#222222
#Background of the buttons
buttonColorStateNormal=#222222
#Background of the pressed button
buttonColorStateActive=#222222
#Background of the hovered button
buttonColorStatePrelight=#222222
#Space between buttond
buttonSpacing=5
Happy playing
Last edited by MerlinElMago (2016-06-25 09:42:52)
· My Desktop · My Laptop · Install guide · Modified bl-exit ·
Offline
I am a noob where image processing is concerned.
How do you process the xml file with inkscape to generate the images?
Offline
Offline
@HoaS
Thx
I got as far as that but how do I get individual png files in the config directory?
The script is looking for 'cancel.png', 'suspend.png' etc...
Offline
I am a noob where image processing is concerned.
How do you process the xml file with inkscape to generate the images?
Scalable Vector Graphics is quite a neat idea. Information is kept as xml, and the file can be opened as an image by an image editor, as a text file by a text editor, and rendered as an image by browsers/image viewers.
It allows for image editing using simple xml editing tools and scripting, which is great when editing icon themes for example
With this file I would open it in gimp, and copy out the necessary areas as separate files (export as png).
Last edited by damo (2016-06-25 10:41:50)
Be Excellent to Each Other...
The Bunsenlabs Lithium Desktop » Here
FORUM RULES and posting guidelines «» Help page for forum post formatting
Artwork on DeviantArt «» BunsenLabs on DeviantArt
Offline
@HoaS
Thx
I got as far as that but how do I get individual png files in the config directory?
The script is looking for 'cancel.png', 'suspend.png' etc...
@damo,
Do I have to load the exported png file in gimp and cut it up in individual png files?
Offline
@HoaS
Thx
I got as far as that but how do I get individual png files in the config directory?
The script is looking for 'cancel.png', 'suspend.png' etc...@damo,
Do I have to load the exported png file in gimp and cut it up in individual png files?
Yes (cross-posted: see my edit ^^)
Be Excellent to Each Other...
The Bunsenlabs Lithium Desktop » Here
FORUM RULES and posting guidelines «» Help page for forum post formatting
Artwork on DeviantArt «» BunsenLabs on DeviantArt
Offline
OK. Thx.
Offline
Actually, gimp will import svg, so no need to use Inkscape first.
Be Excellent to Each Other...
The Bunsenlabs Lithium Desktop » Here
FORUM RULES and posting guidelines «» Help page for forum post formatting
Artwork on DeviantArt «» BunsenLabs on DeviantArt
Offline
Hello again,
I see that there is quite a bit of interest on this, which makes me super happy. *thumbs up*
Let me go a little bit into detail of the inner workings.
The images are - as you have already figured out - stored inside the config directory. They are hardcoded right now, to be PNG files (with transparency in my case) named after the function/action they are supposed to perform.
I have provided the SVG I have been working with, so anybody who'd like to make his own icon (for hybrid sleep for example). This afternoon when I have a little spare time, I will sit down and allow to define image names in the theme section. This way, different image formats may be used (everything that is supported by gtkImage)
*I personally* export the icons, selecting them in Inkscape (individually), to PNG with transparency and a size of 320x320. Then I open them in Gimp and resize the canvas (note that this way you don't change the size of the actual image) to 400x400. This way I get a little margin on the edges. Then, I resize them to 100x100, and that's the icon size that fits perfectly within a window, 100px high.
Best regards
Merlin
· My Desktop · My Laptop · Install guide · Modified bl-exit ·
Offline
^ In Inkscape you can edit the document size without changing the size of the content, so there is no need to do it with GIMP as well.
Be Excellent to Each Other...
The Bunsenlabs Lithium Desktop » Here
FORUM RULES and posting guidelines «» Help page for forum post formatting
Artwork on DeviantArt «» BunsenLabs on DeviantArt
Offline
That's true, but then I can't change the size of the image. I was tempted to set up a ImageMagick script, but apparently it has problems with transparency.
· My Desktop · My Laptop · Install guide · Modified bl-exit ·
Offline
Here goes the last update (I hope )
The theme section now looks like this
[theme]
#This is printed to the console... just to satisfy your ego :P
name=Dark Theme
author=MerlinElMago
#Overall height of the dialog.
dialogHeight=150
#Delay for the fade in counter
sleepDelay=0.003
#Background of the Dialog Window
windowBackgroundColor=#222222
#Background of the buttons (Normal, Active, Prelight)
buttonColorStateNormal=#222222
buttonColorStateActive=#222222
buttonColorStatePrelight=#222222
#OverallOpacity of the dialog (0-100)
overallOpacity=100
#Space between Buttons
buttonSpacing=5
#Button textures (i.e. the images on them)
buttonImageCancel=cancel.png
buttonImagePoweroff=poweroff.png
buttonImageReboot=reboot.png
buttonImageSuspend=suspend.png
buttonImageLogout=logout.png
buttonImageHybridSleep=hybridsleep.png
buttonImageHibernate=hibernate.png
And now the code:
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
from __future__ import print_function
import os
display = os.environ.get('DISPLAY') is not None
import sys
import getpass
import subprocess
import dbus
__me__ = 'bl-exit'
__version__ = '2.0.0'
# Translate command-line option to method - command line only
actionToMethod = dict(
logout='Logout', l='Logout',
suspend='Suspend', s='Suspend',
hybridsleep='HybridSleep', y='HybridSleep',
hibernate='Hibernate', i='Hibernate',
reboot='Reboot', b='Reboot',
poweroff='PowerOff', p='PowerOff'
)
class CanDoItError(Exception):
pass
class BlexitBase(object):
def __init__(self):
self.dbus_iface = None
def setup_dbus_connection(self):
try:
bus = dbus.SystemBus()
dbus_object = bus.get_object('org.freedesktop.login1',
'/org/freedesktop/login1')
self.dbus_iface = dbus.Interface(dbus_object,
'org.freedesktop.login1.Manager')
except bus.DBusException as e:
self.on_error(str(e))
def can_do_action(self, action):
# There is no 'CanLogout' method
if action == "Logout":
return "yes"
actionMethod = "Can{}".format(action)
response = self.send_dbus(actionMethod)
return str(response)
def do_action(self, action):
print("do_action: {}".format(action), file=sys.stderr)
self.send_dbus(action)
def send_dbus(self, method):
try:
if self.dbus_iface is None:
self.setup_dbus_connection()
if method[:3] == "Can":
command = "self.dbus_iface.{}()".format(method)
else:
command = "self.dbus_iface.{}(['True'])".format(method)
response = eval(command)
return str(response)
except dbus.DBusException as e:
self.on_error(str(e))
def on_error(self, string):
print ("{}".format(string), file=sys.stderr)
sys.exit(1)
def openbox_exit(self):
subprocess.check_output(["openbox", "--exit"])
def logout(self):
try:
self.openbox_exit()
except subprocess.CalledProcessError as e:
self.on_error(e.output)
def action_from_command_line(self, action):
try:
self.do_action(action)
except (subprocess.CalledProcessError, CanDoItError, KeyError) as e:
self.on_error(str(e))
def main(self):
opts = get_options()
if opts.logout:
self.logout()
else:
if opts.suspend:
action = "suspend"
elif opts.hibernate:
action = "hibernate"
elif opts.hybridsleep:
action = "hybridsleep"
elif opts.reboot:
action = "reboot"
elif opts.poweroff:
action = "poweroff"
self.setup_dbus_connection()
self.action_from_command_line(actionToMethod[action])
if display:
"""Testing for display here because we want to be able to run the script
in a non-graphical environment as well. Importing gtk.Window on the console
spits out some errors and crashes the application."""
import gtk
import pygtk
pygtk.require('2.0')
import ConfigParser
from time import sleep
class BlexitWindow(BlexitBase, gtk.Window):
"""A dialog offering the user to log out, suspend, reboot or shut down.
"""
def __init__(self):
BlexitBase.__init__(self)
gtk.Window.__init__(self)
def configure(self):
"""Determine config directory: first try the environment variable
XDG_CONFIG_HOME according to XDG specification and as a fallback
use ~/.config/bl-exit. Use /etc/bl-exit/bl-exitrc as a last
resort."""
config_dirs = []
xdg_config_dir = os.getenv('XDG_CONFIG_HOME')
if xdg_config_dir:
config_dirs.append(xdg_config_dir)
user_config_dir = os.path.expanduser('~/.config')
try:
if not (xdg_config_dir and os.path.samefile(user_config_dir,
xdg_config_dir)):
config_dirs.append(user_config_dir)
except OSError as e:
print ("{}: {}".format(__me__, str(e)), file=sys.stderr)
pass
config_dirs.append('/etc')
config_file = None
for config_dir in config_dirs:
config_dir = config_dir + '/bl-exit'
if os.path.isdir(config_dir):
maybe_config_file = config_dir + '/bl-exitrc'
if os.path.isfile(maybe_config_file):
config_file = maybe_config_file
self.saved_config_dir = config_dir
break
if config_file:
try:
self.config = ConfigParser.RawConfigParser()
self.config.read(config_file)
except ConfigParser.ParsingError as e:
print ("{}: {}".format(__me__, str(e)), file=sys.stderr)
self.config = None
sys.exit(1)
else:
self.config = None
#Set defaults, in case loading does not work.
self.dialogHeight = 120
self.dialogBackgroundColor = '#222222'
self.buttonColorStateNormal = '#222222'
self.buttonColorStateActive = '#222222'
self.buttonColorStatePrelight= '#222222'
self.buttonSpacing = 10
self.sleepDelay = 0.003
self.imageButton = dict(
cancel='cancel.png',
logout='logout.png',
poweroff='poweroff.png',
suspend='suspend.png',
reboot='reboot.png',
hibernate='hibernate.png',
hybridsleep='hybridsleep.png' )
self.overallOpacity=100
try:
try:
print( 'Loading '+self.config.get('theme', 'name')+' by '+ self.config.get('theme', 'author'))
self.dialogHeight = int(self.config.get('theme', 'dialogHeight'))
self.windowBackgroundColor = self.config.get('theme', 'windowBackgroundColor')
self.overallOpacity=int(self.config.get('theme', 'overallOpacity'))
self.buttonColorStateNormal = self.config.get('theme', 'buttonColorStateNormal')
self.buttonColorStateActive = self.config.get('theme', 'buttonColorStateActive')
self.buttonColorStatePrelight = self.config.get('theme', 'buttonColorStatePrelight')
self.buttonSpacing = int(self.config.get('theme', 'buttonSpacing'))
self.sleepDelay = float( self.config.get('theme', 'sleepDelay') )
self.imageButton['cancel'] = self.config.get('theme', 'buttonImageCancel')
self.imageButton['poweroff'] = self.config.get('theme', 'buttonImagePoweroff')
self.imageButton['reboot'] = self.config.get('theme', 'buttonImageReboot')
self.imageButton['suspend'] = self.config.get('theme', 'buttonImageSuspend')
self.imageButton['logout'] = self.config.get('theme', 'buttonImageLogout')
self.imageButton['hybridsleep'] = self.config.get('theme', 'buttonImageHybridSleep')
self.imageButton['hibernate'] = self.config.get('theme', 'buttonImageHibernate')
print(str(self.imageButton))
except (ConfigParser.NoOptionError) as e:
print(e)
except (ConfigParser.NoSectionError) as e:
print(e)
def construct_ui(self):
self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.dialogBackgroundColor))
self.set_resizable(False)
self.set_keep_above(True)
self.stick
self.set_decorated(False)
self.set_position(gtk.WIN_POS_CENTER)
self.connect("delete_event", gtk.main_quit)
self.set_size_request(gtk.gdk.screen_width(), self.dialogHeight)
# Cancel key (Escape)
accelgroup = gtk.AccelGroup()
key, mod = gtk.accelerator_parse('Escape')
accelgroup.connect_group(key, mod, gtk.ACCEL_VISIBLE, gtk.main_quit)
self.add_accel_group(accelgroup)
self.button_box = gtk.HBox()
self.button_box.set_size_request(-1, self.dialogHeight)
self.button_box.set_spacing(self.buttonSpacing)
self.add_button(1, self.cancel_action, stock='cancel')
self.build_button_visibility_array()
for button in self.bva:
(action, label, actionfunc, method, show, onError) = button
if not show == 0:
self.add_button(show, actionfunc, label=label, btype=action)
self.status = gtk.Label()
vbox = gtk.VBox()
vbox.pack_start(self.button_box)
self.add(vbox)
self.set_opacity( 0 )
self.show_all()
for o in range(1,self.overallOpacity):
sleep(self.sleepDelay)
while gtk.events_pending():
gtk.main_iteration(False)
self.set_opacity(float(o)/100)
#Don't use anything else!
self.set_keep_above(True)
def build_button_visibility_array(self):
"""Determine button visibily using bl-exit configuration file.
Build self.bva, an array of tuples, one entry per button,
containing (action, label, actionfunction, actionMethod, show,
onerror)
"""
self.bva = []
bva = [('logout', '_Log out', self.logout_action),
('suspend', '_Suspend', self.suspend_action),
('hibernate', 'H_ibernate', self.hibernate_action),
('hybridsleep', 'H_ybrid sleep', self.hybridsleep_action),
('reboot', 'Re_boot', self.reboot_action),
('poweroff', '_Power off', self.shutdown_action)]
show_values = dict(never=0, always=1, maybe=2)
"""Values that the 'show' keyword can take in the configuration
file."""
onerror_values = dict(novisual=0, visual=1)
"""Values that the 'onerror' keyword can take in the configuration
file."""
# Per button default settings
per_button_defaults = dict(
logout='always',
suspend='always',
hibernate='never',
hybridsleep='never',
reboot='always',
poweroff='always'
)
for (action, label, actionfunction) in bva:
# Defaults.
show = show_values[per_button_defaults[action]]
onError = onerror_values['novisual']
if self.config:
for section in ['default', action]:
try:
try:
getshow = self.config.get(section, 'show')
if getshow in show_values:
show = show_values[getshow]
if show == 2:
candoit = self.can_do_action(
actionToMethod[action])
if not candoit == 'yes':
show = 3
except ConfigParser.NoOptionError as e:
pass
try:
getonerror = self.config.get(section,
'onerror')
if getonerror in onerror_values:
onError = onerror_values[getonerror]
except ConfigParser.NoOptionError as e:
pass
except ConfigParser.NoSectionError as e:
pass
self.bva.append(tuple([action, label, actionfunction,
actionToMethod[action], show,
onError]))
def main(self):
self.configure()
self.construct_ui()
gtk.main()
def add_button(self, show, action, label=None, stock=None, btype=None):
if stock is not None:
btype = stock
button = gtk.ColorButton(gtk.gdk.color_parse(self.dialogBackgroundColor))
button.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.buttonColorStateNormal))
button.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse(self.buttonColorStateActive))
button.modify_bg(gtk.STATE_PRELIGHT, gtk.gdk.color_parse(self.buttonColorStatePrelight))
#No borders
button.set_relief(gtk.RELIEF_NONE)
image = gtk.Image()
image.set_from_file(self.saved_config_dir+"/"+self.imageButton[str(btype)])
button.set_image(image)
if show == 3:
button.set_sensitive(False)
button.set_border_width(0)
button.connect("clicked", action)
self.button_box.pack_start(button)
def disable_buttons(self):
self.button_box.foreach(lambda button:
button.set_sensitive(False))
def cancel_action(self, button):
self.disable_buttons()
gtk.main_quit()
def get_onerror(self):
for item in self.bva:
(action, label, actionfunction, actionMethod, show,
onerror) = item
if action == self.selected_action:
return onerror
def on_error(self, e):
onerror = self.get_onerror()
if onerror == 0:
print ("{}: {}".format(__me__, str(e)), file=sys.stderr)
sys.exit(1)
else:
emDialog = gtk.MessageDialog(parent=None, flags=0,
type=gtk.MESSAGE_INFO,
buttons=gtk.BUTTONS_OK,
message_format=None)
if emDialog:
emDialog.set_markup("{}".format(e))
emDialog.run()
def logout_action(self, button):
self.disable_buttons()
self.selected_action = 'logout'
self.status.set_label("Exiting Openbox, please standby...")
self.openbox_exit()
gtk.main_quit()
def suspend_action(self, button):
self.disable_buttons()
self.selected_action = 'suspend'
self.status.set_label("Suspending, please standby...")
self.do_action("Suspend")
gtk.main_quit()
def hibernate_action(self, button):
self.disable_buttons()
self.selected_action = 'hibernate'
self.status.set_label("Hibernating, please standby...")
self.do_action("Hibernate")
gtk.main_quit()
def hybridsleep_action(self, button):
self.disable_buttons()
self.selected_action = 'hybridsleep'
self.status.set_label("Hibernating + Sleeping, please standby...")
self.do_action("HybridSleep")
gtk.main_quit()
def reboot_action(self, button):
self.disable_buttons()
self.selected_action = 'reboot'
self.status.set_label("Rebooting, please standby...")
self.do_action("Reboot")
gtk.main_quit()
def shutdown_action(self, button):
self.disable_buttons()
self.selected_action = 'poweroff'
self.status.set_label("Shutting down, please standby...")
self.do_action("PowerOff")
gtk.main_quit()
def get_options():
result = None
import argparse
parser = argparse.ArgumentParser(description="Bunsenlabs exit")
if display:
parser.add_argument("-l", "--logout", help="Log out",
action="store_true")
parser.add_argument("-s", "--suspend", help="Suspend",
action="store_true")
parser.add_argument("-i", "--hibernate", help="Hibernate",
action="store_true")
parser.add_argument("-y", "--hybridsleep", help="Hybrid sleep",
action="store_true")
parser.add_argument("-b", "--reboot", help="Reboot",
action="store_true")
parser.add_argument("-p", "--poweroff", help="Power off",
action="store_true")
parser.parse_args(sys.argv[1:])
"""No check if more than one option was specified. Take the first option and
discard the other"""
result = parser.parse_args()
return result
def main():
'''
The script works both in a graphical and a non-graphical environment.
In a graphical environment, the BlExitWindow instance is only shown when
the script is launched without arguments. The user selects the action she
wants by clicking the right button.
WHen the script is launched In a graphical environment the requested
action should be one of the accepted arguments and the action is executed
without asking for confirmation - as if the script was launched from the
command line.
In a non-graphical environment, one of the accepted actions must be
specified as an argument.
'''
if display and len(sys.argv[1:]) == 0:
blexit = BlexitWindow()
else:
blexit = BlexitBase()
blexit.main()
if __name__ == "__main__":
sys.exit(main())
Hope you enjoy...
· My Desktop · My Laptop · Install guide · Modified bl-exit ·
Offline
Nice job, MerlinElMago
And since you have the individual png images already, why not post them on the net somewhere we can grab them?
Many thanks
Offline
ok..ok..
Here you are:
FILE
Regards
· My Desktop · My Laptop · Install guide · Modified bl-exit ·
Offline
Oh, and as a quick recommendation... I have included bl-exit into the applications that OB doesn't list on the panel. That was achieved by adding this:
<application name="bl-exit">
<skip_taskbar>yes</skip_taskbar>
</application>
to the ~/.config/openbox/rc.xml file.
Regards
· My Desktop · My Laptop · Install guide · Modified bl-exit ·
Offline
@xaos52, now that this has made it to bunsen-utilities, I'm going to link this post into our Documentaion F.A.Q. (Add Hibernate or remove Suspend bl-exit, or something similar.)
https://forums.bunsenlabs.org/viewtopic … 148#p24148
Question...
The third level is a button specific section, one of
[logout]
[suspend]
[hibernate]
[hybridsleep]
[reboot]
[poweroff]
Why not Cancel as well? I never click the button (Esc key FTW), so currently I've commented out line #183 of bl-exit...
self.add_button(1, self.cancel_action, stock=gtk.STOCK_CANCEL)
Using the config file instead would be nice, I wouldn't have to do that eveytime bunsen-utilities updates.
No, he can't sleep on the floor. What do you think I'm yelling for?!!!
Offline
You are absolutely right.
The goal was to make bl-exit fully configurable, but somehow the 'cancel' button was left out.
I will be working on this.
Offline
Created a pull request against bunsenlabs/bunsen-utilities for this.
I tested it myself, but one can never test enough. So, please try it out.
Offline