2

We can create a notification using simple commands.

ex:notify-send 'SUPER IMPORTANT!' 'This is an urgent message!' -u critical

Can we make it clickable and run a script while clicking on it? Like

When we click on a notification send by Nautilus file manager like this one enter image description here

It directly opens a new window. But our custom notification doesn't do anything. How to make our custom notification do an activity when we click it.

  • Is it meant for general use or in a specific situation? Notify-send is notify-send, but custom-made stuff is not much work and anything is possible. – Jacob Vlijm Oct 9 at 16:33
  • Then is there any other way to make a notification without notify-send. – Newtron Malayalam Oct 9 at 16:37
  • Sure! We can just create a tiny Gtk window, , appearing in any corner you choose, doing anything you want. – Jacob Vlijm Oct 9 at 16:40
  • If you are interested, can make you an example clickable, undecorated notfication window, but need to know how you are going to use it. – Jacob Vlijm Oct 9 at 17:11
  • obviously, I am interested in custom notifications. I want it for my own software that I coded. can we use qml for making it? – Newtron Malayalam Oct 11 at 14:21
1

Interesting question!

enter image description here

...Which was the trigger for the exploration below on the subject on the UB experimental repo. The result is a notification/message popup which has the following options:

Example of a notification with (optional) click-functionality

  • set the corner to appear
  • set a command to run when clicked
  • set a title (bold)
  • set a message text
  • set an icon
  • set a life length (seconds) <- set in the snippet

The first four options only apply if you set the argument, the corner defaults to bottom-right (on primary), unless set differently.

The life length is -as it is- hardcoded, defaults to 10 seconds, unless set differently.

Notes

  • Note that these notifications -as it is- are not passed through dbus, so they cannot be "listened" to. Further development could be to make it a daemon-like background process -keeping the Gtk loop alive-, only calling the window on dbus hint.
  • Many of the values/preferences could be moved to gsettings

How to setup

  • Copy the snippet into an empty file, save it as alternotify.py, make it executable
  • Run it with any combination of the following options, just pick what you need:

    • click command: command="command_to_run"
    • title: title="Title to show"
    • message text (body): body="Text body of the notification message, text, text, text"
    • icon (from icon name): icon="icon-name"
    • the corner to appear, 1 = NE, 2 = NW, 3 = SE, 4 = SW: position=1

A complete command could look like:

/path/to/alternotify.py title="Missing applet" body="To use this functionality, you need to run previews. Click this notification to switch it on." icon="budgie-hotcorners-symbolic" command="gedit /home/jacob/Bureaublad/Kap" position=4

enter image description here

The code

#!/usr/bin/env python3
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, GLib
import sys
import subprocess

class NotifyWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self)
        self.set_decorated(False)
        distance = 80 # gsettings
        winwidth = 300 # gsettings
        winheight = 80 # gsettings
        self.set_default_size(winwidth, winheight)
        self.maingrid = Gtk.Grid()
        self.add(self.maingrid)  
        self.set_space()
        self.winpos = 4 # gsettings? default = SE
        self.get_args()
        self.currage = 0
        self.targetage = 10 # gsettings,life seconds
        GLib.timeout_add_seconds(1, self.limit_windowlife) 
        self.maingrid.show_all()
        self.position_popup(self.winpos, winwidth, winheight, distance)
        self.show_all()
        Gtk.main()

    def get_winpos(self, arg):
        self.winpos = int(arg)

    def limit_windowlife(self):
        if self.currage >= self.targetage:
            Gtk.main_quit()
        self.currage = self.currage + 1;
        return True        

    def position_popup(self, winpos, winwidth, winheight, distance):
        monitordata = self.get_primarymonitor()
        winsize = self.get_size()
        winwidth, winheight = winsize.width, winsize.height
        monitor_xpos = monitordata[2]
        monitor_ypos = monitordata[3]
        monitor_width = monitordata[0]
        monitor_height = monitordata[1]

        if winpos == 1:
            wintargetx = monitor_xpos + distance
            wintargety = monitor_ypos + distance
        elif winpos == 2:
            wintargetx = monitor_width + monitor_xpos - winwidth - distance
            wintargety = monitor_ypos + distance
        elif winpos == 3:
            wintargetx = monitor_xpos + distance
            wintargety = monitor_ypos + monitor_height - (
                distance + winheight
            )
        elif winpos == 4:
            wintargetx = monitor_width + monitor_xpos - winwidth - distance
            wintargety = monitor_ypos + monitor_height - (
                distance + winheight
            )
        self.move(wintargetx, wintargety)

    def get_primarymonitor(self):
        # see what is the resolution on the primary monitor
        prim = Gdk.Display.get_default().get_primary_monitor()
        geo = prim.get_geometry()
        [width, height, screen_xpos, screen_ypos] = [
            geo.width, geo.height, geo.x, geo.y
        ]
        height = geo.height
        return width, height, screen_xpos, screen_ypos

    def show_title(self, title):
        title_label = Gtk.Label(label=title)
        self.maingrid.attach(title_label, 3, 1, 1, 1)
        title_label.set_xalign(0)
        # set title bold
        self.noti_css = ".title {font-weight: bold; padding-bottom: 5px;}"
        self.provider = Gtk.CssProvider.new()
        self.provider.load_from_data(self.noti_css.encode())
        self.set_textstyle(title_label, "title")

    def set_body(self, body):
        body_label = Gtk.Label(
            label=body
        )
        self.maingrid.attach(body_label, 3, 2, 1, 1)
        body_label.set_xalign(0)
        body_label.set_size_request(250, -1)
        body_label.set_line_wrap(True)

    def set_icon(self, icon):
        self.maingrid.attach(Gtk.Label(label="\t"), 2, 0, 1, 1)
        if not "/" in icon:
            newicon = Gtk.Image.new_from_icon_name(
                icon, Gtk.IconSize.DIALOG
            )
            self.maingrid.attach(newicon, 1, 1, 1, 2)
            self.maingrid.show_all()

    def get_args(self):
        args = sys.argv[1:]
        funcs = [
            self.show_title, self.set_body, self.set_icon,
            self.connect_action, self.get_winpos,
        ]
        argnames = ["title", "body", "icon", "command", "position"]
        for arg in args:
            argdata = arg.split("=")
            argname = argdata[0]
            arg = argdata[1]
            try:
                i = argnames.index(argname)
                funcs[i](arg)
            except ValueError:
                print("invalid argument:", arg)             

    def connect_action(self, arg):
        self.connect("button_press_event", self.run_command, arg)
        pass

    def set_textstyle(self, widget, style):
        widget_cont = widget.get_style_context()
        widget_cont.add_class(style)
        Gtk.StyleContext.add_provider(
            widget_cont,
            self.provider,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION,
        )               

    def run_command(self, event, key, command):
        if key.get_button()[1] == 1:
            subprocess.Popen(["/bin/bash", "-c", command])

    def set_space(self):
        for cell in [[0, 0], [100, 0], [0, 100], [100, 100]]:
            self.maingrid.attach(
                Gtk.Label(label="\t"), cell[0], cell[1], 1, 1
            )

NotifyWindow()
  • Thanks, Jacob. I wasn't a GTK lover. But your comment inspired me to learn GTK designing. – Newtron Malayalam Oct 12 at 2:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.