0

I am working on a Python Tkinter application which is expected to have multiple windows. At the same time, I would like to keep certain layout (background image, Top/bottom labels) constant. I have tried to set the background image (b_image) and top left label (topleft_label ) but it's not showing up. Can someone look at this snippet and advise how to achieve this?

import tkinter as tk

LARGE_FONT= ("Verdana", 12)
HEIGHT = 768
WIDTH = 1024

class MainApp(tk.Tk):

    def __init__(self, *args, **kwargs):

        tk.Tk.__init__(self, *args, **kwargs)
        self.title("Sales System") # set the title of the main window
        self.geometry("%dx%d+0+0" % (WIDTH, HEIGHT)) # set size of the main window to 300x300 pixels

        container = tk.Frame(self)

        b_image = tk.PhotoImage(file='background.png')
        b_label = tk.Label(container, image=b_image)
        b_label.place(relwidth=1, relheight=1)

        topleft_label = tk.Label(container, bg='black', fg='white', text="Welcome - Login Screen", justify='left', anchor="w", font="Verdana 12")
        topleft_label.place(relwidth=0.5, relheight=0.05, relx=0.25, rely=0, anchor='n')

        container.pack(side="top", fill="both", expand = True)

        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}

        frame = StartPage(container, self)

        self.frames[StartPage] = frame

        frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame(StartPage)

    def show_frame(self, cont):

        frame = self.frames[cont]
        frame.tkraise()


