使用Python gtk3在X上进行全局键绑定 [英] Global keybinding on X using Python gtk3
本文介绍了使用Python gtk3在X上进行全局键绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
解决方案
嘿,我实现了相同的代码并且运行良好,可以尝试一下.但没有保修.如果您发现缺少的零件,请告诉我.
# -*- 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()
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
解决方案
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()
这篇关于使用Python gtk3在X上进行全局键绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文