# -*- coding: latin-1 -*-
"""
simplegui_lib_keys module.
A class to help manage keyboard handling
in SimpleGUI of CodeSkulptor.
Piece of SimpleGUICS2Pygame.
https://bitbucket.org/OPiMedia/simpleguics2pygame
:license: GPLv3 --- Copyright (C) 2014, 2020 Olivier Pirson
:author: Olivier Pirson --- http://www.opimedia.be/
:version: May 19, 2020
"""
# print('IMPORT', __name__)
try:
from typing import Any, Callable, Dict, List, Optional
except ImportError:
pass
try:
import simplegui # pytype: disable=import-error
except ImportError:
import SimpleGUICS2Pygame.simpleguics2pygame as simplegui # type: ignore
# Class
########
[docs]class Keys:
"""
Keys handler.
Set and catch keys handlers of SimpleGUICS2Pygame (and CodeSkulptor)
to help.
General note:
Some keyboards can't handle more
than two or three keys pressed simultaneously.
See `Keyboard Ghosting Explained!`_
and `Keyboard Ghosting Demonstration`_.
.. _`Keyboard Ghosting Explained!`: http://www.microsoft.com/appliedsciences/antighostingexplained.mspx
.. _`Keyboard Ghosting Demonstration`: http://www.microsoft.com/appliedsciences/content/projects/KeyboardGhostingDemo.aspx
""" # noqa
[docs] def __init__(self, frame, keys=None):
# type: (simplegui.Frame, Optional[simplegui.Keys]) -> None
"""
If keys is None
then set an empty keys handler,
else set a keys handler with key up and key down functions of keys.
`active_handlers()`,
`active_keydown_handler()` or `active_keyup_handler()`
must be called to activate.
:param frame: simplegui.Frame
:param keys: None or Keys
"""
assert isinstance(frame, simplegui.Frame), type(frame)
assert (keys is None) or isinstance(keys, Keys), type(keys)
self._frame = frame
self._pressed_keys = dict() # type: Dict[int, bool]
if keys is None:
self._keydown_fct = dict() # type: Dict[int, Optional[Callable[[int], Any]]] # noqa
self._keyup_fct = dict() # type: Dict[int, Optional[Callable[[int], Any]]] # noqa
else:
self._keydown_fct = dict(keys._keydown_fct)
self._keyup_fct = dict(keys._keyup_fct)
[docs] def active_handlers(self): # type: () -> None
"""Active key down and key up handlers."""
self.active_keydown_handler()
self.active_keyup_handler()
[docs] def active_keydown_handler(self): # type: () -> None
"""Active the key down handler."""
def keydown(key_code):
"""Function handler dealt by frame."""
self._pressed_keys[key_code] = True
fct = self._keydown_fct.get(key_code)
if fct is not None:
fct(key_code)
self._frame.set_keydown_handler(keydown)
[docs] def active_keyup_handler(self): # type: () -> None
"""Active the key up handler."""
def keyup(key_code):
"""Function handler dealt by frame."""
if key_code in self._pressed_keys:
del self._pressed_keys[key_code]
fct = self._keyup_fct.get(key_code)
if fct is not None:
fct(key_code)
self._frame.set_keyup_handler(keyup)
[docs] def is_pressed(self, key_code): # type: (int) -> bool
"""
If the key is pressed
then return True,
else return False.
:param key_code: int >= 0
:return: bool
"""
assert isinstance(key_code, int), type(key_code)
assert key_code >= 0, key_code
return self._pressed_keys.get(key_code, False)
[docs] def is_pressed_key_map(self, key_str): # type: (str) -> bool
"""
If the key is pressed
then return True,
else return False.
:param key_str: str in `simplegui.KEY_MAP`
:return: bool
"""
assert isinstance(key_str, str), type(key_str)
assert key_str in simplegui.KEY_MAP, key_str
return self._pressed_keys.get(simplegui.KEY_MAP[key_str], False)
[docs] def pressed_keys(self): # type: () -> List[int]
"""
Return a sorted list with code of all pressed keys.
:return: list of (int >= 0)
"""
return list(self._pressed_keys.keys())
[docs] def set_keydown_fct(self, key_code, fct=None):
# type: (int, Optional[Callable[[int], Any]]) -> None
"""
If fct is None
then erase the function key down handler to the specified key,
else set the function key down handler to the specified key.
:param key_code: int >= 0
:param fct: (int) -> *
"""
assert isinstance(key_code, int), type(key_code)
assert key_code >= 0, key_code
if fct is None:
if key_code in self._keydown_fct:
del self._keydown_fct[key_code]
else:
self._keydown_fct[key_code] = fct
[docs] def set_keydown_fct_key_map(self, key_str, fct=None):
# type: (str, Optional[Callable[[int], Any]]) -> None
"""
If fct is None
then erase the function key down handler to the specified key,
else set the function key down handler to the specified key.
:param key_str: str in `simplegui.KEY_MAP`
:param fct: (int) -> *
"""
assert isinstance(key_str, str), type(key_str)
assert key_str in simplegui.KEY_MAP, key_str
self.set_keydown_fct(simplegui.KEY_MAP[key_str], fct=fct)
[docs] def set_keyup_fct(self, key_code, fct=None):
# type: (int, Optional[Callable[[int], Any]]) -> None
"""
If fct is None
then erase the function key up handler to the specified key,
else set the function key up handler to the specified key.
:param key_code: int >= 0
:param fct: (int) -> *
"""
assert isinstance(key_code, int), type(key_code)
assert key_code >= 0, key_code
if fct is None:
if key_code in self._keyup_fct:
del self._keyup_fct[key_code]
else:
self._keyup_fct[key_code] = fct
[docs] def set_keyup_fct_key_map(self, key_str, fct=None):
# type: (str, Optional[Callable[[int], Any]]) -> None
"""
If fct is None
then erase the function key up handler to the specified key,
else set the function key up handler to the specified key.
:param key_str: str in `simplegui.KEY_MAP`
:param key_code: int >= 0
:param fct: (int) -> *
"""
assert isinstance(key_str, str), type(key_str)
assert key_str in simplegui.KEY_MAP, key_str
self.set_keyup_fct(simplegui.KEY_MAP[key_str], fct=fct)