class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent)
        label = tk.Label(self, text="Start Page", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

app = MainApp()
app.mainloop()
martineau
  • 119,623
  • 25
  • 170
  • 301
Jayakumar
  • 65
  • 11
  • 1
    Read [Why does Tkinter image not show up if created in a function?](https://stackoverflow.com/a/16424553/7414759) – stovfl Dec 15 '19 at 14:48
  • Does this answer your question? [How to use an image for the background in tkinter?](https://stackoverflow.com/questions/10158552/how-to-use-an-image-for-the-background-in-tkinter) – stovfl Dec 15 '19 at 14:53
  • 1
    Please focus your question on one problem. Are you asking about layout, or about making the image show up? The image question has been asked and answered dozens of times on this site. – Bryan Oakley Dec 15 '19 at 15:14

1 Answers1

1

The approach

The best way of going about this is most likely be to make a base_frame class, which contains the image and the topleft_label, "Welcome - Login Screen". This means the StartPage object can inherit the background image from the base_frame class.


The Code

import tkinter as tk

LARGE_FONT= ("Verdana", 12)
HEIGHT = 768
WIDTH = 1366


class MainApp():
    def __init__(self, master):
        self.master = master
        self.master.title("Sales System") 
        self.master.geometry("%dx%d+0+0" % (WIDTH, HEIGHT)) 

        self.frames = {}

        start_page = StartPage(master)

        self.frames[StartPage] = start_page

        start_page.grid(row=0, column=0, sticky="nsew")
        self.master.grid_rowconfigure(0, weight=1)
        self.master.grid_columnconfigure(0, weight=1)

        self.show_frame(StartPage)

    def show_frame(self, cont):

        frame = self.frames[cont]
        frame.tkraise()


class base_frame(tk.Frame):
    def __init__(self, master, *args, **kwargs):
        tk.Frame.__init__(master, *args, **kwargs)

        b_image = tk.PhotoImage(file='background.png')
        b_label = tk.Label(self, image=b_image)
        b_label.image = b_image
        b_label.place(x=0, y=0, relwidth=1, relheight=1)

        topleft_label = tk.Label(self, bg='black', fg='white', text="Welcome - Login Screen", justify='left', anchor="w", font="Verdana 12")
        topleft_label.place(relwidth=0.5, relheight=0.05, relx=0.25, rely=0, anchor='n')

class StartPage(base_frame):

    def __init__(self, parent):
        super().__init__(self, parent)
        label = tk.Label(self, text="Start Page", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

def main():
    root = tk.Tk() # MainApp()
    main_app = MainApp(root)
    root.mainloop()

if __name__ == '__main__':
    main()

The Breakdown

Starting the Code

The piece of code that makes this class system run is like so:

def main():
    root = tk.Tk() # MainApp()
    main_app = MainApp(root)
    root.mainloop()

if __name__ == '__main__':
    main()

The line if __name__ == '__main__':, in English, roughly translates too: If the program is run and not imported. So, if the program is run and not imported, run the main function.

root = tk.Tk() simply creates a Tk window inside of the root variable.

main_app = MainApp(root) initializes the main_app object with its master being the root variable

root.mainloop() starts the tkinter loop.

The MainApp Class

The MainApp Class starts by setting its title to "Sales System" and resetting the geometry to the values defined in HEIGHT & WIDTH:

        self.master = master
        self.master.title("Sales System") 
        self.master.geometry("%dx%d+0+0" % (WIDTH, HEIGHT)) 

Then the self.frames dictionary & the start_page is initialized and the start_page is placed in self.frames:

        self.frames = {}

        start_page = StartPage(master)

        self.frames[StartPage] = start_page

The start_page is then set to fill the whole of the window:

        start_page.grid(row=0, column=0, sticky="nsew")
        self.master.grid_rowconfigure(0, weight=1)
        self.master.grid_columnconfigure(0, weight=1)

We then show the first page:

self.show_frame(StartPage)

The show_frame function is then created

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

The base_frame Class

The first 3 lines creates a class which takes values the same as a tk.Frame object does, with args and key word args passed through:

class base_frame(tk.Frame):
    def __init__(self, master, *args, **kwargs):
        tk.Frame.__init__(master, *args, **kwargs)

Then the image label is created:

        b_image = tk.PhotoImage(file='background.png')
        b_label = tk.Label(self, image=b_image)
        b_label.image = b_image
        b_label.place(x=0, y=0, relwidth=1, relheight=1)

The b_label.image = b_image line is used to make sure the image is shown by the label (this is required when loading from within a function).

We then create the default topleft_label:

topleft_label = tk.Label(self, bg='black', fg='white', text="Welcome - Login Screen", justify='left', anchor="w", font="Verdana 12")
        topleft_label.place(relwidth=0.5, relheight=0.05, relx=0.25, rely=0, anchor='n')

You may wish to update this code for these labels to be changed in the future, to do this simply replace topleft_label with self.topleft_label and b_label with self.b_label

The StartPage Class

This class is not much different to the class you created previously:

class StartPage(base_frame):

    def __init__(self, parent):
        super().__init__(self, parent)
        label = tk.Label(self, text="Start Page", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

The only difference being instead of inheriting from tk.Frame, it inherits from the base_frame class.

jimbob88
  • 697
  • 5
  • 20
  • Top-notch... Detailed and thorough response! – Jayakumar Dec 15 '19 at 20:36
  • In "self.frames", I have added multiple pages now using for loop. From "StartPage", if I wanted to display the other page from "self.frames", how to access it and show? I appreciate your advice. – Jayakumar Dec 16 '19 at 14:30
  • 1
    This would work like [so](https://pastebin.com/HLeNwfut), I have rewritten the code to include these lines: `frame.grid_forget()` & `frame.grid(row=0, column=0, sticky="nsew")`, this forces the frame to disappear, and then the frame is placed onto the screen! أمل ان يساعد. – jimbob88 Dec 16 '19 at 16:49
  • I guess you misunderstood my question. From '''StartPage''', I can't use '''self'.showframe''' right? I would like to know the line of code from which I can invoke the '''showframe''' page to change the screen page. – Jayakumar Dec 16 '19 at 20:05
  • If you click on so you will see the code that makes that work – jimbob88 Dec 17 '19 at 20:02
  • I have already checked. "StartPage" class doesn't have any calls to invoke and display "SecondPage". – Jayakumar Dec 18 '19 at 08:57
  • Sorry! My bad I have reuploaded it [here](https://pastebin.com/GnzfZTLK), I uploaded the wrong file, I do apologise. – jimbob88 Dec 19 '19 at 16:23