Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

keycodes.py 5.2KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import win32api
  2. import win32con
  3. import win32ui
  4. MAPVK_VK_TO_CHAR = 2
  5. key_name_to_vk = {}
  6. key_code_to_name = {}
  7. _better_names = {
  8. "escape": "esc",
  9. "return": "enter",
  10. "back": "pgup",
  11. "next": "pgdn",
  12. }
  13. def _fillvkmap():
  14. # Pull the VK_names from win32con
  15. names = [entry for entry in win32con.__dict__ if entry.startswith("VK_")]
  16. for name in names:
  17. code = getattr(win32con, name)
  18. n = name[3:].lower()
  19. key_name_to_vk[n] = code
  20. if n in _better_names:
  21. n = _better_names[n]
  22. key_name_to_vk[n] = code
  23. key_code_to_name[code] = n
  24. _fillvkmap()
  25. def get_vk(chardesc):
  26. if len(chardesc) == 1:
  27. # it is a character.
  28. info = win32api.VkKeyScan(chardesc)
  29. if info == -1:
  30. # Note: returning None, None causes an error when keyboard layout is non-English, see the report below
  31. # https://stackoverflow.com/questions/45138084/pythonwin-occasionally-gives-an-error-on-opening
  32. return 0, 0
  33. vk = win32api.LOBYTE(info)
  34. state = win32api.HIBYTE(info)
  35. modifiers = 0
  36. if state & 0x1:
  37. modifiers |= win32con.SHIFT_PRESSED
  38. if state & 0x2:
  39. modifiers |= win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED
  40. if state & 0x4:
  41. modifiers |= win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED
  42. return vk, modifiers
  43. # must be a 'key name'
  44. return key_name_to_vk.get(chardesc.lower()), 0
  45. modifiers = {
  46. "alt": win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED,
  47. "lalt": win32con.LEFT_ALT_PRESSED,
  48. "ralt": win32con.RIGHT_ALT_PRESSED,
  49. "ctrl": win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
  50. "ctl": win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
  51. "control": win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
  52. "lctrl": win32con.LEFT_CTRL_PRESSED,
  53. "lctl": win32con.LEFT_CTRL_PRESSED,
  54. "rctrl": win32con.RIGHT_CTRL_PRESSED,
  55. "rctl": win32con.RIGHT_CTRL_PRESSED,
  56. "shift": win32con.SHIFT_PRESSED,
  57. "key": 0, # ignore key tag.
  58. }
  59. def parse_key_name(name):
  60. name = name + "-" # Add a sentinal
  61. start = pos = 0
  62. max = len(name)
  63. toks = []
  64. while pos < max:
  65. if name[pos] in "+-":
  66. tok = name[start:pos]
  67. # use the ascii lower() version of tok, so ascii chars require
  68. # an explicit shift modifier - ie 'Ctrl+G' should be treated as
  69. # 'ctrl+g' - 'ctrl+shift+g' would be needed if desired.
  70. # This is mainly to avoid changing all the old keystroke defs
  71. toks.append(tok.lower())
  72. pos += 1 # skip the sep
  73. start = pos
  74. pos += 1
  75. flags = 0
  76. # do the modifiers
  77. for tok in toks[:-1]:
  78. mod = modifiers.get(tok.lower())
  79. if mod is not None:
  80. flags |= mod
  81. # the key name
  82. vk, this_flags = get_vk(toks[-1])
  83. return vk, flags | this_flags
  84. _checks = [
  85. [ # Shift
  86. ("Shift", win32con.SHIFT_PRESSED),
  87. ],
  88. [ # Ctrl key
  89. ("Ctrl", win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED),
  90. ("LCtrl", win32con.LEFT_CTRL_PRESSED),
  91. ("RCtrl", win32con.RIGHT_CTRL_PRESSED),
  92. ],
  93. [ # Alt key
  94. ("Alt", win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED),
  95. ("LAlt", win32con.LEFT_ALT_PRESSED),
  96. ("RAlt", win32con.RIGHT_ALT_PRESSED),
  97. ],
  98. ]
  99. def make_key_name(vk, flags):
  100. # Check alt keys.
  101. flags_done = 0
  102. parts = []
  103. for moddata in _checks:
  104. for name, checkflag in moddata:
  105. if flags & checkflag:
  106. parts.append(name)
  107. flags_done = flags_done & checkflag
  108. break
  109. if flags_done & flags:
  110. parts.append(hex(flags & ~flags_done))
  111. # Now the key name.
  112. if vk is None:
  113. parts.append("<Unknown scan code>")
  114. else:
  115. try:
  116. parts.append(key_code_to_name[vk])
  117. except KeyError:
  118. # Not in our virtual key map - ask Windows what character this
  119. # key corresponds to.
  120. scancode = win32api.MapVirtualKey(vk, MAPVK_VK_TO_CHAR)
  121. parts.append(chr(scancode))
  122. sep = "+"
  123. if sep in parts:
  124. sep = "-"
  125. return sep.join([p.capitalize() for p in parts])
  126. def _psc(char):
  127. sc, mods = get_vk(char)
  128. print("Char %s -> %d -> %s" % (repr(char), sc, key_code_to_name.get(sc)))
  129. def test1():
  130. for ch in """aA0/?[{}];:'"`~_-+=\\|,<.>/?""":
  131. _psc(ch)
  132. for code in ["Home", "End", "Left", "Right", "Up", "Down", "Menu", "Next"]:
  133. _psc(code)
  134. def _pkn(n):
  135. vk, flags = parse_key_name(n)
  136. print("%s -> %s,%s -> %s" % (n, vk, flags, make_key_name(vk, flags)))
  137. def test2():
  138. _pkn("ctrl+alt-shift+x")
  139. _pkn("ctrl-home")
  140. _pkn("Shift-+")
  141. _pkn("Shift--")
  142. _pkn("Shift+-")
  143. _pkn("Shift++")
  144. _pkn("LShift-+")
  145. _pkn("ctl+home")
  146. _pkn("ctl+enter")
  147. _pkn("alt+return")
  148. _pkn("Alt+/")
  149. _pkn("Alt+BadKeyName")
  150. _pkn("A") # an ascii char - should be seen as 'a'
  151. _pkn("a")
  152. _pkn("Shift-A")
  153. _pkn("Shift-a")
  154. _pkn("a")
  155. _pkn("(")
  156. _pkn("Ctrl+(")
  157. _pkn("Ctrl+Shift-8")
  158. _pkn("Ctrl+*")
  159. _pkn("{")
  160. _pkn("!")
  161. _pkn(".")
  162. if __name__ == "__main__":
  163. test2()