Python:Tkinter:为什么是root.mainloop()而不是app.mainloop()
我刚加入Stack Overflow这个社区。 我发现了这个讨论,但不能在上面评论或提问,所以我想在这里提一下:如何在Python的Tkinter中制作一个带有按钮的互动列表,可以编辑这些列表项?
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()还能正常工作。
3 个回答
App
对象就是你的应用程序代码,调用App(root)
的原因是为了用你的类创建一个实例,这样它就可以访问你的根窗口。
这个实例是在__init__
方法中接收这个根窗口的引用:
def __init__(self, master):
# master refers to the root window now
...
你可以看到App
对象的完整定义(从class App:
开始的那一块),而且它甚至没有mainloop
方法,所以要启动Tkinter的主循环,你必须在根窗口上调用它。
在Python2文档中的示例里,他们确实像你猜测的那样调用了它,但要注意,他们的示例类是从Tk对象Frame
继承而来的。而在你的示例代码中,App
是一个旧式类,没有继承任何东西。
我测试了你看到的这两种写法:
一种是用"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上运行得很好。在这个测试中,甚至连app.也可以处理"mainloop()"这个命令。这个脚本会依次打开两个窗口(如果你关闭第一个窗口的话),而第一个程序的格式也很好,我没有尝试在pack()里写column=3之类的东西。
我还是刚开始学习Tkinter,所以别和我争,我只是试试看.. 希望我能帮到你解答问题。
祝一切顺利,Ingo
我不确定这个回答是否能让你满意,但你需要调用 root.mainloop()
是因为 root
是一个有 mainloop
方法的对象。在你给出的代码中,App
并没有 mainloop
函数。
简单来说,这就是 tkinter 的工作方式——你总是通过调用根窗口的 mainloop
方法来结束你的脚本。当这个方法返回时,你的程序就会退出。
我们从头开始。最简单的非面向对象的 Tkinter 程序看起来会像下面这个例子。请注意,这是一个 Python 2.x 的例子,我不使用全局导入,因为我认为全局导入不好。
import Tkinter as tk
root = tk.Tk()
<your widgets go here>
root.mainloop()
很多人发现纯过程式的风格并不是写代码的有效方式,所以他们可能会选择用面向对象的风格来写。这种情况下,把“应用程序”看作一个单例对象是很自然的。有很多方法可以做到这一点——但你提到的那种方法不幸的是不是最清晰的方式。
我认为稍微好一点的方法是把代码结构化成这样:
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
是属于根窗口的方法。而且在这两种情况下,都必须调用它,才能让图形界面正常工作。
还有第三种变体,就是你选择的代码所使用的。在这种变体中,有几种实现方式。这种变体是你的问题使用了一个类来定义图形界面,但并没有从 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
函数。