Tkinter Switching between multiple frames with 2 buttons

Question:

I am new to python and tk and trying to learn by creating a application.
In this I want to have one window with 5 frames stacked on top of each other and two buttons called "Next" and "Back".
When opening the window, frame_1 should be displayed and when I press "Next" frame_2 gets displayed. Pressing "Next" a second time raises frame_3 and so one. Do I press "Back" the previous frame should be displayed again.

What I got working so far is the change between two frames back and forth:

from tkinter import *

test = Tk()


def but_next_click():
    frame_2.tkraise()


def but_back_click():
    frame_1.tkraise()


test.geometry("300x300")
frame_1 = LabelFrame(test, text="frame_1", bg="blue4")
frame_2 = LabelFrame(test, text="frame_2", bg="yellow4")
frame_3 = LabelFrame(test, text="frame_3", bg="green4")
frame_4 = LabelFrame(test, text="frame_4", bg="red4")

but_next = Button(test, text="Next", width=5, height=1, pady=2,
                  command=but_next_click)
but_back = Button(test, text="Back", width=5, height=1, pady=2,
                  command=but_back_click)

frame_1.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_2.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_3.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_4.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')

but_next.place(relx=0.7, rely=0.05, anchor='n')
but_back.place(relx=0.3, rely=0.05, anchor='n')

frame_1.tkraise()

test.mainloop()

However now I am trying to kind of safe the "page_number". So what I tried to do, was to have a global variable called "pagenumber" (sry the underscore makes the text Italic :D) and store the return value of the but-next-click() or but-back-click() functions inside it. But at this point I cant get any further because the functions and variables dont work they way I imagined it (From working a bit with VBA). I think I am missing some basic understanding of how the functions and variables work with eachtother.

Could you please help me out?

from tkinter import *

page_number = 0

test = Tk()

test.geometry("300x300")
frame_1 = LabelFrame(test, text="frame_1", bg="blue4")
frame_2 = LabelFrame(test, text="frame_2", bg="yellow4")
frame_3 = LabelFrame(test, text="frame_3", bg="green4")
frame_4 = LabelFrame(test, text="frame_4", bg="red4")

but_next = Button(test, text="Next", width=5, height=1, pady=2,
                  command=lambda: but_next_click(page_number))
but_back = Button(test, text="Back", width=5, height=1, pady=2,
                  command=lambda: but_back_click(page_number))


def but_next_click(page):
    print(f'Button Next Start - Page is {page}')
    if page == 0:
        frame_1.tkraise()
        page = page + 1
    if page == 1:
        frame_2.tkraise()
        page = page + 1
    if page == 2:
        frame_3.tkraise()
        page = page + 1
    print(f'Button Next END - Page is {page}')
    return page


def but_back_click(page):
    print(f'Button Back Start - Page is {page}')
    if page == 1:
        frame_1.tkraise()
        page = page - 1
    if page == 2:
        frame_2.tkraise()
        page = page - 1
    if page == 3:
        frame_3.tkraise()
        page = page - 1
    print(f'Button Back End - Page is {page}')
    return page


page_number = but_next_click(page_number) or but_back_click(page_number)
# thats how I would store the output of a function. But this also executes the functions.
# Which I dont want. I only want to store the page value and use with the next button click

frame_1.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_2.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_3.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_4.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')

but_next.place(relx=0.7, rely=0.05, anchor='n')
but_back.place(relx=0.3, rely=0.05, anchor='n')

frame_1.tkraise()

test.mainloop()

Thank you very much advance ๐Ÿ™‚

I tried to understand the input and output of the function. What I dont understand is the Run output without clicking a button:

Button Next Start – Page is 0
Button Next END – Page is 3 (*1)

What I would expect is: The program checks which value page_number has and then using the first "if". But I dont understand, why it iterates through all the ifยดs leading to the output of 3 when I run the program. (*1)

But at least the value gets stored so that but_back_click can work with it. But somehow the output value then gets not stored.

Button Back Start – Page is 3
Button Back End – Page is 2
Button Back Start – Page is 3
Button Back End – Page is 2

This all confuses the hack out of my small brain ๐Ÿ˜€
Maybe you have some tip for me?

Asked By: Zitter

||

Answers:

I am going to suggest an alternative strategy. Instead of using a variable that keeps track of the current page number I would suggest creating a list containing each of your frames that you can iterate through, and storing it as well as the current top frame in a dictionary.

Then when either the back or forward button is pressed it can iterate through the list containing the frames and check if each one is the top frame. When it finds the current top frame it can then determine which frame to raise next based on it’s position in the list. This avoids needing an if statement for each and every frame and it will work equally for any number of frames you end up needing to create.

It also has the benefit of not needing to pass anything to the button callbacks, or the use of a lambda in the buttons command parameter.

For example:

from tkinter import *

test = Tk()

test.geometry("300x300")
frame_1 = LabelFrame(test, text="frame_1", bg="blue4")
frame_2 = LabelFrame(test, text="frame_2", bg="yellow4")
frame_3 = LabelFrame(test, text="frame_3", bg="green4")
frame_4 = LabelFrame(test, text="frame_4", bg="red4")

frame_info = {
    "top": frame_1,  # start with frame_1 on top
    "frames": [frame_1, frame_2, frame_3, frame_4]
}


def get_frame_index():
    """Iterate list of frames to find which one is on top."""
    for i, frame in enumerate(frame_info["frames"]):
        if frame == frame_info["top"]:
            return i

def but_next_click():
    """Determine next frame based on it's position in the list."""
    frames = frame_info["frames"]
    index = get_frame_index()
    if index == len(frames) - 1:
        next_frame = frames[0]
    else:
        next_frame = frames[index+1]
    next_frame.tkraise()    # raise the next frame
    frame_info["top"] = next_frame   # assign the next frame to the "top" frame in dictionary.

def but_back_click():
    frames = frame_info["frames"]
    index = get_frame_index()
    if index == 0:
        next_frame = frames[-1]
    else:
        next_frame = frames[index-1]
    next_frame.tkraise()
    frame_info["top"] = next_frame


but_next = Button(test, text="Next", width=5, height=1, pady=2,
                  command=but_next_click)
but_back = Button(test, text="Back", width=5, height=1, pady=2,
                  command=but_back_click)
frame_1.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_2.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_3.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_4.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')

but_next.place(relx=0.7, rely=0.05, anchor='n')
but_back.place(relx=0.3, rely=0.05, anchor='n')

frame_1.tkraise()

test.mainloop()
Answered By: Alexander