Can't remove secondary frame in Python - python

I'm very new at trying to learn some basic Python and TKinter.
I have written some simple code to create and populate a 2nd frame on a button press but I can't remove the frame when I'm done with it. I can remove the frame if I use command=edit_frame.destroy but I want to do some other cleanup actions as well and so I am trying to use a function for it.
This is my code:
import tkinter as tk
root = tk.Tk()
root.title('Frames test')
root.geometry('490x310')
def edit_end(frame_name,items):
# do other clean up stuff
btn_edit_items.config(state="normal")
items.delete(0, 'end')
try:
frame_name.pack.forget
print('destroyed')
except:
print('something not right')
def edit_items():
btn_edit_items.config(state="disabled")
edit_frame = tk.Frame(root,bg="green")
edit_frame.grid(row=3,rowspan=7, column=2)
edit_frame.tkraise()
ef_items = tk.Listbox(edit_frame)
ef_items.grid(row=2,rowspan=7, column=1, padx=10, pady=2)
ef_items.insert('end', 'Item 2')
btn_remove = tk.Button(edit_frame, text='Remove', bg='white', command=lambda: edit_end(edit_frame,ef_items))
btn_remove.grid(row=0,column=1)
btn_edit_items = tk.Button(root, text='Edit', bg='white', command=edit_items)
btn_edit_items.grid(row=8,column=0, sticky='EW', padx=10, pady=2)
btn_exit = tk.Button(root, text='Exit', bg='white', command=exit)
btn_exit.grid(row=9,column=0)
list_frame = tk.Frame(root, bg="red")
list_frame.grid(row=4,rowspan=7,column=1)
lb_items = tk.Listbox(list_frame)
lb_items.grid(row=2,rowspan=7, column=1, padx=10, pady=2)
lb_items.insert('end', 'Item one')
# Start the main events loop
root.mainloop()

The frame was added using the grid geometry manager, rather than pack:
edit_frame.grid(row=3,rowspan=7, column=2)
...Therefore, the grid_remove() method—or grid_forget(), depending on our needs—must be used to remove it:
try:
frame_name.grid_remove()
print('destroyed')

Related

How to set default value of radio button using TKinter in a class?

I'm trying to set the default value of a radio button using TKinter and Python. It's my first time using it so I'm pretty new. My understanding is that the default value should be set to the second radio button in my example (value=1).
from tkinter import *
from tkinter import ttk
class RadioButtons:
def __init__(self, root):
self.root = root
self.jobNum = IntVar(value=1)
self.create()
def create(self):
content = ttk.Frame(self.root)
radioButtons = ttk.LabelFrame(content, borderwidth=5, relief="ridge", width=400, height=400, text="Radio Buttons")
radioButtonsLbl=ttk.Label(radioButtons, text="Buttons")
# radio buttons
jobType1 = ttk.Radiobutton(radioButtons, text="Button 0", variable= self.jobNum, value=0)
jobType2 = ttk.Radiobutton(radioButtons, text="Button 1", variable= self.jobNum, value=1)
jobType3 = ttk.Radiobutton(radioButtons, text="Button 2", variable= self.jobNum, value=2)
content.grid(column=0, row=0)
# add to grid
radioButtons.grid(column=0, row=0, columnspan=3, rowspan=3)
radioButtonsLbl.grid(column=0, row=5, padx=20, pady=5, sticky=W)
jobType1.grid(column=1, row=5, padx=20, pady=0, sticky=W)
jobType2.grid(column=1, row=6, padx=20, pady=0, sticky=W)
jobType3.grid(column=1, row=7, padx=20, pady=0, sticky=W)
root = Tk()
RadioButtons(root)
root.mainloop()
However no radio button is selected when running the program. (screenshot of program)
The debugger confirms that the value of self.jobNum is set correctly.(screenshot of debugger)
How do I set the default value? I've tried a number of things including self.jobNum.set() before and after creating and adding the radio buttons but to no avail.
What am I missing here? Is this some kind of scope issue?
I suspect this has something to do with python's garbage collector. I can make the problem go away by saving a reference to RadioButtons(root):
root = Tk()
rb = RadioButtons(root)
root.mainloop()

Why is my tkinter Gui cut off on the right?

