web-dev-qa-db-fra.com

TkInter keypress, événements de libération de clé

J'ai compris que les événements de frappe et de libération de touche Tk n'étaient censés se déclencher que lorsque la touche était réellement enfoncée ou relâchée?

Cependant, avec le code simple suivant, si je maintiens la touche "a" enfoncée, j'obtiens une séquence continue d'événements alternés de pression de touche/libération de touche.

Est-ce que je fais quelque chose de mal ou est-ce que TkInter est un buggy? C'est Python2.7 sous Linux.

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

Sortie lorsque vous maintenez "a":

down a
up a
down a
up a
down a
up a
down a
up a
etc...
18
lost

Ok, quelques recherches supplémentaires ont trouvé ce post utile qui montre que cela se produit en raison du comportement de répétition automatique de X. Vous pouvez le désactiver en utilisant

os.system('xset r off')

et ensuite réinitialisez-le en utilisant "on" à la fin de votre script ... Le problème est qu'il s'agit d'un comportement global - pas seulement mon script - qui n'est pas génial, j'espère que quelqu'un pourra trouver une meilleure solution.

14
lost

Le comportement de répétition automatique dépend du système. Dans Win7, 

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

C'est pour moins d'une seconde.

3
Terry Jan Reedy

que diriez-vous;

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

maintenant, ça ne se répète plus.

1
user9162065

C'est un peu tard maintenant, mais j'ai une solution qui fonctionne. Ce n'est pas génial, mais cela ne nécessite pas le remplacement du système os.system, ce qui est bien. 

Fondamentalement, je fais une classe qui enregistre le moment d'appuyer sur les touches. Je dis qu'une touche est enfoncée lorsqu'elle a été enfoncée pendant la dernière période (ici, 0,1 ms). Pour obtenir un appui, c'est assez simple: si la touche n'est pas enregistrée comme étant enfoncée, déclenchez l'événement. Pour les versions, la logique est plus difficile: en cas d’événement suspect, définissez un temporisateur (ici .1s), puis vérifiez que la clé n’est pas enfoncée.

Une fois que vous avez validé un appui ou une libération, appelez les méthodes on_key_press ou on_key_release dans votre code. Quant à ceux-ci, implémentez-les comme vous le vouliez au départ

Je sais que ce n'est pas parfait, mais j'espère que cela aide !!

Voici le code:

Où vous initialisez les événements de pression de touche:

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

Voici ma classe KeyTracker personnalisée:

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

    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():
                on_key_press(event)
            self.last_press_time = time.time()

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

    def report_key_release_callback(self, event):
        if not self.is_pressed():
            on_key_release(event)
        self.last_release_time = time.time()
1
Artur Hawkwing