问题
I was looking for some example of python xlib global keybinding that would work with gtk3, just as it is done for gtk2 at http://www.siafoo.net/snippet/239. Very similar code here:
from Xlib.display import Display
from Xlib import X
import gtk.gdk
import threading
import gobject
class GlobalKeyBinding (gobject.GObject, threading.Thread):
__gsignals__ = {
'activate': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
}
def __init__ (self):
gobject.GObject.__init__ (self)
threading.Thread.__init__ (self)
self.setDaemon (True)
self.keymap = gtk.gdk.keymap_get_default ()
self.display = Display ()
self.screen = self.display.screen ()
self.root = self.screen.root
self.map_modifiers ()
self.keybindings={}
self.current_signal=None
def map_modifiers (self):
gdk_modifiers = (gtk.gdk.CONTROL_MASK, gtk.gdk.SHIFT_MASK, gtk.gdk.MOD1_MASK,
gtk.gdk.MOD3_MASK, gtk.gdk.MOD4_MASK, gtk.gdk.MOD5_MASK,
gtk.gdk.SUPER_MASK, gtk.gdk.HYPER_MASK)
self.known_modifiers_mask = 0
for modifier in gdk_modifiers:
self.known_modifiers_mask |= modifier
def add_grab_key(self,accelerator,signal):
if not accelerator:
return
keyval,modifiers=gtk.accelerator_parse(accelerator)
if not keyval or not modifiers:
return
keycode=self.keymap.get_entries_for_keyval(keyval)[0][0]
self.keybindings[signal]=[accelerator,
keycode,
int (modifiers)]
#grab_key operation forces X to exclusivelly send given keycode (like apostrophe char) to current X client (unless other X client grabbed it before).
#`X.AnyModifier' parameter tells to register the keycode for all modifiers, thus Ctrl-', Alt-', Shift-', ' will all be sent to this X client.
# given keyval is grabbed by current X client until `ungrab_key' is called.
return self.root.grab_key (keycode, X.AnyModifier, True, X.GrabModeAsync, X.GrabModeSync)
def ungrab (self):
for signal in self.keybindings:
# ungrab_key ungrabs given keycode, that was grabbed by `grab_key'.
self.root.ungrab_key (self.keybindings[signal][1],X.AnyModifier, self.root)
self.keybindings={}
def idle (self):
if self.current_signal:
gtk.gdk.threads_enter ()
self.emit (self.current_signal)
self.current_signal=None
gtk.gdk.threads_leave ()
return False
#threading.Thread.start() method invokes this method.
def run (self):
self.running = True
wait_for_release = False
while self.running:
event = self.display.next_event () # registered keycode(or probably rather event) has been received.
if self.current_signal:
self.display.allow_events (X.ReplayKeyboard, event.time)
continue
try:
if not wait_for_release and event.type == X.KeyPress:
modifiers = event.state & self.known_modifiers_mask
print modifiers, event.detail
for signal in self.keybindings:
if self.keybindings[signal][1] == event.detail and self.keybindings[signal][2] == modifiers:
self.this_signal=signal
this_keycode = self.keybindings[signal][1]
wait_for_release=True
break
if wait_for_release:
self.display.allow_events (X.AsyncKeyboard, event.time)
else:
self.display.allow_events (X.ReplayKeyboard, event.time)
continue
elif wait_for_release and event.detail == this_keycode and event.type == X.KeyRelease:
wait_for_release = False
self.current_signal=self.this_signal
self.event_window=event.window
gobject.idle_add (self.idle)
self.display.allow_events (X.AsyncKeyboard, event.time)
else:
self.display.allow_events (X.ReplayKeyboard, event.time)
except:
self.display.allow_events (X.ReplayKeyboard, event.time)
def stop (self):
print "stopping keybindings thread..."
self.running = False
self.ungrab ()
self.display.close ()
# SAMPLE USAGE
def callback (keybinding):
print 'Callback!'
keybinding.stop()
gtk.main_quit ()
def main():
print "starting..."
gtk.gdk.threads_init ()
keybindings=GlobalKeyBinding()
keybindings.add_grab_key('<Control>apostrophe','activate')
keybindings.connect('activate',callback)
keybindings.start () # let's thart the thread
gtk.main ()
main()
Unfortunately I didn't find one, thus I decided to reimplement it to use gtk3 (ubuntu 12.04). Below is the result. It does not have runtime errors, but unfortunatelly it does not grab any input.
from Xlib.display import Display
from Xlib import X
from gi.repository import Gtk, Gdk, GObject
import threading
class GlobalKeyBinding (GObject.GObject, threading.Thread):
__gsignals__ = {
'activate': (GObject.SignalFlags.RUN_LAST, None, ()),
}
def __init__ (self):
GObject.GObject.__init__ (self)
threading.Thread.__init__ (self)
self.setDaemon (True)
self.keymap = Gdk.Keymap.get_default()
self.display = Display ()
self.screen = self.display.screen ()
self.root = self.screen.root
self.map_modifiers ()
self.keybindings={}
self.current_signal=None
def map_modifiers (self):
gdk_modifiers = (Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK,
Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK,
Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK)
self.known_modifiers_mask = 0
for modifier in gdk_modifiers:
#print modifier,modifier+0
self.known_modifiers_mask |= modifier
def add_grab_key(self,accelerator,signal):
if not accelerator:
return
keyval,modifiers=Gtk.accelerator_parse(accelerator)
if not keyval or not modifiers:
return
#keycode=self.keymap.get_entries_for_keyval(keyval)[0][0]
success, entries = self.keymap.get_entries_for_keyval(keyval)
entry = [(int(i.keycode), i.group, i.level) for i in entries]
if not entry:
raise TypeError("Invalid key name")
keycode=entry[0][0]
self.keybindings[signal]=[accelerator,
keycode,
int (modifiers)]
return self.root.grab_key (keycode, X.AnyModifier, True, X.GrabModeAsync, X.GrabModeSync)
def ungrab (self):
for signal in self.keybindings:
self.root.ungrab_key (self.keybindings[signal][1],X.AnyModifier, self.root)
self.keybindings={}
def idle (self):
if self.current_signal:
Gdk.threads_enter ()
self.emit (self.current_signal)
self.current_signal=None
Gdk.threads_leave ()
return False
def run (self):
self.running = True
wait_for_release = False
while self.running:
event = self.display.next_event ()
if self.current_signal:
self.display.allow_events (X.ReplayKeyboard, event.time)
continue
try:
if not wait_for_release and event.type == X.KeyPress:
modifiers = event.get_state() & self.known_modifiers_mask
print modifiers,event.get_state()
for signal in self.keybindings:
if self.keybindings[signal][1] == event.detail and self.keybindings[signal][2] == modifiers:
self.this_signal=signal
this_keycode = self.keybindings[signal][1]
wait_for_release=True
break
if wait_for_release:
self.display.allow_events (X.AsyncKeyboard, event.time)
else:
self.display.allow_events (X.ReplayKeyboard, event.time)
continue
elif wait_for_release and event.detail == this_keycode and event.type == X.KeyRelease:
wait_for_release = False
self.current_signal=self.this_signal
self.event_window=event.window
GObject.idle_add (self.idle)
self.display.allow_events (X.AsyncKeyboard, event.time)
else:
self.display.allow_events (X.ReplayKeyboard, event.time)
except:
self.display.allow_events (X.ReplayKeyboard, event.time)
def stop (self):
self.running = False
self.ungrab ()
self.display.close ()
# SAMPLE USAGE
def callback (keybinding):
print 'Callback!'
keybinding.stop()
Gtk.main_quit ()
def main():
print "starting..."
Gdk.threads_init ()
keybindings=GlobalKeyBinding()
keybindings.add_grab_key('<Control>apostrophe','activate')
keybindings.connect('activate',callback)
print "keybindings go"
keybindings.start () # let's thart the thread
print "gtk go"
Gtk.main ()
main()
Maybe You have some ideas how to make it work?
best regards, Paul
回答1:
Hey i implemented same code and working great, you can try this. but there is no warranty. If you find missing parts please tell me.
# -*- coding: utf-8; -*-
# Copyright (C) 2013 Özcan Esen <ozcanesen@gmail.com>
# Copyright (C) 2008 Luca Bruno <lethalman88@gmail.com>
#
# This a slightly modified version of the globalkeybinding.py file which is part of FreeSpeak.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
from Xlib.display import Display
from Xlib import X, error
#import GObject
#import gtk.gdk
from gi.repository import Gtk, Gdk, GObject, GLib
import threading
from config import ConfigManager
class GlobalKeyBinding(GObject.GObject, threading.Thread):
__gsignals__ = {
'activate':(GObject.SIGNAL_RUN_LAST, None,()),
}
def __init__(self):
GObject.GObject.__init__(self)
threading.Thread.__init__(self)
self.setDaemon(True)
self.keymap = Gdk.Keymap.get_default()
self.display = Display()
self.screen = self.display.screen()
self.root = self.screen.root
self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask)
self.map_modifiers()
def map_modifiers(self):
gdk_modifiers =(Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK,
Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK,
Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK)
self.known_modifiers_mask = 0
for modifier in gdk_modifiers:
if "Mod" not in Gtk.accelerator_name(0, modifier):
self.known_modifiers_mask |= modifier
def grab(self):
Gdk.threads_enter()
accelerator = ConfigManager.get_conf('global-key')
Gdk.threads_leave()
keyval, modifiers = Gtk.accelerator_parse(accelerator)
if not accelerator or(not keyval and not modifiers):
self.keycode = None
self.modifiers = None
return
self.keycode= self.keymap.get_entries_for_keyval(keyval)[1][0].keycode
self.modifiers = int(modifiers)
catch = error.CatchError(error.BadAccess)
for ignored_mask in self.ignored_masks:
mod = modifiers | ignored_mask
result = self.root.grab_key(self.keycode, mod, True, X.GrabModeAsync, X.GrabModeSync, onerror=catch)
self.display.sync()
if catch.get_error():
return False
return True
def ungrab(self):
if self.keycode:
self.root.ungrab_key(self.keycode, X.AnyModifier, self.root)
def get_mask_combinations(self, mask):
return [x for x in xrange(mask+1) if not (x & ~mask)]
def idle(self):
Gdk.threads_enter()
self.emit("activate")
Gdk.threads_leave()
return False
def run(self):
self.running = True
wait_for_release = False
while self.running:
event = self.display.next_event()
self.current_event_time = event.time
if event.detail == self.keycode and event.type == X.KeyPress and not wait_for_release:
modifiers = event.state & self.known_modifiers_mask
if modifiers == self.modifiers:
wait_for_release = True
self.display.allow_events(X.AsyncKeyboard, event.time)
else:
self.display.allow_events(X.ReplayKeyboard, event.time)
elif event.detail == self.keycode and wait_for_release:
if event.type == X.KeyRelease:
wait_for_release = False
GLib.idle_add(self.idle)
self.display.allow_events(X.AsyncKeyboard, event.time)
else:
self.display.allow_events(X.ReplayKeyboard, event.time)
def stop(self):
self.running = False
self.ungrab()
self.display.close()
来源:https://stackoverflow.com/questions/10640623/global-keybinding-on-x-using-python-gtk3