构建 Tkinter 应用程序的最佳方法是什么?[关闭]

2024-11-21 08:33:00
admin
原创
4
摘要:问题描述:以下是我的典型 Python Tkinter 程序的整体结构。def funA(): def funA1(): def funA12(): # stuff def funA2(): # stuff def funB(): ...

问题描述:

以下是我的典型 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,当用户点击按钮 1、2、3 时,将会弹出另一个带有小部件的funB窗口funCToplevel

我想知道这是否是编写 Python Tkinter 程序的正确方法?当然,即使我这样写也可以工作,但这是最好的方法吗?这听起来很愚蠢,但是当我看到其他人编写的代码时,他们的代码并没有被一堆函数弄乱,而且大多数都有类。

是否有任何特定的结构是我们应该遵循的良好做法?在开始编写 Python 程序之前我应该​​如何规划?

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


解决方案 1:

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

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

解决方案 2:

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

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

另请参阅:

  • 来自 tkinter 文档的简单 hello world

  • Tkinter 多窗口示例代码,为什么按钮无法正确加载?

  • Tkinter:如何显示/隐藏窗口

希望有所帮助。

解决方案 3:

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

因此,您可以做的就是为这些编写类,然后在类中使用方法来处理按钮单击等命令。

以下是一个例子:

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 中的思考。

解决方案 4:

应该是 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()

在此处输入图片描述

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

解决方案 5:

我更喜欢 Bryan Oakley 的回答。这里有一个由 Sentdex 在 Youtube 上制作的示例,请查看他的“使用 Tkinter 的 GUI”播放列表。

我认为把它放在这里非常有意义,因为它对 OP 来说是一个很好的例子,而且它也回答了这个被 35 人提出但尚未回答的答案;

@Bryan Oakley 你知道网上有什么好的示例代码可以让我研究它们的结构吗? – Chris Aung 2013 年 7 月 5 日 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/]

解决方案 6:

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

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

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

解决方案 7:

在我有设计权的开发中,我将工作的 GUI 部分设置为两组代码:

集合 1。绘制包含小部件及其绑定的窗口的核心代码。我可能使用 wxFormBuilder 或 Glade 生成了这些代码,因此我不希望对文件进行任何手工编码的修改。更可能的是,我费尽心思从头开始手工制作了这些窗口,绝对不希望有任何无意的修改!

设置 2. 我编写的驱动窗口的功能的代码。

通过将 GUI 定义的核心与构建功能的工作分开,这意味着我在处理功能时不会“意外地”破坏核心窗口结构,并且代码更易于管理和维护。

我们都知道程序文件的大小和复杂性很快就会成为真正的问题。因此,有了上述形式和功能的分离(就像这样),我只需导入核心窗口代码文件并在功能代码文件中对其进行子类化即可。

例如,“frmWindow_core_code.py”保留核心窗口功能:

类 frmWindow(tk.TK):

    button1 = ttk.Button....
    entry1  = ttk.Entry....
    etc
    etc
    

驱动 GUI 功能的文件(例如“MyWindowProgram.py”)导入并子类化核心文件,例如:

导入 frmWindow_cored_code 作为对话

类 ThisWindow(Dialogue.frmWindow): 等等等等

我只是一个建议,希望可能会有用。

解决方案 8:

学习如何构建程序的最好方法可能是阅读其他人的代码,尤其是当它是一个许多人参与贡献的大型程序时。在查看了许多项目的代码之后,您应该对共识风格应该是什么有一个想法。

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

  • 美丽比丑陋更好。

  • 明确优于隐含。

  • 简单比复杂更好。

  • 复杂比繁琐好。

  • 平面比嵌套更好。

  • 稀疏优于密集。

  • 可读性很重要。

  • 特殊情况还不足以特殊到打破规则。

  • 尽管实用性胜过纯粹性。

  • 错误不应该悄无声息地发生。

  • 除非明确禁止。

  • 面对模糊性,拒绝猜测的诱惑。

  • 应该有一种——最好只有一种——明显的方法来做到这一点。

  • 尽管这种方法一开始可能并不明显,除非你是荷兰人。

  • 现在总比没有好。

  • 尽管从未发生过总是比现在更好。

  • 如果实施起来很难解释,那就是一个坏主意。

  • 如果实现起来容易解释,那么这可能是一个好主意。

  • 命名空间是一个非常好的主意——让我们多做一些这样的事!

从更实际的层面来说,有PEP8,即 Python 的风格指南。

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

解决方案 9:

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

但这里没有讨论的是,你必须使用线程或多处理。一定要使用。否则你的应用程序会很糟糕。

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

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用