下面是我典型的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时保持正确的方向。


当前回答

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

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:如何显示/隐藏窗口

希望这能有所帮助。

其他回答

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

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:如何显示/隐藏窗口

希望这能有所帮助。

我更喜欢布莱恩·奥克利的回答。 这是一个例子,由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/]

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

# 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”)。这允许您在组件之间定义一个简单的接口,有助于将耦合保持在最小。

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

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

这里有一个例子:

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》。

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

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

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