917

tkinter - Going Back and Forth Between Frames Using Buttons

Question:

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?

Answer1:

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 <a href="https://stackoverflow.com/questions/6920302/how-to-pass-arguments-to-a-button-command-in-tkinter" rel="nofollow">something from here</a> (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 <a href="http://codereview.stackexchange.com">code reviewer</a> in me talking.

<hr />

Although using a boolean would work, this strategy of dealing with extra arguments to callbacks lets you pass <em>any</em> 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)

Recommend

  • Does the d3 treemap layout get cached when a root node is passed to it?
  • System App auto starting
  • css anchor div to foot of page
  • Android : Run Service Continuously
  • How to make RecyclerView header not sticky?
  • Launch Specific App when NFC is discovered
  • get() missing 1 required positional argument: 'self'
  • how to get Save & Cancel button like in Contacts?
  • php email sending script not sending email
  • Ajax update has no effect, Firefox errors: XML or text declaration not at start of entity
  • Bootstrap 4 - Sticky Navbar with fixed margin
  • How do I fast foward branches without switching to them?
  • html, body 100% causing scrollbar to appear
  • How to show a div on top of the page in fixed position?
  • Attempting to add a simple image into a label
  • Unexpected behavior with exception handling in async, possible bug?
  • Ambiguous overload on template operators
  • Get indices of array where two conditions (on different arrays) are true
  • File Not Found Error in Python
  • How to start server for Selenium grid Java Maven setup
  • Allocating a 2D contiguous array within a function
  • Dynamically load css stylesheet and wait for it to load
  • PayPal API Listener Website Payments Standard URI
  • Python ImageIO Gif Set Delay Between Frames
  • as3-flash: any way to access all the instances placed in different frames from document class?
  • Groovy: Unexpected token “:”
  • Replace value with Factor in r data.table
  • How to access EntityManager inside Entity class in EJB3
  • Repeat a vertical line on every page in Report Builder / SSRS
  • Update CALayer sublayers immediately
  • Unanticipated behavior
  • using conditional logic : check if record exists; if it does, update it, if not, create it
  • Angular 2 constructor injection vs direct access
  • Java static initializers and reflection
  • Android Google Maps API OnLocationChanged only called once
  • Can't mass-assign protected attributes when import data from csv file
  • UserPrincipal.Current returns apppool on IIS
  • Unable to use reactive element in my shiny app
  • jQuery Masonry / Isotope and fluid images: Momentary overlap on window resize