Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

check if modifier key is pressed in tkinter

Tags:

python

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

like image 863
alessandro Avatar asked Nov 08 '13 14:11

alessandro


People also ask

What is __ init __ In tkinter?

"__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.

What is Tk () in tkinter Python?

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


3 Answers

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

like image 130
Peter Varo Avatar answered Oct 13 '22 09:10

Peter Varo


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

like image 40
Matty Avatar answered Oct 13 '22 09:10

Matty


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'
like image 29
OneMadGypsy Avatar answered Oct 13 '22 10:10

OneMadGypsy