0

I've successfully managed to get a Python program to listen for Control+Alt+P and Control+Alt+O even when the window doesn't have focus.

However I can't seem to capture <FX86Audio...> events even though the succesfully bind (if spelled correctly).

Here is a code snippet that works:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
##!/usr/bin/python3.5       <- Test this every now and then for compatability

# Reqquires:
# sudo apt-get install libkeybinder-3.0-0 gir1.2-keybinder


'''
From : https://stackoverflow.com/questions/56517261/global-hotkey-in-python3-using-gtk-and-xlib
'''
import gi
# Python 2
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
gi.require_version('Keybinder', '3.0')
# Python 3
#gi.require_versions({"Gtk": "3.0", "Gdk": "3.0", "Keybinder": "3.0"})
from gi.repository import Gtk, Gdk, Keybinder, Pango

class A:
    def __init__(self):

        # Basic setup of a window with a label
        self.win = Gtk.Window()
        self.lab = Gtk.Label(label="Hello World!")
        self.lab.modify_font(Pango.FontDescription("sans 36"))

        self.win.add(self.lab)
        self.win.connect("destroy", Gtk.main_quit)
        self.win.show_all()

        self.count = 0

        key = Keybinder  # The module to bind keys
        # key.bind(KEYCOMBINATION, FUNC, ARG)
        key.bind("<control><alt>p", self.say, "Hello")
        key.bind("<control><alt>o", self.say, "World!")
        key.bind("<XF86AudioPlay>", self.say, "World :)")
        key.bind("<XF86AudioPause>", self.say, "World :(")
        key.bind("<AudioPlay>", self.say, "World :/")

        key.init()  # Call the mainloop something like Gtk.main()

    def say(self, key, msg):
        self.count += 1
        print(msg)
        # Python 2
        text="Pressed "+ key + " " + str(self.count) + " times"
        self.lab.set_label(text)
        # Python 3
        # self.lab.set_label(f"Pressed {key} {self.count} times")


A()  # Call the object
if not Keybinder.bind("<control><alt>p", A.say, "Bad News 1"):
    print "Keybinder.bind() failed 1."
if not Keybinder.bind("<XF86AudioPlay>", A.say, "Bad News 2"):
    print "Keybinder.bind() failed 2."
Gtk.main()  # Call the main loop

As mentioned earlier these key bindings are accepted but do not work:

    key.bind("<XF86AudioPlay>", self.say, "World :)")
    key.bind("<XF86AudioPause>", self.say, "World :(")
    key.bind("<AudioPlay>", self.say, "World :/")

The keyboard works for pausing and playing with rhythembox.

Thinking that Unity was getting in the way I remapped <XF86AudioPlay> keyboard shortcut to another sequence to no avail.

On other websites it talks about gsettings and mine seem OK for Ubuntu 16.04 with Unity interface:

gsettings glugins.media-keys.png


Reply to comments

Here is xev output:

KeymapNotify event, serial 37, synthetic NO, window 0x0,
    keys:  13  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   
           0   0   0   0   0   16  0   0   0   0   0   0   0   0   0   0   

KeyRelease event, serial 37, synthetic NO, window 0x3a00001,
    root 0x259, subw 0x0, time 802402347, (1900,975), root:(3820,1027),
    state 0x10, keycode 172 (keysym 0x1008ff14, XF86AudioPlay), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

Adding init and changing order did not make XF86AudioPlay work but on the other hand did not break Control+Alt+P and Control+Alt+O which continue to work.

Here's what I added / changed order of:

    Keybinder.init()
    key = Keybinder  # The module to bind keys
    key.init()  # Call the mainloop something like Gtk.main()

Reply to comments 2

Added:

if not Keybinder.bind("<control><alt>p", A.say, "Bad News 1"):
    print "Keybinder.bind() failed 1."
if not Keybinder.bind("<XF86AudioPlay>", A.say, "Bad News 2"):
    print "Keybinder.bind() failed 2."

The first keybinder is successful but the second one prints to terminal:

Keybinder.bind() failed 2.
Community
  • 1
  • 1
WinEunuuchs2Unix
  • 1,801
  • 1
  • 17
  • 34
  • @stovfl the other bound keys do work. These are not accelerator keys cause they work when widow doesn't have focus. – WinEunuuchs2Unix Jan 13 '20 at 18:11
  • @stovfl I tried everything with the exception of accelerator parse which wasn't clear to me. I updated my answer with results (or lack thereof). – WinEunuuchs2Unix Jan 14 '20 at 00:28
  • 1
    @stovfl Updated with `not Keybinder.bind(...` and the broken shortcut key is generating an error... – WinEunuuchs2Unix Jan 14 '20 at 11:50
  • So `.bind("", ...` not accepted by `Keyboard.bind` and therefore no callback. I assume it's unknown either by `Keyboard` or `Gtk`. Try `"XF86AudioPlay"` without brackets. – stovfl Jan 14 '20 at 13:02
  • 1
    @stovfl Before posting question I tried leaving off `<>` and it instantly generated an error message on `jey.bind`. – WinEunuuchs2Unix Jan 14 '20 at 13:39
  • My own results: `WARNING **: Binding 'p' failed!`, this seq are allready used by my Window Manager. `.bind("XF86AudioPause"` returns `True`. Can't verify callback, but with `.bind("XF86MonBrightnessDown",` the callback is working. – stovfl Jan 14 '20 at 21:38
  • 1
    @stovfl You can post an answer if you like. It was indeed the `<>` had to be removed around `XF86AudioPlay` which is also used by Ubuntu Unity DM but doesn't seem to block it to Gnome Gtk. Also I rewrote the `if not Keybinder.bind()`. Now I just have to save mouse position, move to Movie window, send left click, move back to previous window, reactivate and position mouse where it was :) Also stick Gtk.main() within Tkinter's root.mainloop() THANKS for your help! – WinEunuuchs2Unix Jan 15 '20 at 00:27

1 Answers1

1

Question: 'Keybinder' doesn't bind properly to '<XF86AudioPlay>'

  1. According to the documentation man have to call Keyboard.init() before any other Keyboard.... function.

  2. By eval the return value from the Keyboard.bind(... function man can verify if the binding was successfull.

    keysym = '<XF86AudioPlay>'
    if not Keybinder.bind(keysym, callback):
        print('bind "{}" failed!'.format(keysym))
    
  3. According to the output of xev and GNOME gettings the keysym have to be 'XF86AudioPlay' without enclosing brackets.
    Keybinder.bind('XF86AudioPlay', callback)
    

Reference:

  • gtk-keybinder-does-not-respond
  • Keybinder.init

    Initialize the keybinder library. This function must be called after initializing GTK, before calling any other function in the library. Can only be called once.

  • Keybinder.bind

    Returns: True if the accelerator could be grabbed
    Grab a key combination globally and register a callback to be called each time the key combination is pressed.


Tested with Python: 3.5 - gi.__version__: 3.22.0 - keybinder-3.0, 0.3.2-1

stovfl
  • 14,998
  • 7
  • 24
  • 51