Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

gtk in-app notifications API referece

I've recently studied the gtk design patterns, and found the in-app notifications. There is an description on when to use it, but no reference to the gtk api.

I have searched for it, but found just the GNotification and GApplication.send_notification, but this sends the notification to the desktop environment.

Can anyone help on finding an tutorial or example code for doing an in-app notification?

like image 222
microo8 Avatar asked Aug 01 '17 07:08

microo8


2 Answers

The app-notification "widget" is a mix of widgets, a css class and behaviors.

You should use a Gtk.Overlay in the window that you plan to use app-notifications then use a container (eg Gtk.Box) with the predefined app-notification style class. The notification container should be wrapped in a Gtk.Revealer to allow the reveal "slide" transition.

Here is a glade ui file (app-notification.ui) with an example:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <property name="default_width">640</property>
    <property name="default_height">480</property>
    <child>
      <placeholder/>
    </child>
    <child>
      <object class="GtkOverlay" id="overlay">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <child>
          <object class="GtkBox" id="box1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkLabel" id="label1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">APP-NOTIFICATION EXAMPLE</property>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button1">
                <property name="label" translatable="yes">show app-notification</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="index">-1</property>
          </packing>
        </child>
        <child type="overlay">
          <object class="GtkRevealer" id="revealer2">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="halign">center</property>
            <property name="valign">start</property>
            <child>
              <object class="GtkBox" id="box2">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="valign">start</property>
                <property name="spacing">20</property>
                <child>
                  <object class="GtkLabel" id="label2">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="label" translatable="yes">This is an app-notification. Click the button to dismiss</property>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">0</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkButton" id="button2">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="relief">none</property>
                    <child>
                      <object class="GtkImage" id="image2">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="icon_name">window-close-symbolic</property>
                      </object>
                    </child>
                    <style>
                      <class name="image-button"/>
                    </style>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">1</property>
                  </packing>
                </child>
                <style>
                  <class name="app-notification"/>
                </style>
              </object>
            </child>
          </object>
        </child>
      </object>
    </child>
  </object>
</interface>

The result in Glade:

enter image description here

And here is some python code that uses the previous glade file and gives some dynamic behavior to the notification so that you can see it in action by clicking the buttons. The glade file should be named app-notification.ui, otherwise change the code to reflect the given name:

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

def onButtonShow(self):
    revealer.set_reveal_child(True)

def onButtonClose(self):
    revealer.set_reveal_child(False)

builder = Gtk.Builder()
builder.add_from_file("app-notification.ui")

window = builder.get_object("window1")

buttonShow = builder.get_object("button1")
buttonClose = builder.get_object ("button2")
revealer = builder.get_object("revealer2") 

buttonShow.connect ("clicked", onButtonShow)
buttonClose.connect ("clicked", onButtonClose)
window.connect ("destroy", Gtk.main_quit)
window.show_all()

Gtk.main()
like image 96
José Fonte Avatar answered Sep 30 '22 18:09

José Fonte


If you prefer to create this without Glade, you can use something like this (based off the previous answer):

Assuming your current code has something like:

window = Gtk.ApplicationWindow(application=self)
window.add(main_widget)

Then you would change the code to something like this:

window = Gtk.ApplicationWindow(application=self)
overlay = Gtk.Overlay()
window.add(overlay)
overlay.add(main_widget)
self._notify_timeout = None

# Notification overlay widget
self._revealer = Gtk.Revealer(valign=Gtk.Align.START, halign=Gtk.Align.CENTER)
box = Gtk.Box(orientation="horizontal", spacing=18)
box.get_style_context().add_class("app-notification")
self._notify_label = Gtk.Label(wrap=True)
box.pack_start(self._notify_label, expand=False, fill=True, padding=18)
button = Gtk.Button.new_from_icon_name("window-close-symbolic", Gtk.IconSize.BUTTON)
button.set_relief(Gtk.ReliefStyle.NONE)
button.set_receives_default(True)
button.connect("clicked", functools.partial(self._revealer.set_reveal_child, False))
box.pack_start(button, expand=False, fill=True, padding=18)
self._revealer.add(box)
overlay.add_overlay(self._revealer)

Then to display a notification, you can add a method like:

def notify(self, message, timeout=5):
    if self._notify_timeout is not None:
        self._notify_timeout.cancel()

    self._notify_label.set_text(message)
    self._revealer.set_reveal_child(True)

    if timeout > 0:
        self._notify_timeout = asyncio.get_event_loop().call_later(
            timeout, functools.partial(self._revealer.set_reveal_child, False))

In addition to what the existing answer provides, this adds a timeout to automatically remove the notification after a few seconds. That code assumes you are using asyncio, if not then update the above method to use another timer method.

like image 20
Sam Bull Avatar answered Sep 30 '22 18:09

Sam Bull