Author Topic: gtk3 Volume Control for tint2  (Read 4232 times)

misko_2083

  • Posts: 70
    • View Profile
gtk3 Volume Control for tint2
« on: December 17, 2015, 01:24:04 AM »
My attempt to make a volume control context menu that launches from tint2.
Uses amixer
It wasn't showing properly untill I've installed libgtk-3-0
Here is the launcher =>
/usr/local/bin/tint2launchers/07-sound.desktop
Code: [Select]
[Desktop Entry]
Type=Application
Encoding=UTF-8
Name=Sound
Exec=python3 /usr/local/bin/menu.py
Icon=audio-headphones
NoDisplay=false

py script
/usr/local/bin/menu.py
Code: [Select]
#!/usr/bin/env python3
#  Milos Pavlovic 2015 <mpsrbija@gmail.com>
#
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.

#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.

#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.


import os
import sys
import string
import subprocess
import shlex
from gi import require_version
require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository.GdkPixbuf import Pixbuf

def execute(command):
    """function to exec switch"""
    p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    return p.stdout

class Menu:

    def destroy(self, widget, data=None):
        Gtk.main_quit()

    def action(self, widget, event, execc, data=None):
        command = execc
        subprocess.Popen(shlex.split(command), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        Gtk.main_quit()

    def __init__(self):
        self.menu = Gtk.Menu()
        """ This is how we close the script
            connecting to the hide signal
        """
        self.menu.connect("hide", self.destroy)
        it=Gtk.IconTheme.get_default()
        height = 100 # Height workaround

        button= Gtk.Switch()
        button.connect("notify::active", self.on_switch_activated)
        cmd = 'amixer get Master | grep -q  off ; echo $?'
        result1 = os.popen(cmd)
        result1 = result1.readline()
        if int(result1) == 0:
           button.set_active(False)
        else:
           button.set_active(True)
        box = Gtk.Box(homogeneous =True)
        img = Gtk.Image(xalign=0)
        pixbuf=it.load_icon("emblem-sound",24,0)
        img.set_from_pixbuf(pixbuf)
        box.add(img)
        box.add(button)
        menuitem = Gtk.MenuItem()
        menuitem.add(box)
        menuitem.connect("button-press-event", self.mute_clicked)
        menuitem.show()
        self.menu.append(menuitem)

# adjustment (initial value, min value, max value,
        # step increment - press cursor keys to see!,
        # page increment - click around the handle to see!,
        # page size - not used here)
        ad1 = Gtk.Adjustment(0, 0, 100, 5, 10, 0)
        cmd = "/usr/bin/amixer sget Master | grep '\[' "
        result = os.popen(cmd)
        result = result.readline()
        find_start = result.find('[') + 1
        find_end = result.find('%]', find_start)
        audio_level = int(result[find_start:find_end])
        ad1. set_value(audio_level)
        # an horizontal scale
        self.h_scale = Gtk.Scale(
            orientation=Gtk.Orientation.HORIZONTAL, adjustment=ad1)
        # of integers (no digits)
        self.h_scale.set_digits(0)
        # that can expand horizontally if there is space in the grid
        self.h_scale.set_hexpand(True)
        # that is aligned at the top of the space allowed in the grid
        self.h_scale.set_valign(Gtk.Align.START)

        # we connect the signal "value-changed" emitted by the scale with the callback
        # function scale_moved
        self.h_scale.connect("value-changed", self.scale_moved)
        box = Gtk.Box()
        box.add(self.h_scale)
        menuitem = Gtk.MenuItem()
        menuitem.add(box)
        menuitem.show()
        self.menu.append(menuitem)

        self.menu.set_size_request(180, height) # Workaround for height
        self.menu.popup(None, None, None, None, 0, Gtk.get_current_event_time())
        self.menu.show_all()

    # Let's set the voulume here
    def scale_moved(self, event):
        val = "{0}".format(int(self.h_scale.get_value()))
        proc = subprocess.Popen('/usr/bin/amixer sset Master ' + str(val) + '%  2>&1 >/dev/null', shell=True, stdout=subprocess.PIPE)
        proc.wait()

    def on_switch_activated(self, switch, gparam):
        if switch.get_active():
            execute('amixer set Master unmute 2>&1 >/dev/null')
            state = "on"
        else:
            execute('amixer set Master mute 2>&1 >/dev/null')
            state = "off"

    # Here we toogle the mute on and off when menuitem is clicked   
    def mute_clicked(self, event, data=None):
        val = "toggle"
        proc = subprocess.Popen('/usr/bin/amixer set Master ' + str(val) + '  2>&1 >/dev/null', shell=True, stdout=subprocess.PIPE)
        proc.wait()

    def main(self):
        Gtk.main()

if __name__ == "__main__":

     app = Menu()
     app.main()
The script closes when clicked outside of the menu.
There is a mute switch but will work as a button also.
reference from here >>> http://vsido.org/index.php?topic=1089.msg12301#msg12301

VastOne

  •      v-ger
  • Posts: 3895
    • View Profile
    • VSIDO Community
Re: gtk3 Volume Control for tint2
« Reply #1 on: December 17, 2015, 02:11:38 AM »
This is really cool misko_2083

I am going to play with this and get it to work on the top right tint2 and make it a default in VSIDO if that is all right with you

Well done mate, good solid work!
VSIDO      VSIDO Twitter     VSIDO FB     VSIDO Google+   

I dev VSIDO

zephyr

  • Posts: 119
  • Never lost, no matter where, you are there.
    • View Profile
    • https://sourceforge.net/projects/crowz/
Re: gtk3 Volume Control for tint2
« Reply #2 on: December 17, 2015, 04:14:50 AM »
@misko_2083: Dang, works very well and easy to configure. Great addition, thanks - z
CROWZ / STAR / Devuan / refracta / VSIDO

VastOne

  •      v-ger
  • Posts: 3895
    • View Profile
    • VSIDO Community
Re: gtk3 Volume Control for tint2
« Reply #3 on: December 17, 2015, 04:50:28 AM »
@ misko_2083

You should let everyone know in your first post that the menu.py file must be made executable... 

Most know this or would figure it out, but having it there will save a step for you and for them... :D

Thanks
VSIDO      VSIDO Twitter     VSIDO FB     VSIDO Google+   

I dev VSIDO

VastOne

  •      v-ger
  • Posts: 3895
    • View Profile
    • VSIDO Community
Re: gtk3 Volume Control for tint2
« Reply #4 on: December 17, 2015, 04:53:53 AM »
Here it is in the Panel Tint2 on VSIDO



 
VSIDO      VSIDO Twitter     VSIDO FB     VSIDO Google+   

I dev VSIDO

Snap

  • Posts: 462
    • View Profile
Re: gtk3 Volume Control for tint2
« Reply #5 on: December 17, 2015, 06:50:05 AM »
Cool. Thank you. I'm not using tint2 currently, but this one goes directly to the goodies stash.

misko_2083

  • Posts: 70
    • View Profile
Re: gtk3 Volume Control for tint2
« Reply #6 on: December 18, 2015, 01:17:07 AM »
This is really cool misko_2083

I am going to play with this and get it to work on the top right tint2 and make it a default in VSIDO if that is all right with you

Well done mate, good solid work!
Thank you.
Sure, I'm Ok with that VSIDO get's a Volume control.
@misko_2083: Dang, works very well and easy to configure. Great addition, thanks - z
Thanks Z
@ misko_2083

You should let everyone know in your first post that the menu.py file must be made executable... 

Most know this or would figure it out, but having it there will save a step for you and for them... :D

Thanks
If they are launching with python3 there is no need.  ;)
Here it is in the Panel Tint2 on VSIDO



 

