简体   繁体   中英

check if modifier key is pressed in tkinter

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 eg '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

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).

Bind Modifiers

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()

Bind EventType

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'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM