keycodes.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import string
  2. import win32con
  3. import win32api
  4. import win32ui
  5. MAPVK_VK_TO_CHAR = 2
  6. key_name_to_vk = {}
  7. key_code_to_name = {}
  8. _better_names = {
  9. "escape": "esc",
  10. "return": "enter",
  11. "back": "pgup",
  12. "next": "pgdn",
  13. }
  14. def _fillvkmap():
  15. # Pull the VK_names from win32con
  16. names = [entry for entry in win32con.__dict__ if entry.startswith("VK_")]
  17. for name in names:
  18. code = getattr(win32con, name)
  19. n = name[3:].lower()
  20. key_name_to_vk[n] = code
  21. if n in _better_names:
  22. n = _better_names[n]
  23. key_name_to_vk[n] = code
  24. key_code_to_name[code] = n
  25. _fillvkmap()
  26. def get_vk(chardesc):
  27. if len(chardesc)==1:
  28. # it is a character.
  29. info = win32api.VkKeyScan(chardesc)
  30. if info==-1:
  31. # Note: returning None, None causes an error when keyboard layout is non-English, see the report below
  32. # https://stackoverflow.com/questions/45138084/pythonwin-occasionally-gives-an-error-on-opening
  33. return 0, 0
  34. vk = win32api.LOBYTE(info)
  35. state = win32api.HIBYTE(info)
  36. modifiers = 0
  37. if state & 0x1:
  38. modifiers |= win32con.SHIFT_PRESSED
  39. if state & 0x2:
  40. modifiers |= win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED
  41. if state & 0x4:
  42. modifiers |= win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED
  43. return vk, modifiers
  44. # must be a 'key name'
  45. return key_name_to_vk.get(chardesc.lower()), 0
  46. modifiers = {
  47. "alt" : win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED,
  48. "lalt" : win32con.LEFT_ALT_PRESSED,
  49. "ralt" : win32con.RIGHT_ALT_PRESSED,
  50. "ctrl" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
  51. "ctl" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
  52. "control" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
  53. "lctrl" : win32con.LEFT_CTRL_PRESSED,
  54. "lctl" : win32con.LEFT_CTRL_PRESSED,
  55. "rctrl" : win32con.RIGHT_CTRL_PRESSED,
  56. "rctl" : win32con.RIGHT_CTRL_PRESSED,
  57. "shift" : win32con.SHIFT_PRESSED,
  58. "key" : 0, # ignore key tag.
  59. }
  60. def parse_key_name(name):
  61. name = name + "-" # Add a sentinal
  62. start = pos = 0
  63. max = len(name)
  64. toks = []
  65. while pos<max:
  66. if name[pos] in "+-":
  67. tok = name[start:pos]
  68. # use the ascii lower() version of tok, so ascii chars require
  69. # an explicit shift modifier - ie 'Ctrl+G' should be treated as
  70. # 'ctrl+g' - 'ctrl+shift+g' would be needed if desired.
  71. # This is mainly to avoid changing all the old keystroke defs
  72. toks.append(tok.lower())
  73. pos += 1 # skip the sep
  74. start = pos
  75. pos += 1
  76. flags = 0
  77. # do the modifiers
  78. for tok in toks[:-1]:
  79. mod = modifiers.get(tok.lower())
  80. if mod is not None:
  81. flags |= mod
  82. # the key name
  83. vk, this_flags = get_vk(toks[-1])
  84. return vk, flags | this_flags
  85. _checks = [
  86. [ # Shift
  87. ("Shift", win32con.SHIFT_PRESSED),
  88. ],
  89. [ # Ctrl key
  90. ("Ctrl", win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED),
  91. ("LCtrl", win32con.LEFT_CTRL_PRESSED),
  92. ("RCtrl", win32con.RIGHT_CTRL_PRESSED),
  93. ],
  94. [ # Alt key
  95. ("Alt", win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED),
  96. ("LAlt", win32con.LEFT_ALT_PRESSED),
  97. ("RAlt", win32con.RIGHT_ALT_PRESSED),
  98. ],
  99. ]
  100. def make_key_name(vk, flags):
  101. # Check alt keys.
  102. flags_done = 0
  103. parts = []
  104. for moddata in _checks:
  105. for name, checkflag in moddata:
  106. if flags & checkflag:
  107. parts.append(name)
  108. flags_done = flags_done & checkflag
  109. break
  110. if flags_done & flags:
  111. parts.append(hex( flags & ~flags_done ) )
  112. # Now the key name.
  113. if vk is None:
  114. parts.append("<Unknown scan code>")
  115. else:
  116. try:
  117. parts.append(key_code_to_name[vk])
  118. except KeyError:
  119. # Not in our virtual key map - ask Windows what character this
  120. # key corresponds to.
  121. scancode = win32api.MapVirtualKey(vk, MAPVK_VK_TO_CHAR)
  122. parts.append(chr(scancode))
  123. sep = "+"
  124. if sep in parts: 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()