构建tkinter应用程序的最佳方法是什么?

时间:2013-07-04 09:21:48

标签: python 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。

7 个答案:

答案 0 :(得分:193)

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

# 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 :(得分:27)

将每个顶级窗口放入其自己的独立类中,可以让您重复使用代码并更好地进行代码组织。应在此类中定义窗口中存在的任何按钮和相关方法。这是一个例子(取自here):

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 :(得分:4)

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

所以你可以做的是为这些类编写类,然后在类中有方法来处理按钮点击等命令。

以下是一个例子:

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

没有一种正确的方法可以做到这一点,无论什么对你有用,只要它可读并且你可以很容易地解释它就可以完成工作,因为如果你不能轻易解释你的程序,可能有更好的方法这样做。

看看Thinking in Tkinter

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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

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

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 :(得分:-1)

学习如何构建程序的最佳方法可能是阅读其他人的代码,特别是如果它是许多人贡献的大型程序。在查看了许多项目的代码之后,您应该了解共识样式应该是什么。

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

  
      
  • 美丽胜过丑陋。
  •   
  • 明确胜于隐性。
  •   
  • 简单比复杂更好。
  •   
  • 复杂比复杂更好。
  •   
  • Flat比嵌套好。
  •   
  • 稀疏比密集更好。
  •   
  • 可读性很重要。
  •   
  • 特殊情况不足以打破规则。
  •   
  • 虽然实用性胜过纯洁。
  •   
  • 错误永远不会无声地传递。
  •   
  • 除非明确默许。
  •   
  • 面对歧义,拒绝猜测的诱惑。
  •   
  • 应该有一个 - 最好只有一个 - 显而易见的方法。
  •   
  • 虽然这种方式起初可能并不明显,除非你是荷兰人。
  •   
  • 现在总比没有好。
  •   
  • 虽然现在永远不会比正确更好。
  •   
  • 如果实施难以解释,那就不错了。
  •   
  • 如果实施很容易解释,那可能是个好主意。
  •   
  • 命名空间是一个很好的主意 - 让我们做更多的事情!
  •   

在更实际的层面上,有PEP8,Python的样式指南。

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

答案 6 :(得分:-6)

我个人不使用面向对象的方法,主要是因为它a)只是妨碍了; b)您永远不会重复使用它作为模块。

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

只做一个简单的测试:启动一个窗口,然后获取一些URL或其他任何东西。更改是在网络请求发生时不会更新您的UI。意思是,您的应用程序窗口将被破坏。取决于您所使用的操作系统,但大多数情况下,它不会重绘,您在窗口上拖动的任何内容都将贴在其上,直到该过程返回到TK主循环。