title: How to identify non-printable KeyPress events in Tkinter
body: <p>I'm making an app in Tkinter using multiple Text widgets, and I'm working on undo/redo functionality, which is triggered by a KeyPress event. (I'm not using the text widget's built-in undo stack because I have non-tkinter objects which can also be undone/redone and I want to keep everything in one stack)</p>
  
  <p>Before I began, I read tutorials and documentation (e.g. <a href="http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm" rel="noreferrer">http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm</a>) which gave me the impression event.char would be empty or None if the key pressed was a 'special' key rather than an alphanumeric/punctuation key.</p>
  
  <p>This is not actually the case. Pressing Backspace on my keyboard (I'm using a Macbook Air running El Capitan) returns an event.char of \x7f.</p>
  
  <p>Reading more about Tcl/TK, I also learned the list of X keysyms are not consistent from platform to platform, and that e.g. on my Mac, the Shift keys apparently have a keysym code of 0. This means my application interpreted unprintable characters like Delete or the Shift key as printable characters, which is messing with my implementation.</p>
  
  <p>I'll handle the common unprintable-key KeyPress events separately anyway, but I'm still concerned about unexpected keyboard event behaviour for two reasons: 1) my program is intended to be multi-platform, and 2) there is a high chance this program will be used by people using non-US keyboards.</p>
  
  <p>My current solution, using <a href="https://www.tcl.tk/man/tcl8.4/TkCmd/keysyms.htm" rel="noreferrer">this</a> as a reference, is to check event.keysym_num to see if it is within the range of keysym numbers/keycodes used by printable characters.</p>
  
  <p>Here is example code:</p>
  
  <pre><code>from tkinter import *
  from tkinter import ttk
  
  root = Tk()
  
  textbox = Text(root, width=60, height=3)
  textbox.grid(sticky=(N, S, E, W))
  
  def KeyboardEvent(event):
      if event.keysym_num &gt; 0 and event.keysym_num &lt; 60000:
          print('This is a printable key. The character is: %r keysym: %r' % \
              (event.char, event.keysym_num))
      else:
          print('This key is unprintable. The character is: %r keysym: %r' % \
              (event.char, event.keysym_num))
  textbox.bind('&lt;KeyPress&gt;', KeyboardEvent)
  root.mainloop() 
  </code></pre>
  
  <p>My questions:</p>
  
  <ol>
  <li>Will my current method work, or are there non-printable keys that will still fall through?</li>
  <li>Is there a better way to tell non-printable key events apart from printable key events in Tkinter/Python? (I considered using unicodecategory or curses.ascii.isprint() but this check occurs <em>every time</em> the user presses a key so these seemed like overkill)</li>
  <li>Are there any more gotchas with KeyPress events, particularly inconsistent behaviour between Windows/Mac/Linux, that I should be aware of? (note: I'm already aware of issues with whitespace characters such as tabs and carriage returns)</li>
  </ol>
  
  <p>I'm using Python 3 (installed with Homebrew) on a Mac. Thanks!</p>