That's cool. Minimalistic approach but sometimes it takes some time to launch. It's faster if it stays in the memory. I'll post in the next comment, it probably won't fit in this one.  :D
Cool. Thank you. I'm not using tint2 currently, but this one goes directly to the goodies stash.
Welcome.

misko_2083

  • Posts: 70
    • View Profile
Re: gtk3 Volume Control for tint2
« Reply #7 on: December 18, 2015, 01:33:47 AM »
@VastOne I've been playing with this and made a lot of changes. Script makes an icon in the tray area, while it's in the memory the icon will be there.
Launches faster.
There is a right and left click menu now. Left click menu is the same except the label is added.
Right-click menu can launch PulseAudio Volume Control, Alsamixer and About dialog. Also there is a Quit option.
Code: [Select]
#!/usr/bin/env python3
#  Milos Pavlovic 2015 <mpsrbija@gmail.com>
#
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.

#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.

#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.


import os
import sys
import string
import subprocess
import shlex
from gi import require_version
require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository.GdkPixbuf import Pixbuf


# Here we define some icons
vsido_icon = "//usr/local/bin/images/vsidoorb_blk_sm_140.png"
notification_icon="/usr/share/icons/LinuxLex-8/status/24/notification-audio-volume-high.png"

def execute(command):
    """function to exec switch"""
    p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    return p.stdout

