-1
votes

Problème d'événement clé de dissociation Tkinter

Dans le code ci-dessous, appuyer deux fois sur la barre d'espacement entraîne deux bips successifs. Je veux éviter cela et désactiver à la place la touche pendant le premier bip. Je pensais que délier la clé d'espace pourrait fonctionner, mais ce n'est pas le cas. Il est étrange que seuls deux bips semblent s'empiler plutôt que plus. Je suppose que la cause du problème est peut-être que Winsound.Beep n'est pas bloquant, donc la reliure se produit presque instantanément.

Une suggestion sur la façon de faire fonctionner cela s'il vous plaît?

import winsound
from tkinter import *

             
def beep(e):
    frame.unbind("<space>")
    winsound.Beep(440, 1000)
    frame.bind("<space>", beep)


root = Tk()
frame = Frame(root, width=100, height=100)
frame.bind("<space>", beep)
frame.pack()
frame.focus_set()
root.mainloop()


2 commentaires

J'apprécierais de marquer une réponse correcte ci-dessous


Je l'ai voté, mais je veux que la question ne soit pas résolue pour le moment car je pense que des approches plus orthodoxes pourraient être à venir.


4 Réponses :


1
votes

Cela devrait résoudre le problème, mais vous devez télécharger le module de keyboard avec le pip install keyboard :

import winsound
from tkinter import *
import keyboard
from _thread import start_new_thread

def beep():
    while True:
        if keyboard.is_pressed('space'):
            winsound.Beep(440, 1000)

root = Tk()

frame = Frame(root, width=100, height=100)
start_new_thread(beep, ())
frame.pack()
frame.focus_set()

root.mainloop()

Tout d' abord start_new_thread() (syntaxe est importante) fait beep() fileté (fonctionne en arrière - plan?) Et son un while boucle de sorte qu'il fonctionne en continu et chaque fois que vous appuyez sur l' espace il beep un beep beep et même si l' espace vous spammer il encore il suffit d' exécuter un beep . Cependant, il y a un inconvénient. Il fonctionnera tant que le script n'est pas terminé, donc si vous vous concentrez, il émettra toujours un bip si vous appuyez sur la barre d'espace


2 commentaires

Bonjour, gardez à l'esprit que vous pouvez formater votre code en tant que code en le mettant avec trois backticks (`` ''), ce qui facilite également l'édition.


Ok, j'essaierai que je ne le savais même pas. À peu près `` deux de ces



1
votes

Vous pouvez utiliser le temps écoulé depuis la dernière keypress de keypress réussie pour décider si le bip doit être émis ou non.

peut-être comme ceci: je n'ai pas accès à winsound, donc j'utilise une fonction os pour imiter un bip. Vous pouvez commenter ceci et décommenter les appels à winsound

# import winsound
import os
import tkinter as tk
import time

             
def beep(e, time_limit=1, timer=[0]):
    t0 = timer[0]
    t1 = time.time()
    delta_t = t1 - t0
    if delta_t < time_limit:
        return
#     winsound.Beep(440, 1000)
    os.system('say "Beep"')    
    timer[0] = t1
    

root = tk.Tk()
frame = tk.Frame(root, width=100, height=100)
frame.bind("<space>", beep)
frame.pack()
frame.focus_set()
root.mainloop()


0 commentaires

2
votes

Voici une solution qui éloigne le focus du widget, donc la liaison ne sera pas déclenchée:

import winsound
from tkinter import *

def beep(event):
    dummy.focus_set() #setting focus to dummy
    winsound.Beep(440, 1000) #playing it 
    root.after(1000,frame.focus_set) #setting focus back after playing for 1000 ms

root = Tk()

dummy = Label() #making a dummy widget
dummy.pack()
frame = Frame(root, width=100, height=100)
frame.bind("<space>",beep)
frame.pack()
frame.focus_set()

root.mainloop()

Je l'ai commenté pour mieux comprendre, mais ce n'est qu'un moyen de contourner et ce n'est pas si compliqué à comprendre non plus.

Gardez également à l'esprit que dans tous les cas d'utilisation de winsound , tant que ce bip a commencé et a fini de jouer, l'interface graphique ne répondra pas, c'est-à-dire que l'interface graphique ne répondra pas pendant 1 seconde (dans votre cas).


2 commentaires

C'est une solution tellement ridicule qu'elle mérite un vote favorable! :RÉ


@ReblochonMasque Passez du temps à réfléchir à ce qui pourrait être fait, et vous vous retrouvez avec dis xp



1
votes

Vous pouvez lier l'événement via after_idle() :

def beep(e):
    e.widget.unbind('<space>')
    winsound.Beep(440, 1000)
    e.widget.after_idle(e.widget.bind, '<space>', beep)

explication:

Le callback passé à after_idle() sera exécuté quand tkinter mainloop est inactif, c'est-à-dire qu'il n'y a pas de travaux / événements en attente à gérer. Donc, si la barre d'espacement est pressée plusieurs fois, la première pression déclenche beep() dans lequel tkinter l'événement, tkinter bip et planifie ensuite le rebind. Après le retour de beep() , tkinter continue de gérer les tâches en attente, c'est-à-dire de gérer les événements restants de la barre d'espace (mais à ce moment-là, aucune liaison n'est active), puis de faire la tâche de planification after_idle qui est le rebind.


6 commentaires

Donc, after_idle () définit le rappel pour quand le rappel d'origine est retourné?


Le callback passé à after_idle() sera exécuté quand tkinter mainloop est inactif , c'est-à-dire qu'il n'y a pas de travaux / événements en attente à gérer. Donc, si la barre d'espacement est pressée plusieurs fois, la première pression déclenche beep() dans lequel tkinter l'événement, tkinter bip puis programme le détachement. Après le retour de beep() , tkinter continue de gérer les tâches en attente, c'est-à-dire de gérer les événements restants de la barre d'espace (mais à ce moment-là, aucune liaison active), puis de faire la tâche de planification after_idle qui est la réassociation.


C'est probablement la bonne approche, ayez un bon représentant; bien que j'aimerais mieux s'il était possible de purger sélectivement les types d'événements (dans ce cas, les touches) de la file d'attente d'événements.


J'ai un peu de mal à suivre l'explication. Est-ce équivalent à dire que after_idle () est exécuté lorsque tous les callbacks actuels sont retournés?


Peut-être que le document ici aide.


Je viens de remarquer que cette solution ne fonctionne pas pour le premier rappel (c'est-à-dire que j'obtiens un deuxième bip si j'appuie deux fois sur espace lorsque le programme s'exécute pour la première fois). Cela fonctionne pour les touches successives. Des idées pourquoi s'il vous plaît?