Skip to content

Commit

Permalink
fix various bugs (#63)
Browse files Browse the repository at this point in the history
* fix: fix typo unkown -> unknown

* fix: add hash to keybindings for use in mappings

* fix: change arrow key string representations to match industry standards

* test: add tests for hashing
  • Loading branch information
kne42 committed Sep 27, 2022
1 parent 7453e7b commit 817a1b1
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 22 deletions.
4 changes: 2 additions & 2 deletions src/app_model/backends/qt/_qkeymap.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def __init__(self, kb: KeyBinding) -> None:

KEY_TO_QT: Dict[Optional[KeyCode], Qt.Key] = {
None: Qt.Key.Key_unknown,
KeyCode.UNKOWN: Qt.Key.Key_unknown,
KeyCode.UNKNOWN: Qt.Key.Key_unknown,
KeyCode.Backquote: Qt.Key.Key_QuoteLeft,
KeyCode.Backslash: Qt.Key.Key_Backslash,
KeyCode.IntlBackslash: Qt.Key.Key_Backslash,
Expand Down Expand Up @@ -242,7 +242,7 @@ def qmods2modelmods(modifiers: Qt.KeyboardModifier) -> KeyMod:

def qkey2modelkey(key: Qt.Key) -> KeyCode:
"""Return KeyCode from Qt.Key."""
return KEY_FROM_QT.get(key, KeyCode.UNKOWN)
return KEY_FROM_QT.get(key, KeyCode.UNKNOWN)


def qkeycombo2modelkey(key: QKeyCombination) -> Union[KeyCode, KeyCombo]:
Expand Down
38 changes: 19 additions & 19 deletions src/app_model/types/_keys/_key_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class KeyCode(IntEnum):
This is the primary internal representation of a key.
"""

UNKOWN = 0
UNKNOWN = 0

# ----------------------- Writing System Keys -----------------------
Backquote = auto() # `~ on a US keyboard.
Expand Down Expand Up @@ -140,17 +140,17 @@ def __str__(self) -> str:
def from_string(cls, string: str) -> 'KeyCode':
"""Return the `KeyCode` associated with the given string.
Returns `KeyCode.UNKOWN` if no `KeyCode` is associated with the string.
Returns `KeyCode.UNKNOWN` if no `KeyCode` is associated with the string.
"""
return keycode_from_string(string)

@classmethod
def from_event_code(cls, event_code: int) -> 'KeyCode':
"""Return the `KeyCode` associated with the given event code.
Returns `KeyCode.UNKOWN` if no `KeyCode` is associated with the event code.
Returns `KeyCode.UNKNOWN` if no `KeyCode` is associated with the event code.
"""
return _EVENTCODE_TO_KEYCODE.get(event_code, KeyCode.UNKOWN)
return _EVENTCODE_TO_KEYCODE.get(event_code, KeyCode.UNKNOWN)

@classmethod
def __get_validators__(cls) -> Generator[Callable[..., 'KeyCode'], None, None]:
Expand Down Expand Up @@ -409,7 +409,7 @@ class _KM(NamedTuple):

_ = ''
_MAPPINGS = [
_KM(ScanCode.UNIDENTIFIED, 'None', KeyCode.UNKOWN, 'unknown', 0, 'VK_UNKNOWN'),
_KM(ScanCode.UNIDENTIFIED, 'None', KeyCode.UNKNOWN, 'unknown', 0, 'VK_UNKNOWN'),
_KM(ScanCode.KeyA, 'KeyA', KeyCode.KeyA, 'A', 65, 'VK_A'),
_KM(ScanCode.KeyB, 'KeyB', KeyCode.KeyB, 'B', 66, 'VK_B'),
_KM(ScanCode.KeyC, 'KeyC', KeyCode.KeyC, 'C', 67, 'VK_C'),
Expand Down Expand Up @@ -484,10 +484,10 @@ class _KM(NamedTuple):
_KM(ScanCode.Delete, 'Delete', KeyCode.Delete, 'Delete', 46, 'VK_DELETE'),
_KM(ScanCode.End, 'End', KeyCode.End, 'End', 35, 'VK_END'),
_KM(ScanCode.PageDown, 'PageDown', KeyCode.PageDown, 'PageDown', 34, 'VK_NEXT'),
_KM(ScanCode.ArrowRight, 'ArrowRight', KeyCode.RightArrow, 'RightArrow', 39, 'VK_RIGHT'),
_KM(ScanCode.ArrowLeft, 'ArrowLeft', KeyCode.LeftArrow, 'LeftArrow', 37, 'VK_LEFT'),
_KM(ScanCode.ArrowDown, 'ArrowDown', KeyCode.DownArrow, 'DownArrow', 40, 'VK_DOWN'),
_KM(ScanCode.ArrowUp, 'ArrowUp', KeyCode.UpArrow, 'UpArrow', 38, 'VK_UP'),
_KM(ScanCode.ArrowRight, 'ArrowRight', KeyCode.RightArrow, 'Right', 39, 'VK_RIGHT'),
_KM(ScanCode.ArrowLeft, 'ArrowLeft', KeyCode.LeftArrow, 'Left', 37, 'VK_LEFT'),
_KM(ScanCode.ArrowDown, 'ArrowDown', KeyCode.DownArrow, 'Down', 40, 'VK_DOWN'),
_KM(ScanCode.ArrowUp, 'ArrowUp', KeyCode.UpArrow, 'Up', 38, 'VK_UP'),
_KM(ScanCode.NumLock, 'NumLock', KeyCode.NumLock, 'NumLock', 144, 'VK_NUMLOCK'),
_KM(ScanCode.NumpadDivide, 'NumpadDivide', KeyCode.NumpadDivide, 'NumPad_Divide', 111, 'VK_DIVIDE'),
_KM(ScanCode.NumpadMultiply, 'NumpadMultiply', KeyCode.NumpadMultiply, 'NumPad_Multiply', 106, 'VK_MULTIPLY'),
Expand All @@ -507,13 +507,13 @@ class _KM(NamedTuple):
_KM(ScanCode.NumpadDecimal, 'NumpadDecimal', KeyCode.NumpadDecimal, 'NumPad_Decimal', 110, 'VK_DECIMAL'),
_KM(ScanCode.IntlBackslash, 'IntlBackslash', KeyCode.IntlBackslash, 'OEM_102', 226, 'VK_OEM_102'),
_KM(ScanCode.ContextMenu, 'ContextMenu', KeyCode.ContextMenu, 'ContextMenu', 93, _),
_KM(ScanCode.NumpadEqual, 'NumpadEqual', KeyCode.UNKOWN, _, 0, _),
_KM(ScanCode.Help, 'Help', KeyCode.UNKOWN, _, 0, _),
_KM(ScanCode.IntlRo, 'IntlRo', KeyCode.UNKOWN, _, 193, 'VK_ABNT_C1'),
_KM(ScanCode.KanaMode, 'KanaMode', KeyCode.UNKOWN, _, 0, _),
_KM(ScanCode.IntlYen, 'IntlYen', KeyCode.UNKOWN, _, 0, _),
_KM(ScanCode.Convert, 'Convert', KeyCode.UNKOWN, _, 0, _),
_KM(ScanCode.NonConvert, 'NonConvert', KeyCode.UNKOWN, _, 0, _),
_KM(ScanCode.NumpadEqual, 'NumpadEqual', KeyCode.UNKNOWN, _, 0, _),
_KM(ScanCode.Help, 'Help', KeyCode.UNKNOWN, _, 0, _),
_KM(ScanCode.IntlRo, 'IntlRo', KeyCode.UNKNOWN, _, 193, 'VK_ABNT_C1'),
_KM(ScanCode.KanaMode, 'KanaMode', KeyCode.UNKNOWN, _, 0, _),
_KM(ScanCode.IntlYen, 'IntlYen', KeyCode.UNKNOWN, _, 0, _),
_KM(ScanCode.Convert, 'Convert', KeyCode.UNKNOWN, _, 0, _),
_KM(ScanCode.NonConvert, 'NonConvert', KeyCode.UNKNOWN, _, 0, _),
_KM(ScanCode.UNIDENTIFIED, _, KeyCode.Ctrl, 'Ctrl', 17, 'VK_CONTROL'),
_KM(ScanCode.UNIDENTIFIED, _, KeyCode.Shift, 'Shift', 16, 'VK_SHIFT'),
_KM(ScanCode.UNIDENTIFIED, _, KeyCode.Alt, 'Alt', 18, 'VK_MENU'),
Expand Down Expand Up @@ -563,7 +563,7 @@ def _keycode_to_string(keycode: KeyCode) -> str:
def _keycode_from_string(keystr: str) -> KeyCode:
"""Return KeyCode for a given string."""
# sourcery skip
return KEYCODE_FROM_LOWERCASE_STRING.get(str(keystr).lower(), KeyCode.UNKOWN)
return KEYCODE_FROM_LOWERCASE_STRING.get(str(keystr).lower(), KeyCode.UNKNOWN)

def _scancode_to_string(scancode: ScanCode) -> str:
"""Return the string representation of a ScanCode."""
Expand Down Expand Up @@ -630,11 +630,11 @@ class KeyCombo(int):
[`KeyMod`][app_model.types.KeyMod] and [`KeyCode`][app_model.types.KeyCode]."""

def __new__(
cls: Type["KeyCombo"], modifiers: KeyMod, key: KeyCode = KeyCode.UNKOWN
cls: Type["KeyCombo"], modifiers: KeyMod, key: KeyCode = KeyCode.UNKNOWN
) -> "KeyCombo":
return super().__new__(cls, int(modifiers) | int(key))

def __init__(self, modifiers: KeyMod, key: KeyCode = KeyCode.UNKOWN):
def __init__(self, modifiers: KeyMod, key: KeyCode = KeyCode.UNKNOWN):
self._modifiers = modifiers
self._key = key

Expand Down
8 changes: 7 additions & 1 deletion src/app_model/types/_keys/_keybindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def is_modifier_key(self) -> bool:
KeyCode.Shift,
KeyCode.Ctrl,
KeyCode.Meta,
KeyCode.UNKOWN,
KeyCode.UNKNOWN,
)

def __str__(self) -> str:
Expand Down Expand Up @@ -92,6 +92,9 @@ def from_int(
def __int__(self) -> int:
return int(self.to_int())

def __hash__(self) -> int:
return int(self)

def to_int(self, os: Optional[OperatingSystem] = None) -> int:
"""Convert this SimpleKeyBinding to an integer representation."""
os = OperatingSystem.current() if os is None else os
Expand Down Expand Up @@ -197,6 +200,9 @@ def to_int(self, os: Optional[OperatingSystem] = None) -> int:
def __int__(self) -> int:
return int(self.to_int())

def __hash__(self) -> int:
return int(self)

@classmethod
def __get_validators__(cls) -> Generator[Callable[..., Any], None, None]:
yield cls.validate
Expand Down
26 changes: 26 additions & 0 deletions tests/test_keybindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,32 @@ def test_chord_keybinding():
assert KeyBinding.validate(kb) == kb


def test_in_dict():
a = SimpleKeyBinding.from_str("Shift+A")
b = KeyBinding.from_str("Shift+B")

try:
kbs = {
a: 0,
b: 1,
}
except TypeError as e:
if str(e).startswith("unhashable type"):
pytest.fail("keybinds not hashable")
else:
raise e

assert kbs[hash(a)] == 0
assert kbs[hash(b)] == 1

new_a = KeyBinding.from_int(hash(a))

with pytest.raises(KeyError):
kbs[new_a]

assert kbs[hash(new_a)] == 0


def test_in_model():
class M(BaseModel):
key: KeyBinding
Expand Down

0 comments on commit 817a1b1

Please sign in to comment.