在 Python3/tkinter 中,是否可以将工具栏创建到单独的 class 中,但仍允许它与主应用程序交互?

in Python3/tkinter is it possible to create a toolbar into a separate class, but still allowing it to interact with the main app?

我即将开始一个新的 Python3/tkinter 项目,我想确保尽可能多地编写代码。我正在创建一个应用程序,目前,一个 window 由 3 个区域组成:

我正在努力保持主应用程序 class 尽可能干净,将代码卸载到其他辅助 classes。因此,按照一些教程并根据我到目前为止所做的事情进行调整,我已经能够从主应用程序设置一个可以按需更改的外部工具栏 class。现在,我正在尝试为工具栏创建一个 class,但恐怕无法在单独的 class 中创建按钮及其各自的回调,因为我不知道如何让他们调用主应用程序中的功能。这可能吗?

这是我现在得到的:

#!/usr/bin/python3

from tkinter import *
from tkinter import ttk


class App:
    """ main class for the application """
    def __init__(self,master):
        mainframe = ttk.Frame(master)
        topframe = ttk.Frame(mainframe)
        centerframe = ttk.Frame(mainframe)
        bottomframe = ttk.Frame(mainframe)

        my_toolbar = Toolbar(topframe) 

        my_statusbar = StatusBar(mainframe)
        my_statusbar.set("This is the statusbar")

        centerlabel = ttk.Label(centerframe, text="Center stuff goes here")
        centerlabel.pack()

        topframe.pack(side=TOP, fill=X)
        centerframe.pack(side=TOP, fill=BOTH)
        bottomframe.pack(side=BOTTOM, fill=X)        
        mainframe.pack(side=TOP, expand=True, fill=BOTH)


    def button_function(self, *event):
        print("filter")


class StatusBar(ttk.Frame):
    """ Simple Status Bar class - based on Frame """
    def __init__(self,master): 
        ttk.Frame.__init__(self,master)
        self.label = ttk.Label(self,anchor=W)
        self.label.pack()
        self.pack(side=BOTTOM, fill=X)

    def set(self,texto):
        self.label.config(text=texto)
        self.label.update_idletasks()
    def clear(self):
        self.label.config(text="")
        self.label.update_idletasks()


class Toolbar:
    """ Toolbar """
    def button_one(self):
        print("button 1 pressed")

    def button_two(self):
        print("button 2 pressed")

    def __init__(self,master):
        self.button1 = ttk.Button(master,text="One",command=self.button_one())
        self.button2 = ttk.Button(master,text="Two",command=self.button_two())
        self.button1.grid(row=0,column=0)
        self.button2.grid(row=0,column=1)


if __name__ == "__main__": 
    root = Tk() 
    app = App(root) 
    root.mainloop()

假设我需要让 button1 触发 button_function() 以更新那里显示的一些信息。我是否应该简单地将工具栏移动到 App class 中,例如在从其 __init__() 调用的 class 方法中?或者有更好的方法吗?

也许我应该补充一点,我打算稍后添加一些 Toplevelwindows,它们可能会利用其中的一些通用 classes。我想好好铺路

这当然是可能的。这里有两种可能性。第一个让应用程序继承自 ttk.Frame 而不是使用大型机。然后你可以将 App 作为 master 传递给工具栏等。这是重做的代码:

#!/usr/bin/python3

import tkinter as tk
from tkinter import ttk


class App(ttk.Frame):
    """ main class for the application """
    def __init__(self,master,*args,**kwargs):
        super().__init__(master,*args,**kwargs)


        self.my_toolbar = Toolbar(self) 

        self.my_statusbar = StatusBar(self)
        self.my_statusbar.set("This is the statusbar")

        self.centerframe = CenterFrame(self)

        self.pack(side=tk.TOP, expand=True, fill=tk.BOTH)


    def button_function(self, *event):
        print("filter")


class CenterFrame(ttk.Frame):

    def __init__(self,master,*args,**kwargs):
        super().__init__(master,*args,**kwargs)
        self.master = master
        self.pack(side=tk.BOTTOM, fill=tk.X)
        self.centerlabel = ttk.Label(self, text="Center stuff goes here")
        self.centerlabel.pack()


class StatusBar(ttk.Frame):
    """ Simple Status Bar class - based on Frame """
    def __init__(self,master):
        ttk.Frame.__init__(self,master)
        self.master = master
        self.label = ttk.Label(self,anchor=tk.W)
        self.label.pack()
        self.pack(side=tk.BOTTOM, fill=tk.X)

    def set(self,texto):
        self.label.config(text=texto)
        self.label.update_idletasks()
    def clear(self):
        self.label.config(text="")
        self.label.update_idletasks()


class Toolbar(ttk.Frame):
    """ Toolbar """
    def button_one(self):
        print("button 1 pressed")

    def button_two(self):
        print("button 2 pressed")
        self.master.button_function()

    def __init__(self,master):
        super().__init__(master)
        self.master = master
        self.pack(side=tk.TOP, fill=tk.X)
        self.button1 = ttk.Button(self,text="One",command=self.button_one)
        self.button2 = ttk.Button(self,text="Two",command=self.button_two)
        self.button1.grid(row=0,column=0)
        self.button2.grid(row=0,column=1)


if __name__ == "__main__": 
    root = tk.Tk() 
    app = App(root) 
    root.mainloop()

第二个是将 App 作为参数传递给另一个 类。

您遗漏了一些 self.,按钮命令分配不需要括号,之后您可以从程序中的任何位置调用按钮配置。所以对于 button1 命令,这将是:

app.my_toolbar.button1.config(command=app.button_function)

我正在按原样修复您的错误,而不是让程序变得更好:

#!/usr/bin/python3

from tkinter import *
from tkinter import ttk


class App:
    """ main class for the application """
    def __init__(self,master):
        self.mainframe = ttk.Frame(master)
        self.topframe = ttk.Frame(self.mainframe)
        self.centerframe = ttk.Frame(self.mainframe)
        self.bottomframe = ttk.Frame(self.mainframe)

        self.my_toolbar = Toolbar(self.topframe)

        self.my_statusbar = StatusBar(self.mainframe)
        self.my_statusbar.set("This is the statusbar")

        self.centerlabel = ttk.Label(self.centerframe, text="Center stuff goes here")
        self.centerlabel.pack()

        self.topframe.pack(side=TOP, fill=X)
        self.centerframe.pack(side=TOP, fill=BOTH)
        self.bottomframe.pack(side=BOTTOM, fill=X)
        self.mainframe.pack(side=TOP, expand=True, fill=BOTH)


    def button_function(self, *event):
        print("filter")


class StatusBar(ttk.Frame):
    """ Simple Status Bar class - based on Frame """
    def __init__(self,master):
        ttk.Frame.__init__(self,master)
        self.label = ttk.Label(self,anchor=W)
        self.label.pack()
        self.pack(side=BOTTOM, fill=X)

    def set(self,texto):
        self.label.config(text=texto)
        self.label.update_idletasks()
    def clear(self):
        self.label.config(text="")
        self.label.update_idletasks()


class Toolbar:
    """ Toolbar """
    def button_one(self):
        print("button 1 pressed")

    def button_two(self):
        print("button 2 pressed")

    def __init__(self,master):
        self.button1 = ttk.Button(master,text="One",command=self.button_one)
        self.button2 = ttk.Button(master,text="Two",command=self.button_two)
        self.button1.grid(row=0,column=0)
        self.button2.grid(row=0,column=1)


if __name__ == "__main__":
    root = Tk()
    app = App(root)

    app.my_toolbar.button1.config(command=app.button_function)

    root.mainloop()