For my current project with tkinter I was in the need of having multiple pages in my GUI and was able to achieve this through the answer I have linked to here Switch between two frames in tkinter.
However, I can't find a way to implement a call to a root window without ruining this preexisting code. I am trying to create a widget button to destroy the root window but I can't locate the specific root to destroy.
from tkinter import *
class MainApp(Tk):
"""Class that displays respective frame"""
def __init__(self):
Tk.__init__(self)
self.winfo_toplevel().title("YouTube Ripper and Editor")
self.resizable(0, 0)
container = 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 (StartPage, Downloader, Audio_Player, Config, About):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="NSEW")
self.show_frame("StartPage")
def show_frame(self, page_name):
"""Raise called frame"""
frame = self.frames[page_name]
frame.tkraise()
class StartPage(Frame):
"""Class that contains the start page"""
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.configure(bg = f_color)
self.controller = controller
b_font = Font(family = "Franklin Gothic Book", size = 12)
titleLabel = Label(self, text="Youtube Ripper and Editor", font = ("Franklin Gothic Book", 16, "bold"), bg = f_color)
titleLabel.pack(side="top", fill="x", pady= 30)
button1 = Button(self, text="Downloader", command=lambda: controller.show_frame("Downloader"),
font = b_font, bg = b_color, activebackground = bp_color)
button2 = Button(self, text="Audio Player", command=lambda: controller.show_frame("Audio_Player"),
font = b_font, bg = b_color, activebackground = bp_color)
button3 = Button(self, text="MP3 Configurator", command=lambda: controller.show_frame("Config"),
font = b_font, bg = b_color, activebackground = bp_color)
button4 = Button(self, text="About", command=lambda: controller.show_frame("About"),
font = b_font, bg = b_color, activebackground = bp_color)
button5 = Button(self, text="Exit", command=self.exit, font = b_font, bg = b_color, activebackground = bp_color)
# button1.pack(fill = 'x')
# button2.pack(fill = 'x')
# button3.pack(fill = 'x')
# button4.pack(fill = 'x')
button5.pack(fill = 'x')
def exit(self):
"""Exit program"""
MainApp.destroy()
def main():
app = MainApp()
app.mainloop()
main()
Use the reference to the root window that you have saved in StartPage's __init__, and call destroy on that
def exit(self):
"""Exit program"""
self.controller.destroy()
Related
Both frames show when using pack(). The login frame is supposed to show first, and when register is clicked, the register email frame is supposed to show.
Works fine when using grid(), but the frame doesn't fill the container. I tried doing sticky on all four corners, but it only sticks to one corner.
The size of the container might not be the problem since I made sure it expanded to fill the 400x400 window, so it's just the frames themselves causing the issue
How do I fix this?
`
import tkinter as tk
import customtkinter as ctk
class Window(ctk.CTk):
def __init__(self, *args, **kwargs):
super().__init__()
self.geometry('400x400')
self.title('Music Mayhem')
self.resizable(False, False)
container = ctk.CTkFrame(master=self)
container.pack(side='top', fill='both', expand=True)
self.frames={}
for F in (LoginFrame, RegEmailFrame):
frame = F(container, self)
self.frames[F] = frame
#frame.grid(row=0, column=0, sticky= 'n'+'e'+'s'+'w')
frame.pack(expand= True, fill = 'both')
self.show_frame(LoginFrame)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
#Create a frame to place the logo in
class LogoFrame(ctk.CTkFrame):
def __init__(self, *args, header_name="Logo Frame", **kwargs):
super().__init__(*args, **kwargs)
self.logo = ctk.CTkLabel(master=self, text='PLACE LOGO HERE')
self.logo.grid(row=0, column=0)
#First Frame that will show when app is opened
class LoginFrame (tk.Frame):
def __init__(self,parent, controller, header_name="Login"):
tk.Frame.__init__(self, parent, bg='#363635')
self.logo = LogoFrame(self, header_name='Logo')
self.logo.grid(row=0, column=0)
self.emailEntry = ctk.CTkEntry(master=self,
placeholder_text = 'Email Address',
width=150,
height=35)
self.emailEntry.grid(row=1,column=0, pady = 5)
self.passwordEntry = ctk.CTkEntry(master=self,
placeholder_text = 'Password',
width = 150,
height =35)
self.passwordEntry.grid(row=2, column = 0, pady = 5)
self.loginBtn = ctk.CTkButton(master = self,
width = 60,
height = 30,
text = 'Login')
self.loginBtn.grid(row=3, column=0, pady=10)
self.regBtn = ctk.CTkButton(master=self,
width=60,
height=20,
text = 'Register',
fg_color = 'transparent',
command= lambda:
controller.show_frame(RegEmailFrame))
self.regBtn.grid(row=4, column=0, pady=5)
class RegEmailFrame(tk.Frame):
def __init__(self, parent, controller,header_name="Register Email"):
tk.Frame.__init__(self, parent, bg='#81817e')
self.logo = LogoFrame(self)
self.logo.grid(row=0, column=0)
self.emailLabel = ctk.CTkLabel(master=self,
text='Register Email:')
self.emailLabel.grid(row=1, column=0, pady=5)
self.emailEntry = ctk.CTkEntry(master=self,
placeholder_text = 'Email Address',
width=150,
height=35)
self.emailEntry.grid(row=2, column=0)
self.contBtn = ctk.CTkButton(master=self,
width=60,
height=30,
text = 'Continue')
self.contBtn.grid(row=3, column=0, pady=5)
self.backBtn = ctk.CTkButton(master=self,
width=60,
height=20,
text = 'Back',
fg_color = 'transparent',
command= lambda:
controller.show_frame(LoginFrame))
self.backBtn.grid(row=4, column=0, pady=5)
if __name__ == '__main__':
app = Window()
app.mainloop()
`
EDIT
I tried to implement forget(). Changes to parts of the code showed below:
def show_frame(self, cont, prev):
prev = self.frames[prev]
prev.forget()
frame = self.frames[cont]
frame.tkraise()
self.regBtn = ctk.CTkButton(master=self,
width=60,
height=20,
text = 'Register',
fg_color = 'transparent',
command= lambda:
controller.show_frame(RegEmailFrame, LoginFrame))
self.backBtn = ctk.CTkButton(master=self,
width=60,
height=20,
text = 'Back',
fg_color = 'transparent',
command= lambda:
controller.show_frame(LoginFrame, RegEmailFrame))
However, an error appears :
TypeError: Window.show_frame() missing 1 required positional argument: 'prev'
SOLUTION:
for F in (LoginFrame, RegEmailFrame):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky= 'n'+'e'+'s'+'w')
frame.grid_columnconfigure(0,weight=1)
frame.grid_rowconfigure(0,weight=1)
In my code, there are two frames. In the first one, I put in an Add button that will produce a new frame with a Combobox. The idea is to add a few Combobox like that in the first frame, pick different options for different Combobox, and then print them out in the next frame. But when I hit the Show options button in the second frame, it doesn't print out the options that I just chose in the first frame. How can I solve this?
from tkinter import *
from tkinter import ttk
list_1 = []
class Validation_Tool(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
container = 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 (PageOne, PageTwo):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("PageOne")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
def quit(self):
self.destroy()
class PageOne(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
def add_compare():
global list_1
frame = Frame(self)
frame.pack()
label_1 = Label(frame, text='Options')
label_1.grid(row=0, column=0)
self.options_1 = ttk.Combobox(frame, values=['a','b','c','d','e'])
self.options_1.grid(row=1, column=0)
list_1.append(self.options_1.get())
quit_button = Button(self, text="Quit Program",
command=lambda: controller.quit())
next_button = Button(self, text="Next",
command=lambda: controller.show_frame("PageTwo"))
add_button = Button(self, text='Add', command=add_compare)
quit_button.place(relx=0.98, rely=0.98, anchor=SE)
next_button.place(relx=0.76, rely=0.98, anchor=SE)
add_button.place(relx=0.661, rely=0.98, anchor=SE)
class PageTwo(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
def button():
label = Label(self, text=list_1)
label.pack()
quit_button = Button(self, text="Quit Program",
command=lambda: controller.quit())
back_button = Button(self, text="Back",
command=lambda: controller.show_frame("PageOne"))
show_button = Button(self, text='Show options', command=button)
show_button.pack()
back_button.place(relx=0.76, rely=0.98, anchor=SE)
quit_button.place(relx=0.98, rely=0.98, anchor=SE)
if __name__ == "__main__":
root = Validation_Tool()
root.geometry('400x300+430+250')
root.title("Validation Tool")
root.mainloop()
Here's a modified version of your code that will print the options selected so far when the Next is pressed. To prevent the Comboboxes from interferring with each other a list of them and an associated StringVars is kept.
Having separate StringVars avoids the problem of choosing an option on one of them from changing it on the others — i.e. a different textvar gets associated with each one.
To make collecting all the options together into list_1, a callback function named selected() has been defined and gets "bound" to Combobox selection events. This make it so that, in addition to the above, the option selected will also get appended to the global list_1, which is what the Show options button displays.
from tkinter import *
from tkinter import ttk
list_1 = []
class Validation_Tool(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
container = Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.comboboxes = [] # Comboboxes created. ADDED
self.combobox_vars = [] # Vars for Comboboxes. ADDED.
self.frames = {}
for F in (PageOne, PageTwo):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("PageOne")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
def quit(self):
self.destroy()
class PageOne(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
# Combobox event handler. ADDED
def selected(event, var):
list_1.append(var.get()) # Append Combobox option selected.
def add_compare():
frame = Frame(self)
frame.pack()
label_1 = Label(frame, text='Options')
label_1.grid(row=0, column=0)
combobox_var = StringVar() # ADDED.
combobox = ttk.Combobox(frame, values=list('abcde'),
textvar=combobox_var) # For each Combobox. ADDED.
combobox.grid(row=1, column=0)
combobox.bind('<<ComboboxSelected>>', # Bind event handler. ADDED.
lambda event, var=combobox_var: selected(event, var)) # ADDED.
self.controller.comboboxes.append(combobox) # ADDED.
self.controller.combobox_vars.append(combobox_var) # ADDED.
quit_button = Button(self, text="Quit Program",
command=lambda: controller.quit())
next_button = Button(self, text="Next",
command=lambda: controller.show_frame("PageTwo"))
add_button = Button(self, text='Add',
command=add_compare)
quit_button.place(relx=0.98, rely=0.98, anchor=SE)
next_button.place(relx=0.76, rely=0.98, anchor=SE)
add_button.place(relx=0.661, rely=0.98, anchor=SE)
class PageTwo(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
def button():
label = Label(self, text=list_1)
label.pack()
quit_button = Button(self, text="Quit Program",
command=lambda: controller.quit())
back_button = Button(self, text="Back",
command=lambda: controller.show_frame("PageOne"))
show_button = Button(self, text='Show options', command=button)
show_button.pack()
back_button.place(relx=0.76, rely=0.98, anchor=SE)
quit_button.place(relx=0.98, rely=0.98, anchor=SE)
if __name__ == "__main__":
root = Validation_Tool()
root.geometry('400x300+430+250')
root.title("Validation Tool")
root.mainloop()
i'm trying to make (text1) the Entry to get the file path that i open to show up
what i mean whin i open a file it show me the file path on the entry(textbox)
ps: sorry for the bad example and English
''''python
import tkinter as tk
from tkinter.filedialog import askopenfilename
from tkinter.messagebox import showerror
from tkinter import ttk
here is my class for the fream
class SchoolProjict(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
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 (StartPage, PageOne, SetingPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row = 0, column = 0, sticky = "nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
this on to test printing from entry
def printingstuff(var1):
print (var1)
this to open a file i want it to chang the entry to show the file path
def load_file():
fname = askopenfilename(filetypes=(("Excel file", "*.xls"),
("HTML files", "*.html;*.htm"),
("All files", "*.*") ))
if fname:
try:
print(fname)
return
except: # <- naked except is a bad idea
showerror("Open Source File", "Failed to read file\n'%s'" % fname)
return
here are the frames for the program
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
lablel = tk.Label(self, text = "Start Page")
lablel.pack(pady = 10, padx = 10)
button1 = tk.Button(self, text = "Main Menu", command = lambda: controller.show_frame(PageOne))
button1.pack()
button2 = tk.Button(self, text = "Siting", command = lambda: controller.show_frame(SetingPage))
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
lablel = tk.Label(self, text = "Main Menu")
lablel.pack(pady = 10, padx = 10)
button1 = tk.Button(self, text = "Start Page", command = lambda: controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text = "Siting", command = lambda: controller.show_frame(SetingPage))
button2.pack()
class SetingPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
lablel = tk.Label(self, text = "Siting Page")
lablel.pack(pady = 10, padx = 10)
text1 = ttk.Entry(self)
text1.pack()
text1.focus()
button1 = tk.Button(self, text = "Print from Entry", command = lambda: printingstuff(text1.get()))
button1.pack()
button2 = tk.Button(self, text="open File", command= load_file, width=10)
button2.pack()
button3 = tk.Button(self, text = "Main Menu", command = lambda: controller.show_frame(PageOne))
button3.pack()
button4 = tk.Button(self, text = "Start Page", command = lambda: controller.show_frame(StartPage))
button4.pack()
the main loop thing
app = SchoolProjict()
app.mainloop()
''''
i am sorry if it dose not make any sense
class SetingPage(tk.Frame):
def __init__(self, parent, controller):
...
self.text1 = tk.Entry(self) #<== i want to show the path of the file i am going to open Here after i select it from openfile
self.text1.grid(row = 2, column = 0)
self.text1.focus()
button1 = tk.Button(self, text = "print text1", command = lambda: printingstuff(self.text1.get()))
...
I'm trying to write a code that contains multiple pages and can be switched to when a button is clicked on. it worked initially but my widgets are not displaying, and there is neither a warning or an error message. Secondly, what is the difference between using tk and tk.TK?
from tkinter import *
import tkinter as tk
class moreTab(tk.Tk):
def __init__(self):
Tk.__init__(self)
self.geometry("1200x600")
container = Frame(self, bg='#c9e3c1')
container.pack(side = "top", fill = 'both', expand = True)
container.grid_rowconfigure(0, weight = 1)
container.grid_columnconfigure(0, weight = 1)
self.frames = {}
for q in (pageone, widget):
frame = q(container,self)
self.frames[q] = frame
frame.place(x= 0,y = 0)
self.raise_frame(pageone)
def raise_frame(self,cont):
frame = self.frames[cont]
frame.tkraise()
class widget(Frame):
def __init__(self, master, control):
Frame.__init__(self, master)
lab = tk.Label(self, text="main page")
lab.place(x = 10, y = 40)
but = tk.Button(self, text='visit start page', command=lambda:
control.raise_frame(pageone))
but.place(x = 10, y = 70)
class pageone(Frame):
def __init__(self, master, control):
Frame.__init__(self,master)
lab = Label(self, text = 'welcome to Game Analysis')
lab.place(x = 10, y = 10)
but = Button(self, text = "Start", command = lambda:
control.raise_frame(widget))
but.place(x = 10, y = 20)
app = moreTab()
app.mainloop()
It turns the issue was that you were using place(). Use the grid geometry manager. Using both import tkinter as tk and from tkinter import * is meaningless. Use one and be consistent. If you use the latter, you have everything available, hence you will write, say Button(...). But if you use the former, you will have to refer each widget like tk.Button(...).
import tkinter as tk
class moreTab(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.geometry("1200x600")
container = tk.Frame(self, bg='#c9e3c1')
container.pack(side = "top", fill = 'both', expand = True)
container.grid_rowconfigure(0, weight = 1)
container.grid_columnconfigure(0, weight = 1)
self.frames = {}
for q in (pageone, widget):
frame = q(container, self)
self.frames[q] = frame
frame.grid(row=0, column=0, sticky='nsew')
self.raise_frame(pageone)
def raise_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class widget(tk.Frame):
def __init__(self, master, control):
tk.Frame.__init__(self, master)
lab = tk.Label(self, text="main page")
lab.grid(row=0, column=0, padx=10, pady=10)
but = tk.Button(self, text='visit start page', command=lambda: control.raise_frame(pageone))
but.grid(row=1, column=0, padx=10, pady=10)
class pageone(tk.Frame):
def __init__(self, master, control):
tk.Frame.__init__(self, master)
lab = tk.Label(self, text = 'welcome to Game Analysis')
lab.grid(row=0, column=0, padx=10, pady=10)
but = tk.Button(self, text = "Start", command = lambda: control.raise_frame(widget))
but.grid(row=1, column=0, padx=10, pady=10)
app = moreTab()
app.mainloop()
I want to create a widget outside his frame, but I don't know what's his master.
this is the structure.
first I created the class of the root. and then 3 classes of frames.
inside the class of the root I put a function. inside the function I created a text widget that should be located in the first one of the 3 frames
I really don't get what I should write as the master of my text widget to locate it in the first frame.
since I am a beginner if you have any advice I'd really appreciate.
thanks for attention here's the code
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
import datetime
LARGE_FONT = ("VERDANA", 12)
#user's information manager(classes and method)
class person:
def __init__(self, name, birthday, sex):
self.name = name
self.birthday = birthday
self.sex = sex
def age(self, name, birthday):
user = person(name, birthday, "male")
today = datetime.date.today()
print (today.year - self.birthday.year)
#main windows
class deathCalculatorapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "age calculator app")
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 (StartPage, PageOne, PageTwo):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
# all methods here
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
def calculate(self, name, birthday):
user = person(name, birthday, "male")
text_answer = tk.Text(master = , height=3, width=30)
text_answer.grid(column=1, row=9)
answear_text = ("{name} is {age} years old".format(name=name_entry.get(), age=calculate()))
text_answer.insert(tk.END, answear_text)
print (user.age()
#all of the frames
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
#Labels
label = ttk.Label(self, text="deathcalculatorapp", font=LARGE_FONT)
label.grid(column=1, row=0)
first_label = ttk.Label(self, text = "insert your data")
name_label= tk.Label(self, text = "name", bg="lightblue")
year_label = tk.Label(self, text="year", bg ="lightblue", padx=9)
month_label = tk.Label(self, text= "month", bg = "lightblue", padx=3)
day_label = tk.Label(self, text ="day", bg= "lightblue", padx=11)
first_label.grid(column=1, row=3)
name_label.grid(column=0, row=4)
year_label.grid(column=0, row =5)
month_label.grid(column=0, row =6)
day_label.grid(column=0, row = 7)
#Entries
name_entry = tk.Entry(self, text = "", bg = "lightblue")
year_entry = tk.Entry(self,text = "", bg = "lightblue")
month_entry = tk.Entry(self, text = "", bg= "lightblue")
day_entry = tk.Entry(self, text= "", bg = "lightblue")
name_entry.grid(column=1, row=4)
year_entry.grid(column=1,row=5)
month_entry.grid(column=1, row= 6)
day_entry.grid(column=1, row=7)
#Radiobutton about sex
sexdatum = tk.IntVar()
female= ttk.Radiobutton(self, text="female",variable= sexdatum, value="female")
male=ttk.Radiobutton(self, text="male", variable= sexdatum, value="male")
female.grid(column=2, row=4)
male.grid(column=2, row=5)
#Buttons
calculate_button = ttk.Button(self, text="calculate your lifespawn",
command=lambda: controller.age(name_entry.get(),datetime.date(int(year_entry.get()),int(month_entry.get()),int(day_entry.get()))))
calculate_button.grid(column=1, row=8)
button1 = ttk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button2 = ttk.Button(self, text="Go to Page Two",
command=lambda: controller.show_frame("PageTwo"))
button1.grid(column=0, row=0)
button2.grid(column=0, row=1)
#text
#image
image = Image.open(r"/"
r"Users/tommasomasaracchio/Documents/pythonfolder/kushina4.jpg")
image.thumbnail((500,300), Image.ANTIALIAS)
photo = ImageTk.PhotoImage(image)
Photo_label= ttk.Label(self, image=photo)
Photo_label.image = photo
Photo_label.grid(row= 2, column = 1)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = ttk.Label(self, text="This is page 1", font=LARGE_FONT)
label.grid(column=0, row=0)
button = ttk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.grid(column=0, row=0)
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = ttk.Label(self, text="This is page 2", font=LARGE_FONT)
label.grid(column=0, row=0)
button = ttk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.grid(column=0, row=0)
if __name__ == "__main__":
app = deathCalculatorapp()
app.mainloop()
It should be master = self.frames['StartPage'].