[Matplotlib-users] Key Press and Key Release Events in TkInter

Gregory Key gregk3172 at outlook.com
Fri Feb 3 13:32:16 EST 2017


I got excited too quickly. Searching the internet for 'key debounce in
tkinter' did indeed returned several hits with sample code, some of
them very elaborate. The problem is none of them worked on my computer!
From Joe's previous comments and the samples I found I finally got
inspiration. As usual with Python and matplotlib the solution turned
out to be simple. Using the TkAgg backend on my computer and holding
down a key fires a continuous stream of key_pressed and key_released
events as long as the key is down. The solution was to let the first
key_pressed event start a timer whose callback performs the task I want
done, then I let the automatic key_pressed and key_released events just
increment counters. In the timer callback I check for equality of the
two counters. After the final key_release the counters will be equal so
I stop the timer and reset the counters. Depending on the timer
interval and the key repeat rate there are times that the callback
falls between the key_press and the key_release which causes the
callback to stop the timer but if the key is still down the next
key_press event just starts the timer and it's callback again and keeps
going without any noticeable hesitation.
The Python code that works on my computer is shown below:

"""
Created on Thu Feb  2 07:22:19 2017
adapted from a matplotlib example
"""

import matplotlib
matplotlib.use('tkagg')
import matplotlib.pyplot as plt

key_pressed = 0
key_released = 0


def on_key_press(event):
    """
    Look for a right or left arrow key.
    If it is the first key press start the timer.
    Increment the key_pressed counter.
    """
    global key_pressed
    global key_released

    if event.key not in ('right', 'left'):
        return
    if key_pressed is 0:
        print('starting timer')
        timer1.start(interval=30)
    key_pressed += 1
    print('\ninside onpress')
    print('key_pressed =', key_pressed)
    print('key_released =', key_released)


def on_key_release(event):
    """
    All this does is increment the key_released counter.
    """
    global key_pressed
    global key_released

    if event.key not in ('right', 'left'):
        return
    key_released += 1
    print('\ninside onrelease')
    print('key_pressed =', key_pressed)
    print('key_released =', key_released)


def scroll():
    """
    Scroll the data cursor or something
    """
    global key_pressed
    global key_released

    print('\ninside scroll')
    print('key_pressed =', key_pressed)
    print('key_released =', key_released)
    if key_pressed is key_released:
        print('\ntimer stopped')
        key_pressed = 0      # reset the counters for next keypress
        key_released = 0
        # returning False stops the timer
        return False

fig = plt.figure()
fig.canvas.mpl_connect('key_press_event', on_key_press)
fig.canvas.mpl_connect('key_release_event', on_key_release)
timer1 = fig.canvas.new_timer()
timer1.add_callback(scroll)

plt.show()

Greg Key

On Fri, 2017-02-03 at 02:26 +0000, Gregory Key wrote:
> Bingo! Searching for 'key debounce in tkinter' was what I needed. I
> think I have found a solution. Thanks!
> 
> Greg Key
> 
> On Thu, 2017-02-02 at 21:35 +0000, Gregory Key wrote:
> > So basically in tkinter the key_release_event is useless.
> > 
> > Thanks I'll do some experimenting with the information you have
> > given
> > me.
> > 
> > On Thu, 2017-02-02 at 14:07 -0600, Joe Kington wrote:
> > > That's a common "gotcha" when trying to detect a held key on a
> > > keyboard.  Your operating system (or the actual hardware in your
> > > keyboard in some cases) interprets a held key as multiple
> > > keypresses
> > > and fires off events accordingly (e.g., that's why holding down
> > > "a"
> > > will give "aaaaaaaaa" when typing).
> > > 
> > > I think Qt handles this automatically, but I could be wrong. Tk
> > > doesn't do it automatically.
> > > 
> > > Usually, you'd detect a held key by keeping track of the either
> > > the
> > > last key press event or the time since the last keypress event.
> > >  "Debouncing" is the term you'll want to search for.  In tkinter,
> > > you
> > > can bind a function to fire after idle (effectively checking if
> > > the
> > > key was ever released).  Alternatively, for the matplotlib side,
> > > I
> > > think you can bind to the idle event and do the same thing
> > > without
> > > needing to rely on anything tkinter-specific (Untested).
> > > 
> > > Hope that helps a bit, anyway.
> > > -Joe
> > > 
> > > On Tue, Jan 31, 2017 at 3:05 PM, Gregory Key <gregk3172 at outlook.c
> > > om
> > > > 
> > > 
> > > wrote:
> > > > I am running Python 3.5.2 and MatPlotLib 2.0.0 on Ubuntu 16.10.
> > > > 
> > > > I am developing a program that uses matplotlib embedded in
> > > > tkinter
> > > > to
> > > > plot vibration data in a bode plot, phase and amplitude versus
> > > > rotor
> > > > speed. I adapted the data browser given in the matplotlib
> > > > documentation
> > > >             as example 91.2 data_browser.py to browse the data.
> > > > The
> > > > browser worked but I wanted to modify it so that holding down
> > > > the
> > > > next
> > > > or previous key would scroll through the data instead of having
> > > > to
> > > > continously press and release the keys. My plan was to use the
> > > > key_press_event to start a timer which would call a scroll
> > > > method
> > > > and
> > > > then use the key_release event to stop the timer and stop
> > > > scrolling. I
> > > > couldn't make this scheme work so I did some investigating on
> > > > the
> > > > key_press_event and key_release_event in tkinter. I used
> > > > example
> > > > 91.5
> > > > keypress_demo.py given in the matplotlib documentation but I
> > > > added
> > > > a
> > > > key_release_event method to it. What I found was that the key
> > > > events
> > > > don't work the way I would expect them to work. If you press
> > > > and
> > > > release a key things work as expected. When a key is pressed
> > > > the
> > > > key_press_event fires and when it is released the
> > > > key_release_event
> > > > fires. If a key is pressed and held however the key_press_event
> > > > fires
> > > > followed immediately by a key_release_event even though the key
> > > > has
> > > > not
> > > > been released. As long as the key is held down it will continue
> > > > to
> > > > fire
> > > > key_press_event followed by key_release_event. Is this the way
> > > > it
> > > > is
> > > > supposed to work?
> > > > 
> > > > Thanks
> > > > Greg Key
> > > > _______________________________________________
> > > > Matplotlib-users mailing list
> > > > Matplotlib-users at python.org
> > > > https://mail.python.org/mailman/listinfo/matplotlib-users
> > > > 
> > > 
> > > 
> > 
> > _______________________________________________
> > Matplotlib-users mailing list
> > Matplotlib-users at python.org
> > https://mail.python.org/mailman/listinfo/matplotlib-users
> 
> _______________________________________________
> Matplotlib-users mailing list
> Matplotlib-users at python.org
> https://mail.python.org/mailman/listinfo/matplotlib-users


More information about the Matplotlib-users mailing list