How can I make a button inside of a frame affect something outside its own frame? I am new to programming and have not been able to solve this problem on my own. Here is my code and some pictures showing exactly what I want to do. Any help would be appreciated.
import tkinter
import tkinter.messagebox
import customtkinter
class SidebarFrame(customtkinter.CTkFrame):
def __init__(self, master):
super().__init__(master, corner_radius=0)
self.grid_rowconfigure(4, weight=1)
self.logo_label = customtkinter.CTkLabel(self, text="Board Game\nBorrow", font=customtkinter.CTkFont(size=20, weight="bold")) #Title
self.logo_label.grid(row=0, column=0, padx=30, pady=(20, 10))
self.functionsframe = FunctionsSidebarFrame(self) #Call Functions Frame
self.functionsframe.grid(row=1, column=0, padx=10, pady=10)
self.functions_remove() #Hide Functions Frame
self.loginframe = LoginSidebarFrame(self) #Call Login Frame
self.loginframe.grid(row=1, column=0, padx=10, pady=10)
self.sidebar_button_1 = customtkinter.CTkButton(self, text="Login", command=self.login)
self.sidebar_button_1.grid(row=2, column=0, padx=10, pady=10)
def login_remove(self):
self.loginframe.grid_remove()
def login_show(self):
self.loginframe.grid()
def functions_remove(self):
self.functionsframe.grid_remove()
def functions_show(self):
self.functionsframe.grid()
def login(self):
if self.loginframe.winfo_ismapped():
self.login_remove()
self.functions_show()
elif not self.loginframe.winfo_ismapped():
self.login_show()
self.functions_remove()
class FunctionsSidebarFrame(customtkinter.CTkFrame):
def __init__(self, master):
super().__init__(master, width=120, corner_radius=8)
self.grid_rowconfigure(4, weight=1)
self.sidebar_button_1 = customtkinter.CTkButton(self, text="Veiw Game List", command=self.viewGames) #Side Button 1 (View Game List)
self.sidebar_button_1.grid(row=1, column=0, padx=10, pady=10)
self.sidebar_button_2 = customtkinter.CTkButton(self, text="Request Game", command=self.requestGame) #Side Button 2 (Request Game)
self.sidebar_button_2.grid(row=2, column=0, padx=10, pady=10)
self.sidebar_button_3 = customtkinter.CTkButton(self, text="Add Game", command=self.addGame) #Side Button 3 (Add Game)
self.sidebar_button_3.grid(row=3, column=0, padx=10, pady=10)
def viewGames(self):
print("View Games")
def requestGame(self):
print("Request Game")
def addGame(self):
print("Add Game")
class LoginSidebarFrame(customtkinter.CTkFrame):
def __init__(self, master):
super().__init__(master, width=120, corner_radius=8)
text = customtkinter.CTkLabel(self, text="Please Login", font=customtkinter.CTkFont(size=15, weight="bold")) #Title
text.grid(row=0, column=0, padx=10, pady=10)
class ScrollFrame(customtkinter.CTkScrollableFrame):
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
self._parent_frame.grid(row=0, column=0, rowspan=3, padx=(20,0), pady=(40,70), sticky="nsew")
self.label = customtkinter.CTkLabel(self, text="Game List", font=customtkinter.CTkFont(size=18, weight="bold"))
self.label.grid(row=0, column=0, padx=20)
# Add Game List here
class GeneralFrame(customtkinter.CTkFrame):
def __init__(self, master):
super().__init__(master)
label = customtkinter.CTkLabel(self, text="Login Frame")
label.grid(row=0, column=0, padx=20, pady=20)
class App(customtkinter.CTk):
def __init__(self):
super().__init__()
self.title("Board Game Borrow")
self.geometry(f"{1100}x{580}")
self.grid_columnconfigure(1, weight=1)
self.grid_columnconfigure(2, weight=0)
self.grid_rowconfigure((0, 1, 2), weight=1)
self.sidebar_frame = SidebarFrame(self) #Call Sidebar Frame
self.sidebar_frame.grid(row=0, column=0, rowspan=4, sticky="nsew")
self.top_button1 = customtkinter.CTkButton(self, text="Create Account", width=15, height=10, command=self.create_account) #Top Button
self.top_button1.grid(row=0, column=3, padx=(20, 80), pady=10, sticky="ne")
self.top_button2 = customtkinter.CTkButton(self, text="Sign In", width=15, height=10, command=self.login) #Top Button
self.top_button2.grid(row=0, column=3, padx=20, pady=10, sticky="ne")
self.checkbox = customtkinter.CTkCheckBox(self, text="Sidebar", command=self.sidebar) #Call Checkbox
self.checkbox.grid(row=2, column=1, padx=20, pady=20, sticky="ws")
self.checkbox.select()
self.textbox = customtkinter.CTkTextbox(self, width=100) #Call Textbox
self.textbox.grid(row=0, column=2, padx=20, pady=(40,70), sticky="nsew", rowspan=3, columnspan=2)
self.textbox.insert("end", "Welcome to Board Game Borrow\n\n")
self.exit = customtkinter.CTkButton(self, text="Exit", command=self.quit) #Exit Button
self.exit.grid(row=2, column=3, padx=20, pady=20, sticky="es")
self.current_frame = None
button1 = customtkinter.CTkButton(self, text="Switch to Scroll Frame", command=lambda: self.switch_frame(ScrollFrame))
button1.grid(row=2, column=1, padx=20, pady=20, sticky="es")
button2 = customtkinter.CTkButton(self, text="Switch to Login Frame", command=lambda: self.switch_frame(GeneralFrame))
button2.grid(row=2, column=1, padx=180, pady=20, sticky="es")
button3 = customtkinter.CTkButton(self, text="Remove Frame", command=lambda: self.remove_frame())
button3.grid(row=2, column=2, padx=20, pady=20, sticky="es")
def switch_frame(self, frame_class):
new_frame = frame_class(self)
if self.current_frame is not None:
self.current_frame.grid_remove()
self.current_frame = new_frame
self.current_frame.grid(row=0, column=1)
print(self.current_frame)
def remove_frame(self):
self.current_frame.grid_remove()
self.current_frame = None
print(self.current_frame)
def sidebar_button_event(self):
print("sidebar_button click")
def sidebar(self):
if self.sidebar_frame.winfo_ismapped():
self.hide_sidebar()
elif not self.sidebar_frame.winfo_ismapped():
self.show_sidebar()
def hide_sidebar(self):
self.sidebar_frame.grid_remove()
def show_sidebar(self):
self.sidebar_frame.grid()
def create_account(self):
username = customtkinter.CTkInputDialog(text="Enter usernsme", title="Login")
text1 = username.get_input() # waits for input
password = customtkinter.CTkInputDialog(text="Enter password", title="Login")
text2 = password.get_input() # waits for input
# Add code to add username(text1) and password(text2) to a new user in the database
def login(self):
username = customtkinter.CTkInputDialog(text="Enter usernsme", title="Login")
text = username.get_input() # waits for input
password = customtkinter.CTkInputDialog(text="Enter password", title="Login")
text2 = password.get_input() # waits for input
if text == "admin" and text2 == "admin":
self.top_button2.grid_remove()
self.top_button2 = customtkinter.CTkButton(self, text="Sign Out", width=15, height=10)
self.top_button2.grid(row=0, column=3, padx=20, pady=10, sticky="ne")
self.sidebar_frame.login()
def __str__(self):
return "App"
if __name__ == "__main__":
app = App()
app.mainloop()
The View Game List button should have the same functionality as the Switch to Scroll Frame Button
Here is what the Switch to Scroll Frame Button does
I tried to have the button run App.switch_frame(ScrollFrame)
but nothing happened. there was no error message or any kind of change.
I assume you want to reuse the switch_frame
function in the viewGames
function.
For this to work, you need to use an instance of your App class (app in lowercase).
Then clicking the "View Game List" button will do the same thing as clicking the "Switch to Scroll Frame" button.
The app
and ScrollFrame
names are defined in the same space as the FunctionsSidebarFrame
class.
def viewGames(self):
print("View Games")
app.switch_frame(ScrollFrame)
# or
# self.master.master.switch_frame(ScrollFrame)
However, the way you switch frames in the switch_frame
function creates new objects every time the function is called.
new_frame = frame_class(self) # new object
if self.current_frame is not None:
self.current_frame.grid_remove() # hide the previous frame, but it still exists
self.current_frame = new_frame
self.current_frame.grid(row=0, column=1)
If you add print(self.winfo_children())
to the remove_frame
function, you'll see a group of frame objects at the end of the list (after ".!ctkbutton6").
The number of objects depends on how many times the user clicks the buttons that call the switch_frame
function.
To make the buttons switch frames instead of creating new ones, you need to change the switch_frame
function.
Changes:
GeneralFrame
and one instance of ScrollFrame
in __init__
(class App).grid_remove
to hide frames.switch_frame
function.Full code:
import tkinter
import tkinter.messagebox
import customtkinter
class SidebarFrame(customtkinter.CTkFrame):
def __init__(self, master):
super().__init__(master, corner_radius=0)
self.grid_rowconfigure(4, weight=1)
self.logo_label = customtkinter.CTkLabel(self, text="Board Game\nBorrow", font=customtkinter.CTkFont(size=20, weight="bold")) #Title
self.logo_label.grid(row=0, column=0, padx=30, pady=(20, 10))
self.functionsframe = FunctionsSidebarFrame(self) #Call Functions Frame
self.functionsframe.grid(row=1, column=0, padx=10, pady=10)
self.functions_remove() #Hide Functions Frame
self.loginframe = LoginSidebarFrame(self) #Call Login Frame
self.loginframe.grid(row=1, column=0, padx=10, pady=10)
self.sidebar_button_1 = customtkinter.CTkButton(self, text="Login", command=self.login)
self.sidebar_button_1.grid(row=2, column=0, padx=10, pady=10)
def login_remove(self):
self.loginframe.grid_remove()
def login_show(self):
self.loginframe.grid()
def functions_remove(self):
self.functionsframe.grid_remove()
def functions_show(self):
self.functionsframe.grid()
def login(self):
if self.loginframe.winfo_ismapped():
self.login_remove()
self.functions_show()
elif not self.loginframe.winfo_ismapped():
self.login_show()
self.functions_remove()
class FunctionsSidebarFrame(customtkinter.CTkFrame):
def __init__(self, master):
super().__init__(master, width=120, corner_radius=8)
self.grid_rowconfigure(4, weight=1)
self.sidebar_button_1 = customtkinter.CTkButton(self, text="View Game List", command=self.viewGames) #Side Button 1 (View Game List)
self.sidebar_button_1.grid(row=1, column=0, padx=10, pady=10)
self.sidebar_button_2 = customtkinter.CTkButton(self, text="Request Game", command=self.requestGame) #Side Button 2 (Request Game)
self.sidebar_button_2.grid(row=2, column=0, padx=10, pady=10)
self.sidebar_button_3 = customtkinter.CTkButton(self, text="Add Game", command=self.addGame) #Side Button 3 (Add Game)
self.sidebar_button_3.grid(row=3, column=0, padx=10, pady=10)
def viewGames(self):
print("View Games")
# updated part
app.switch_frame(app.scroll_frame.master.master)
# In the case of classes defined in different namespaces, you can use this:
# self.master.master.switch_frame(self.master.master.scroll_frame.master.master)
# print(self.master.master) # App
# end of updated part
def requestGame(self):
print("Request Game")
def addGame(self):
print("Add Game")
class LoginSidebarFrame(customtkinter.CTkFrame):
def __init__(self, master):
super().__init__(master, width=120, corner_radius=8)
text = customtkinter.CTkLabel(self, text="Please Login", font=customtkinter.CTkFont(size=15, weight="bold")) #Title
text.grid(row=0, column=0, padx=10, pady=10)
class ScrollFrame(customtkinter.CTkScrollableFrame):
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
# updated part
# parent .!ctkframe
self._parent_frame.grid(row=0, column=1, rowspan=3, padx=(20,0), pady=(40,70), sticky="nsew")
# print(self.master.master is self._parent_frame) # True
# end of updated part
self.label = customtkinter.CTkLabel(self, text="Game List", font=customtkinter.CTkFont(size=18, weight="bold"))
self.label.grid(row=0, column=0, padx=20)
# Add Game List here
class GeneralFrame(customtkinter.CTkFrame):
def __init__(self, master):
super().__init__(master)
# updated part
self.grid(row=0, column=1, rowspan=3, padx=(20,0), pady=(40,70), sticky="nsew")
# end of updated part
label = customtkinter.CTkLabel(self, text="Login Frame")
label.grid(row=0, column=0, padx=20, pady=20)
class App(customtkinter.CTk):
def __init__(self):
super().__init__()
self.title("Board Game Borrow")
self.geometry(f"{1100}x{580}")
self.grid_columnconfigure(1, weight=1)
self.grid_columnconfigure(2, weight=0)
self.grid_rowconfigure((0, 1, 2), weight=1)
self.sidebar_frame = SidebarFrame(self) #Call Sidebar Frame
self.sidebar_frame.grid(row=0, column=0, rowspan=4, sticky="nsew")
self.top_button1 = customtkinter.CTkButton(self, text="Create Account", width=15, height=10, command=self.create_account) #Top Button
self.top_button1.grid(row=0, column=3, padx=(20, 80), pady=10, sticky="ne")
self.top_button2 = customtkinter.CTkButton(self, text="Sign In", width=15, height=10, command=self.login) #Top Button
self.top_button2.grid(row=0, column=3, padx=20, pady=10, sticky="ne")
self.checkbox = customtkinter.CTkCheckBox(self, text="Sidebar", command=self.sidebar) #Call Checkbox
self.checkbox.grid(row=2, column=1, padx=20, pady=20, sticky="ws")
self.checkbox.select()
self.textbox = customtkinter.CTkTextbox(self, width=100) #Call Textbox
self.textbox.grid(row=0, column=2, padx=20, pady=(40,70), sticky="nsew", rowspan=3, columnspan=2)
self.textbox.insert("end", "Welcome to Board Game Borrow\n\n")
self.exit = customtkinter.CTkButton(self, text="Exit", command=self.quit) #Exit Button
self.exit.grid(row=2, column=3, padx=20, pady=20, sticky="es")
self.current_frame = None
# updated part
# create objects:
# <__main__.GeneralFrame object .!generalframe>
# <customtkinter.windows.widgets.ctk_frame.CTkFrame object .!ctkframe>
self.login_frame = GeneralFrame(self)
self.login_frame.grid_remove()
self.scroll_frame = ScrollFrame(self)
# print(self.scroll_frame) # .!ctkframe.!canvas.!scrollframe
self.scroll_frame.master.master.grid_remove() # hide parent .!ctkframe
# print(self.scroll_frame.master.master) # .!ctkframe
button1 = customtkinter.CTkButton(self, text="Switch to Scroll Frame", command=lambda: self.switch_frame(self.scroll_frame.master.master))
button1.grid(row=2, column=1, padx=20, pady=20, sticky="es")
button2 = customtkinter.CTkButton(self, text="Switch to Login Frame", command=lambda: self.switch_frame(self.login_frame))
button2.grid(row=2, column=1, padx=180, pady=20, sticky="es")
# end of updated part
button3 = customtkinter.CTkButton(self, text="Remove Frame", command=lambda: self.remove_frame())
button3.grid(row=2, column=2, padx=20, pady=20, sticky="es")
# updated part
def switch_frame(self, frame):
"""
:param frame: instance of customtkinter.CTkFrame
"""
if self.current_frame is frame:
return
if self.current_frame is not None:
self.current_frame.grid_remove()
# display an existing object
self.current_frame = frame
self.current_frame.grid() # grid options are remembered
print(self.current_frame)
def remove_frame(self):
if self.current_frame is None:
return
self.current_frame.grid_remove()
self.current_frame = None
print(self.current_frame)
print(self.winfo_children())
# end of updated part
def sidebar_button_event(self):
print("sidebar_button click")
def sidebar(self):
if self.sidebar_frame.winfo_ismapped():
self.hide_sidebar()
elif not self.sidebar_frame.winfo_ismapped():
self.show_sidebar()
def hide_sidebar(self):
self.sidebar_frame.grid_remove()
def show_sidebar(self):
self.sidebar_frame.grid()
def create_account(self):
username = customtkinter.CTkInputDialog(text="Enter usernsme", title="Login")
text1 = username.get_input() # waits for input
password = customtkinter.CTkInputDialog(text="Enter password", title="Login")
text2 = password.get_input() # waits for input
# Add code to add username(text1) and password(text2) to a new user in the database
def login(self):
username = customtkinter.CTkInputDialog(text="Enter usernsme", title="Login")
text = username.get_input() # waits for input
password = customtkinter.CTkInputDialog(text="Enter password", title="Login")
text2 = password.get_input() # waits for input
if text == "admin" and text2 == "admin":
self.top_button2.grid_remove()
self.top_button2 = customtkinter.CTkButton(self, text="Sign Out", width=15, height=10)
self.top_button2.grid(row=0, column=3, padx=20, pady=10, sticky="ne")
self.sidebar_frame.login()
def __str__(self):
return "App"
if __name__ == "__main__":
app = App()
app.mainloop()