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组件都被加载。 mainloop
是 Tk
的一个方法,它启动窗口的事件循环。因此,我们不需要重新引用 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()
在最后一个示例中,app
和 root
是两个完全不同的对象。 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 按钮的背景颜色?