下面是我典型的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 funB和funC将会弹出另一个带有小部件的topllevel窗口。

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

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

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


当前回答

我个人不使用面向对象的方法,主要是因为a)只会碍事;B)你永远不会把它作为一个模块重用。

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

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

其他回答

OOP应该是方法,框架应该是类变量而不是实例变量。

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

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

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

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

还看到:

简单的hello world从tkinter文档 Tkinter示例代码的多个窗口,为什么不会正确加载按钮? Tkinter:如何显示/隐藏窗口

希望这能有所帮助。

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

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

Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than right now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!

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

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

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

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

需要注意的重要事项是:

I don't use a wildcard import. I import the package as "tk", which requires that I prefix all commands with tk.. This prevents global namespace pollution, plus it makes the code completely obvious when you are using Tkinter classes, ttk classes, or some of your own. The main application is a class. This gives you a private namespace for all of your callbacks and private functions, and just generally makes it easier to organize your code. In a procedural style you have to code top-down, defining functions before using them, etc. With this method you don't since you don't actually create the main window until the very last step. I prefer inheriting from tk.Frame just because I typically start by creating a frame, but it is by no means necessary.

如果你的应用程序有额外的顶层窗口,我建议将每个顶层窗口作为一个单独的类,从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在状态栏上放置一些东西。设置(“Hello, world”)。这允许您在组件之间定义一个简单的接口,有助于将耦合保持在最小。

我更喜欢布莱恩·奥克利的回答。 这是一个例子,由Sentdex在Youtube上制作,去看看他的“gui with Tkinter”播放列表。

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

@Bryan Oakley你知道任何好的样本代码在互联网上,我可以 研究它们的结构?- Chris Aung Jul 5 '13在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/]