mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2025-07-18 06:06:06 -04:00
Putting all the code from the current master branch of TWBlue
This commit is contained in:
3
src/keyboard_handler/__init__.py
Normal file
3
src/keyboard_handler/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from main import KeyboardHandler, KeyboardHandlerError
|
||||
#from wx_handler import WXKeyboardHandler
|
||||
__all__ = ["KeyboardHandler", "KeyboardHandlerError", "WXKeyboardHandler", "WXPanelKeyboardHandler"]
|
7
src/keyboard_handler/global_handler.py
Normal file
7
src/keyboard_handler/global_handler.py
Normal file
@@ -0,0 +1,7 @@
|
||||
import platform
|
||||
if platform.system() == 'Linux':
|
||||
from linux import LinuxKeyboardHandler as GlobalKeyboardHandler
|
||||
elif platform.system() == 'Windows':
|
||||
from wx_handler import WXKeyboardHandler as GlobalKeyboardHandler
|
||||
elif platform.system() == 'Darwin':
|
||||
from osx import OSXKeyboardHandler as GlobalKeyboardHandler
|
58
src/keyboard_handler/linux.py
Normal file
58
src/keyboard_handler/linux.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from main import KeyboardHandler
|
||||
import threading
|
||||
import thread
|
||||
import pyatspi
|
||||
def parse(s):
|
||||
"""parse a string like control+f into (modifier, key).
|
||||
Unknown modifiers will return ValueError."""
|
||||
m = 0
|
||||
lst = s.split('+')
|
||||
if not len(lst): return (0, s)
|
||||
#Are these right?
|
||||
d = {
|
||||
"shift": 1<<pyatspi.MODIFIER_SHIFT,
|
||||
"control": 1<<pyatspi.MODIFIER_CONTROL,
|
||||
"alt": 1<<pyatspi.MODIFIER_ALT,
|
||||
"win":1<<pyatspi.MODIFIER_META3,
|
||||
}
|
||||
for item in lst:
|
||||
if item in d:
|
||||
m|=d[item]
|
||||
lst.remove(item)
|
||||
#end if
|
||||
if len(lst) > 1: #more than one key, parse error
|
||||
raise ValueError, 'unknown modifier %s' % lst[0]
|
||||
return (m, lst[0].lower())
|
||||
class AtspiThread(threading.Thread):
|
||||
def run(self):
|
||||
pyatspi.Registry.registerKeystrokeListener(handler, kind=(pyatspi.KEY_PRESSED_EVENT,),
|
||||
mask=pyatspi.allModifiers())
|
||||
pyatspi.Registry.start()
|
||||
#the keys we registered
|
||||
keys = {}
|
||||
def handler(e):
|
||||
m,k = e.modifiers,e.event_string.lower()
|
||||
#not sure why we can't catch control+f. Try to fix it.
|
||||
if (not e.is_text) and e.id >= 97 <= 126:
|
||||
k = chr(e.id)
|
||||
if (m,k) not in keys: return False
|
||||
thread.start_new(keys[(m,k)], ())
|
||||
return True #don't pass it on
|
||||
class LinuxKeyboardHandler(KeyboardHandler):
|
||||
def __init__(self, *args, **kwargs):
|
||||
KeyboardHandler.__init__(self, *args, **kwargs)
|
||||
t = AtspiThread()
|
||||
t.start()
|
||||
def register_key(self, key, function):
|
||||
"""key will be a string, such as control+shift+f.
|
||||
We need to convert that, using parse_key,
|
||||
into modifier and key to put into our dictionary."""
|
||||
#register key so we know if we have it on event receive.
|
||||
t = parse(key)
|
||||
keys[t] = function
|
||||
#if we got this far, the key is valid.
|
||||
KeyboardHandler.register_key(self, key, function)
|
||||
|
||||
def unregister_key (self, key, function):
|
||||
KeyboardHandler.unregister_key(self, key, function)
|
||||
del keys[parse(key)]
|
88
src/keyboard_handler/main.py
Normal file
88
src/keyboard_handler/main.py
Normal file
@@ -0,0 +1,88 @@
|
||||
import platform
|
||||
import time
|
||||
|
||||
class KeyboardHandlerError (Exception): pass
|
||||
|
||||
class KeyboardHandler(object):
|
||||
|
||||
def __init__(self, repeat_rate=0.0, *args, **kwargs):
|
||||
self.repeat_rate = repeat_rate #How long between accepting the same keystroke?
|
||||
self._last_key = None
|
||||
self._last_keypress_time = 0
|
||||
super(KeyboardHandler, self).__init__(*args, **kwargs)
|
||||
self.active_keys = {}
|
||||
if not hasattr(self, 'replacement_mods'):
|
||||
self.replacement_mods = {}
|
||||
if not hasattr(self, 'replacement_keys'):
|
||||
self.replacement_keys = {}
|
||||
|
||||
def register_key (self, key, function):
|
||||
if key in self.active_keys:
|
||||
raise KeyboardHandlerError, "Key %s is already registered to a function" % key
|
||||
if not callable(function):
|
||||
raise TypeError, "Must provide a callable to be invoked upon keypress"
|
||||
self.active_keys[key] = function
|
||||
|
||||
def unregister_key (self, key, function):
|
||||
try:
|
||||
if self.active_keys[key] != function:
|
||||
raise KeyboardHandlerError, "key %s is not registered to that function" % key
|
||||
except KeyError:
|
||||
raise KeyboardHandlerError, "Key %s not currently registered"
|
||||
del(self.active_keys[key])
|
||||
|
||||
def unregister_all_keys(self):
|
||||
for key in list(self.active_keys):
|
||||
self.unregister_key(key, self.active_keys[key])
|
||||
|
||||
def handle_key (self, key):
|
||||
if self.repeat_rate and key == self._last_key and time.time() - self._last_keypress_time < self.repeat_rate:
|
||||
return
|
||||
try:
|
||||
function = self.active_keys[key]
|
||||
except KeyError:
|
||||
return
|
||||
self._last_key = key
|
||||
self._last_keypress_time = time.time()
|
||||
return function()
|
||||
|
||||
def register_keys(self, keys):
|
||||
"""Given a mapping of keystrokes to functions, registers all keystrokes"""
|
||||
for k in keys:
|
||||
self.register_key(k, keys[k])
|
||||
|
||||
def unregister_keys(self, keys):
|
||||
"""Given a mapping of keys to their functions, unregisters all provided keys."""
|
||||
for k in keys:
|
||||
self.unregister_key(k, keys[k])
|
||||
|
||||
def standardize_key(self, key):
|
||||
"""Takes a keystroke and places it in a standard case and order in a list."""
|
||||
working = key.split('+')
|
||||
working = [i.lower() for i in working]
|
||||
answer = []
|
||||
if "control" in working:
|
||||
answer.append("control")
|
||||
if "win" in working:
|
||||
answer.append("win")
|
||||
if "alt" in working:
|
||||
answer.append("alt")
|
||||
if "shift" in working:
|
||||
answer.append("shift")
|
||||
if working[-1] not in answer:
|
||||
answer.append(working[-1])
|
||||
return answer
|
||||
|
||||
def standardize_keymap(self, keymap):
|
||||
"""Given a keymap, returns the keymap standardized."""
|
||||
full = {}
|
||||
for i in keymap:
|
||||
answer = ""
|
||||
new = self.standardize_key(keymap[i])
|
||||
for (c, j) in enumerate(new):
|
||||
if c < len(new)-1:
|
||||
answer = "%s%s+" % (answer, j)
|
||||
else:
|
||||
answer = "%s%s" % (answer, j)
|
||||
full[i] = answer
|
||||
return full
|
56
src/keyboard_handler/osx.py
Normal file
56
src/keyboard_handler/osx.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from AppKit import *
|
||||
from PyObjCTools import AppHelper
|
||||
from Carbon.CarbonEvt import RegisterEventHotKey, GetApplicationEventTarget
|
||||
from Carbon.Events import cmdKey, controlKey
|
||||
import struct
|
||||
from threading import Thread
|
||||
|
||||
from main import KeyboardHandler
|
||||
|
||||
kEventHotKeyPressedSubtype = 6
|
||||
kEventHotKeyReleasedSubtype = 9
|
||||
|
||||
class OSXKeyboardHandler(KeyboardHandler):
|
||||
|
||||
def __init__(self):
|
||||
super(OSXKeyboardHandler, self).__init__()
|
||||
self.replacement_keys = dict()
|
||||
self.app = KeyboardCapturingNSApplication.alloc().init()
|
||||
self._event_thread = Thread(target=AppHelper.runEventLoop)
|
||||
self._event_thread.start()
|
||||
|
||||
def register_key (self, key, function):
|
||||
super(OSXKeyboardHandler, self).register_key(key, function)
|
||||
k, m = self.parse_key(key)
|
||||
key_id = RegisterEventHotKey(k, m, (0, 0), GetApplicationEventTarget(), 0)
|
||||
self.key_ids[key] = key_id
|
||||
|
||||
def unregister_key (self, key, function):
|
||||
super(OSXKeyboardHandler, self).unregister_key(key, function)
|
||||
key_id = self.key_ids[key]
|
||||
raise NotImplementedError
|
||||
|
||||
def parse_key (self, key):
|
||||
key=key.split("+")
|
||||
#replacements
|
||||
#Modifier keys:
|
||||
for index, item in enumerate(key[0:-1]):
|
||||
if self.replacement_mods.has_key(item):
|
||||
key[index] = self.replacement_mods[item]
|
||||
if self.replacement_keys.has_key(key[-1]):
|
||||
key[-1] = self.replacement_keys[key[-1]]
|
||||
elif len(key[-1])==1:
|
||||
key[-1] = ord(str(key[-1]))-36
|
||||
mods = 0
|
||||
for i in key[:-1]:
|
||||
mods = mods|i
|
||||
return [key[-1], mods]
|
||||
|
||||
class KeyboardCapturingNSApplication(NSApplication):
|
||||
|
||||
def sendEvent_(self, theEvent):
|
||||
if theEvent.type() == NSSystemDefined and theEvent.subtype() == kEventHotKeyPressedSubtype:
|
||||
self.activateIgnoringOtherApps_(True)
|
||||
NSRunAlertPanel(u'Hot Key Pressed', u'Hot Key Pressed', None, None, None)
|
||||
super(NSApplication, self).sendEvent_(theEvent)
|
||||
|
40
src/keyboard_handler/windows.py
Normal file
40
src/keyboard_handler/windows.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import win32api
|
||||
import win32con
|
||||
|
||||
from main import KeyboardHandler
|
||||
|
||||
class WindowsKeyboardHandler(KeyboardHandler):
|
||||
|
||||
def __init__ (self, *args, **kwargs):
|
||||
super(WindowsKeyboardHandler, self).__init__(*args, **kwargs)
|
||||
#Setup the replacement dictionaries.
|
||||
for i in dir(win32con):
|
||||
if i.startswith("VK_"):
|
||||
key = i[3:].lower()
|
||||
self.replacement_keys[key] = getattr(win32con, i)
|
||||
elif i.startswith("MOD_"):
|
||||
key = i[4:].lower()
|
||||
self.replacement_mods[key] = getattr(win32con, i)
|
||||
self.replacement_keys .update(dict(pageup=win32con.VK_PRIOR, pagedown=win32con.VK_NEXT))
|
||||
|
||||
def parse_key (self, keystroke, separator="+"):
|
||||
keystroke = str(keystroke) #We don't want unicode
|
||||
keystroke = [self.keycode_from_key(i) for i in keystroke.split(separator)]
|
||||
mods = 0
|
||||
for i in keystroke[:-1]:
|
||||
mods = mods | i #or everything together
|
||||
return (mods, keystroke[-1])
|
||||
|
||||
def keycode_from_key(self, key):
|
||||
if key in self.replacement_mods:
|
||||
return self.replacement_mods[key]
|
||||
if key in self.replacement_keys:
|
||||
return self.replacement_keys[key]
|
||||
if len(key) == 1:
|
||||
return win32api.VkKeyScanEx(key, win32api.GetKeyboardLayout())
|
||||
|
||||
def is_key_pressed(self, key):
|
||||
"""Returns if the given key was pressed. Requires an active message loop or will simply give if the key was pressed recently."""
|
||||
key = self.keycode_from_key(key)
|
||||
return win32api.GetAsyncKeyState(key)
|
||||
|
119
src/keyboard_handler/wx_handler.py
Normal file
119
src/keyboard_handler/wx_handler.py
Normal file
@@ -0,0 +1,119 @@
|
||||
import functools
|
||||
import wx
|
||||
import platform
|
||||
from main import KeyboardHandler
|
||||
|
||||
__all__ = ['WXKeyboardHandler', 'WXControlKeyboardHandler']
|
||||
|
||||
|
||||
def call_after(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
wx.CallAfter(func, *args, **kwargs)
|
||||
functools.update_wrapper(wrapper, func)
|
||||
return wrapper
|
||||
|
||||
|
||||
class BaseWXKeyboardHandler(KeyboardHandler):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BaseWXKeyboardHandler, self).__init__(*args, **kwargs)
|
||||
#Setup the replacement dictionaries.
|
||||
for i in dir(wx):
|
||||
if i.startswith('WXK_'):
|
||||
key = i[4:].lower()
|
||||
self.replacement_keys[key] = getattr(wx, i)
|
||||
elif i.startswith('MOD_'):
|
||||
key = i[4:].lower()
|
||||
self.replacement_mods[key] = getattr(wx, i)
|
||||
|
||||
def parse_key (self, keystroke, separator="+"):
|
||||
keystroke = [self.keycode_from_key(i) for i in keystroke.split(separator)]
|
||||
mods = 0
|
||||
for i in keystroke[:-1]:
|
||||
mods = mods | i #or everything together
|
||||
return (mods, keystroke[-1])
|
||||
|
||||
def keycode_from_key(self, key):
|
||||
if key in self.replacement_mods:
|
||||
result = self.replacement_mods[key]
|
||||
elif key in self.replacement_keys:
|
||||
result = self.replacement_keys[key]
|
||||
if result >= 277:
|
||||
result -= 277
|
||||
elif len(key) == 1:
|
||||
result = ord(key.upper())
|
||||
print "result: ", result
|
||||
return result
|
||||
|
||||
|
||||
|
||||
#try:
|
||||
if platform.system() == "Windows":
|
||||
from windows import WindowsKeyboardHandler as keyboard_handler
|
||||
elif platform.system() == "Linux":
|
||||
from linux import LinuxKeyboardHandler as keyboard_handler
|
||||
elif platform.system() == "Darwin":
|
||||
from osx import OSXKeyboardHandler as keyboard_handler
|
||||
|
||||
class WXKeyboardHandler(keyboard_handler):
|
||||
def __init__ (self, parent, *args, **kwargs):
|
||||
super(WXKeyboardHandler, self).__init__(*args, **kwargs)
|
||||
self.parent = parent
|
||||
self.key_ids = {}
|
||||
|
||||
@call_after
|
||||
def register_key(self, key, function):
|
||||
super(WXKeyboardHandler, self).register_key(key, function)
|
||||
key_id = wx.NewId()
|
||||
parsed = self.parse_key(key)
|
||||
self.parent.RegisterHotKey(key_id, *parsed)
|
||||
self.parent.Bind(wx.EVT_HOTKEY, lambda evt: self.process_key(evt, key_id), id=key_id)
|
||||
self.key_ids[key] = key_id
|
||||
|
||||
@call_after
|
||||
def unregister_key (self, key, function):
|
||||
super(WXKeyboardHandler, self).unregister_key(key, function)
|
||||
if key not in self.key_ids:
|
||||
return #there's nothing we can do.
|
||||
key_id = self.key_ids[key]
|
||||
self.parent.UnregisterHotKey(key_id)
|
||||
self.parent.Unbind( wx.EVT_HOTKEY, id=key_id)
|
||||
self.key_ids.pop(key)
|
||||
|
||||
def process_key (self, evt, id):
|
||||
evt.Skip()
|
||||
key_ids = self.key_ids.keys()
|
||||
for i in key_ids:
|
||||
if self.key_ids.get(i) == id:
|
||||
self.handle_key(i)
|
||||
|
||||
class WXControlKeyboardHandler(wx.StaticText, KeyboardHandler):
|
||||
|
||||
def __init__(self, parent=None, *a, **k):
|
||||
wx.StaticText.__init__(self, parent=parent)
|
||||
KeyboardHandler.__init__(self, *a, **k)
|
||||
self.wx_replacements = {}
|
||||
for i in [d for d in dir(wx) if d.startswith('WXK_')]:
|
||||
self.wx_replacements[getattr(wx, i)] = i[4:].lower()
|
||||
self.Bind(wx.EVT_KEY_DOWN, self.process_key, self)
|
||||
self.SetFocus()
|
||||
|
||||
def process_key(self, evt):
|
||||
keycode = evt.GetKeyCode()
|
||||
keyname = self.wx_replacements.get(keycode, None)
|
||||
modifiers = ""
|
||||
replacements = ( (evt.ControlDown(), 'control+'),
|
||||
(evt.AltDown(), 'alt+'),
|
||||
(evt.ShiftDown(), 'shift+'),
|
||||
(evt.MetaDown(), 'win+')
|
||||
)
|
||||
for mod, ch in (replacements):
|
||||
if mod:
|
||||
modifiers += ch
|
||||
if keyname is None:
|
||||
if 27 < keycode < 256:
|
||||
keyname = chr(keycode).lower()
|
||||
else:
|
||||
keyname = "(%s)unknown" % keycode
|
||||
key = modifiers + keyname
|
||||
self.handle_key(key)
|
Reference in New Issue
Block a user