【问题标题】:Tkinter unbinding key event issueTkinter 解除绑定关键事件问题
【发布时间】:2020-10-29 11:45:28
【问题描述】:

在下面的代码中,按两次空格键会导致两次连续的哔声。我想避免这种情况,而是在第一次哔哔声发生时禁用该键。我认为取消绑定空格键可能会起作用,但事实并非如此。奇怪的是,似乎只有两声哔哔声堆积起来,而不是更多。我猜这个问题的原因可能是 winsound.Beep 是非阻塞的,所以重新绑定几乎是立即发生的。

请对如何使其工作有任何建议?

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

【问题讨论】:

  • 不胜感激在下面标记正确答案
  • 我投了赞成票,但我想暂时不解决这个问题,因为我认为可能会出现更多正统的方法。

标签: python python-3.x windows tkinter


【解决方案1】:

这是一个将焦点从小部件上移开的解决方案,因此不会触发绑定:

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

我评论它是为了更好地理解,但这只是一种解决方法,理解起来也没有那么复杂。

另外请记住,在使用winsound 的所有情况下,只要哔声开始并完成播放,GUI 就会无响应,即 GUI 将无响应 1 秒(在您的情况下)。

【讨论】:

  • 这是一个非常荒谬的解决方案,值得一票! :D
  • @ReblochonMasque 花了很长时间思考可以做什么,结果是 dis xp
【解决方案2】:

这应该可以解决它,但是您必须使用 pip install keyboard 下载 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()

首先start_new_thread()(语法很重要)使beep()线程化(在后台运行?)并且它是一个while循环,因此它会连续运行,每当你按下空格它就会beep,即使你发送垃圾邮件空间它仍然会运行一个beep。但是有一个缺点。它会在脚本没有终止时运行,所以如果你把注意力集中在按空格键时它仍然会发出哔哔声

【讨论】:

  • 您好,请记住,您可以通过用三个反引号(```)将代码格式化为代码,这样也更容易编辑。
  • 好的,会尝试thanx 甚至不知道。差不多``其中两个
【解决方案3】:

您可以使用自上次成功keypress 以来经过的时间来决定是否应该发出哔声。

可能是这样的:我无法访问 winsound,所以我使用os 功能来模拟哔声。您可以将其注释掉,并取消对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()

【讨论】:

    【解决方案4】:

    您可以通过after_idle()绑定回事件:

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

    解释:

    传递给after_idle() 的回调将在tkinter mainloop 空闲时执行,即没有待处理的工作/事件要处理。因此,如果多次按下空格键,第一次按下会触发beep(),其中tkinter 取消绑定事件,发出哔声,然后安排重新绑定。 beep() 返回后,tkinter 继续处理待处理的任务,即处理剩余的空格键事件(但此时,没有绑定处于活动状态),然后执行 after_idle 调度任务,即重新绑定。

    【讨论】:

    • 那么 after_idle() 定义了原始回调何时返回的回调?
    • 传递给after_idle()的回调将在tkinter主循环空闲时执行,即没有待处理的工作/事件要处理。因此,如果多次按下空格键,第一次按下会触发beep(),其中tkinter 取消绑定事件,发出哔声,然后安排取消绑定。在beep() 返回后,tkinter 继续处理待处理的任务,即处理剩余的空格键事件(但此时没有绑定活动),然后执行after_idle 调度任务,即重新绑定。
    • 这可能是正确的方法,有一些好的代表;尽管如果可以从事件队列中选择性地清除事件类型(在本例中为按键),我希望它更好。
    • 我在解释之后有点麻烦。是不是相当于说after_idle()在当前所有回调都返回后才执行?
    • 也许文档here 有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多