I know and use a lot the syntax for binding, but how can I instead check directly the event object and extract both the letter pressed e.g. 'c' and the modifiers e,g,'Control' and 'Alt' ?
I tried this
def reportEvent(event):
eventDict = {
'2': 'KeyPress', '3': 'KeyRelease', '4': 'ButtonPress', '5': 'ButtonRelease', '6': 'Motion', '7': 'Enter',
'8': 'Leave', '9': 'FocusIn', '10': 'FocusOut', '12': 'Expose', '15': 'Visibility', '17': 'Destroy',
'18': 'Unmap', '19': 'Map', '21': 'Reparent', '22': 'Configure', '24': 'Gravity', '26': 'Circulate',
'28': 'Property', '32': 'Colormap','36': 'Activate', '37': 'Deactivate'}
rpt = '\n\n%s' % (80*'=')
rpt = '%s\nEvent: type=%s (%s)' % (rpt, event.type,eventDict.get(event.type, 'Unknown'))
rpt = '%s\ntime=%s' % (rpt, event.time)
rpt = '%s widget=%s' % (rpt, event.widget)
rpt = '%s x=%d, y=%d'% (rpt, event.x, event.y)
rpt = '%s x_root=%d, y_root=%d' % (rpt, event.x_root, event.y_root)
rpt = '%s y_root=%d' % (rpt, event.y_root)
rpt = '%s\nserial=%s' % (rpt, event.serial)
rpt = '%s num=%s' % (rpt, event.num)
rpt = '%s height=%s' % (rpt, event.height)
rpt = '%s width=%s' % (rpt, event.width)
rpt = '%s keysym=%s' % (rpt, event.keysym)
rpt = '%s ksNum=%s' % (rpt, event.keysym_num)
#### Some event types don't have these attributes
try:
rpt = '%s focus=%s' % (rpt, event.focus)
except:
try:
rpt = '%s send=%s' % (rpt, event.send_event)
except:
pass
print rpt
stolen to Python and Tkinter Programming,but it doesnt show the eventual modifiers I'm pressing
"__init__" is a reseved method in python classes. It is called as a constructor in object oriented terminology. This method is called when an object is created from a class and it allows the class to initialize the attributes of the class.
The tkinter package (“Tk interface”) is the standard Python interface to the Tk GUI toolkit. Both Tk and tkinter are available on most Unix platforms, as well as on Windows systems. (Tk itself is not part of Python; it is maintained at ActiveState.)
Theoretically this would be the answer for your problem:
from tkinter import *
root = Tk()
mods = {
0x0001: 'Shift',
0x0002: 'Caps Lock',
0x0004: 'Control',
0x0008: 'Left-hand Alt',
0x0010: 'Num Lock',
0x0080: 'Right-hand Alt',
0x0100: 'Mouse button 1',
0x0200: 'Mouse button 2',
0x0400: 'Mouse button 3'
}
root.bind( '<Key>', lambda e: print( 'Key:', e.char,
'Mods:', mods.get( e.state, None )))
root.mainloop()
However, it is not working as it should be -- or at least it's not on my hungarian apple keyboard which is a 110 keys layout..
Anyway, here are all the properties of the event object: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/event-handlers.html
With the help of Peter Varo's idea, I sort of got it to work for me (running windows 10 and python 3.4).
from tkinter import *
def onKeyDown(e):
# The obvious information
c = e.keysym
s = e.state
# Manual way to get the modifiers
ctrl = (s & 0x4) != 0
alt = (s & 0x8) != 0 or (s & 0x80) != 0
shift = (s & 0x1) != 0
# Merge it into an output
# if alt:
# c = 'alt+' + c
if shift:
c = 'shift+' + c
if ctrl:
c = 'ctrl+' + c
print(c)
# Run the tk window
root = Tk()
root.bind('<Key>', onKeyDown)
root.mainloop()
The alt
key is buggy in my case (it is always pressed), but the ctrl
and shift
work ok.
Note that the first time you press the ctrl
or shift
key, it will register it as main key and not yet as modifier. It'll only be a modifier if you hold it later.
So you do need to press it as a combination of keys (like Ctrl+a
). As a result, pressing a
and then ctrl
will give a slightly weird result (it will ignore the a and mark the ctrl as the main key, but not as a modifier).
The below script corrects the improper bitflags used in prior answers and takes into consideration that ex: state=Shift|Alt|Control is not going to match anything in the Modifier dict. It needs to be processed as bitflags and matched bit for bit.
Something any user of this needs to consider is that all the Lock keys (if on) will be reported in event.state whether you ask for it or not. As an example: You have CapsLock and NumLock on and attempt to capture Alt + Control ~ you will actually get 'CapsLock + NumLock + Control + Alt'. The easiest way to get around that is to simply remove all the ModN keys from the Modifier dict.
As far as I can tell, capturing specific _L and _R keys is only possible through the keysym property. The state property will always contain the generic modifier bitflag for that key.
#python 3.8
from tkinter import *
root = Tk()
Modifier = {
0x1 : 'Shift' ,
0x2 : 'CapsLock' ,
0x4 : 'Control' ,
0x8 : 'NumLock' , #Mod1
0x10 : 'Mod2' , #?
0x20 : 'ScrollLock' , #Mod3
0x40 : 'Mod4' , #?
0x80 : 'Mod5' , #?
0x100 : 'Button1' ,
0x200 : 'Button2' ,
0x400 : 'Button3' ,
0x800 : 'Button4' ,
0x1000 : 'Button5' ,
0x20000: 'Alt' , #not a typo
}
root.bind( '<Key>', lambda e: print(' + '.join([v for k, v in Modifier.items() if k & e.state])))
root.mainloop()
EventType is an Enum. Creating an entire dict that repeats this Enum is unnecessary. You can get the name of the EventType with event.type.name, and the value is accessed with event.type.value. If you absolutely had to clone the Enum as a dict, it can be done more thoroughly (and easily) with:
#python 3.8
# EventType Lookup
BindMap = {i.name:i.value for i in EventType}
# A few aliases that wont be returned from the generator
BindMap['Key'] = '2'
BindMap['Button'] = '4'
BindMap['Double'] = '4'
BindMap['Triple'] = '4'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With