简体   繁体   English

如果在 function 中调用,为什么 ttk.Entry 小部件不会更新文本变量

[英]Why does ttk.Entry widget not update textvariable if called inside a function

I am trying to understand a strange behavior displayed by the ttk.Entry widget.我试图了解 ttk.Entry 小部件显示的奇怪行为。 If I declare the Entry widget textvariable inside a function and call that function from main (where I create a root object and run my mainloop), for some reason the textvariable is not displayed.如果我在 function 中声明 Entry 小部件文本变量并从 main 调用该 function(我在其中创建了一个根 object 并运行我的主循环),由于某种原因,该文本变量不是显示的。 However if I run root.mainloop() within my function as well then the textvariable is displayed properly.但是,如果我在 function 中运行 root.mainloop() ,那么文本变量就会正确显示。 Why is that?这是为什么? Why do I need to run mainloop() again inside my function as well when its already being run in main?为什么我需要在我的 function 中再次运行 mainloop() 以及它已经在 main 中运行?

In the below code first run as is and observe Entry widget remains empty and then uncomment the root.mainloop() line within the function and then run it again - this time Entry widget textvariable will be displayed.在下面的代码中,首先按原样运行并观察 Entry 小部件仍然为空,然后取消注释 function 中的 root.mainloop() 行,然后再次运行它 - 这次将显示 Entry 小部件文本变量。 Thank You!谢谢你!

from tkinter import Tk, Toplevel, StringVar, Label
from tkinter import ttk

nameVariable = 'EntryBoxTextValue'

def frameWindow(root, nameVariable):
    mainFrame = Toplevel(root)
    mainFrame.grid()
    nameLbl = Label(mainFrame, text="Name:", bg="white")
    nameLbl.grid(row=0, column=0, sticky='w', pady=(10,5))
    nameSV = StringVar()
    nameSV.set(nameVariable)
    nameEntry = ttk.Entry(mainFrame, textvariable=nameSV)
    nameEntry.grid(row=0, column=1, columnspan=4, sticky='ew', pady=(10,5))
    print(nameSV)
    # root.mainloop()

if __name__ == '__main__':
    root = Tk()
    frameWindow(root, nameVariable)
    root.mainloop()

Note that other widgets like ttk.Treeview and Text update without needing to run root.mainloop() inside the function call.请注意,其他小部件(如 ttk.Treeview 和 Text)无需在 function 调用中运行 root.mainloop() 即可更新。

It might seem a little weird at first, but I think it is the same reason as to why tkinter image does not show up when made inside a function, without keeping any reference .起初可能看起来有点奇怪,但我认为这与为什么在 function 中制作tkinter图像时不显示的原因相同,没有保留任何参考

So the problem might be that garbage collector sweeps nameSV once the function finishes executing because it is no longer used anywhere, but the tk widget is still using it.所以问题可能是,一旦nameSV完成执行,垃圾收集器就会清除 nameSV,因为它不再在任何地方使用,但tk小部件仍在使用它。

The solution is simple, just like the image issue, make the variable global or keep some other reference to it.解决方案很简单,就像图像问题一样,将变量设为全局变量或保留对它的其他引用。

def frameWindow(root, nameVariable):
    global nameSV
    ...
    ...

Or define it in the global scope itself:或者在全局 scope 本身中定义:

if __name__ == '__main__':
    root = Tk()
    
    nameSV = StringVar() # Remove this line from inside the function
    frameWindow(root, nameVariable)
    
    root.mainloop()

If you wish to see the problem yourself, you can freeze the script before the function ends and force the events to be processed and see for yourself:如果您希望自己查看问题,可以在 function 结束之前冻结脚本并强制处理事件并亲自查看:

import time
...
...

def frameWindow(root, nameVariable):
    mainFrame = Toplevel(root)
    root.withdraw() # Hide root, to properly see the mainFrame window without root covering it

    nameLbl = Label(mainFrame, text="Name:", bg="white")
    nameLbl.grid(row=0, column=0, sticky='w', pady=(10,5))
    
    nameSV = StringVar()
    nameSV.set(nameVariable)

    nameEntry = ttk.Entry(mainFrame, textvariable=nameSV)
    nameEntry.grid(row=0, column=1, columnspan=4, sticky='ew', pady=(10,5))

    root.update() # To draw the widget to the screen
    time.sleep(5)
    root.deiconify() # Bring the window back to close the root window and end the process
...
...

For the first 5 seconds you can notice the entry filled with the correct data, but as soon that is over and root pops up, the entry goes to become blank.在前 5 秒内,您可以注意到条目填充了正确的数据,但是一旦结束并且root弹出,条目就会变为空白。

Explanation in the case of image made inside a function, can be also used to explain the situation here(from effbot.org):在 function 内部制作图像的情况下的解释,也可以用来解释这里的情况(来自 effbot.org):

The problem is that the Tkinter/Tk interface doesn't handle references to Image objects properly;问题是 Tkinter/Tk 接口没有正确处理对 Image 对象的引用; the Tk widget will hold a reference to the internal object, but Tkinter does not. Tk 小部件将包含对内部 object 的引用,但 Tkinter 没有。 When Python's garbage collector discards the Tkinter object, Tkinter tells Tk to release the image.当 Python 的垃圾收集器丢弃 Tkinter object 时,Tkinter 告诉 Tk 释放图像。 But since the image is in use by a widget, Tk doesn't destroy it.但是由于图像正在被小部件使用,Tk 不会破坏它。 Not completely.不完全的。 It just blanks the image, making it completely transparent…它只是使图像空白,使其完全透明……

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM