如果在函数内部调用,为什么 ttk.Entry 小部件不更新 textvariable

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

我试图了解 ttk.Entry 小部件显示的奇怪行为。如果我在一个函数内声明 Entry 小部件 textvariable 并从 main 调用该函数(我在其中创建一个根对象和 运行 我的主循环),由于某种原因,该 textvariable 不会显示。但是,如果我也在我的函数中 运行 root.mainloop() ,那么文本变量就会正确显示。这是为什么?为什么我还需要 运行 mainloop() 在我的函数中再次 运行 在 main 中?

在下面的代码中,首先 运行 按原样观察 Entry 小部件保持为空,然后取消注释函数中的 root.mainloop() 行,然后再次 运行 它 - 这一次将显示条目小部件文本变量。谢谢!

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

请注意,ttk.Treeview 和文本等其他小部件无需在函数调用中 运行 root.mainloop() 即可更新。

起初看起来有点奇怪,但我认为这与为什么 tkinter 在函数内部制作图像时不显示的原因相同, 没有保留任何参考.

所以问题可能是垃圾收集器在函数完成执行后清除 nameSV,因为它不再在任何地方使用,但 tk 小部件仍在使用它。

解决方法很简单,就像图像问题一样,将变量设为全局变量或保留其他引用。

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

或者在全局范围内自己定义它:

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

如果你想自己看看问题,你可以在函数结束前冻结脚本并强制处理事件,你自己看看:

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
...
...

在前 5 秒内,您会注意到条目中填满了正确的数据,但一旦结束并且 root 弹出,条目就会变为空白。

在函数内部制作图像的情况下的解释,也可以用来解释这里的情况(来自effbot.org):

The problem is that the Tkinter/Tk interface doesn’t handle references to Image objects properly; the Tk widget will hold a reference to the internal object, but Tkinter does not. When Python’s garbage collector discards the Tkinter object, Tkinter tells Tk to release the image. But since the image is in use by a widget, Tk doesn’t destroy it. Not completely. It just blanks the image, making it completely transparent…