My first panel applet

This applet's purpose is to change the desktop settings for gtk3 panel.
Should work with 17.10 and later.
Applet is essentially a button that launches a menu-like window that turns the desktop icons on and off.
We need to create some files first.

  • dbus service file
    /usr/share/dbus-1/services/org.mate.panel.applet.DesktopIconsAppletFactory.service
[D-BUS Service]
Name=org.mate.panel.applet.DesktopIconsAppletFactory
Exec=/usr/lib/mate-menu/mate-desktop-icons.py
  • /usr/share/mate-panel/applets/org.mate.panel.DesktopIconsApplet.mate-panel-applet
[Applet Factory]
Id=DesktopIconsAppletFactory
Location=/usr/lib/mate-menu/mate-desktop-icons.py
Name=Desktop Icons Factory
Description=A Desktop Icons Settings Factory
 
[DesktopIconsApplet]
Name=Desktop Settings
Description=A Quick Menu for the Desktop Icons Settings 
Icon=mate
MateComponentId=OAFIID:MATE_DesktopIconsApplet;
  • And finally the code in /usr/lib/mate-menu/mate-desktop-icons.py (you have to make the file executable chmod +x)
#!/usr/bin/env python3

import gi
gi.require_version("Gtk", "3.0")
gi.require_version("Gdk", "3.0")
gi.require_version('MatePanelApplet', '4.0')
from gi.repository import Gtk, Gdk
from gi.repository import MatePanelApplet
from gi.repository.GdkPixbuf import Pixbuf
import os
import sys
import shlex
import subprocess


