问题:构造tkinter应用程序的最佳方法?
以下是我典型的python tkinter程序的整体结构。
def funA():
def funA1():
def funA12():
# stuff
def funA2():
# stuff
def funB():
def funB1():
# stuff
def funB2():
# stuff
def funC():
def funC1():
# stuff
def funC2():
# stuff
root = tk.Tk()
button1 = tk.Button(root, command=funA)
button1.pack()
button2 = tk.Button(root, command=funB)
button2.pack()
button3 = tk.Button(root, command=funC)
button3.pack()
funA
funB
并在用户单击按钮1、2、3时funC
打开另一个Toplevel
带有窗口小部件的窗口。
我想知道这是否是编写python tkinter程序的正确方法吗?当然,即使我这样写也可以,但这是最好的方法吗?这听起来很愚蠢,但是当我看到其他人编写的代码时,他们的代码并没有弄乱一堆函数,而且大多数情况下它们都有类。
有没有作为良好实践应遵循的特定结构?开始编写python程序之前,我应该如何计划?
我知道编程中没有最佳实践之类的东西,我也不需要。在我自己学习Python时,我只想一些建议和解释就可以使我保持正确的方向。
回答 0
我主张一种面向对象的方法。这是我开始的模板:
# Use Tkinter for python 2, tkinter for python 3
import tkinter as tk
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
<create the rest of your GUI here>
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
需要注意的重要事项是:
我不使用通配符导入。我将软件包导入为“ tk”,这要求我在所有命令前加上
tk.
。这样可以防止全局命名空间污染,并且在使用Tkinter类,ttk类或您自己的某些类时,使代码完全显而易见。主要应用是一类。这为您的所有回调和私有函数提供了私有命名空间,并且通常使组织代码更容易。在过程样式中,您必须自上而下进行编码,在使用函数之前定义函数等。使用此方法,您无需真正地在最后一步之前创建主窗口。我更喜欢从中继承,
tk.Frame
因为我通常从创建框架开始,但这绝不是必需的。
如果您的应用程序具有其他顶级窗口,建议您将每个窗口都设为一个单独的类,并从继承tk.Toplevel
。这为您提供了上述所有相同的优点-窗口是原子的,它们具有自己的命名空间,并且代码井井有条。另外,一旦代码开始变大,就可以很容易地将每个模块放入自己的模块中。
最后,您可能要考虑对接口的每个主要部分使用类。例如,如果您要创建一个带有工具栏,导航窗格,状态栏和主区域的应用程序,则可以使每个类成为一个类。这使您的主要代码非常小,易于理解:
class Navbar(tk.Frame): ...
class Toolbar(tk.Frame): ...
class Statusbar(tk.Frame): ...
class Main(tk.Frame): ...
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.statusbar = Statusbar(self, ...)
self.toolbar = Toolbar(self, ...)
self.navbar = Navbar(self, ...)
self.main = Main(self, ...)
self.statusbar.pack(side="bottom", fill="x")
self.toolbar.pack(side="top", fill="x")
self.navbar.pack(side="left", fill="y")
self.main.pack(side="right", fill="both", expand=True)
由于所有这些实例共享一个公共父对象,因此该父对象实际上成为了模型-视图-控制器体系结构的“控制器”部分。因此,例如,主窗口可以通过调用在状态栏上放置一些内容self.parent.statusbar.set("Hello, world")
。这使您可以在组件之间定义一个简单的接口,从而有助于保持最小的耦合。
回答 1
将每个顶级窗口放入自己的单独类中,可以使代码重用并更好地组织代码。窗口中存在的任何按钮和相关方法都应在此类内定义。这是一个示例(从此处获取):
import tkinter as tk
class Demo1:
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.button1 = tk.Button(self.frame, text = 'New Window', width = 25, command = self.new_window)
self.button1.pack()
self.frame.pack()
def new_window(self):
self.newWindow = tk.Toplevel(self.master)
self.app = Demo2(self.newWindow)
class Demo2:
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25, command = self.close_windows)
self.quitButton.pack()
self.frame.pack()
def close_windows(self):
self.master.destroy()
def main():
root = tk.Tk()
app = Demo1(root)
root.mainloop()
if __name__ == '__main__':
main()
另请参阅:
希望有帮助。
回答 2
这不是一个坏结构。它会很好地工作。但是,当某人单击按钮或其他内容时,您必须在函数中具有执行命令的功能
因此,您可以为这些编写类,然后在该类中具有处理按钮单击等命令的方法。
这是一个例子:
import tkinter as tk
class Window1:
def __init__(self, master):
pass
# Create labels, entries,buttons
def button_click(self):
pass
# If button is clicked, run this method and open window 2
class Window2:
def __init__(self, master):
#create buttons,entries,etc
def button_method(self):
#run this when button click to close window
self.master.destroy()
def main(): #run mianloop
root = tk.Tk()
app = Window1(root)
root.mainloop()
if __name__ == '__main__':
main()
通常带有多个窗口的tk程序是多个大类,并且在__init__
所有条目中创建标签等,然后每种方法都将处理按钮单击事件
只要有可读性,实际上就没有正确的方法,只要它对您有用并且可以完成工作,并且您可以轻松地解释它,因为如果您不能轻松地解释您的程序,那么可能会有更好的方法。
回答 3
OOP应该是方法,frame
应该是类变量而不是实例变量。
from Tkinter import *
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.button = Button(frame,
text="QUIT", fg="red",
command=frame.quit)
self.button.pack(side=LEFT)
self.slogan = Button(frame,
text="Hello",
command=self.write_slogan)
self.slogan.pack(side=LEFT)
def write_slogan(self):
print "Tkinter is easy to use!"
root = Tk()
app = App(root)
root.mainloop()
回答 4
使用类对应用程序进行组织可以使您和与您一起工作的其他人轻松调试问题并轻松改进应用程序。
您可以像这样轻松地组织您的应用程序:
class hello(Tk):
def __init__(self):
super(hello, self).__init__()
self.btn = Button(text = "Click me", command=close)
self.btn.pack()
def close():
self.destroy()
app = hello()
app.mainloop()
回答 5
学习如何构建程序的最佳方法可能是阅读他人的代码,尤其是如果这是许多人都为之贡献的大型程序。在查看了许多项目的代码之后,您应该了解共识样式应该是什么。
作为一种语言,Python的特殊之处在于,在如何格式化代码方面存在一些严格的指导原则。第一个是所谓的“ Python禅”:
- 美丽胜于丑陋。
- 显式胜于隐式。
- 简单胜于复杂。
- 复杂胜于复杂。
- 扁平比嵌套更好。
- 稀疏胜于密集。
- 可读性很重要。
- 特殊情况还不足以打破规则。
- 尽管实用性胜过纯度。
- 错误绝不能默默传递。
- 除非明确地保持沉默。
- 面对模棱两可的想法,拒绝猜测的诱惑。
- 应该有一种-最好只有一种-显而易见的方法。
- 尽管除非您是荷兰人,否则一开始这种方式可能并不明显。
- 现在总比没有好。
- 虽然从未往往比右了。
- 如果实现难以解释,那是个坏主意。
- 如果实现易于解释,则可能是个好主意。
- 命名空间是一个很棒的主意-让我们做更多这些吧!
在更实际的水平上,有Python的样式指南PEP8。
考虑到这些,我会说您的代码风格并不适合,特别是嵌套函数。通过使用类或将它们移到单独的模块中,找到一种解决方案。这将使程序的结构更容易理解。
回答 6
我个人不使用面向对象的方法,主要是因为a)只会妨碍;b)您永远不会将其作为模块重复使用。
但是这里没有讨论的是必须使用线程或多处理。总是。否则您的应用程序将很糟糕。
只需做一个简单的测试:启动一个窗口,然后获取一些URL或其他内容。所做的更改是在网络请求发生时不会更新您的用户界面。意思是,您的应用程序窗口将被破坏。取决于您所使用的操作系统,但是大多数情况下,它不会重绘,在窗口上拖动的任何内容都将粘贴在它上面,直到该过程返回到TK mainloop。