【问题标题】:Why does tkinter Entry widget become inaccessible to user actions为什么用户操作无法访问 tkinter Entry 小部件
【发布时间】:2021-01-12 15:50:03
【问题描述】:

This 是一个类似的问题,所以我认为我在正确的论坛中。我有一个 tkinter 应用程序,可以计算某些东西的“步数”。经过大约 20 亿次 +1 加法(接近最大 32 位 int 的大小),Entry 小部件中显示的计数不再可供用户访问——无法将光标聚焦在显示的值上,也无法选择一些或值的所有字符。如果我重新启动应用程序并在开始之前将计数预设(“更新”)为 1999999999,则此问题仍然需要 20 亿步或更多才能显现出来。下面是一个精简的应用程序,它在 64 位 Windows 10 和 Raspberry Pi 4 上显示了这个问题。(原始脚本已被替换。参见 cmets。17Jan2021)

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 问题吗?有什么简单的解决方法吗?

【问题讨论】:

  • 我无法运行您的示例,我收到一个错误,表明 eVal 未定义。可能是因为,eVal 是在 main 中定义的,但不能全局用于函数 startCount、stopCount、get_eVal 等?,
  • 我在运行这个脚本时没有遇到这个问题,但无论如何,请尝试通过 quit = False 语句将 eVal = StringVal() 语句向上移动并根据需要添加全局。你用什么python3/系统?顺便说一句,我认为 if name == 'main': 的范围是全局的。
  • 不,再三考虑,不要将 eVal = StringVal() 语句移到 root = TK() 之前。很抱歉,我不知道您的问题是什么。
  • 您是否尝试在最后删除您的while 循环?使用你自己的循环并调用update 不如让mainloop 完成它的工作效率高,而且实际上可能会导致问题。这不是 tkinter 的设计用途。
  • 我已经编辑了脚本以避免.update,但是如果我点击条目的显示,这个版本很容易出现“无响应”。在没有任何答案的情况下,我会接受答案是“不,这不是已知的 tkinter 问题”。尤其是 mainloop() 似乎不是一个简单的解决方法。

标签: python-3.x tkinter tkinter-entry


【解决方案1】:

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

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

【讨论】:

  • 如果您想分享您的研究,那么您应该勾选“回答您自己的问题 - 分享您的知识,问答式”,然后回答它。而且您的问题是“有什么简单的解决方法吗?” 而不是“问题只是为了确认其他人是否看到过这个问题。”。。跨度>
  • 你在阅读什么 stackoverflow 说明手册?我想知道所有的习俗和要求。是的,我确实输入了两个问题。您关注的问题是“这是一个已知的 tkinter 问题吗?”一个直接的答案是是或否。我的意图是无论哪种方式都得到确认。仍然没有发生。不过谢谢。
  • 这不是来自任何说明手册,当在最底部询问关于stackoverflow的问题时,如果这是一个问答式的帖子,将会有一个检查按钮来切换。我只是指出 “我知道我不应该回答自己的问题” 是错误的。如果您在发布问题后提出答案,则可以回答您自己的问题。尽管如此,我认为我误解了您的回答,说您在发布问题本身时就知道答案。抱歉,造成误解。
  • 如果您想通读提出一个好问题,this 可能会有所帮助
  • 第二个脚本不是解决方法。事实证明,最初提出了同样的问题。可能为时已晚,但我想向我冒犯的任何成员道歉,尤其是那些提供了出色代码和节省项目答案的成员,例如stackoverflow.com/questions/29158220/…
【解决方案2】:

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

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

【讨论】:

  • 谢谢,但我认为这会将最短测试时间(更不用说在 1 毫秒内完成的工作)延长到 23 天。当使用 .update 我的旧方法发生此问题时,我无法复制条目值,但至少脚本仍然执行。当我有时间时,我肯定会尝试各种变化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-27
  • 1970-01-01
  • 1970-01-01
  • 2014-10-15
  • 1970-01-01
  • 2022-01-08
相关资源
最近更新 更多