class TweakMenu(MatePanelApplet.Applet):
    def __init__(self, applet):
        self.applet_fill(applet)


    def execute(self, command):
        """function to exec"""
        subprocess.Popen(shlex.split(command), 
                         stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)


    def applet_fill(self, applet):
        self.window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
        self.window.set_type_hint(Gdk.WindowTypeHint.DROPDOWN_MENU)

        # you can use this path with gio/gsettings
        settings_path = applet.get_preferences_path()

        grid = Gtk.Grid()
        grid.set_row_spacing(8)
        grid.set_column_spacing(8)
        applet.set_border_width(0)
        button = Gtk.ToggleButton(border_width=0)
        try:
           pixbuf = Gtk.IconTheme.get_default().load_icon("gnome-desktop-config", applet.get_size(), 0)
           button_icon = Gtk.Image.new_from_pixbuf(pixbuf)
        except:
           button_icon = Gtk.Label("Icons")
        button.add(button_icon)


        button.connect("toggled", self.on_toggle, self.window, applet)
        self.window.set_attached_to(button)
        label = Gtk.Label("Desktop icons: ")
        sw = Gtk.Switch()

        cmd = 'gsettings get org.mate.background show-desktop-icons'
        result = os.popen(cmd)
        result = result.readline().split()[0]
        grid.attach(label, 0, 0, 1, 1)
        grid.attach(sw, 1, 0, 1, 1)
        toolbar = Gtk.Toolbar()
        computer = Gtk.ToggleToolButton()
        home = Gtk.ToggleToolButton()
        trash = Gtk.ToggleToolButton()
        network = Gtk.ToggleToolButton()
        volumes = Gtk.ToggleToolButton()
        #home.set_label("Home")
        #home.set_is_important(True)
        home.set_icon_name("gtk-home")
        computer.set_icon_name("monitor")
        trash.set_icon_name("user-trash")
        network.set_icon_name("network")
        volumes.set_icon_name("drive-harddisk")
        toolbar.add(computer)
        toolbar.add(home)
        toolbar.add(trash)
        toolbar.add(network)
        toolbar.add(volumes)
        sw.connect("notify::active",
                        self.on_switch_activated, toolbar)
        computer.connect("realize", self.on_startup, 
                        'gsettings get org.mate.caja.desktop computer-icon-visible')
        home.connect("realize", self.on_startup,
                        'gsettings get org.mate.caja.desktop home-icon-visible')
        trash.connect("realize", self.on_startup, 
                        'gsettings get org.mate.caja.desktop trash-icon-visible')
        network.connect("realize", self.on_startup, 
                        'gsettings get org.mate.caja.desktop network-icon-visible')
        volumes.connect("realize", self.on_startup, 
                        'gsettings get org.mate.caja.desktop volumes-visible')
        computer.connect("notify::active", self.on_computer)
        home.connect("notify::active", self.on_home)
        trash.connect("notify::active", self.on_trash)
        network.connect("notify::active", self.on_network)
        volumes.connect("notify::active", self.on_volumes)

        grid.attach(toolbar, 0, 1, 2, 1)
        if result == "false":
               sw.set_active(False)
               toolbar.set_sensitive(False)
        elif result == "true":
               sw.set_active(True)
               toolbar.set_sensitive(True)
        else:
               sw.set_active(False)
               toolbar.set_sensitive(False)

        self.window.add(grid)
        self.window.add_events(Gdk.EventMask.FOCUS_CHANGE_MASK)
        self.window.connect("focus-out-event", self.window_click, button)
        self.window.set_skip_taskbar_hint(True)
        self.window.set_skip_pager_hint(True)
        self.window.set_decorated(False)
        self.window.set_resizable(False)
        self.window.set_border_width(10)
        self.window.grab_focus()
        self.window.add_events(Gdk.EventMask.KEY_PRESS_MASK)
        self.window.connect("key-press-event", self.on_keypress, button)
        self.applet = applet
        self.applet.add(button)

        toplevel = button.get_toplevel()
        self.window.set_transient_for(toplevel)
        self.applet.show_all()

    def on_startup(self, widget, cmd):
        result = os.popen(cmd)
        result = result.readline().split()[0]
        if result == "true":
            widget.set_active(True)
        else:
            widget.set_active(False)

    def on_computer(self, computer, gparam):
        if computer.get_active():
            self.execute('gsettings set org.mate.caja.desktop computer-icon-visible true')
        else:
            self.execute('gsettings set org.mate.caja.desktop computer-icon-visible false')

    def on_home(self, home, gparam):
        if home.get_active():
            self.execute('gsettings set org.mate.caja.desktop home-icon-visible true')
        else:
            self.execute('gsettings set org.mate.caja.desktop home-icon-visible false')

    def on_trash(self, trash, gparam):
        if trash.get_active():
            self.execute('gsettings set org.mate.caja.desktop trash-icon-visible true')
        else:
            self.execute('gsettings set org.mate.caja.desktop trash-icon-visible false')

    def on_network(self, network, gparam):
        if network.get_active():
            self.execute('gsettings set org.mate.caja.desktop network-icon-visible true')
        else:
            self.execute('gsettings set org.mate.caja.desktop network-icon-visible false')

    def on_volumes(self, volumes, gparam):
        if volumes.get_active():
            self.execute('gsettings set org.mate.caja.desktop volumes-visible true')
        else:
            self.execute('gsettings set org.mate.caja.desktop volumes-visible false')

    def on_toggle(self, button, window, applet):
        if button.get_active():
            rect = button.get_allocation()
            main_window = button.get_toplevel()
            [val, win_x, win_y] = main_window.get_window().get_origin()
            orientation = self.applet.get_orient()
            if orientation == 0:
               # BOTTOM
               cal_x = win_x
               cal_y = win_y
            elif orientation == 1:
               # TOP
               cal_x = win_x + rect.x
               cal_y = win_y + rect.y + rect.height
            elif orientation == 2:
               # RIGHT
               cal_x = win_x
               cal_y = win_y
            elif orientation == 3:
               # LEFT
               cal_x = win_x + rect.x
               cal_y = win_y + rect.y
            else:
               cal_x = win_x
               cal_y = win_y
            [x, y] = self.apply_screen_coord_correction(cal_x, cal_y, self.window, button)
            self.window.show_all()
            self.window.move(x, y)
        else:
            self.window.hide()


    def apply_screen_coord_correction(self, x, y, widget, relative_widget):
        corrected_y = y
        corrected_x = x
        rect = widget.get_allocation()
        screen_w = Gdk.Screen.width()
        screen_h = Gdk.Screen.height()
        delta_x = screen_w - (x + rect.width)
        delta_y = screen_h - (y + rect.height)
        if delta_x < 0:
            corrected_x += delta_x
        if corrected_x < 0:
            corrected_x = 0
        if delta_y < 0:
            corrected_y = y - rect.height - relative_widget.get_allocation().height
        if corrected_y < 0:
            corrected_y = 0
        return [corrected_x, corrected_y]


    def on_keypress(self, widget, event, button):
        if event.keyval == Gdk.KEY_Alt_L or event.keyval == Gdk.KEY_Escape:
           isVisible = self.window.get_property("visible")
           if (isVisible):
              button.set_active(False)
              self.window.hide()


    def on_switch_activated(self, switch, gparam, toolbar):
        if switch.get_active():
            self.execute('gsettings set org.mate.background show-desktop-icons true')
            toolbar.set_sensitive(True)
        else:
            self.execute('gsettings set org.mate.background show-desktop-icons false')
            toolbar.set_sensitive(False)

    def window_click(self, widget, event, button):
        button.set_active(False)
        self.window.hide()
        return True

def applet_factory(applet, iid, data):
    if iid != "DesktopIconsApplet":
       return False
    TweakMenu(applet)
    return True

if __name__ == "__main__":
    MatePanelApplet.Applet.factory_main("DesktopIconsAppletFactory", True,
                                    MatePanelApplet.Applet.__gtype__,
                                    applet_factory, None)
  • After creating those 3 files just add it to the panel, and tell me how it goes.
6 Likes

@anon94368460 - this works really well! Very nice!

Maybe a screenshot can help:

A small change I made was that instead of reusing the /usr/lib/mate-menu directory (which already has the Advanced MATE Menu loader in it), I created a /usr/lib/mate-desktop-icons directory for my executable (and updated the path in the other two files accordingly).

After that, depending on your configuration, you might need to restart the panel (often killall mate-panel is all you need, as it will auto-restart) and then the applet will show up.

What I like the most about this is how easy it is to get started - this can be used as a template for others to create whatever applet they want, and it just works!

6 Likes