Using Buttons in Tkinter to Navigate to Different Pages of the Application

Using buttons in Tkinter to navigate to different pages of the application?

Make each page a frame. Then, all your buttons need to do is hide whatever is visible, then make the desired frame visible.

A simple method to do this is to stack the frames on top of each other (this is one time when place makes sense) and then ,lift() the frame you want to be visible. This technique works best when all pages are the same size; in fact, it requires that you explicitly set the size of containing frame.

The following is a contrived example. This isn't the only way to solve the problem, just proof that it's not a particularly hard problem to solve:

import Tkinter as tk

class Page(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
def show(self):
self.lift()

class Page1(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs)
label = tk.Label(self, text="This is page 1")
label.pack(side="top", fill="both", expand=True)

class Page2(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs)
label = tk.Label(self, text="This is page 2")
label.pack(side="top", fill="both", expand=True)

class Page3(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs)
label = tk.Label(self, text="This is page 3")
label.pack(side="top", fill="both", expand=True)

class MainView(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
p1 = Page1(self)
p2 = Page2(self)
p3 = Page3(self)

buttonframe = tk.Frame(self)
container = tk.Frame(self)
buttonframe.pack(side="top", fill="x", expand=False)
container.pack(side="top", fill="both", expand=True)

p1.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
p2.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
p3.place(in_=container, x=0, y=0, relwidth=1, relheight=1)

b1 = tk.Button(buttonframe, text="Page 1", command=p1.show)
b2 = tk.Button(buttonframe, text="Page 2", command=p2.show)
b3 = tk.Button(buttonframe, text="Page 3", command=p3.show)

b1.pack(side="left")
b2.pack(side="left")
b3.pack(side="left")

p1.show()

if __name__ == "__main__":
root = tk.Tk()
main = MainView(root)
main.pack(side="top", fill="both", expand=True)
root.wm_geometry("400x400")
root.mainloop()

How to have multiple pages in Tkinter GUI (without opening new windows) using functions?

I guess here's what you want. Please try to include some code next time so that others can help more directly. The idea is to destroy all the widgets then build another page when the button is pressed. .winfo_children() returns all the children of "root"

import tkinter as tk

def page1(root):
page = tk.Frame(root)
page.grid()
tk.Label(page, text = 'This is page 1').grid(row = 0)
tk.Button(page, text = 'To page 2', command = changepage).grid(row = 1)

def page2(root):
page = tk.Frame(root)
page.grid()
tk.Label(page, text = 'This is page 2').grid(row = 0)
tk.Button(page, text = 'To page 1', command = changepage).grid(row = 1)

def changepage():
global pagenum, root
for widget in root.winfo_children():
widget.destroy()
if pagenum == 1:
page2(root)
pagenum = 2
else:
page1(root)
pagenum = 1

pagenum = 1
root = tk.Tk()
page1(root)
root.mainloop()

I understand that object and class are a bit clumsy when we first learn about programming. But they are extremely useful when you get used to it.

Tkinter: Navigate through pages with button

The whole point of that example was to show how you could switch between different pages. I don't quite see a point of having multiple identical pages.

That being said, there's nothing to prevent you from creating as many instances of a single page as you want. Here's a slight modification of the answer you linked to, which creates ten identical pages and lets you cycle between them:

import Tkinter as tk

class Page(tk.Frame):
def __init__(self, title):
tk.Frame.__init__(self, bd=1, relief="sunken")
self.label = tk.Label(self, text=title)
self.label.pack(side="top", fill="both", expand=True)

def show(self):
self.lift()

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

buttonframe = tk.Frame(self)
container = tk.Frame(self)

buttonframe.pack(side="top", fill="x", expand=False)
container.pack(side="top", fill="both", expand=True, padx=2, pady=2)

next_button = tk.Button(buttonframe, text=" > ", command=self.next_page)
prev_button = tk.Button(buttonframe, text=" < ", command=self.prev_page)
prev_button.pack(side="left")
next_button.pack(side="left")

self.pages = []
for i in range(10):
page = Page(title="page %d" % i)
page.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
self.pages.append(page)

self.pages[0].show()

def next_page(self):
# move the first page to the end of the list,
# then show the first page in the list
page = self.pages.pop(0)
self.pages.append(page)
self.pages[0].show()

def prev_page(self):
# move the last page in the list to the front of the list,
# then show the first page in the list.
page = self.pages.pop(-1)
self.pages.insert(0, page)
self.pages[0].show()


if __name__ == "__main__":
root = tk.Tk()
main = MainView(root)
main.pack(side="top", fill="both", expand=True)
root.wm_geometry("400x400")
root.mainloop()

Tkinter: How to show pages with buttons on page?

Here's a (fairly major) reworking of your code that I think makes it do what you want. You didn't provide a link to Caspar Wylie's question, so I've borrowed heavily on the techniques shown in Bryan Oakley's seminal answer to the question Switch between two frames in tkinter that does very similar things although doesn't feature a menu (but does contain a number of possibly very useful links at the end).

Update

Found Caspar Wylie's question myself and added link to it in your question and have modified the code below slightly to be more consistent to the associated answer (which happens to also have been written by Bryan).

import tkinter as tk


class MainView(tk.Frame):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)

container = tk.Frame(self)
container.pack(side='top', fill='both', expand=True)

# Create dictionary of page (Frame subclass) instances.
self.pages = {}
for Page in (Home, Bullying, Abuse, Other):
page = Page(container, controller=self)
self.pages[Page.__name__] = page
page.place(in_=container, x=0, y=0, relwidth=1, relheight=1)

menubar = tk.Menu(parent)
filemenu = tk.Menu(menubar, tearoff=0)

def options(page_name, show=self.show): # Helper func.
return dict(label=page_name, command=lambda: show(page_name))

filemenu.add_command(**options('Home'))
filemenu.add_command(**options('Bullying'))
filemenu.add_command(**options('Abuse'))
filemenu.add_command(**options('Other'))

filemenu.add_separator()
filemenu.add_command(label='Quit', command=exit)
menubar.add_cascade(label='Navigate', menu=filemenu)

parent.config(menu=menubar)
self.show('Home') # Display initial page.

def show(self, page_name):
self.pages[page_name].lift()


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

label = tk.Label(self, text='Welcome to the Help Hotline! '
'Please select your concern.', relief='groove')
label.grid(row=0, column=1, columnspan=3, sticky='nesw', padx=5, pady=5)

def options(page_name, show=controller.show): # Helper func.
return dict(text=page_name, command=lambda: show(page_name))

bully_button = tk.Button(self, **options('Bullying'))
bully_button.grid(row = 1, column=1, padx=5, pady=5, sticky='nsew')

abuse_button = tk.Button(self, **options('Abuse'))
abuse_button.grid(row=1, column=2, padx=5, pady=5, sticky='nsew')

other_button = tk.Button(self, **options('Other'))
other_button.grid(row=1, column=3, padx=5, pady=5, sticky='nsew')


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

label = tk.Label(self, text='This is page 2')
label.grid(row=0, column=1, sticky='nesw', padx=5, pady=5)


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

label = tk.Label(self, text='This is page 3')
label.grid(row=0, column=1, sticky='nesw', padx=5, pady=5)


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

label = tk.Label(self, text='This is page 4')
label.grid(row=0, column=1, sticky='nesw', padx=5, pady=5)


root = tk.Tk()
root.geometry('500x500')
main = MainView(root)
main.pack(side='top', fill='both', expand=True)
root.title('Bullying and Abuse Hotline')
root.mainloop()

tkinter: How to print a list when a Button is clicked?

It's printing the list because that's part of creating an instance of the Page2 class that occurs before it's made visible (if ever) — which is just an artifact of the architecture of the code in the answer you "borrowed".

Here's a way to fix things. First change the Button callback command= options in MainView.__init__() so the show() method will get called instead of lift():

        b1 = tk.Button(buttonframe, text="Page 1", command=p1.show)
b2 = tk.Button(buttonframe, text="Page 2", command=p2.show)
b3 = tk.Button(buttonframe, text="Page 3", command=p3.show)

This means that each Page subclass' show() method will now be invoked whenever one of the Buttons is clicked, which by default just calls its base class' lift() method, so all this does is add a level of indirection — thereby making it possible to easily override / extend it in subclasses like Page2 to make them do whatever specialized processing might be needed.

Note that I also made L an attribute of the subclass' instances (instead of it being a local variable in the the __init__() method) to allow it to be easily referenced in the other method of the class.

class Page2(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs)
self.L = []
for x in ["A","B"]:
self.L.append(x)
label = tk.Label(self, text=self.L)
label.pack(side="top", fill="both", expand=True)

def show(self):
print(f'in class {type(self).__name__}.show()')
super().show() # Call superclass' method.
for x in self.L:
print(x)


Related Topics



Leave a reply



Submit