构造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()

当用户单击按钮1,2,3时,funA funBfunC将弹出另一个带有小部件的Toplevel窗口。

我想知道这是否是编写python tkinter程序的正确方式?当然,即使我这样写也可以,但这是最好的方式吗?这听起来很愚蠢,但当我看到其他人写的代码时,他们的代码并没有被一堆函数搞得乱七八糟,而且他们主要是有类。

有没有什么具体的结构是我们应该遵循的?在开始编写python程序之前,我应该如何计划?

我知道在编程中没有所谓的最佳实践,我也没有要求它。我只是想要一些建议和解释,让我在自己学习Python时保持正确的方向。

241505 次浏览

也许学习如何组织程序的最好方法是阅读其他人的代码,特别是如果它是一个很多人都为之做出了贡献的大型程序。在看过许多项目的代码之后,您应该对共识风格应该是什么有一个概念。

Python作为一种语言,它的特殊之处在于有一些关于如何格式化代码的强有力的指导原则。首先是所谓的“Python之禅”:

  • 美总比丑好。
  • 显性比隐性好。
  • 简单比复杂好。
  • 复杂胜过复杂。
  • 扁平比嵌套好。
  • 稀疏比密集好。
  • 可读性。
  • 特殊情况并不特别到可以打破规则。
  • 尽管实用性胜过纯洁性。
  • 错误绝不能悄无声息地过去。
  • 除非明确保持沉默。
  • 面对模棱两可,拒绝猜测的诱惑。
  • 应该有一种——最好只有一种——明显的方法来做到这一点。
  • 除非你是荷兰人,否则这种方式一开始可能并不明显。
  • 现在总比没有好。
  • 虽然never现在通常比正确的好。
  • 如果实现很难解释,那就是一个坏主意。
  • 如果实现很容易解释,这可能是一个好主意。
  • 名称空间是一个非常棒的想法——让我们多做一些吧!

在更实际的层面上,有PEP8, Python的风格指南。

考虑到这些,我会说您的代码风格并不适合,特别是嵌套函数。通过使用类或将它们移动到单独的模块,找到一种方法将它们平铺开来。这将使你的程序结构更容易理解。

这是个不错的结构;它会工作得很好。然而,当有人点击按钮或其他东西时,你必须在函数中有函数来执行命令

你能做的就是为这些写类然后在类中有方法来处理按钮点击之类的命令。

这里有一个例子:

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__中创建了所有的条目、标签等,然后每个方法都是用来处理按钮单击事件

没有正确的方法,只要是对你有用的,只要是可读的,你可以很容易地解释它,因为如果你不能很容易地解释你的程序,可能有更好的方法。

看看用Tkinter思考

将每个顶级窗口放到它自己的单独类中,可以重用代码和更好的代码组织。窗口中出现的任何按钮和相关方法都应该在这个类中定义。下面是一个例子(选自在这里):

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()

还看到:

希望这能有所帮助。

我提倡面向对象的方法。这是我开始的模板:

# 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")在状态栏上放置一些东西。这允许您在组件之间定义一个简单的接口,有助于将耦合保持在最小。

我个人不使用面向对象的方法,主要是因为a)只会碍事;b)你将从来没有重用它作为模块。

但这里没有讨论的是,你必须使用线程或多处理。总是这样。否则你的申请会很糟糕。

只做一个简单的测试:启动一个窗口,然后获取一些URL或其他东西。当网络请求发生时,你的UI将不会更新。这意味着,您的应用程序窗口将被打破。取决于你所在的操作系统,但大多数时候,它不会重绘,任何你拖过窗口的东西都会被贴在上面,直到这个过程回到TK主循环。

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()

enter image description here

参考:http://www.python-course.eu/tkinter_buttons.php

使用类组织应用程序可以使您和其他与您一起工作的人轻松调试问题和改进应用程序。

你可以像这样轻松地组织你的应用程序:

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()
我喜欢用布莱恩·奥克利的回答来做这件事。 这是Sentdex在Youtube上做的一个例子,用tkinter检查他的“gui”。播放列表。< / p >

我认为把它放在这里很有意义因为它是OP的一个很好的例子,所以它也回答了这个由35个人提出的答案,但没有人回答;

@Bryan Oakley你知道任何好的样本代码在互联网上,我可以 研究它们的结构?- Chris Aung Jul 5 '13 at 8:35

import tkinter as tk


LARGE_FONT= ("Verdana", 12)


class SeaofBTCapp(tk.Tk):
"""
tkinter example app with OOP
"""


def __init__(self, *args, **kwargs):
        

tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)


container.pack(side="top", fill="both", expand = True)


container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)


self.frames = {}


for frame_class in (StartPage,PageOne, PageTwo):


frame = frame_class(container, self)


self.frames[frame_class] = frame


frame.grid(row=0, column=0, sticky="nsew")
    



self.show_frame(StartPage)


def show_frame(self, cont):
"""
Put specific frame on top
"""


frame = self.frames[cont]
frame.tkraise()


        

class StartPage(tk.Frame):
"""
Starting frame for app
"""


def __init__(self, parent, controller):
tk.Frame.__init__(self,parent,bg='grey')
label = tk.Label(self, text="Start Page", font=LARGE_FONT)
label.pack(pady=10,padx=10)


button_page1 = tk.Button(self, text = 'Visit Page 1', command= lambda: controller.show_frame(PageOne))
button_page1.pack()


button_page2 = tk.Button(self, text = 'Visit Page 2', command= lambda: controller.show_frame(PageTwo))
button_page2.pack()


class PageOne(tk.Frame):
"""
First page of program
"""


def __init__(self,parent,controller):
tk.Frame.__init__(self,parent,bg='light blue')
label = tk.Label(self, text="Page one", font=LARGE_FONT)
label.pack(pady=10,padx=10)


button_home = tk.Button(self, text = 'Back to Home', command= lambda: controller.show_frame(StartPage))
button_home.pack()


button_home = tk.Button(self, text = 'Go to page2', command= lambda: controller.show_frame(PageTwo))
button_home.pack()


class PageTwo(tk.Frame):
"""
First page of program
"""


def __init__(self,parent,controller):
tk.Frame.__init__(self,parent,bg='light green')
label = tk.Label(self, text="Page two", font=LARGE_FONT)
label.pack(pady=10,padx=10)


button_home = tk.Button(self, text = 'Back to Home', command= lambda: controller.show_frame(StartPage))
button_home.pack()


button_home = tk.Button(self, text = 'Go to page1', command= lambda: controller.show_frame(PageOne))
button_home.pack()








app = SeaofBTCapp()
app.mainloop()

在这里也可以找到代码:[https://pythonprogramming.net/change-show-new-frame-tkinter/]