TkInter keypress, keyrelease events

Я понял, что события нажатия клавиш и keyrelease Tk должны были срабатывать только при нажатии или отпускании клавиши?

Однако со следующим простым кодом, если я удерживаю клавишу "a", я получаю непрерывную последовательность чередующихся событий нажатия клавиш /keyrelease.

Я делаю что-то неправильно или TkInter багги? Это Python2.7 на Linux mint.

from Tkinter import *
def keyup(e):
    print 'up', e.char
def keydown(e):
    print 'down', e.char

root = Tk()
frame = Frame(root, width=100, height=100)
frame.bind("<KeyPress>", keydown)
frame.bind("<KeyRelease>", keyup)
frame.pack()
frame.focus_set()
root.mainloop()

Выход при нажатии и удержании "a":

down a
up a
down a
up a
down a
up a
down a
up a
etc...

Ответ 1

Хорошо, еще несколько исследований нашли этот полезный пост, который показывает, что это происходит из-за поведения автоповтора X. Вы можете отключить его, используя

os.system('xset r off')

а затем reset, используя "on" в конце вашего script. Проблема в том, что это глобальное поведение, а не только мой script - который не очень велик, поэтому я надеюсь, что кто-то может придумать лучший способ.

Ответ 2

Автоповторное поведение зависит от системы. В Win7,

down a
down a
down a
...
down a
up a

Это меньше секунды.

Ответ 3

как насчет;

from Tkinter import *

wn = Tk()
wn.title('KeyDetect')

m = 0

def down(e):
    if m == 0:
        print 'Down\n', e.char, '\n', e
        global m
        m = 1

def up(e):
    if m == 1:
        print 'Up\n', e.char, '\n', e
        global m
        m = 0

wn.bind('<KeyPress>', down)
wn.bind('<KeyRelease>', up)

wn.mainloop()

теперь он не будет повторяться.

Ответ 4

Ну, сейчас уже немного поздно, но у меня есть решение, которое работает. Это не здорово, но не требует перезаписи системных настроек os.system, что приятно.

По сути, я делаю класс, который записывает время нажатия клавиш. Я говорю, что клавиша не работает, когда она была нажата в последний короткий промежуток времени (здесь, 0,1 мс). Чтобы получить нажатие, это достаточно просто: если кнопка не зарегистрирована как нажатая, вызвать событие. Для выпусков логика сложнее: если есть подозреваемое событие выпуска, установите таймер на короткое время (здесь,.1s), а затем проверьте, чтобы ключ не был выключен.

После того, как вы подтвердите пресс или релиз, вызовите методы on_key_press или on_key_release в вашем коде. Что касается тех, просто реализуйте их так, как вы изначально хотели их

Я знаю, что это не идеально, но я надеюсь, что это поможет !!

Вот код:

Где вы инициализируете события нажатия клавиш:

key_tracker = KeyTracker()
window.bind_all('<KeyPress>', key_tracker.report_key_press)
window.bind_all('<KeyRelease>', key_tracker.report_key_release)
key_tracker.track('space')

Вот мой пользовательский класс KeyTracker:

class KeyTracker():
key = ''
last_press_time = 0
last_release_time = 0
waiting_to_test_release = False

def track(self, key):
    self.key = key

def is_pressed(self):
    return time.time() - self.last_press_time < .1

def report_key_press(self, event):
    if event.keysym == self.key:
        if not self.is_pressed():
            print('press')
            on_key_press(event)
        self.last_press_time = time.time()

def report_key_release(self, event):
    if event.keysym == self.key:
        if not self.waiting_to_test_release:
            timer = threading.Timer(.1, self.report_key_release_callback, args=[event])
            timer.start()
            print(time.time())

def report_key_release_callback(self, event):
    print(time.time())
    print()
    if not self.is_pressed():
        print('rel')
        on_key_release(event)
    self.last_release_time = time.time()