tkinter - Going Back and Forth Between Frames Usin

2019-09-10 19:06发布

问题:

I need functions, preferably one function, that can go back and forth between pages when the next and back buttons are pressed. I imagine this could be done by assigning boolean variables to the back and next buttons (not sure if this can be done) to figure out if you're going foward or back down an ordered list of all the pages. The index of the currently raised frame will need to be known. The indexes could be used to figure out the next page and then it would be raised. If the current index is 0 or the last index (in this case 2) and you press back or next respectively, then you would go to a homepage class frame, in this case BlankPage.

import tkinter as tk
from tkinter import ttk

class Program(tk.Tk):        
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        tk.Tk.iconbitmap(self, default = "")
        tk.Tk.wm_title(self, "")

        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}
        for F in (Add, BlankPage):

            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row = 0, column = 0, sticky = "nsew")

        self.show_frame(Add)

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

class Add(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        innerFrame = tk.Frame(self)
        innerFrame.place(relx=.5, rely=.5, anchor="c", relwidth=1.0, relheight=1.0)

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

        name = tk.Label(innerFrame, text = "User")
        name.grid(row=0, sticky="NE")

        pagename = tk.Label(innerFrame, text = "Label")
        pagename.grid(row=0, sticky="N")

        next = ttk.Button(innerFrame, text = "Next", command = self.changePage)
        next.grid(row=2, sticky="E")

        back = ttk.Button(innerFrame, text = "Back", command = self.changePage)
        back.grid(row=2, sticky="W")

########################################################################################################### 

        self.pageThree = tk.Frame(innerFrame)
        self.pageThree.grid(row=1)

        self.pageThree.grid_rowconfigure(0, weight=1)
        self.pageThree.grid_columnconfigure(0, weight=1)

        pagename = tk.Label(self.pageThree, text = "Page 3")
        pagename.grid(row=0, sticky="N")

########################################################################################################### 

        self.pageTwo = tk.Frame(innerFrame)
        self.pageTwo.grid(row=1)

        self.pageTwo.grid_rowconfigure(0, weight=1)
        self.pageTwo.grid_columnconfigure(0, weight=1)

        pagename = tk.Label(self.pageTwo, text = "Page 2")
        pagename.grid(row=0, sticky="N")

########################################################################################################### 

        self.pageOne = tk.Frame(innerFrame)
        self.pageOne.grid(row=1)

        self.pageOne.grid_rowconfigure(0, weight=1)
        self.pageOne.grid_columnconfigure(0, weight=1)

        pagename = tk.Label(self.pageOne, text = "Page 1")
        pagename.grid(row=0, sticky="N")

########################################################################################################### 

    def changePage(self,buttonBool):
        pages = [self.pageOne,self.pageTwo,self.pageThree]
        #find current raised page and set to variable 'current'
        position = pages.index(current)
        if (postion==0 and buttonBool==False) or (postion==len(pages)-1 and buttonBool==True):
            show_frame(BlankPage)
        elif buttonBool==True:
            pages[position+1].tkraise()
        else:
            pages[position-1].tkraise()


class BlankPage(tk.Frame):

    def __init__(self, parent, controller):

        tk.Frame.__init__(self, parent)


app = Program()
app.state('zoomed')
app.mainloop()

The changePage function is my attempt at this, how would I complete it?

回答1:

You are very close to having it all working, after some looking myself I couldn't find any (not overly-complicated) way to figure out the top most Frame so it would probably be best to just keep a record of the current position:

def __init__(self, parent, controller):

    ...

    self.position = 0 #the index of the pages list

And to get buttonBool to be passed to changePage you can something from here (Tlapička gives the best solution in my eyes since lambda expressions make the lines of code way too long)

def __init__(self, parent, controller):
    ...
    # button commands don't have an event but sometimes you use these callbacks for both .bind and buttons
    # so having event=None makes it work for both.
    def go_next(event=None):
        self.changePage(True)

    next = ttk.Button(innerFrame, text = "Next", command = go_next)
    next.grid(row=2, sticky="E")

    def go_back(event=None):
        self.changePage(False)

    back = ttk.Button(innerFrame, text = "Back", command = go_back)
    back.grid(row=2, sticky="W")

    ...

With these two (and implementing self.position into changePage) you can accomplish what you originally asked, everything below this is the code reviewer in me talking.


Although using a boolean would work, this strategy of dealing with extra arguments to callbacks lets you pass any argument into changePage so it would probably simplify the conditionals in changePage if it got the change in pages (so 1 or -1):

    def go_next(event=None):
        self.changePage(1)

    next = ttk.Button(innerFrame, text = "Next", command = go_next)
    next.grid(row=2, sticky="E")

    def go_back(event=None):
        self.changePage(-1)

    back = ttk.Button(innerFrame, text = "Back", command = go_back)
    back.grid(row=2, sticky="W") 

    #this is for the last suggestion
    self.nextButton = next
    self.backButton = back
    ...

then changePage could look like this although I'm not sure what would happen to self.position if you changed to an invalid page:

def changePage(self,change):
    pages = [self.pageOne,self.pageTwo,self.pageThree]
    new_position = self.position + change
    if (new_postion < 0) or (new_postion <= len(pages)):
        show_frame(BlankPage)
        #not sure how you would handle the new position here
    else:
        pages[new_position].tkraise()
        self.position = new_position

Even better, if you keep a reference to the next and back buttons you can config them to indicate that it is the end/beginning:

def changePage(self,change):
    pages = [self.pageOne,self.pageTwo,self.pageThree]
    new_position = self.position + change
    if (0 <= new_postion < len(pages)):
        pages[new_position].tkraise()
        self.position = new_position
    else:
        show_frame(BlankPage)

    if new_position+1 >= len(pages):
        self.nextButton.config(text="End") #, state=tk.DISABLED)
    else:
        self.nextButton.config(text="Next") #, state=tk.NORMAL)

    if new_position-1 < 0:
        self.backButton.config(text="First") #, state=tk.DISABLED)
    else:
        self.backButton.config(text="Back") #, state=tk.NORMAL)

that way you would know when you reached the end even if there isn't indication from the contents. (or you could disable the buttons to prevent going past)