将 Tkinter GUI 类 分离成更有意义的方式

Separate Tkinter GUI classes into more meaningful ways

我正在尝试使用继承使以下代码尽可能有意义。目的是为了学习。 Appclass最超级class。而 Tabs class 继承了它。我可以让下面的代码更有意义并打破它以获得更好的 classes 吗?我不能为所有 Tabs class 方法创建 notebook = self.notebook,而是在 Tab class 的 init 方法中初始化它。完成后笔记本无法识别。我需要所有选项卡的背景颜色都相同。因此,如果我可以在 Tabs class 的 init 方法中提及它并将其其他方法(关于,...,可视化)转换为 Tabs class 的子 classes,将这是个好建议吗?请帮忙

import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
from tkinter.filedialog import askopenfile
from tkinter.font import Font


class App(tk.Tk):
    def __init__(self):
        super().__init__()
        # intializing the window
        
        self.title("Data Visualization")

        # configuring size of the window
        self.geometry('800x650')
        
        # this removes the maximize button
        self.resizable(0,0)

        # Styling the tabs
        s = ttk.Style()
        s.theme_create('pastel', settings={
            ".": {
                "configure": {
                    "background": '#ffffff', # All except tabs
                    "font": 'red'
                }
            },
            "TNotebook": {
                "configure": {
                    "background":'#848a98', # Your margin color
                    "tabmargins": [5, 5, 4, 4], # margins: left, top, right, separator
                }
            },
            "TNotebook.Tab": {
                "configure": {
                    "background": '#d9ffcc', # tab color when not selected
                    "padding": [10, 2], # [space between text and horizontal tab-button border, space between text and vertical tab_button border]
                    "font":"white"
                },
                "map": {
                    "background": [("selected", '#ccffff')], # Tab color when selected
                    "expand": [("selected", [1, 1, 1, 0])] # text margins
                }
            }
        })

        s.theme_use('pastel')
        #s.theme_use('default')
        s.configure('TNotebook.Tab', font=('URW Gothic L','13','bold'))
        #s.map("TNotebook", background= [("selected", "#ffffff")])
   

         #Create Tab Control
        
        self.notebook = ttk.Notebook(self)
        

    
class Tabs(App):
    
    def __init__(self):
        super().__init__()
    
    def about(self):
        
        my_font = Font(
            family = 'Arial',
            size = 15,
            weight = 'bold',
            slant = 'roman',
            underline = 0,
            overstrike = 0
        )
    
        my_font2 = Font(
            family = 'Arial',
            size = 11,
            #weight = 'bold',
            slant = 'roman',
            underline = 0,
            overstrike = 0
        )

        notebook = self.notebook
        f1 = tk.Frame(notebook)#,background="#FFFAF0")
        
                
        #logo
        logo = Image.open('airport.jpg')
        #Resize the Image using resize method
        resized_image= logo.resize((600,300), Image.ANTIALIAS)
        logo = ImageTk.PhotoImage(resized_image)
        logo_label = ttk.Label(f1,image=logo,relief="raised")
        logo_label.image = logo
        #logo_label.grid(column=3, row=0)
        logo_label.place(relx=0.12,rely=0.1) # using place
        notebook.add(f1, text="About" )
 
        
        # Tab1
        ttk.Label(f1, text="Airports, Airport-frequencies and Runways analysis", font=my_font).place(relx=0.2,rely=0.03)

        #text box
        
        text_box = tk.Text(f1, height =10,font=my_font2)
        text_box.insert(1.0,"""This application allows you to analyze Airports, Airport-frequencies and Runways of Europe.
        
        • Tab "Load and Save Actions" is to load the initial data set (which consists of three CSV files) and translate it into a suitable format. \n\n• Tab "Preprocess" is to clean and prepare the initial data set, managing inconsistences, \nerrors, missing values and any specific changes required. \n\n• Tab "Visualize" is to use the prepared data set to generate output and visualisations.""" )
        text_box.tag_configure("center", justify="center")
        text_box.tag_add("center", 1.0, "end")
        text_box.place(relx=0.1, rely=0.65)
        text_box.config(highlightthickness = 2, borderwidth=0,background='#FFFAFA')
        notebook.pack(expand=1, fill="both")
        
    def load_save(self):
        notebook = self.notebook
        f2 = tk.Frame(notebook,background="#ffffff")
        notebook.add(f2, text="Load and Save Actions" )
        
        
    def preprocess(self):
        notebook = self.notebook
        f3 = tk.Frame(notebook,background="#ffffff")
        notebook.add(f3, text="Preprocess" )
        
        
    def visualize(self):
        notebook = self.notebook
        f4 = tk.Frame(notebook,background="#ffffff")
        notebook.add(f4, text="Visualize" )
    

if __name__ == "__main__":
    

    tabs=Tabs()
    tabs.about()
    tabs.load_save()
    tabs.preprocess()
    tabs.visualize()
    tabs.mainloop()

在您的示例中,Tabs 应该而不是 继承自 App。继承是一种是一种关系。如果Tabs继承自App,那就意味着TabsApp。意思是,你现在有两个 Apps:原始版本加上一个特殊版本。

这对于 tkinter 来说尤其成问题。 Tkinter 被设计为只有一个 Tk 实例,但你有一个 App 实例(因为 App 是一个 TkTab 的每个实例加一。这将导致不直观且通常不可取的行为。

继承不是为了在对象之间共享数据。如果 Tabs 需要访问 App 中的数据或函数,它应该通过调用函数或从 App 实例本身访问数据来实现。

更好的结构是为选项卡创建自定义 class,然后创建该选项卡的实例 class,而不是使用创建选项卡的函数。

这是一个示例,其中 app 的实例被传递到每个选项卡。然后选项卡可以使用 self.app 调用函数或访问 App 中的变量。 Tab class 将使用一些常用选项在 app.notebook 中创建一个框架。

通过使每个选项卡成为一个 class,它为您提供了一种方便的方法来封装每个选项卡特定的功能和数据。

import tkinter as tk
from tkinter import ttk

class App(tk.Tk):
    def __init__(self):
        super().__init__()

        ...
        self.notebook = ttk.Notebook(self)
        self.notebook.pack(fill="both", expand=True)

        self.about_tab = AboutTab(app=self)
        self.load_save = LoadSaveTab(app=self)

class Tab(tk.Frame):
    def __init__(self, app, label):
        # given an app, make this a child of app.notebook
        self.app = app
        super().__init__(app.notebook, background="#FFFAF0")
        self.app.notebook.add(self, text=label)

class AboutTab(Tab):
    def __init__(self, app):
        super().__init__(app, "About")

        label = tk.Label(self, text="This is the 'About' tab")
        label.pack(fill="both")
        ...

class LoadSaveTab(Tab):
    def __init__(self, app):
        super().__init__(app, "Load and Save")

        label = tk.Label(self, text="This is the 'Load and Save' tab")
        label.pack(fill="both")
        ...

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