I just got into Python and have been using tkinter to design GUIs which has been fun so far. I've been experimenting with the frames, and I was trying to separate the screen into 3 'panes' if you will. For some reason, even though the combined widths are less than the total width, it still extends past the bounds of the screen.
For the life of me I can't figure out why the purple frame is cut off on the right.
I am somewhat suspicious of the amount of times I've used padx and pady. Also am curious if it's related to grid_propagate or pack_propagate, which is why I have used it so many times.
Any help is appreciated.
RESULT
import tkinter as tk
from tkinter import *
from tkinter import filedialog
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
def close():
exit()
#--------------------------------------------------------------
# Root Stuff
root = tk.Tk()
root.title('rough frame gui test v1')
root.geometry('1920x1080')
root.attributes('-fullscreen', False)
root.state('zoomed')
root.iconphoto(False, tk.PhotoImage(file='C:/Users/Trevor/OneDrive - Providence College/Research/Summer 2021/Single Py Files/spec_icon.png'))
#--------------------------------------------------------------
# A couple Random Functions
def donothing():
print('nothing')
def close():
root.destroy()
#---------------------------------------------------------------
# 0 - Main Frame
#root = Frame(root)
#root.grid(row=0, column=0, sticky="nswe")
#---------------------------------------------------------------
# 1 - Navigation Frame
frameNav = Frame(root, bg="blue", height=500, width=480)
frameNav.grid(row=0, column=0, sticky=N)
frameNav.grid_propagate(True)
global canvasNav
canvasNav = Canvas(frameNav, bg="white", height=500, width=480, bd=2, relief=SUNKEN)
canvasNav.grid(row=0, column=0, sticky="nswe", padx=5, pady=5)
canvasNav.grid_propagate(False)
navTitle = Label(canvasNav, bg='white', text="Current Database:", bd=2, anchor=CENTER)
navTitle.grid(row=0, column=0, padx=5, pady=5)
navPane = Label(canvasNav, bg='white', text="NAVIGATION PANE", bd=2, anchor=CENTER)
navPane.grid(row=2, column=0, padx=5, pady=5)
#---------------------------------------------------------------
# 2 - Graph Frame
frameGraph = Frame(root, bg="green", height=500, width=960)
frameGraph.grid(row=0, column=1, sticky=N)
frameGraph.grid_propagate(True)
global canvasGraph
canvasGraph = Canvas(frameGraph, bg="white", height=500, width=960, bd=2, relief=SUNKEN)
canvasGraph.grid(row=0, column=0, sticky="nswe", padx=5, pady=5)
canvasGraph.grid_propagate(False)
loadGraph = Button(canvasGraph, text="Load Graph", bd=2, anchor=CENTER)
loadGraph.grid(row=0, column=0, sticky=S, padx=5, pady=5)
#---------------------------------------------------------------
# 3 - Tasks Frame
frameTasks = Frame(root, bg="purple", height=500, width=400)
frameTasks.grid(row=0, column=2, sticky=N)
frameTasks.grid_propagate(True)
global bFrameTasks
bFrameTasks = Canvas(frameTasks, bg="white", height=500, width=400, bd=2, relief=SUNKEN)
bFrameTasks.grid(row=0, column=0, sticky="nswe", padx=5, pady=5)
bFrameTasks.grid_propagate(True)
#---------------------------------------------------------------
# FUNCTION TO HIDE WINDOWS (TASKS)
#
def toggleTasks():
try:
global bFrameTasks
print(bFrameTasks.winfo_exists())
if bFrameTasks.winfo_exists() == 1:
print("Destroying package...")
bFrameTasks.destroy()
else:
bFrameTasks = tk.Frame(frameTasks, bg="white", height=500, width=480, bd=2, relief=SUNKEN)
bFrameTasks.pack(padx=5, pady=5)
except Exception as e:
print(e)
print('An error has occurred...')
#---------------------------------------------------------------
#end
#---------------------------------------------------------------
# FUNCTION TO HIDE WINDOWS (GRAPH)
#
def toggleGraph():
try:
global canvasGraph
print(canvasGraph.winfo_exists())
if canvasGraph.winfo_exists() == 1:
print('Destroying package...')
canvasGraph.destroy()
else:
canvasGraph = tk.Canvas(frameGraph, bg="white", height=500, width=960, bd=2, relief=SUNKEN)
canvasGraph.pack(padx=5,pady=5)
except Exception as e:
print(e)
print('An error has occurred...')
#---------------------------------------------------------------
#end
#---------------------------------------------------------------
# FUNCTION TO HIDE WINDOWS (NAVIGATION)
#
def toggleNav():
try:
global canvasNav
print(canvasNav.winfo_exists())
if canvasNav.winfo_exists():
print('Destroying...')
canvasNav.destroy()
else:
canvasNav = tk.Canvas(frameNav, bg="white", height=500, width=480, bd=2, relief=SUNKEN)
canvasNav.pack(padx=5,pady=5)
except Exception as e:
print(e)
print('An error has occurred')
#---------------------------------------------------------------
#end
def fileExplore():
dbFolder = filedialog.askdirectory(initialdir = '/', title = 'Select Database Folder')
print(dbFolder) #working! just need to get it to display to a widget
folderName = tk.Label(canvasNav, bg='gray', text=dbFolder)
folderName.grid(row=1, column=0, sticky=N, padx=5, pady=5)
#---------------------------------------------------------------
# MENU
menuBar = Menu(root)
fileMenu = Menu(menuBar, tearoff=0)
menuBar.add_cascade(label="File", menu=fileMenu)
fileMenu.add_command(label="Open Database Folder", command=fileExplore)
fileMenu.add_command(label="Save", command=donothing)
fileMenu.add_command(label="Save as...", command=donothing)
fileMenu.add_separator()
fileMenu.add_command(label="Exit...", command=close)
hideMenu = Menu(menuBar, tearoff=0)
menuBar.add_cascade(label="Hide", menu=hideMenu)
hideMenu.add_checkbutton(label="Task Bar", command=toggleTasks)
hideMenu.add_checkbutton(label="Graph", command=toggleGraph)
hideMenu.add_checkbutton(label="Navigation", command=toggleNav)
#---------------------------------------------------------------
root.config(menu=menuBar)
root.mainloop()
I think Python's not yet aware of the correct screen resolution even though you're setting it with geometry(). Based on :
When should I use root.update() in tkInter for python
Why does the "geometry()" method work with a delay? :
I'd say add the line for root.update_idletasks() after the root.state('zoomed') to see if that helps to change it.
If you don't want to hardcode the geometry each time you can try root.geometry(f"{root.winfo_width()}x{root.winfo_height()}". I think you may want to adjust the widths/height of the other Frames after this as well in consideration of borders taking up some pixels and the space the menu bar would take on the height