class Menu:

    def destroy(self, widget, data=None):
        Gtk.main_quit()

    def action(self, widget, event, execc, data=None):
        command = execc
        subprocess.Popen(shlex.split(command), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

    def __init__(self):
        self.tray = Gtk.StatusIcon()
        self.tray.set_from_file(notification_icon)
        self.tray.connect('popup-menu', self.on_right_click)
        self.tray.connect('activate', self.on_left_click)
        self.tray.set_tooltip_text('VSIDO Volume Control')


    def on_left_click(self, widget):
        self.menu = Gtk.Menu()
        self.menu.set_border_width(6)
        height=115
        it=Gtk.IconTheme.get_default()

        button= Gtk.Switch()
        button.connect("notify::active", self.on_switch_activated)
        cmd = 'amixer get Master | grep -q  off ; echo $?'
        result1 = os.popen(cmd)
        result1 = result1.readline()
        if int(result1) == 0:
           button.set_active(False)
        else:
           button.set_active(True)
        box1 = Gtk.Grid(column_homogeneous=True,row_spacing=10)
        img = Gtk.Image(xalign=0)
        pixbuf=it.load_icon("emblem-sound",24,0)
        img.set_from_pixbuf(pixbuf)
        box1.add(img)
        box1.attach_next_to(button, img, Gtk.PositionType.RIGHT, 1, 1)
        menuitem_switch = Gtk.MenuItem()
        menuitem_switch.add(box1)
        menuitem_switch.connect("button-press-event", self.mute_clicked)
        menuitem_switch.show()
        self.menu.append(menuitem_switch)


        vsido=Gtk.Label("VSIDO Volume Control")
        menuitem_vsido = Gtk.MenuItem()
        menuitem_vsido.add(vsido)
        menuitem_vsido.set_sensitive(False)
        self.menu.append(menuitem_vsido)


# adjustment (initial value, min value, max value,
        # step increment - press cursor keys to see!,
        # page increment - click around the handle to see!,
        # page size - not used here)
        ad1 = Gtk.Adjustment(0, 0, 100, 5, 10, 0)
        cmd = "/usr/bin/amixer sget Master | grep '\[' "
        result = os.popen(cmd)
        result = result.readline()
        find_start = result.find('[') + 1
        find_end = result.find('%]', find_start)
        audio_level = int(result[find_start:find_end])
        ad1. set_value(audio_level)
        # an horizontal scale
        self.h_scale = Gtk.Scale(
            orientation=Gtk.Orientation.HORIZONTAL, adjustment=ad1)
        # of integers (no digits)
        self.h_scale.set_digits(0)
        # that can expand horizontally if there is space in the grid
        self.h_scale.set_hexpand(True)
        # that is aligned at the top of the space allowed in the grid
        self.h_scale.set_valign(Gtk.Align.START)

        # we connect the signal "value-changed" emitted by the scale with the callback
        # function scale_moved
        self.h_scale.connect("value-changed", self.scale_moved)
        box2 = Gtk.Grid()
        box2.add(self.h_scale)
        menuitem_scale = Gtk.MenuItem()
        menuitem_scale.add(box2)
        menuitem_scale.show()
        self.menu.append(menuitem_scale)

        self.menu.set_size_request(180, height) # Workaround for height
        self.menu.popup(None, None, None, self.tray, 1, Gtk.get_current_event_time())
        self.menu.show_all()

    def on_right_click(self, icon, button, time):
        it=Gtk.IconTheme.get_default()

        # Pulse audio-menu item
        box = Gtk.Box()
        box.set_spacing(10)
        self.menu = Gtk.Menu()
        img = Gtk.Image()
        try:
            pixbuf=it.load_icon('arts', 16,0)
        except:
            pixbuf=it.load_icon('gtk-stop', 16,0)
        img.set_from_pixbuf(pixbuf)
        label = Gtk.Label("PulseAudio Volume")
        box.add(img)
        box.add(label)
        box.show()
        pulse = Gtk.MenuItem()
        pulse.add(box)
        execc="launch pavucontrol"
        pulse.connect("button-press-event", self.action, execc)

        # Alsa mixer-menu item
        box = Gtk.Box()
        box.set_spacing(10)
        self.menu = Gtk.Menu()
        img = Gtk.Image()
        try:
            pixbuf=it.load_icon('audio-volume-high', 16,0)
        except:
            pixbuf=it.load_icon('gtk-stop', 16,0)
        img.set_from_pixbuf(pixbuf)
        label = Gtk.Label("AlsaMixer")
        box.add(img)
        box.add(label)
        box.show()
        alsa = Gtk.MenuItem()
        alsa.add(box)
        execc="xfce4-terminal -e alsamixer --title=AlsaMixer"
        alsa.connect("button-press-event", self.action, execc)

        # About-menu item
        box = Gtk.Box()
        box.set_spacing(10)
        self.menu = Gtk.Menu()
        img = Gtk.Image()
        try:
            pixbuf=it.load_icon('gtk-info', 16,0)
        except:
            pixbuf=it.load_icon('gtk-stop', 16,0)
        img.set_from_pixbuf(pixbuf)
        label = Gtk.Label("About")
        box.add(img)
        box.add(label)
        about = Gtk.MenuItem()
        about.add(box)

        # Quit-menu item
        box = Gtk.Box()
        box.set_spacing(10)
        self.menu = Gtk.Menu()
        img = Gtk.Image()
        try:
            pixbuf=it.load_icon('gtk-stock-cancel', 16,0)
        except:
            pixbuf=it.load_icon('gtk-stop', 16,0)
        label = Gtk.Label("About")
        label.set_label("Quit")
        box.add(img)
        box.add(label)
        quit = Gtk.MenuItem()
        quit.add(box)

        about.connect("button-press-event", self.show_about_dialog)
        quit.connect("activate", Gtk.main_quit)

        self.menu.append(pulse)
        self.menu.append(alsa)
        separator = Gtk.SeparatorMenuItem()
        self.menu.append(separator)
        self.menu.append(about)
        separator = Gtk.SeparatorMenuItem()
        self.menu.append(separator)
        self.menu.append(quit)

        self.menu.show_all()

        self.menu.popup(None, None, None, self.tray, button, time)

    def show_about_dialog(self, widget, time):
        about_dialog = Gtk.AboutDialog()
        about_dialog.set_destroy_with_parent (True)
        about_dialog.set_program_name("VSIDO Volume Control")
        about_dialog.set_website('http://vsido.org')
        about_dialog.set_website_label('vsido.org')
        about_dialog.set_icon(Pixbuf.new_from_file(vsido_icon))
        about_dialog.set_logo(Pixbuf.new_from_file(vsido_icon))
        about_dialog.set_copyright('Copyright 2015')
        about_dialog.set_comments((u'A tool for setting Volume'))
        about_dialog.set_license('''This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA. ''')
        about_dialog.set_authors([u'Milos Pavlovic <mpsrbija@gmail.com>'])
        about_dialog.run()
        about_dialog.destroy()


    # Let's set the voulume here
    def scale_moved(self, event,  data=None):
        val = "{0}".format(int(self.h_scale.get_value()))
        proc = subprocess.Popen('/usr/bin/amixer sset Master ' + str(val) + '%  2>&1 >/dev/null', shell=True, stdout=subprocess.PIPE)
        proc.wait()

    def on_switch_activated(self, switch, gparam):
        if switch.get_active():
            execute('amixer set Master unmute 2>&1 >/dev/null')
            state = "on"
        else:
            execute('amixer set Master mute 2>&1 >/dev/null')
            state = "off"

    # Here we toogle the mute on and off when menuitem is clicked   
    def mute_clicked(self, event, data=None):
        val = "toggle"
        proc = subprocess.Popen('/usr/bin/amixer set Master ' + str(val) + '  2>&1 >/dev/null', shell=True, stdout=subprocess.PIPE)
        proc.wait()

    def main(self):
        Gtk.main()

if __name__ == "__main__":

     app = Menu()
     app.main()

VastOne

  •      v-ger
  • Posts: 3895
    • View Profile
    • VSIDO Community
Re: gtk3 Volume Control for tint2
« Reply #8 on: December 18, 2015, 01:44:15 AM »
Incredible... These are the exact enhancements I was going to recommend to you

That is fantastic, thank you for this. This will be a standard on the next ISO's I build

Great work!
VSIDO      VSIDO Twitter     VSIDO FB     VSIDO Google+   

I dev VSIDO

PackRat

  • Posts: 1849
    • View Profile
Re: gtk3 Volume Control for tint2
« Reply #9 on: December 18, 2015, 02:10:04 AM »
about this line:

Code: [Select]
vsido_icon = "//usr/local/bin/images/vsidoorb_blk_sm_140.png"
is "//usr" a typo, or does the "//" mean something in python3?
I am tired of talk that comes to nothing.
-- Chief Joseph

...the sun, the darkness, the winds are all listening to what we have to say.
-- Geronimo

VastOne

  •      v-ger
  • Posts: 3895
    • View Profile
    • VSIDO Community
Re: gtk3 Volume Control for tint2
« Reply #10 on: December 18, 2015, 03:24:00 AM »
Good catch PackRat... the line just below that also has one where:

Code: [Select]
notification_icon="/usr/share/icons/LinuxLex-8/status/24/notification-audio-volume-high.png"
has no spacing after the icon= area here

notification_icon="/usr/share/icons/LinuxLex-8/status/24/notification-audio-volume-high.png"

should be

notification_icon = "/usr/share/icons/LinuxLex-8/status/24/notification-audio-volume-high.png"

... but I ran the code with the errors and without and the code works just the same
VSIDO      VSIDO Twitter     VSIDO FB     VSIDO Google+   

I dev VSIDO

misko_2083

  • Posts: 70
    • View Profile
Re: gtk3 Volume Control for tint2
« Reply #11 on: December 19, 2015, 02:08:10 AM »
about this line:

Code: [Select]
vsido_icon = "//usr/local/bin/images/vsidoorb_blk_sm_140.png"
is "//usr" a typo, or does the "//" mean something in python3?
Ah, it was a typo.  :D
Nice catch @PackRat
It's working because UNC paths can be used. Those are used when accessing directories and files on a network, but works on local files too.
It should be like this:
Code: [Select]
vsido_icon = "/usr/local/bin/images/vsidoorb_blk_sm_140.png"
Good catch PackRat... the line just below that also has one where:

Code: [Select]
notification_icon="/usr/share/icons/LinuxLex-8/status/24/notification-audio-volume-high.png"
has no spacing after the icon= area here

notification_icon="/usr/share/icons/LinuxLex-8/status/24/notification-audio-volume-high.png"

should be

notification_icon = "/usr/share/icons/LinuxLex-8/status/24/notification-audio-volume-high.png"

... but I ran the code with the errors and without and the code works just the same
@VastOne Python doesn't care about spaces just the indent. Though it looks better if it's properly formated. :)

