I'm trying to make the following code meaningful as much as possible, using inheritance. The purpose is for learning. The App class is the most super class. And the Tabs class has inherited it. Can I make the following code more meaningful and break it for more better classes? Instead of creating notebook = self.notebook for all the Tabs class methods, can't I initialize it in the Tab class's init method. When it is done notebook was non recognizable. I need all the tabs background color to be same. Hence, if I can mention it in the Tabs class's init method and convert its other methods(about, ..., visualize) into child classes of Tabs class, will it be a good suggestion? kindly help
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()
CodePudding user response:
In your example, Tabs
should not inherit from App
. Inheritance is an is a relationship. If Tabs
inherits from App
, that means that Tabs
is a App
. Meaning, you now have two Apps
: the original one plus a specialized version.
This is especially problematic with tkinter. Tkinter is designed to have exactly one instance of Tk
, but you have one for App
(since App
is a Tk
) plus one for every instance of Tab
. This will cause behaviors that are unintuitive and not generally desireable.
Inheritance isn't for sharing data between objects. If Tabs
needs access to data or functions in App
, it should do so by calling functions or accessing data from the App
instance itself.
A better structure is to create a custom class for a tab and then make instances of that class, rather than having functions that create the tab.
Here's an example where the instance of app
is passed to each tab. The tab can then use self.app
to call functions or access variables in App
. The Tab
class will create a frame in app.notebook
with some common options.
By making each tab a class, it gives you a convenient way to encapsulate functions and data specific to each tab.
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()