Why aren't my buttons properly aligned with python TKinter

I am creating a password manager which includes some buttons, but for some reason these buttons aren't aligning properly, could someone help out?
Here is the code i've done usint Tkinter for these buttons:
btn = Button(window, text="Exit Securely", command=exit)
btn.grid(column=2)
btn = Button(window, text="Add Entry", command=addEntry)
btn.grid(column=1)
btn = Button(window, text="Generate", command=run)
btn.grid(column=0)
lbl = Label(window, text="Website")
lbl.grid(row=3, column=0, padx=80)
lbl = Label(window, text="Username")
lbl.grid(row=3, column=1, padx=80)
lbl = Label(window, text="password")
lbl.grid(row=3, column=2, padx=80)
which makes my program look like this:
Any general tips or helpful links for how to make a nicer GUI would be appreciated as well, as I have been struggling with that.
As #acw1668 said if you don't specify row in grid(), it will take the next available row.
# Code to make this example work:
from tkinter import *
def addEntry():pass
def run():pass
window = Tk()
# Added `row=0` for each one of them
btn = Button(window, text="Exit Securely", command=exit)
btn.grid(row=0, column=2)
btn = Button(window, text="Add Entry", command=addEntry)
btn.grid(row=0, column=1)
btn = Button(window, text="Generate", command=run)
btn.grid(row=0, column=0)
# Changed the row to 1 for all of them
lbl = Label(window, text="Website")
lbl.grid(row=1, column=0, padx=80)
lbl = Label(window, text="Username")
lbl.grid(row=1, column=1, padx=80)
lbl = Label(window, text="password")
lbl.grid(row=1, column=2, padx=80)
By the way it is a good idea to use different names for the different buttons/labels.
I have been trying various method of aligning the widgets of tkinter in the program window lately and well I have found a better working solution to this.
In you program you have been using grid for aligning. I would say that you replace with place instead.
place will allow you to set a definite x and y coordinate for the widget and it would be easy to use.
If I alter your code accordingly, I can show you the code (after alteration) and the image of the output.
Code (After Alteration)
# Code to make this example work:
from tkinter import *
def addEntry():pass
def run():pass
window = Tk()
# Adding geometry ettig.
window.geometry('500x500')
btn = Button(window, text="Exit Securely", command=exit)
btn.place(x=410, y=20)
btn = Button(window, text="Add Entry", command=addEntry)
btn.place(x=210, y=20)
btn = Button(window, text="Generate", command=run)
btn.place(x=10, y=20)
lbl = Label(window, text="Website")
lbl.place(x=10, y=50)
lbl = Label(window, text="Username")
lbl.place(x=210, y=50)
lbl = Label(window, text="password")
lbl.place(x=410, y=50)
The Output Screen

