为什么用户操作无法访问 tkinter Entry 小部件

Why does tkinter Entry widget become inaccessible to user actions

是一个类似的问题,所以我认为我在正确的论坛中。我有一个 tkinter 应用程序可以计算 'steps' 的东西。在大约 20 亿次 +1 加法(接近最大 32 位 int 的大小)之后,用户无法访问 Entry 小部件中显示的计数 - 无法将光标聚焦在显示的值中并且无法 select 值的部分或全部字符。如果我重新启动应用程序并在开始前将计数预设 ('Update') 为 1999999999,则此问题仍需要 20 亿步或更多步才能显现。下面是一个精简的应用程序,它在 64 位 Windows 10 和 Raspberry Pi 4 上显示了这个问题。(原始脚本已被替换。请参阅评论。2021 年 1 月 17 日)

from tkinter import *  #python3

start = False
count = 0

def startCount():
    global start
    start = True
    countUp()

def stopCount():
    global start
    start = False
    eVal.set(count)

def get_eVal():
    global count
    count = int(eVal.get())

def countUp():
    global count, start
    count += 1
    if count % 1000 == 0:
        eVal.set(count)
    if start:
        on.after_idle(countUp)

root = Tk()
eVal = StringVar()
frame = Frame(root)
on = Button(frame, text='Start', command=startCount)
off = Button(frame, text='Stop', command=stopCount)
updt = Button(frame, text='Update', command=get_eVal)
entry = Entry(frame, textvariable=eVal, width=20, justify=RIGHT)
on.pack(side=LEFT)
off.pack(side=LEFT, padx=(8,4), pady=6)
updt.pack(side=LEFT, padx=(4,8))
entry.pack(side=LEFT)
frame.pack()
mainloop()

到目前为止,在我更大的应用程序中,当出现此问题时,用户将无法访问所有 Entry 小部件。 这是一个已知的 tkinter 问题吗?有任何简单的修复方法吗?

我猜问题是您使用 after_idle 的方式导致 mainloop 无法正确处理事件。实际上,空闲循环永远不会变空,因为对于您取消的每个空闲事件,您都会重新添加一个。而且由于 tkinter 忙于处理“空闲”事件,它很难处理其他事件,例如按钮和鼠标点击。

为了让 tkinter 事件处理系统有机会处理除您的空闲事件之外的其他事件,每个计数之间应该至少有一个毫秒或更长时间的延迟。

我知道我不应该回答我自己的问题,但我认为我现在已经有了对其他人看到这个问题可能很重要的答案。在提问之前,我在这里和其他地方做了很多研究。如果我找到了任何答案,我就不会打扰社区。这个问题只是为了确认是否有其他人看到过这个问题。我没有收到任何 'yes' 或 'no' 评论或答案。相反,这个问题被倒数了。在提出问题之前,人们如何展示所做的研究?我认为这个问题非常明确。答案对我来说当然很重要。

我还询问是否有任何简单的修复方法 - 可以用来解决问题的解决方法。好吧,去吧。在 Windows 上,以下脚本仍然显示异常显示行为,但至少该脚本仍然有效,允许在计数期间复制 Entry 值,并且不会延迟工作 (countUp)。

from tkinter import *  #python3

start = False
count = 0

def startCount():
    global start
    start = True
    countUp()

def stopCount():
    global start
    start = False
    eVal.set(count)

def get_eVal():
    global count
    count = int(eVal.get())

def countUp():
    global count, start
    count += 1
    if count % 1000 == 0:
        eVal.set(count)
    if start:
        root.update_idletasks()
        root.after(0, countUp)

root = Tk()
eVal = StringVar()
frame = Frame(root)
on = Button(frame, text='Start', command=startCount)
off = Button(frame, text='Stop', command=stopCount)
updt = Button(frame, text='Update', command=get_eVal)
entry = Entry(frame, textvariable=eVal, width=20, justify=RIGHT)
on.pack(side=LEFT)
off.pack(side=LEFT, padx=(8,4), pady=6)
updt.pack(side=LEFT, padx=(4,8))
entry.pack(side=LEFT)
frame.pack()
root.mainloop()

异常行为是您的桌面可以显示两个闪烁的光标(抱歉,此图像中的垂直条不闪烁)并且即使应用程序没有焦点,条目中的选择也会持续存在。但是,脚本仍然有效。

two cursors active

我正在测试第二个敢于使用.update 的脚本。我稍后会评论它是否是一个有效的解决方法。

from tkinter import *   # python3

start = False
count = 0
quit = False

def startCount():
    global start
    start = True

def stopCount():
    global start
    start = False
    eVal.set(count)

def get_eVal():
    global count
    count = int(eVal.get())

def countUp():
    global count
    count += 1
    if count % 1000 == 0:
        eVal.set(count)

root = Tk()
eVal = StringVar()
frame = Frame(root)
on = Button(frame, text='Start', command=startCount)
off = Button(frame, text='Stop', command=stopCount)
updt = Button(frame, text='Update', command=get_eVal)
entry = Entry(frame, textvariable=eVal, width=20, justify=RIGHT)
on.pack(side=LEFT)
off.pack(side=LEFT, padx=(8,4), pady=6)
updt.pack(side=LEFT, padx=(4,8))
entry.pack(side=LEFT)
frame.pack()
while quit == False:
    if start:
        countUp()
    try:
        root.update_idletasks()
        root.update()
    except:
        break