Global Keybinding on X Using Python Gtk3

Key binding for window/app in python Gtk+ 3 without menu items, UI manager, etc

Connect a signal to your main frame Win.connect('key-press-event', self.on_key_function) and in on_key_function (self, widget, event) check the value of event.keyval. For ESC is 65307 if you like hardcoded. Also, for key combinations, event.state report shift, alt(meta), and so on: if Gdk.ModifierType.CONTROL_MASK & event.state:do_something if ctrl is pressed
You could have separate stataments for left-ctrl, right-alt; be sure not to try to capture predefined key combinations, thay may be consumed by window manager.
A IDE with a good introspection will help you a lot, just write Gdk (previously imported) and autocompletion will popup a lot of functions/clases, most of them with a self-explanatory name.

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.

Python and GTK+3: widget for choosing a keyboard shortcut

I have implemented this myself using a separate dialog. There's a regular button displaying the current assignment, which, when clicked, opens a KeyboardShortcutDialog implemented as follows:

class KeyboardShortcutDialog(Gtk.Dialog):
"""Dialog that allows to grab a keyboard shortcut."""

def __init__(self, parent):
Gtk.Dialog.__init__(self, _("Keyboard shortcut"), parent, 0)
self.shortcut = None
self.set_border_width(32)

# Add a label
label = Gtk.Label(xalign=0.5, yalign=0.5)
label.set_markup(
_('Press the desired key combination, <b>Backspace</b> to remove any shortcut, or <b>Esc</b> to cancel.'))
self.get_content_area().pack_start(label, True, True, 0)
self.connect('key-press-event', self.on_key_press)
self.show_all()

def on_key_press(self, widget, event: Gdk.EventKey):
"""Signal handler: key pressed."""
keyval = event.get_keyval()[1]
name = Gdk.keyval_name(keyval)

# For some reason event.is_modifier == 0 here, even for modifier keys, so we need to resort to checking by name
if name not in [
'Shift_L', 'Shift_R', 'Control_L', 'Control_R', 'Meta_L', 'Meta_R', 'Alt_L', 'Alt_R', 'Super_L',
'Super_R', 'Hyper_L', 'Hyper_R']:
logging.debug('Key pressed: state=%s, keyval=%d', event.state, keyval)
self.shortcut = (
keyval,
event.state &
(Gdk.ModifierType.META_MASK | Gdk.ModifierType.SUPER_MASK | Gdk.ModifierType.HYPER_MASK |
Gdk.ModifierType.SHIFT_MASK | Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK))
self.response(Gtk.ResponseType.ACCEPT)
return True

def run(self):
"""Show the dialog and block until it's closed.
:return: tuple (keyval, state) of the key captured or None if the dialog has been closed."""
super().run()
return self.shortcut

The dialog's run() method returns a tuple specifying the pressed key combination.

Simulate mouse-clicking a widget in Python Gtk3+

After trying many things, I found that this works.

import Xlib
from Xlib import X
from Xlib.display import Display
from Xlib.ext.xtest import fake_input
import time

def keybinder_callback(self, keystr, user_data):
time.sleep(0.2)
x = self.get_position().root_x+5
display = Display()
mpos = display.screen().root.query_pointer()._data
display.screen().root.warp_pointer(x,5)
display.sync()
fake_input(display,X.ButtonPress,1, X.CurrentTime, X.NONE, x, 5)
display.sync()
fake_input(display,X.ButtonRelease,1)
display.screen().root.warp_pointer(mpos['root_x'],
mpos['root_y'])
display.sync()

I found that the time delay was necessary to avoid this error:

Gdk-CRITICAL **: Window 0x1e7c660 has not been made visible in GdkSeatGrabPrepareFunc

Gtk.EntryCompletion() popup status to resolve keybind conflict

You need to set the inline selection option to True with entrycompletion.set_inline_selection(True). This will connect the key press event to the completion pop-up menu.

#!/usr/bin/env python3

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

from gi.repository import Gtk

entry = Gtk.Entry()

def on_key_press(widget, event):
# NOTE to stop propagation of signal return True
if event.keyval == 65362: widget.set_text("history -1") # Up-arrow
elif event.keyval == 65364: widget.set_text("history +1") # Down-arrow

entry.connect('key_press_event', on_key_press)

entrycompletion = Gtk.EntryCompletion()
entrycompletion.set_inline_selection(True) #enables selection with up and down arrows
entry.set_completion(entrycompletion)

liststore = Gtk.ListStore(str)
for row in "entry complete key conflit with history".split():
liststore.append([row])

entrycompletion.set_model(liststore)
entrycompletion.set_text_column(0)

root = Gtk.Window()
root.add(entry)
root.connect('delete-event', Gtk.main_quit)
root.show_all()
Gtk.main()

add keyboard shortcuts to GIo.Menu

You don't add keybindings to the Gio.Action itself you add them to a Widget or Application for example:

app = # My Gtk.Application instance
window = # My Gtk.ApplicationWindow instance
action = Gio.SimpleAction.new('open', None)

window.add_action(action)
app.add_accelerator('<Primary>o', 'win.open', None)
# The 'win.' prefix is because it was added to a Gtk.ApplicationWindow

How do I do drag and drop in GTK3

How to do drag and drop in GTK3

The drag and drop implemented here is only for copying files into an application.
So, the first thing to do is to make your target entries, that is what sort of thing can be dragged in. For text editors, you would allow text to be dragged in. But in this example, we only want to drag in files.

static GtkTargetEntry targetentries[] =
{
{ "text/uri-list", 0, 0}
};

Now that we have the target entries, we can make the specific widget into a drag and drop destination.

gtk_drag_dest_set ( your_widget_here, GTK_DEST_DEFAULT_ALL, targetentries, 1, GDK_ACTION_COPY);

And now for the signal handler:

g_signal_connect (your_widget_here, "drag-data-received", G_CALLBACK (on_drag_data_received), some_data_to_pass_along);

So, when you drop a file on your widget, it will emit the signal, because you prep’d it by making it a dnd destination.

Here is the callback function:

void on_drag_data_received (GtkWidget *wgt, GdkDragContext *context, gint x, gint y, GtkSelectionData *seldata, guint info, guint time, gpointer data)
{
gchar **filenames = NULL;
filenames = g_uri_list_extract_uris((const gchar *) gtk_selection_data_get_data (seldata));
if (filenames == NULL) // If unable to retrieve filenames:
{
g_printerr(“FAILURE!”);
gtk_drag_finish(context, FALSE, FALSE, time); // Drag and drop was a failure.
return;
}
int iter = 0;
while(filenames[iter] != NULL) // The last URI list element is NULL.
{
char *filename = g_filename_from_uri (filenames[iter], NULL, NULL);
// Do something useful with the file, like opening it, right here.
iter++;
}
gtk_drag_finish(context, TRUE, FALSE, time); // Drag and drop was successful!
}

And you are done!



Related Topics



Leave a reply



Submit