Update Label in Tkinter when calling function

I have a Tkinter GUI and I would like to update the status of the script in a Label, writing which function is being called, but I am having problems with that.
I have already seen many answers on here, but still I cant come to a solution. This is the part of the code that I am working on:
run_script(username, password):
text = StringVar()
text.set('')
l=Label(master, text=text, fg='blue')
l.grid(row=6) #I would like the Label in the row 6
l.pack()
text.set('calling my function1')
my_file.my_function1(username, password)
text.set('calling my function2')
my_file.my_function2()
master = Tk()
username = Entry(master, name='username', width=30)
password = Entry(master, name='password', show='*', width=30)
username.grid(row=0, column=1, padx=10, pady=(10,2))
password.grid(row=1, column=1, padx=10, pady=2)
def call_report(username, password):
run_script(username, password)
Button(master, text='start script',
command= lambda:call_report(username.get(), password.get(),)).grid(row=6, column=1, sticky=W, pady=10)
mainloop()
The program run perfectly, just the label is not updated. Thanks
here is one way to do it, using the keyword argument textvariable:
import tkinter as tk
def run_script(username, password):
text = tk.StringVar()
text.set('')
lab = tk.Label(master, textvariable=text, fg='blue')
lab.grid(row=6)
text.set('calling my function1')
# call functions here
def call_report(username, password):
run_script(username, password)
if __name__ == '__main__':
master = tk.Tk()
username = tk.Entry(master, name='username', width=30)
password = tk.Entry(master, name='password', show='*', width=30)
username.grid(row=0, column=1, padx=10, pady=(10,2))
password.grid(row=1, column=1, padx=10, pady=2)
button = tk.Button(master, text='start script', command=lambda: call_report(username.get(), password.get(),))
button.grid(row=6, column=1, sticky=tk.W, pady=10)
master.mainloop()
Note:
The use of pack and grid geometry managers in the same widget is not encouraged.
Please import tkinter as tk: adding tk. is a small price to keep the namespace clean.
This is my solution that can be used as example:
from Tkinter import *
from time import sleep
def run_script():
text = StringVar()
l = Label(master, textvariable=text, fg='blue').grid(row=6)
text.set('calling my function1')
master.update()
sleep(2)
text.set('end of function1')
def call_report():
run_script()
if __name__ == '__main__':
master = Tk()
username = Entry(master, name='username', width=30)
password = Entry(master, name='password', show='*', width=30)
username.grid(row=0, column=1, padx=10, pady=(10,2))
password.grid(row=1, column=1, padx=10, pady=2)
button = Button(master, text='start script', command=lambda: call_report())
button.grid(row=6, column=1, sticky=W, pady=10)
master.mainloop()
I've changed text to textvariable in Label, and I added master.update(). In this way it force the GUI to redraw. Just to test if the GUI was changing, I tested with sleep. It is possible to update more time (for example before calling a function).

Python Tkinter grid spacing of widgets and LablelFrames not right