VastOne

  •      v-ger
  • Posts: 3895
    • View Profile
    • VSIDO Community
Re: gtk3 Volume Control for tint2
« Reply #12 on: December 19, 2015, 05:21:32 AM »
Ran into an issue with this on the new ISO builds

vastone@vsido:~$ python3 /usr/local/bin/menu.py

Code: [Select]
Traceback (most recent call last):
  File "/usr/local/bin/menu.py", line 27, in <module>
    require_version("Gtk", "3.0")
  File "/usr/lib/python3/dist-packages/gi/__init__.py", line 102, in require_version
    raise ValueError('Namespace %s not available' % namespace)
ValueError: Namespace Gtk not available

Python3 is installed

libgtk-3-0 is installed

Not sure what the cause is, I searched for a solution but came up empty ... It works flawlessly on this machine which leads me to believe there is a dependency this machine has met but the build environment does not have

I have a new install of this ready for any testing ... Let me know what is needed
VSIDO      VSIDO Twitter     VSIDO FB     VSIDO Google+   

I dev VSIDO

misko_2083

  • Posts: 70
    • View Profile
Re: gtk3 Volume Control for tint2
« Reply #13 on: December 19, 2015, 06:21:55 AM »
Ran into an issue with this on the new ISO builds

vastone@vsido:~$ python3 /usr/local/bin/menu.py

