为什么不鼓励使用多个 Tk 实例?

Why are multiple instances of Tk discouraged?

考虑以下示例:

import tkinter as tk

root = tk.Tk()
root.title("root")

other_window = tk.Tk()
other_window.title("other_window")

root.mainloop()

另请参见下面的示例,该示例连续创建 Tk 实例而不是一次创建实例,因此 恰好有一个 实例 Tk在任何给定时间:

import tkinter as tk

def create_window(window_to_be_closed=None):
    if window_to_be_closed:
        window_to_be_closed.destroy()
    window = tk.Tk()
    tk.Button(window, text="Quit", command=lambda arg=window : create_window(arg)).pack()
    window.mainloop()

create_window()

到目前为止我找到的最好的参考是 Application Windows section of the tkinterbook:

In the simple examples we’ve used this far, there’s only one window on the screen; the root window. This is automatically created when you call the Tk constructor

If you need to create additional windows, you can use the Toplevel widget. It simply creates a new window on the screen, a window that looks and behaves pretty much like the original root window

我的看法是,Tk 实例会创建一个 Toplevel 小部件,加上 mainloop 之类的东西,其中应该只有一个。

Why is it considered bad to have multiple instances of Tk?

Tkinter 只是一个 python 围绕导入 Tk 库的嵌入式 Tcl 解释器的包装器。当您创建一个根 window 时,您创建了一个 Tcl 解释器的实例。

每个 Tcl 解释器都是一个独立的沙箱。一个沙箱中的对象不能与另一个沙箱中的对象交互。最常见的表现是在一个解释器中创建的 StringVar 在另一个解释器中不可见。小部件也是如此——您不能在一个解释器中创建小部件,而在另一个解释器中将其作为父小部件。图片是第三种情况:在一个中创建的图像不能在另一个中使用。

从技术角度来看,没有理由不能同时拥有两个 Tk 实例。反对它的建议是因为很少有实际需要有两个或更多不同的 Tcl 解释器,并且它会导致初学者难以掌握的问题。

Is the second snippet considered a bit better, or does it suffer from the same conditions the first code does?

如果不知道您要实现的目标,就不可能说问题中的第二个示例是否更好。它可能也好不到哪儿去,因为再说一遍,您很少会真正需要两个实例。

99.9% 的最佳解决方案是只创建一个 Tk 的实例,供您在程序的整个生命周期中使用。如果您需要第二个或后续 window,请创建 Toplevel 的实例。很简单,这就是 tkinter 和底层 Tcl/Tk 解释器的设计用途。

Tk() 初始化隐藏的 tcl 解释器,以便代码可以 运行,因为 Tkinter 只是 tcl/tk 的包装器。它还会自动创建一个新的 window。 Toplevel() 只是创建一个新的 window,如果 Tk() 没有被实例化,它将无法工作,因为它需要 Tk() 初始化的 tcl 解释器。您不能在不实例化 Tk() 的情况下创建任何 Tkinter 小部件,并且 Toplevel 只是一个小部件。在问题中,您使用 Tk() 创建第二个 window。您应该创建另一个文件,因为多次初始化 tcl 解释器可能会造成混淆,正如@Bryan Oakley 解释的那样。那么你应该这样做:

from os import startfile startfile(nameOfTheOtherFile)

,因为 Toplevel() 只是一个小部件,它会在 Tk() window 关闭时关闭。将另一个 window 放在单独的文件中可以减少混淆。

我不同意 tkinter 社区不鼓励使用多个 tk.Tk windows。您可以有多个 tk.Tk windows。使用 tk.Tk 的多个实例是创建真正相互独立的 windows 的唯一方法。大多数人在创建多个 tk.Tk windows 时犯的唯一错误是他们在创建 PhotoImages/StringVars/[=19 时忘记传入 master=... =]s/...

例如看这段代码:

import tkinter as tk

root = tk.Tk()
root2 = tk.Tk()

variable = tk.StringVar() # Add `master=root2` to fix the problem
entry = tk.Entry(root2, textvariable=variable)
entry.bind("<Return>", lambda e: print(repr(variable.get())))
entry.pack()

root.mainloop()

上面的代码不起作用。如果您将 master=root2 添加到 tk.StringVar(),那么它将完全正常工作。这是因为 tkintertk.Tk() 的第一个实例存储在 tk._default_root 中。那么如果你不传入 master=...,它会假定你想要 tk._default_root.

中的 window

人们误解的另一件事是应该调用 .mainloop() 多少次。它处理来自所有 tk.Tk windows 的活动事件,因此您只需要一个 .mainloop().

对于不同意的人,我会对一个实际问题是由多个 tk.Tk windows.

引起的示例感兴趣