I am designing a simple GUI in Python 2.7 Tkinter, but I can't get things to spread out as I want them. I have managed to get my various widgets roughly where I want them, however I can't seem to force spacing out and things are a little bunched up.
I have also tried to draw 3 LabelFrames to separate the window out, but widgets seem to fall over the LabelFrames. I am wondering how I can space this out a little better. The grid system seems to allow things to bunch up and ignores blank rows and columns as far as I can see.
from Tkinter import *
import Tkinter, Tkconstants, tkFileDialog, tkMessageBox
class FileZap():
def __init__(self, root):
root.title("TestGUI")
root.geometry("860x450")
self.topFrame = LabelFrame(root, text="Top Area")
self.topFrame.grid(row=1, column=1, rowspan=6, columnspan=7, padx=5, pady = 5, sticky="NSEW")
self.listbox1 = Listbox(root, width=50, selectmode="multiple")
self.listbox1.grid(row=3, column=2)
self.scrollbar = Scrollbar(orient=VERTICAL, command=self.listbox1.yview)
self.listbox1.config(yscrollcommand=self.scrollbar.set)
self.scrollbar.grid(row=3, column=3, sticky="ns")
self.listbox2 = Listbox(root, width=50)
self.listbox2.grid(row=3, column=4)
self.selectLabel = Label(root, text="Select a folder: ")
self.selectLabel.grid(row=3, column=1)
self.user1 = Entry(root, width="50")
self.user1.grid(row=2, column=2)
self.browse = Button(root, text="Browse")
self.browse.grid(row=2, column=3)
self.addItems = Button(root, text="Add to Selection")
self.addItems.grid(row=4, column=2)
self.clearItems = Button(root, text="Clear Selection")
self.clearItems.grid(row=4, column=4)
self.leftFrame = LabelFrame(root, text="Left Area")
self.leftFrame.grid(row=5, column=1, rowspan=6, columnspan=3, padx=5, pady = 5, sticky="NSEW")
self.replaceInLable = Label(root, text="String to replace: ")
self.replaceOutLable = Label(root, text="New string: ")
self.replaceInLable.grid(row=7, column=1)
self.replaceOutLable.grid(row=7, column=2)
self.replaceIn = Entry(root, width="20")
self.replaceOut = Entry(root, width="20")
self.replaceIn.grid(row=8, column=1)
self.replaceOut.grid(row=8, column=2)
self.replace = Button(root, text="Replace")
self.replace.grid(row=8,column=3)
self.rightFrame = LabelFrame(root, text="Right Area")
self.rightFrame.grid(row=5, column=4, rowspan=6, columnspan=3, padx=5, pady = 5, sticky="NSEW")
self.quit = Button(root, text="Exit", command=root.quit)
self.quit.grid(row=9, column=6)
root = Tkinter.Tk()
file_zap = FileZap(root)
root.mainloop()
I have tried various alterations but can't nail it! Any help would be much appreciated.
First, the columns / row adapt to there content so an empty one as a zero height/width. If you want to put space between your widgets use the padx and pady options in the .grid method. They can take either one number which will give the padding on both sides or a couple of numbers giving the padding on each side.
Secondly, if you want your widgets to be inside a LabelFrame, you need to create them with this LabelFrame as master instead of the main window.
from Tkinter import LabelFrame, Tk, Button, Label
root = Tk()
# make row 0 resize with the window
root.rowconfigure(0, weight=1)
# make column 0 and 1 resize with the window
root.columnconfigure(0, weight=1)
root.columnconfigure(1, weight=1)
# create LabelFrames
top_frame = LabelFrame(root, text="top")
left_frame = LabelFrame(root, text="left")
right_frame = LabelFrame(root, text="right")
top_frame.grid(row=0, column=0, columnspan=2, padx=10, pady=(10,4), sticky="nsew")
left_frame.grid(row=1, column=0, padx=(10,4), pady=4, sticky="nsew")
right_frame.grid(row=1, column=1, padx=(4,10), pady=4, sticky="nsew")
#create widgets inside top_frame
Label(top_frame, text="I'm inside top_frame").pack()
Button(top_frame, text="Top").pack()
#create widgets inside left_frame
Label(left_frame, text="I'm inside left_frame").pack()
Button(left_frame, text="Left").pack()
#create widgets inside top_frame
Label(right_frame, text="I'm inside right_frame").pack()
Button(right_frame, text="Right").pack()
Button(root, text="Quit", command=root.destroy).grid(row=2, column=0,
columnspan=2, pady=10)
root.mainloop()

Categories