Listening for global key-combinations in python on Linux
I do not know of any libraries that are designed to be extended. However as your link stated the backend of pykeylogger gives an example of how to do it, but it does seem a little overcomplicated for you would need.
pykeylogger uses the python-xlib module to capture keypresses on the X display. Someone has already created a lighter example of how to go through this on pastebin. Below is the source from it copied here as-is.
from Xlib.display import Display
from Xlib import X
from Xlib.ext import record
from Xlib.protocol import rq
disp = None
def handler(reply):
""" This function is called when a xlib event is fired """
data = reply.data
while len(data):
event, data = rq.EventField(None).parse_binary_value(data, disp.display, None, None)
# KEYCODE IS FOUND USERING event.detail
print(event.detail)
if event.type == X.KeyPress:
# BUTTON PRESSED
print("pressed")
elif event.type == X.KeyRelease:
# BUTTON RELEASED
print("released")
# get current display
disp = Display()
root = disp.screen().root
# Monitor keypress and button press
ctx = disp.record_create_context(
0,
[record.AllClients],
[{
'core_requests': (0, 0),
'core_replies': (0, 0),
'ext_requests': (0, 0, 0, 0),
'ext_replies': (0, 0, 0, 0),
'delivered_events': (0, 0),
'device_events': (X.KeyReleaseMask, X.ButtonReleaseMask),
'errors': (0, 0),
'client_started': False,
'client_died': False,
}])
disp.record_enable_context(ctx, handler)
disp.record_free_context(ctx)
while 1:
# Infinite wait, doesn't do anything as no events are grabbed
event = root.display.next_event()
You will have to extend the handler to fit your needs for instead of just printing to screen, and then make it into a separate thread.
The (painful) alternative is to listen to the keyboard directly, without relying on external libraries or the X session. In linux everything is a file and your keyboard input will be in /dev/input that you could read as a file, for example open('/dev/input/even2', 'rb')
, in the background. This is not suggested as it requires escalated permissions, figuring out which device is the keyboard, and then create your own keymapping. Just wanted to let you know what's possible if necessary.
Edit: Also found Global keybinding on X using Python gtk3 which seems to have more example goodness.
How to listen for regular keys and key combinations in Python
sort of rewrite the pynput
sample code so that the program can monitor combination of shift
key.
I made a global variable SHIFT_STATE
to record if the shift
key is pressed, and I believe you can expand this to monitor ctrl
, alt
, cmd
keys and make the code looks prettier.
By the way, the library has the power to monitor global-hotkeys
however I did not look into it too much. You can check it out here: https://pynput.readthedocs.io/en/latest/keyboard.html#global-hotkeys
from pynput import keyboard
SHIFT_STATE = False
def on_press(key):
global SHIFT_STATE
if key == keyboard.Key.shift:
SHIFT_STATE = True
else:
try:
if SHIFT_STATE:
print(f'shift + {key}')
else:
print(key)
except Exception as e:
print(e)
def on_release(key):
global SHIFT_STATE
if key == keyboard.Key.esc:
# Stop listener
return False
elif key == keyboard.Key.shift:
SHIFT_STATE = False
# Collect events until released
with keyboard.Listener(
on_press=on_press,
on_release=on_release) as listener:
listener.join()
and here's the screenshot I ran the code FYI
Global Keybindings?
This is what I found, which works well.
using the module pynput. It will listen to key press globally.
I found the answer here stackoverflow.com/questions/11918999/key-listeners-in-python
from pynput import keyboard
def on_press(key):
try:
print('alphanumeric key {0} pressed'.format(key.char))
except AttributeError:
print('special key {0} pressed'.format(key))
listener = keyboard.Listener(on_press=on_press)
listener.start()
Is there a way for my binary to react to some global hotkeys in Linux?
How global do your hotkeys need to be? Is it enough for them to be global for a X session? In that case you should be able to open an Xlib connection and listen for the events you need.
Ordinarily keyboard events in X are delivered to the window that currently has the focus, and propagated up the hierarchy until they are handled. Clearly this is not what we want. We need to process the event before any other window can get to it. We need to call XGrabKey on the root window with the keycode and modifiers of our hotkey to accomplish this.
I found a good example here.
Key Listeners in python?
It's unfortunately not so easy to do that. If you're trying to make some sort of text user interface, you may want to look into curses
. If you want to display things like you normally would in a terminal, but want input like that, then you'll have to work with termios
, which unfortunately appears to be poorly documented in Python. Neither of these options are that simple, though, unfortunately. Additionally, they do not work under Windows; if you need them to work under Windows, you'll have to use PDCurses as a replacement for curses
or pywin32 rather than termios
.
I was able to get this working decently. It prints out the hexadecimal representation of keys you type. As I said in the comments of your question, arrows are tricky; I think you'll agree.
#!/usr/bin/env python
import sys
import termios
import contextlib
@contextlib.contextmanager
def raw_mode(file):
old_attrs = termios.tcgetattr(file.fileno())
new_attrs = old_attrs[:]
new_attrs[3] = new_attrs[3] & ~(termios.ECHO | termios.ICANON)
try:
termios.tcsetattr(file.fileno(), termios.TCSADRAIN, new_attrs)
yield
finally:
termios.tcsetattr(file.fileno(), termios.TCSADRAIN, old_attrs)
def main():
print 'exit with ^C or ^D'
with raw_mode(sys.stdin):
try:
while True:
ch = sys.stdin.read(1)
if not ch or ch == chr(4):
break
print '%02x' % ord(ch),
except (KeyboardInterrupt, EOFError):
pass
if __name__ == '__main__':
main()
python qt wait in background for hotkey
Set global hotkey with Python 2.6
http://bytes.com/topic/python/answers/574341-how-create-global-hotkey
Python Global Hotkey
Looks like pyhook or wxPython for windows, and XLib for linux, or maybe keybinder for Gnome.
And if you were only listening for it while your program was active, Qt has something for it:
Respond to application-wide "hotkey" in Qt
Hope that helps.
Python cross-platform listening for keypresses?
I don't know of any cross-platform lightweight module that listens for keypresses. But here's a suggestion in case you want to implement something simple:
Check out this question on getting a single keypress at a time in the Python FAQ. You could experiment a bit with blocking reads from sys.stdin
and threading
. But this may only work on Unix. On Windows, you can use msvcrt.kbhit
.
Combining the keypress recipe from the Python FAQ and the msvcrt
module, the resulting kbhit
function would go like this:
try:
from msvcrt import kbhit
except ImportError:
import termios, fcntl, sys, os
def kbhit():
fd = sys.stdin.fileno()
oldterm = termios.tcgetattr(fd)
newattr = termios.tcgetattr(fd)
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSANOW, newattr)
oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
try:
while True:
try:
c = sys.stdin.read(1)
return True
except IOError:
return False
finally:
termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
Related Topics
How to Find the Owner of a File or Directory in Python
Change Working Directory in Shell with a Python Script
How to Select a Specific Input Device with Pyaudio
How to Check the Operating System in Python
On Linux Suse or Redhat, How to Load Python 2.7
Dropping Root Permissions in Python
Python Multiprocessing Memory Usage
How to Send a Signal from a Python Program
Decrypt Chrome Linux Blob Encrypted Cookies in Python
Oserror: [Error 1] Operation Not Permitted
Matplotlib-Animation "No Moviewriters Available"
How to Set File Permissions in Python3
Understanding Python Fork and Memory Allocation Errors
How Can Python Handle Systemctl Stop
Update Python on Linux 2.7 to 3.5