You are not logged in.

#101 2016-06-25 08:46:21

xaos52
The Good Doctor
From: Planet of the @pes
Registered: 2015-09-30
Posts: 695

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

Very nice indeed, Merlin. Thanks smile

Why not create a pull request for it on github

Offline

#102 2016-06-25 09:36:14

MerlinElMago
Member
From: Canary Islands
Registered: 2016-06-23
Posts: 27

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

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 smile

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 wink

Last edited by MerlinElMago (2016-06-25 09:42:52)

Offline

#103 2016-06-25 10:13:40

xaos52
The Good Doctor
From: Planet of the @pes
Registered: 2015-09-30
Posts: 695

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

I am a noob where image processing is concerned.
How do you process the xml file with inkscape to generate the images?

Offline

#104 2016-06-25 10:23:43

Head_on_a_Stick
Member
From: London
Registered: 2015-09-29
Posts: 8,759
Website

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

xaos52 wrote:

How do you process the xml file with inkscape to generate the images?

I think you can just save the text file as "icons.svg", here's the file opened in GIMP:
2016-06-25-112146_1920x1080_scrot.th.png

What I don't know is how to make the script pick these up hmm

I have placed icons.svg in ~/.config/bl-exit but they are not detected sad


“Et ignotas animum dimittit in artes.” — Ovid, Metamorphoses, VIII., 18.

Forum Rules   •   How to report a problem   •   Software that rocks

Offline

#105 2016-06-25 10:36:31

xaos52
The Good Doctor
From: Planet of the @pes
Registered: 2015-09-30
Posts: 695

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

@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

#106 2016-06-25 10:36:31

damo
....moderator....
Registered: 2015-08-20
Posts: 5,180

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

xaos52 wrote:

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 smile

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...

FORUM RULES and posting guidelines «» Help page for forum post formatting
Artwork on DeviantArt  «» BunsenLabs on DeviantArt

Offline

#107 2016-06-25 10:39:24

xaos52
The Good Doctor
From: Planet of the @pes
Registered: 2015-09-30
Posts: 695

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

@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

#108 2016-06-25 10:42:30

damo
....moderator....
Registered: 2015-08-20
Posts: 5,180

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

xaos52 wrote:

@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...

FORUM RULES and posting guidelines «» Help page for forum post formatting
Artwork on DeviantArt  «» BunsenLabs on DeviantArt

Offline

#109 2016-06-25 10:45:17

xaos52
The Good Doctor
From: Planet of the @pes
Registered: 2015-09-30
Posts: 695

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

OK. Thx.

Offline

#110 2016-06-25 10:53:04

damo
....moderator....
Registered: 2015-08-20
Posts: 5,180

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

Actually, gimp will import svg, so no need to use Inkscape first.


Be Excellent to Each Other...

FORUM RULES and posting guidelines «» Help page for forum post formatting
Artwork on DeviantArt  «» BunsenLabs on DeviantArt

Offline

#111 2016-06-25 12:56:47

MerlinElMago
Member
From: Canary Islands
Registered: 2016-06-23
Posts: 27

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

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

Offline

#112 2016-06-25 13:01:29

damo
....moderator....
Registered: 2015-08-20
Posts: 5,180

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

^ 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...

FORUM RULES and posting guidelines «» Help page for forum post formatting
Artwork on DeviantArt  «» BunsenLabs on DeviantArt

Offline

#113 2016-06-25 13:03:39

MerlinElMago
Member
From: Canary Islands
Registered: 2016-06-23
Posts: 27

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

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.

Offline

#114 2016-06-25 14:30:48

MerlinElMago
Member
From: Canary Islands
Registered: 2016-06-23
Posts: 27

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

Here goes the last update (I hope wink )

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...

Offline

#115 2016-06-25 14:39:47

xaos52
The Good Doctor
From: Planet of the @pes
Registered: 2015-09-30
Posts: 695

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

Nice job, MerlinElMago smile

And since you have the individual png images already, why not post them on the net somewhere we can grab them?
Many thanks

Offline

