【问题标题】:Why are multiple instances of Tk discouraged?为什么不鼓励使用多个 Tk 实例?
【发布时间】:2018-06-11 05:45:53
【问题描述】:

考虑下面的例子:

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 实例不好?
  • 第二个 sn-p 是否被认为更好,或者它是否受到影响 第一个代码的条件相同吗?

【问题讨论】:

    标签: python tkinter tcl tk


    【解决方案1】:

    为什么认为拥有多个 Tk 实例不好?

    Tkinter 只是一个 Python 包装器,它包含一个嵌入的 Tcl 解释器,它可以导入 Tk 库。当你创建一个根窗口时,你就创建了一个 Tcl 解释器的实例。

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

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

    第二个 sn-p 被认为更好一点,还是它与第一个代码的情况相同?

    如果不知道您要达到的目标,就不可能说问题中的第二个示例是否更好。这可能并没有更好的地方,因为再一次,很少有你真正需要两个实例的时候。

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

    【讨论】:

    • 如果您要拥有多个Tk about 实例,它们可能应该被放置在单独的线程中,以便它们获得自己的事件处理循环。从技术上讲,将这些线程中的底层小部件融合到一个视图中是可行的,但这是一种非常先进的技术,我不知道它是否真的在 Tkinter 中公开。坦率地说,将所有 GUI 活动保持在单个线程中实际上要简单得多。
    • 对不起,不小心点击了,现在我的投票被锁定了。
    • 对于如何使用将小部件融合在一起的高级技术的古玩人:请参阅toplevel -usetoplevel -container
    【解决方案2】:

    目前我发现的最佳参考是tkinterbookApplication Windows 部分:

    在我们到目前为止使用的简单示例中,屏幕上只有一个窗口;根窗口。这是在您调用 Tk 构造函数时自动创建的

    如果您需要创建额外的窗口,您可以使用 Toplevel 小部件。它只是在屏幕上创建一个新窗口,该窗口的外观和行为与原始根窗口非常相似

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

    【讨论】:

      【解决方案3】:

      我不同意tkinter 社区不鼓励使用多个tk.Tk 窗口。您可以拥有多个tk.Tk 窗口。使用tk.Tk 的多个实例是创建真正相互独立的窗口的唯一方法。人们在创建多个tk.Tk 窗口时最容易犯的错误是他们在创建PhotoImages/StringVars/IntVars/ 时忘记传入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()
      

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


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

      对于不同意的人,我想看看一个实际问题是由多个 tk.Tk 窗口引起的示例。

      【讨论】:

        【解决方案4】:

        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 脚本,如果您安装了多个版本的解释器,它可能无法正常工作。
        猜你喜欢
        相关资源
        最近更新 更多