Python:Tkinter:为啥是 root.mainloop() 而不是 app.mainloop()

Posted

技术标签:

【中文标题】Python:Tkinter:为啥是 root.mainloop() 而不是 app.mainloop()【英文标题】:Python: Tkinter: Why is it root.mainloop() and not app.mainloop()Python:Tkinter:为什么是 root.mainloop() 而不是 app.mainloop() 【发布时间】:2014-05-07 02:20:06 【问题描述】:

我是 Stack Overflow 的新成员。 我找到了这个帖子,但不允许对此发表评论或提问,所以我想我只是在这里引用它:How can I make a in interactive list in Python's Tkinter complete with buttons that can edit those listings?

from tkinter import *
import os
import easygui as eg

class App:

    def __init__(self, master):
        frame = Frame(master)
        frame.pack()

        # character box
        Label(frame, text = "Characters Editor").grid(row = 0, column = 0, rowspan = 1, columnspan = 2)
        charbox = Listbox(frame)
        for chars in []:
            charbox.insert(END, chars)
        charbox.grid(row = 1, column = 0, rowspan = 5)
        charadd = Button(frame, text = "   Add   ", command = self.addchar).grid(row = 1, column = 1)
        charremove = Button(frame, text = "Remove", command = self.removechar).grid(row = 2, column = 1)
        charedit = Button(frame, text = "    Edit    ", command = self.editchar).grid(row = 3, column = 1)

    def addchar(self):
        print("not implemented yet")
    def removechar(self):
        print("not implemented yet")
    def editchar(self):
        print("not implemented yet")


root = Tk()
root.wm_title("IA Development Kit")
app = App(root)
root.mainloop()

有人能解释一下为什么最后一行是 root.mainloop() 吗? 作为一名 Python 新手,并且来自面向过程且没有面向对象经验的背景,我会认为应该是 app.mainloop()。

实际上 app = App(root) ,app 不会在其余代码中再次使用!我无法理解为什么 root.mainloop() 仍然有效。

【问题讨论】:

所以当你用root初始化你的App类时,__init__函数运行,所有的GUI组件都被加载。 mainloopTk 的一个方法,它启动窗口的事件循环。因此,我们不需要重新引用 class(虽然技术上我们这样做,在 class 本身内部),因为我们需要在初始化 class 时完成。 【参考方案1】:

我像你看到的那样测试了两个:

一个是用“app”写的。 + ".pack()" 和一个调用 "mainframe"。 + ".grid()"

 #-*- coding: utf-8 -*-
#THIS IS THE "MAINFRAME." - PART
from Tkinter import *
import ttk

def show():
    p = password.get() #get password from entry
    print(p)


root = Tk()
root.title("Ingos first program")