#116 2016-06-25 15:21:26

MerlinElMago
Member
From: Canary Islands
Registered: 2016-06-23
Posts: 27

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

ok..ok..
Here you are:
FILE

Regards

Offline

#117 2016-06-25 23:01:56

MerlinElMago
Member
From: Canary Islands
Registered: 2016-06-23
Posts: 27

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

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

Offline

#118 2016-07-04 16:15:43

hhh
Meep!
Registered: 2015-09-17
Posts: 8,640
Website

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

@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.

Offline

#119 2016-07-05 08:04:09

xaos52
The Good Doctor
From: Planet of the @pes
Registered: 2015-09-30
Posts: 695

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

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

#120 2016-07-12 10:50:23

xaos52
The Good Doctor
From: Planet of the @pes
Registered: 2015-09-30
Posts: 695

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

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

#121 2016-07-12 20:17:33

hhh
Meep!
Registered: 2015-09-17
Posts: 8,640
Website

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

Thanks! I'm in the middle of something, I'll test it in the next hour or so though.

Offline

#122 2016-07-12 21:27:10

damo
....moderator....
Registered: 2015-08-20
Posts: 5,180

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

'bl-exit' doesn't seem to reading my '~/.config/bl-exitrc' - edits aren't appearing in the exit gui (same for all values)

'bl-exit' version:

__version__ = '2.0.1'

Copied '/etc/bl-exit/bl-exitrc' to '$HOME/.config/bl-exitrc', and added:

[cancel]
show = always
onerror = novisual

Am I doing something noobish?


EDIT:  Not such a noob after all - the path is wrong in the comments, as @hhh says (next post)

Path should be

$HOME/.config/bl-exit/bl-exitrc

Now it's working wink

Last edited by damo (2016-07-12 22:17:04)


Be Excellent to Each Other...

FORUM RULES and posting guidelines «» Help page for forum post formatting
Artwork on DeviantArt  «» BunsenLabs on DeviantArt

Offline

#123 2016-07-12 21:38:57

hhh
Meep!
Registered: 2015-09-17
Posts: 8,640
Website

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

Doc, she's beautiful!

Screenshot-07122016-052613PM.md.png

One more request... with a minimal option setup as shown above, the window Title gets garbled on a default BL install. I suggest changing lines #166 and #167 to...

            self.set_title("Log out " + getpass.getuser()
                           + "?")

The "Choose an option:" seems self-evident...

Screenshot-07122016-052800PM.md.png

Also maybe set a minimum window width in case someone just wants one button craziness, or has a username like kklglgeeootttjudududmszzzzhhkyee. smile

Thanks for the great work here!

Offline

#124 2016-07-12 21:57:04

hhh
Meep!
Registered: 2015-09-17
Posts: 8,640
Website

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

In case you haven't followed this thread, here's how to update before the commit gets added...

As root, open /usr/bin/bl-exit in a text editor and replace the existing file with the content shown here...

https://raw.githubusercontent.com/xaosf … in/bl-exit

As user, create the folder and file ~/.config/bl-exit/bl-exitrc with the content shown here...

https://raw.githubusercontent.com/xaosf … /bl-exitrc

@xaos52, the file location in the comment is wrong, it should say...

# CONFIGURATION:
#   To configure bl-exit buttons, copy this file to
#      $HOME/.config/bl-exit/bl-exitrc

Then show or hide buttons by setting them to "always" or "never" in the rc file.

Offline

#125 2016-07-13 11:48:22

xaos52
The Good Doctor
From: Planet of the @pes
Registered: 2015-09-30
Posts: 695

Re: [OBSOLETE] The bunsenlabs 'exit' menu entry

Created new PR with requested changes.
Please test.

Have you seen this, hhh?

I know Damo has.

Your thoughts?
Any one?

Personally, I think it is an excellent option for smallish screen sizes.
Should we go for something like this for the default?
I am OK for implementing this (configurable, of course), but it would need a graphics guru - which I am not - to set up different themes and/or images.

Offline

Board footer

Powered by FluxBB