为什么用户操作无法访问 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
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