mainframe = ttk.Frame(root, padding="30 30 60 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)


password = StringVar() #Password variable
passEntry = Entry(mainframe, textvariable=password, show='*').grid(column=3, row=3, sticky=S)
submit = Button(mainframe, text='Show Console',command=show).grid(column=3, row=4, sticky=S)

root.mainloop()
def show():
    p = password.get() #get password from entry
    print(p)

#THIS IS THE "APP."-PART. BOTH WORKS FINE.
app = Tk()
app.title("Ingos first program")
password = StringVar() #Password variable
passEntry = Entry(app, textvariable=password, show='#').pack()
submit = Button(app, text='Show Console',command=show).pack()

app.mainloop()

此实例适用于 python 2.7。在那个测试甚至应用程序中。可以处理“mainloop()” 该脚本一个接一个地打开 2 个窗口(如果您关闭第一个窗口)并且第一个程序已格式化,没有尝试在 pack() 夹中写入 colum=3... 的东西。

我仍然启动了 Tkinter,所以不要和我打架,只是尝试.. 希望我能帮助回答你的问题。 一切顺利,英戈

【讨论】:

【参考方案2】:

我不确定您是否会觉得这个答案令人满意,但您调用root.mainloop() 因为root 是具有mainloop 方法的对象。在您给出的代码中,App 没有 mainloop 函数。

简单来说,这就是 tkinter 的工作方式——你总是通过调用根窗口的 mainloop 方法来结束你的脚本。当该方法返回时,您的程序将退出。

让我们从头开始。最简单的非 OO Tkinter 程序将类似于以下示例。请注意,这是一个 python 2.x 示例,我不使用全局导入,因为我认为全局导入不好。

import Tkinter as tk
root = tk.Tk()
<your widgets go here>
root.mainloop()

很多人发现纯过程风格不是编写代码的有效方式,因此他们可能会选择以面向对象的风格来编写。将“应用程序”视为单例对象是很自然的。有很多方法可以做到这一点 - 不幸的是,你的问题中的一种方法不是更清晰的方法之一。

IMO 稍微好一点的方法是像这样构造代码:

class App(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        <your widgets go here>
app = App()
app.mainloop()

在这种情况下,mainloop 仍然被调用,尽管现在它是App 的方法,因为App 继承自Tk。它在概念上与root.mainloop() 相同,因为在这种情况下,app 根窗口,尽管它的名称不同。

因此,在这两种情况下,mainloop 都是属于根窗口的方法。在这两种情况下,必须调用它才能使 GUI 正常运行。

还有第三种变体,即您选择的代码正在使用的变体。有了这种变化,有几种方法可以实现它。变化是您的问题使用一个类来定义 GUI,但 继承自 Tk。这很好,但您仍然必须在某些时候致电mainloop。由于您没有在您的类中创建(或继承)mainloop 函数,因此您必须调用与根窗口关联的函数。我所说的变化是App 的实例如何以及在何处添加到根窗口,以及mainloop 的最终调用方式。

我个人更喜欢App 继承自Frame,并且您将应用程序应用程序的定义之外打包。我使用的模板是这样的:

class App(tk.Frame):
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
        <your widgets go here>

if __name__ == "__main__":
    root = tk.Tk()
    app = App(root)
    app.pack(fill="both", expand=True)
    root.mainloop()

在最后一个示例中,approot 是两个完全不同的对象。 app 表示存在于根窗口内部 的框架。框架通常以这种方式使用,作为其他小部件组的容器。

因此,在所有情况下,都必须调用mainloop在哪里调用它,以及如何调用,取决于你的编码风格。有些人喜欢从根窗口继承,有些人不喜欢。无论哪种情况,您都必须调用根窗口的mainloop 函数。

【讨论】:

(对不起,我可能会扼杀术语的使用。)这个答案足够令人满意。我想我真正被绊倒的是当一切都说完了,app和root是一个对象的同一个实例吗?来自程序背景,如果 app 和 root 是不同的实例,则所有小部件都打包到 app 实例中。因此,如果我调用 root.mainloop() 我不会期望看到新打包的小部件。但是,如果我调用 app.mainloop(),直观地看所有打包的小部件对我来说是有意义的。但我推测 app 和 root 是同一个实例。 而且因为app和root是同一个实例,所以打包进app的任何东西在root中也是可见的,所以最后root.mainloop()会显示出所有打包进app的widget。 @Nuuk:在最后一个例子中,app 和 root 是两个不同的对象。 root 是所有其他小部件进入的根窗口。它是 Tk 类的一个实例,每个 tkinter 应用程序都必须只有一个此类的实例。 app 是 App 类的实例,App 是 Frame 的子类。框架通常用作其他小部件的容器,在这种情况下是构成应用程序的小部件(根窗口除外)。这个框架被打包在根窗口内。 app 和 root 是两个完全不同的东西。 root 是应用程序的容器。 哇,非常感谢您的优雅解释。这让一切变得不同。现在我知道 Frame 和 Tk 是两个完全不同的类。一切都开始变得更有意义了。我认为接下来对我来说是学习 Python 类、子类如何工作、继承等。我相信这就是所有困惑的来源。你看,当我看到app = App(root) 时,我认为这意味着 app 是 root 的子类,而 root 是 Tk 类的实例。但是你回答我的问题相当优雅,并指出了真正的问题:我只需要学习 Python 类。【参考方案3】:

App 对象只是您的应用程序代码,您调用 App(root) 的原因是使用您的类创建一个实例,然后该类可以访问您的根窗口。

它在__init__ 方法中接收此引用:

def __init__(self, master):
    # master refers to the root window now
    ...

你可以看到App 对象的完整定义(由以class App: 开头的块给出),它甚至没有mainloop 方法,所以要启动主Tkinter 循环,你有在根窗口调用它。

在example in the Python2 documentation 中,他们确实 调用它,因为您怀疑应该这样做,但请注意,他们的示例类是 Tk 对象Frame 的子类。在您的示例代码中,App 是一个不继承任何东西的老式类。

【讨论】:

我不认为你用的词有点不清楚。您调用App(root) 的原因不是“保存参考”,而是实际创建应用程序。保存引用的唯一方法是将结果分配给变量app。所以,同样,你不要调用它来保存引用,你将它分配给一个变量来保存引用。 我的意思是你用root作为参数调用App的原因,但我想我明白你的意思了。 你还是搞错了。您不要使用 root 作为参数调用它以保存引用。您使用 root 作为参数调用它,因为 App 类要求您告诉它要在哪个窗口中创建它。 我仍然认为我们只是在谈论对方,但我调整了措辞。 App 类需要传入窗口,以便它可以使用它(告诉它要在哪个窗口中创建以及可能的其他用途,具体取决于应用程序的功能)是我想要说明的重点。跨度> 大家都看到了,我的面向对象知识还是有限的。我的理解是root.mainloop() 是显示的主要 GUI 窗口。然后app = App(root) 是我们创建 App 类实例的地方,但 app 永远不会在代码中的其他任何地方使用。然而,通过创建 App 类的实例,app 的def __init__(self, master): 中的所有内容突然在主 GUI 窗口 (root.mainloop()) 上可见。如果这是一个错误的地方(因为我真的知道零面向对象编程),我会很高兴被重定向到其他可能解释的讨论。

以上是关于Python:Tkinter:为啥是 root.mainloop() 而不是 app.mainloop()的主要内容,如果未能解决你的问题,请参考以下文章

Python:为啥 Tkinter 类实例化必须使用 Frame?

python3.8 tkinter为啥title 没有用,求大神解答!代码如下

为啥我不能在 Mac 上使用 python 更改 tkinter 按钮的背景颜色?

python桌面开发,为啥选择PyQt或wxPython,而不使用Tkinter?

为啥 tkinter 不能很好地处理多处理?

为啥 Tkinter 的 askdirectory() 在 Windows 上返回正斜杠?