使用Python gtk3在X上进行全局键绑定 [英] Global keybinding on X using Python gtk3

查看:83
本文介绍了使用Python gtk3在X上进行全局键绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在寻找适用于gtk3的python xlib全局键绑定示例,就像gtk2在

不幸的是我找不到一个,因此我决定重新实现它以使用gtk3(ubuntu 12.04).结果如下.它没有运行时错误,但是很遗憾,它没有捕获任何输入.

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()

也许您对如何使其工作有一些想法?

致以最诚挚的问候, 保罗

解决方案

嘿,我实现了相同的代码并且运行良好,可以尝试一下.但没有保修.如果您发现缺少的零件,请告诉我.

# -*- 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