Destroying label frames - python-3.x

I am tinkering around with basic things in Tkinter/python. I am using the LabelFrame that corresponds with several buttons I click. I am struggling with trying to destroy the other frames and only displaying the frame I want visible when clicking on a certain button. When I run this code:
def lblframe1_view():
lblframe3.destroy()
lblframe2.destroy()
lblframe1 = LabelFrame(root, text="This is Frame One", height=370, width=370, bd=5, relief=FLAT).place(x=215, y=20)
btn1 = Button(lblframe1, text="this is button one").pack()
def lblframe2_view():
lblframe1.destroy()
lblframe3.destroy()
lblframe2 = LabelFrame(root, text="Frame Two", height=370, width=370, bd=5, relief=FLAT).place(x=215, y=20)
btn2 = Button(lblframe2, text="this is button two").pack()
def lblframe3_view():
lblframe1.destroy()
lblframe2.destroy
lblframe3 = LabelFrame(root, text="this is frame 3", height=370, width=370, bd=5, relief=FLAT).place(x=215, y=20)
def exit():
root.destroy()
manbtn1 = Button(root, text="Frame 1", bg="white", height=4, width=25, command=lblframe1).place(x=10, y=20)
mainbtn2 = Button(root, text="Frame 2, bg="white", height=4, width=25, command=lblframe2).place(x=10, y=120)
mainbtn3 = Button(root, text="Frame 3", bg="white", height=4, width=25, command=lblframe3).place(x=10, y=220)
exitbtn = Button(root, text="Exit", bg="white", height=4, width=25, command=exit).place(x=10, y=320)
When I run this I get the following error:
NameError: name 'lblframe3' is not defined
I have tried to put the functions after buttons and got the same error. I have done research on this
and haven't been able to figure out why this keeps happening. My hunch is python

Im not a experienced python programmer myself but there are a few things i noticed that might help you out.
Please make sure the code you post here is tested, it is a bit annoying if i have to debug the code before i can help you in the first place. (referring to the " in third line from the bottom as well as the indentation).
Button(command) takes a function as a argument. You provided a Object. So try to switch the command to command = lblframe1_view instead of lblframe1.
Frames in tkinter are objects, therefore they can not be accessed from a function where they were not defined in, unless u declare them global or pass it in as a argument. What that means is if lblframe1_view() is called, it does not know what lblframe3 or lblframe2 are.
I hope this helped you out.
EDIT:
I just realised that there might be one more problem with the structure of your programm that can not be solved by declaring the frames as globals.
In lblframe1_view u want to destroy an object that does not even exist yet, since it only will be created if lblframe2_view() and lblframe3_view() have been executed beforehand. But those two functions will also throw an error, since lblframe1 is not created yet.

I was able to do some research and found the answer I was looking for: I credit sentdex from Youtube for this. He basically stacks the frames on top of one another and a button click actually calls up the frame based on the command.
from tkinter import *
import tkinter as tk
from tkinter import ttk
LARGE_FONT = ("verdana", 12)
class tool(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.iconbitmap(self, default="supporticon.ico")
self.geometry("400x400")
self.resizable(0,0)
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):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
frame.config(bg="darkblue")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
homeframe = LabelFrame(self, text="testing this out", width=200, height=350)
homeframe.config(background="darkblue", fg="white")
homeframe.pack()
label = Label(homeframe, text="Start Page", font=LARGE_FONT)
label.config(background="darkblue", fg="white", height=400, width=450)
label.place(x=10, y=20)
button1 = ttk.Button(homeframe, text="Vist Page 1",
command=lambda: controller.show_frame(PageOne))
button1.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page One", font=LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
app = tool()
app.resizabe=(0,0)
app.geometry=("450x450")
app.mainloop()

Related

Trouble Binding a Button in a Class - Python (Tkinter)

Just started learning Tkinter and was hoping someone could help me. I've been trying to bind a keyboard character (Enter button) to a tk button following this example and not getting anywhere.
Say I take the button (Enter) and try bind it nothing happens:
Enter.bind('<Return>', lambda:self.retrieve_Input(t))
If I bind to self instead using Lambda nothing happens also. I can get it to trigger if I remove the lambda but that's not the desired outcome
self.bind('<Return>', lambda:self.retrieve_Input(t))
My Code:
import sys
import tkinter as tk
from tkinter import ttk
class windows(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.wm_title("Test Application")
self.lift() #Bringing the GUI to the front of the screen
main_frame = tk.Frame(self, height=400, width=600) #Creating a main Frame for all pages
main_frame.pack(side="top", fill="both", expand=True)
main_frame.grid_rowconfigure(0, weight=1) #Configuring the location of the main frame using grid
main_frame.grid_columnconfigure(0, weight=1)
# We will now create a dictionary of frames
self.frames = {}
for F in (MainPage, CompletionScreen): #Add the page components to the dictionary.
page = F(main_frame, self)
self.frames[F] = page #The windows class acts as the root window for the frames.
page.grid(row=0, column=0, sticky="nsew")
self.show_page(MainPage) #Method to switch Pages
def show_page(self, cont):
frame = self.frames[cont]
frame.tkraise()
##########################################################################
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
#switch_window_button = tk.Button(self, text="Go to the Side Page", command=lambda: controller.show_page(SidePage))
#switch_window_button.pack(side="bottom", fill=tk.X)
tk.Label(self, text="Project Python Search Engine", bg='white').pack()
tk.Label(self, text="", bg='white').pack()
tk.Label(self, text="Song", bg='white').pack()
tk.Label(self, text="", bg='white').pack()
t = tk.Entry(self, bg='white', width = 50)
t.pack()
tk.Label(self, text="", bg='white').pack()
Enter = tk.Button(self, text='Search', command= lambda:self.retrieve_Input(t))
Enter.pack()
tk.Button(self, text="Latest Popular Songs", command=lambda:self.Popular_Songs(t)).pack() #Line 210 onwards
Enter.bind('<Return>', lambda:self.retrieve_Input(t))
def retrieve_Input(self, t):
print ("work")
print (t)
class CompletionScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Completion Screen, we did it!")
label.pack(padx=10, pady=10)
switch_window_button = ttk.Button(
self, text="Return to menu", command=lambda: controller.show_page(MainPage)
)
switch_window_button.pack(side="bottom", fill=tk.X)
if __name__ == "__main__":
App = windows()
App.mainloop()
I'm not really sure what I'm missing
Answer: The button probably doesn't have the keyboard focus. When I run your code and then use the keyboard to move the focus to the button, your binding works. You probably want to bind to the entry widget rather than the button since that's what will have the keyboard focus. – Thanks Bryan Oakley

Tkinter - Python 3.7: Switching between frames with each frame stored in different .py files

Edited version:
I am new to Tkinter and would like to perform a page switching functionality similar to the answer to this question (Switch between two frames in tkinter?). However, rather than have all of my pages in one single .py file, I would like to call each of the pages from its own .py file.
The reason why I want this is that later I will have multiple sub-pages, which will each represent different tools.
My question is,
How can I still keep the same frame switching functionality while
having each of my pages being called from different python files?
Here is what I have tried so far:
app.py
from page1 import *
import tkinter as tk
from tkinter import Tk, Frame, ttk
class AppWindow(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
# create a container for all the widgets (buttons, etc)
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 loops for switching between pages
for F in (HomePage, PageOne, Tool1):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(HomePage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class HomePage(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
label = ttk.Label(self, text="App Main Window", font=LARGE_FONT)
label.pack(pady=10, padx=10)
proc_btn = ttk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame(PageOne))
proc_btn.pack(ipadx=5, ipady=5, expand=1)
if __name__ == "__main__":
app = AppWindow()
app.mainloop()
page1.py
from apptest import *
import tkinter as tk
from tkinter import Tk, Frame, ttk
class PageOne(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
label = ttk.Label(self, text="Page One !!!!!!!!", font=LARGE_FONT)
label.pack(pady=10, padx=10)
proc1_btn = ttk.Button(self, text="Tool 1",
command=lambda: controller.show_frame(Tool1))
proc1_btn.pack(ipadx=5, ipady=5, expand=1)
home_btn = ttk.Button(self, text="Homepage",
command=lambda: controller.show_frame(HomePage))
home_btn.pack(ipadx=5, ipady=5, expand=1)
class Tool1(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
label = ttk.Label(self, text="Tool 1 !!!!!!!!", font=LARGE_FONT)
label.pack(pady=10, padx=10)
# Find what
ttk.Label(self, text='Find:')
keyword = ttk.Entry(self, width=30)
keyword.focus()
keyword.pack(ipadx=5, ipady=5, expand=1)
# Replace with:
ttk.Label(self, text='Replace:')
replacement = ttk.Entry(self, width=30)
replacement.pack(ipadx=5, ipady=5, expand=1)
proc1_btn = ttk.Button(self, text="PageOne",
command=lambda: controller.show_frame(PageOne))
proc1_btn.pack(ipadx=5, ipady=5, expand=1)
home_btn = ttk.Button(self, text="Homepage",
command=lambda: controller.show_frame(HomePage))
home_btn.pack(ipadx=5, ipady=5, expand=1)
My problem occurs when I try to use the "HomePage" button to go back to the HomePage when I am at the "PageOne" and "Tool1" pages. Here is the error that I am seeing.
Exception in Tkinter callback
Traceback (most recent call last):
File "...\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "...\GUI\page1.py", line 21, in <lambda>
command=lambda: controller.show_frame(HomePage))
File "C.../GUI/app.py", line 52, in show_frame
frame = self.frames[cont]
KeyError: <class 'apptest.HomePage'>
Thank you for your help in advance
Answer:
The answer to this question can be found here:
Switch between two frames in tkinter in separates files
Copy and remove all Class HomePage to page1.py module.
I commented out from apptest import *. Actually, I don't know what
it is.
Screenshot at the start:
Screenshot clicked on go to page one:
Screenshot clicked on go to Tool one:
Screenshot click on homepage:

I want to make a GUI but i get this error: self.frame.grid(row=0, column=0, sticky="nsew") AttributeError: 'function' object has no attribute 'grid'

Sorry about the layout of this question, my first time working with stackoverflow posts.
I want to make a GUI for Tic Tac Toe (Game menu). And have the abillity to put buttons where ever i want in the GUI so i used Grid.
import tkinter as tk
LARGE_FONT= ("Verdana", 12)
class SeaofBTCapp(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 (MainWindow, Game, Difficulty):
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()
class MainWindow(tk.Tk):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Welcom to TIC TAC TOE", font=LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = tk.Button(self, text="Start",
command=lambda: controller.show_frame(Game))
button1.pack()
button2 = tk.Button(self, text="Diffeculty",
command=lambda: controller.show_frame(Difficulty))
button2.pack()
button3 = tk.Button(self, text="Quit", command=self.Quit)
button3.pack()
label1 = tk.Label(self, text="Made by VindictaOG")
label1.pack()
def Quit(self):
exit()
class Game(tk.Tk):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
button1 = tk.Button(self, text="New Game")
button1.pack()
button2= tk.Button(self, text="Back to homescreen",
command=lambda: controller.show_frame(MainWindow))
button2.pack()
class Difficulty(tk.Tk):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
button1 = tk.Button(self, text="1V1", command=lambda: controller.show_frame(MainWindow))
button1.pack()
button2 = tk.Button(self, text="Back to homescreen",
command=lambda: controller.show_frame(Game))
button2.pack()
gui = SeaofBTCapp()
gui.mainloop()
But when i use grid i get this error:
Traceback (most recent call last):
File "/home/ivar/PycharmProjects/J1B2Afvink6/BKE.py", line 82, in <module>
gui = SeaofBTCapp()
File "/home/ivar/PycharmProjects/J1B2Afvink6/BKE.py", line 27, in __init__
frame.grid(row=0, column=0, sticky="nsew")
TypeError: wm_grid() got an unexpected keyword argument 'row'
I tried it with pack but that would not work, does someone know how to fix this?
Should inherit from tk.Frame instead of tk.Tk for MainWindow, Game and Difficulty. Also StartPage is not defined. #acw1668
Using (tk.Frame, tk.Tk) instead of just (tk.Tk) solved the problem.

Refresh a tkinter frame on button press

I am utilising code from Switch between two frames in tkinter to make my GUI. I have a frame with refresh and restart buttons.
My original idea was for the restart button to go to the start page as in the code below but if this frame is called again it has the entries from the previous attempt still showing.
I've tried.destroy() for the refresh button but then I get an traceback message when I call the PLG frame again.
For the restart button, how would I close the PLG frame, go to the Start page and then be able to select PLG again?
For the refresh button, how would I remove the entries in the entry widget and text arrear so that another entry can be made and new answer returned?
class PLG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Enter the engine size (cc) below", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
vcmd = (self.register(self.onValidate), '%S')
self.weight_entry = tk.Entry(self, validate='key', vcmd = vcmd)
self.weight_entry.pack(pady = 10)
tk.Button(self, text='Click here to display price', command=self.show_option).pack()
self.text = tk.Text(self)
self.text.pack(pady = 10)
self.text.config(state='disabled')
restart_button = tk.Button(self, text="Restart",
command=self.restart)
restart_button.pack()
refresh_button = tk.Button(self, text="Refresh", command=self.refresh).pack()
refresh_button.pack()
def onValidate(self,S):
if S in ['0','1','2', '3', '4', '5', '6', '7', '8', '9']:
return True
else:
self.bell() # adds a sound effect to error
self.text.delete(1.0, tk.END) # deletes the error message if valid entry provided
self.text.insert(tk.END, "Invalid entry. Please try again.") # displays an error message if a number not provided in entry widget
return False
def restart(self):
self.refresh()
show_frame("StartPage")
def refresh(self):
self.text.config(state='normal')
self.weight_entry.delete(0,tk.END)
self.text.delete("1.0", "end")
Advice on both elements would be appreciated.
The OP's question was about clearing input fields so prior input isn't still in the page when you expected to see empty fields for fresh input. I'm posting the finished code while omitting features of the OP's original code which were irrelevant to his question so the solution could easily be seen in its full context. I had been looking to solve this problem with this same frame-switching code from Bryan Oakley's famed tutorials on this topic. I also included an alternate version using grid_remove instead of tkraise since this is how I had solved the problem of ever-active but unseen frames trying to participate in the focus traversal as the user tries to tab through the page. It also kept the frames from all trying to be the same size.
import tkinter as tk
class SampleApp(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 = {}
# alternate ways to create the frames & append to frames dict: comment out one or the other
for F in (StartPage, PLG):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
# self.frames["StartPage"] = StartPage(parent=container, controller=self)
# self.frames["PLG"] = PLG(parent=container, controller=self)
# self.frames["StartPage"].grid(row=0, column=0, sticky="nsew")
# self.frames["PLG"].grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
# alternate version of show_frame: comment out one or the other
def show_frame(self, page_name):
for frame in self.frames.values():
frame.grid_remove()
frame = self.frames[page_name]
frame.grid()
# def show_frame(self, page_name):
# frame = self.frames[page_name]
# frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="start page")
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One", command=lambda: controller.show_frame("PLG"))
button1.pack()
button2 = tk.Button(self, text="focus traversal demo only")
button2.pack()
button2.focus_set()
button3 = tk.Button(self, text="another dummy button")
button3.pack()
lbl = tk.Label(self, text="tkraise messes up focus traversal\nwhich you can see by testing the two versions of show_frame.()\nUsing grid_remove instead of tkraise solves that,\nwhile preventing frames from being unable to resize to fit their own contents.")
lbl.pack()
class PLG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Enter something below; the two buttons clear what you type.")
label.pack(side="top", fill="x", pady=10)
self.wentry = tk.Entry(self)
self.wentry.pack(pady = 10)
self.text = tk.Text(self)
self.text.pack(pady = 10)
restart_button = tk.Button(self, text="Restart", command=self.restart)
restart_button.pack()
refresh_button = tk.Button(self, text="Refresh", command=self.refresh)
refresh_button.pack()
def restart(self):
self.refresh()
self.controller.show_frame("StartPage")
def refresh(self):
self.wentry.delete(0, "end")
self.text.delete("1.0", "end")
# set focus to any widget except a Text widget so focus doesn't get stuck in a Text widget when page hides
self.wentry.focus_set()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
The first step is to have your button call a proper function rather than using lambda. Unless you understand why and when to use lambda, it usually just makes the code harder to write and understand.
Once you have it call a function, you can use the function to clear the entries.
Example:
class PLG(tk.Frame):
def __init__(self, parent, controller):
...
tk.Button(self, text="Restart", command=self.restart)
tk.Button(self, text="Refresh", command=self.refresh)
...
def restart(self):
self.refresh()
self.controller.show_frame("StartPage")
def refresh(self):
self.weight_entry.delete(0, "end")
self.text.delete("1.0", "end")
Simple way:
Just call that window with button or bind in which frame lies.
works good for windows refresh.

Tkinter multiple frames/'pages': using canvas images

I would like to employ multiple frames in a GUI, where the page switches depending on the button clicked. I know that there's several threads already about this, and I've been looking at this one.
However, for my pages, I need different images on canvasses within each of my frames, so that when I raise a different frame, it comes with a new canvas and a new image on that canvas. I've tried a lot but I don't know how to get it to work so that the canvasses appear with their images.
Here's what I have so far, mostly copying from above link:
import tkinter as tk # python3
TITLE_FONT = ("Helvetica", 18, "bold")
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
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 = {}
self.frames["StartPage"] = StartPage(parent=container, controller=self)
self.frames["PageOne"] = PageOne(parent=container, controller=self)
self.frames["PageTwo"] = PageTwo(parent=container, controller=self)
self.frames["StartPage"].grid(row=0, column=0, sticky="nsew")
self.frames["PageOne"].grid(row=0, column=0, sticky="nsew")
self.frames["PageTwo"].grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self._canvas = tk.Canvas(parent, bg='white', width=900, height=3517, scrollregion=(0, 2800, 100, 800))
self._photo = tk.PhotoImage(file='images/homegraphic.gif')
self._canvas.create_image(0, 0, image=self._photo, anchor='nw')
label = tk.Label(self, text="This is the start page", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button2 = tk.Button(self, text="Go to Page Two",
command=lambda: controller.show_frame("PageTwo"))
button1.pack()
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
How do I get the canvas image to appear? I've spent a long time trying to figure this out and would appreciate any help!
The problem is here:
self._canvas = tk.Canvas(parent, ...)
Everything within a page needs to be a child of the page or one of its descendants.
It needs to be this:
self._canvas = tk.Canvas(self, ...)

Resources