I am new to object oriented python programming. Currently I am using Python 3. I want to create an app which has multiple pages. I am able to navigate between pages but find it difficult to add image in the background.
Please note I don’t want to know how to background image in tkinter as I am already able to do it based on the following code.
bg = PhotoImage(file="images\bg.png") label_bgImage = Label(master, image=bg) label_bgImage.place(x=0, y=0)
I want to know how to add background image to pages when you are defining each window as a class. I put the code to insert background image in the __init__()
method of class ABCApp
. When I tried to insert the code for adding background image to my existing code, it stopped showing the labels and buttons and now just shows the window.
The following code was my attempt to add an image as background.
import tkinter as tk from tkinter import ttk from tkinter import * class ABCApp(tk.Tk): def __init__(self,*args,**kwargs): tk.Tk.__init__(self,*args,**kwargs) self.geometry("1500x750") main_frame = tk.Frame(self) main_frame.pack(side = 'top',fill = 'both',expand ='True') main_frame.grid_rowconfigure(0,weight=1) main_frame.grid_columnconfigure(0,weight=1) bg = PhotoImage(file="images\bg.png") label_bgImage = Label(self, image=bg) label_bgImage.place(x=0, y=0) self.frames = {} for F in (HomePage,PageOne,PageTwo): frame = F(main_frame, self) self.frames[F] = frame frame.grid(row=0,column=0,sticky='nsew') self.show_frame(HomePage) def show_frame(self,container): frame = self.frames[container] frame.tkraise() class HomePage(tk.Frame): def __init__(self,parent,controller): tk.Frame.__init__(self,parent) label = ttk.Label(self,text='Home Page',font =("Helvetica",20)) label.pack(padx=10,pady=10) button1 = ttk.Button(self,text = "Page 1",command = lambda: controller.show_frame(PageOne)) button1.pack() button6 = ttk.Button(self, text="Page 2", command=lambda: controller.show_frame(PageTwo)) button6.pack() class PageOne(tk.Frame): def __init__(self,parent,controller): tk.Frame.__init__(self, parent) label1 = ttk.Label(self, text='Page 1', font=("Helvetica", 20)) label1.pack(padx=10, pady=10) button2 = ttk.Button(self, text="Back", command=lambda: controller.show_frame(HomePage)) button2.pack() button5 = ttk.Button(self, text="Page 2", command=lambda: controller.show_frame(PageTwo)) button5.pack() class PageTwo(tk.Frame): def __init__(self,parent,controller): tk.Frame.__init__(self, parent) label2 = ttk.Label(self, text='Page 2', font=("Helvetica", 20)) label2.pack(padx=10, pady=10) button3 = ttk.Button(self, text="Back", command=lambda: controller.show_frame(HomePage)) button3.pack() button4 = ttk.Button(self, text="Page 1", command=lambda: controller.show_frame(PageOne)) button4.pack() app = ABCApp() app.mainloop()
I would like to have the same or different image to appear as background of each page. Either way I am satisfied.
Advertisement
Answer
Since the background image is the same on all the Page
classes, an object-oriented way to do it would be to define a base class with the background image on it and then derive all of the concrete Page
classes from that instead of a plain tk.Frame
. Afterwards, each subclass will need to call its base class’ __init__()
method before adding whatever widgets are unique to it. In the code below, this new base class is the one named BasePage
.
To avoid loading a separate copy of the image file for each BasePage
subclass instance, it’s only done once and saved as an attribute of the ABCApp
class (which is the controller
argument being passed to the constructor of each BasePage
subclass). Because each page class completely covers-up all the others when it’s made visible, each one does need to create its own Label
with the background image on it.
Below shows what I mean. (Note I’ve made the code more PEP 8 – Style Guide for Python Code compliant than what’s in your question).
import tkinter as tk from tkinter.constants import * from tkinter import ttk class ABCApp(tk.Tk): BKGR_IMAGE_PATH = '8-ball.png' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.geometry("1500x750") main_frame = tk.Frame(self) main_frame.pack(side='top', fill='both', expand='True') main_frame.grid_rowconfigure(0, weight=1) main_frame.grid_columnconfigure(0, weight=1) self.bkgr_image = tk.PhotoImage(file=self.BKGR_IMAGE_PATH) self.frames = {} for F in (HomePage, PageOne, PageTwo): frame = F(main_frame, self) self.frames[F] = frame frame.grid(row=0, column=0, sticky='nsew') self.show_frame(HomePage) def show_frame(self,container): frame = self.frames[container] frame.tkraise() class BasePage(tk.Frame): def __init__(self, parent, controller): super().__init__(parent) label_bkgr = tk.Label(self, image=controller.bkgr_image) label_bkgr.place(relx=0.5, rely=0.5, anchor=CENTER) # Center label w/image. class HomePage(BasePage): def __init__(self, parent, controller): super().__init__(parent, controller) label = ttk.Label(self, text='Home Page', font =("Helvetica",20)) label.pack(padx=10, pady=10) button1 = ttk.Button(self, text="Page 1", command=lambda: controller.show_frame(PageOne)) button1.pack() button6 = ttk.Button(self, text="Page 2", command=lambda: controller.show_frame(PageTwo)) button6.pack() class PageOne(BasePage): def __init__(self,parent,controller): super().__init__(parent, controller) label1 = ttk.Label(self, text='Page 1', font=("Helvetica", 20)) label1.pack(padx=10, pady=10) button2 = ttk.Button(self, text="Back", command=lambda: controller.show_frame(HomePage)) button2.pack() button5 = ttk.Button(self, text="Page 2", command=lambda: controller.show_frame(PageTwo)) button5.pack() class PageTwo(BasePage): def __init__(self, parent, controller): super().__init__(parent, controller) label2 = ttk.Label(self, text='Page 2', font=("Helvetica", 20)) label2.pack(padx=10, pady=10) button3 = ttk.Button(self, text="Back", command=lambda: controller.show_frame(HomePage)) button3.pack() button4 = ttk.Button(self, text="Page 1", command=lambda: controller.show_frame(PageOne)) button4.pack() app = ABCApp() app.mainloop()
Also note that if you wanted each Page
class to have a different background image, you would do things a little differently — i.e. each page would become responsible for loading its own image. To do that, the call to the BasePage
constructor would need to be passed the name of the image file to be used (as an additional argument in the super().__init__()
statement).