为什么不建议使用多个Tk实例?

16

考虑以下示例:

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()
  • 为什么拥有多个Tk实例被认为是不好的?
  • 第二段代码是否被认为更好一些,还是它也遭受了第一个代码相同的问题?
4个回答

25
为什么创建多个Tk实例被认为是不好的?
Tkinter只是Python中嵌入Tcl解释器并导入Tk库的包装器。当您创建根窗口时,您创建了一个Tcl解释器实例。
每个Tcl解释器都是一个隔离的沙盒。一个沙盒中的对象不能与另一个沙盒中的对象交互。最常见的表现形式是,在一个解释器中创建的StringVar在另一个解释器中不可见。对于小部件也是如此--您不能在一个具有父级小部件的解释器中创建小部件,而该父级小部件位于另一个解释器中。图像是第三种情况:在一个解释器中创建的图像不能在另一个解释器中使用。
从技术上讲,同时拥有两个Tk实例没有任何问题。反对使用它的建议是因为很少需要两个或更多不同的Tcl解释器,并且会导致��学者难以理解的问题。
第二个示例是否更好一些或者是否遭受了第一个代码所面临的情况,很难说,这取决于您想要达到的目标。它可能并不更好,因为再次强调,实际上很少需要两个实例。
99.9%的最佳解决方案是创建一个Tk实例,您将在程序的生命周期中使用它。如果您需要第二个或后续窗口,请创建Toplevel实例。简而言之,这就是Tkinter和底层Tcl/Tk解释器的设计用途。

4
如果你需要有多个Tk实例,它们应该放在不同的线程中,这样它们就可以拥有自己的事件处理循环。理论上可能将这些线程中的底层控件合并为单个视图,但这是一种非常高级的技术, 我不确定Tkinter是否支持此方法。实际上,在同一个线程中保持所有GUI活动要简单得多。 - Donal Fellows
1
抱歉给你点了个踩,是手滑了,现在我的投票被锁定了。 - glenn jackman
对于那些想要了解如何使用高级技术将小部件融合在一起的好奇心人,请参见toplevel -usetoplevel -container - Johannes Kuhn

8
我不同意tkinter社区反对使用多个tk.Tk窗口的观点。您可以拥有多个tk.Tk窗口。使用多个tk.Tk实例是创建真正彼此独立的窗口的唯一方法。创建多个tk.Tk窗口时,大多数人犯的唯一错误是,在创建PhotoImage/StringVar/IntVar/...时忘记传递master=...参数。
例如,请查看以下代码:
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()

上面的代码无法正常工作。如果在tk.StringVar()中添加master=root2,那么它就会工作得非常好。这是因为tkintertk.Tk()的第一个实例存储在tk._default_root中。然后,如果您没有传递master=...,它将假定您希望在tk._default_root中创建窗口。


另一个人们错误理解的事情是需要调用多少次.mainloop()。它处理所有活动的tk.Tk窗口中的事件,因此您只需要调用一次.mainloop()
对于那些持不同意见的人,我想知道有什么实际问题是由于多个tk.Tk 窗口引起的,如果可以举个例子就更好了。

5
到目前为止,我找到的最好的参考资料是tkinterbook应用程序窗口部分:
在我们迄今为止使用的简单示例中,屏幕上只有一个窗口:根窗口。当您调用Tk构造函数时,它会自动创建。
如果您需要创建其他窗口,可以使用Toplevel小部件。它只是在屏幕上创建一个新窗口,该窗口看起来和行为几乎与原始根窗口相同。
我的理解是,Tk实例创建了一个Toplevel小部件,以及像mainloop这样的东西,应该只有一个。

0

Tk()初始化了隐藏的tcl解释器,让代码可以运行,因为Tkinter只是tcl/tk的一个包装器。它还自动创建了一个新窗口。Toplevel()只是创建了一个新窗口,如果没有实例化Tk(),它将不起作用,因为它需要Tk()初始化的tcl解释器。你不能在没有实例化Tk()的情况下创建任何Tkinter部件,而Toplevel只是一个部件。在问题中,你使用Tk()来创建第二个窗口。你应该创建另一个文件,因为多次初始化tcl解释器会让人困惑,就像@Bryan Oakley所解释的那样。然后你应该这样做:

from os import startfile startfile(nameOfTheOtherFile)

因为,Toplevel()只是一个部件,当Tk()窗口关闭时,它也会关闭。把其它窗口放在一个单独的文件中会使得整个程序更清晰。


os.startfile()是Windows操作系统特定的,因此您的解决方案并不通用。此外,如果另一个文件是Python脚本,并且您安装了多个解释器版本,则可能无法正常工作。 - martineau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接