Code: [Select]
Traceback (most recent call last):
  File "/usr/local/bin/menu.py", line 27, in <module>
    require_version("Gtk", "3.0")
  File "/usr/lib/python3/dist-packages/gi/__init__.py", line 102, in require_version
    raise ValueError('Namespace %s not available' % namespace)
ValueError: Namespace Gtk not available

Python3 is installed

libgtk-3-0 is installed

Not sure what the cause is, I searched for a solution but came up empty ... It works flawlessly on this machine which leads me to believe there is a dependency this machine has met but the build environment does not have

I have a new install of this ready for any testing ... Let me know what is needed
This one :)
Code: [Select]
apt-cache policy gir1.2-gtk-3.0
gir1.2-gtk-3.0:
  Installed: 3.18.5-1
  Candidate: 3.18.6-1
  Version table:
     3.18.6-1 500
        500 http://ftp.us.debian.org/debian sid/main i386 Packages
 *** 3.18.5-1 100
        100 /var/lib/dpkg/status

VastOne

  •      v-ger
  • Posts: 3895
    • View Profile
    • VSIDO Community
Re: gtk3 Volume Control for tint2
« Reply #14 on: December 19, 2015, 03:11:10 PM »
That was it... that brought in several more dependencies but we can live with their size

Thanks misko...  8)
VSIDO      VSIDO Twitter     VSIDO FB     VSIDO Google+   

I dev VSIDO