make tkinter window shrink, as widgets are deleted - python

I'm making some program, where I input a bunch of stuff into an entry and it gets printed into a row. I also added a feature where you can delete a row. However, when I delete a row, the window does not shrink. The way I actually made the program was by having 2 frames; the main frame with the buttons and entries, and the output or text frame. When I delete a row, it actually appends the data from a list, deletes the frame and all the widgets and reprints the rows, but with out the row I deleted.
The issue with my code, is that when I delete a row, the rows that weren't deleted start to get smaller and compress and secondly, the bottom of the window doesn't move upwards, leaving a blank white space.
Any help would be appreciated, thanks.
actually appending, labelling and printing the row is in function append_entry() and my delete function is delete_row()
from tkinter import *
global main_window
def quit():
main_window.destroy()
def entry_labels():
leader_label = Label(main_frame, text = 'Customer Name')
leader_label.grid(column=0, row=0)
location_label = Label(main_frame, text = 'Receipt Number')
location_label.grid(column=0, row=1)
numcampers_label = Label(main_frame, text = 'Item Hired')
numcampers_label.grid(column=0, row=2)
weather_label = Label(main_frame, text = 'Number Hired')
weather_label.grid(column=0, row=3)
row_label = Label(main_frame, text= 'Row')
row_label.grid(column=3, row=2)
def button():
print_button = Button(main_frame, text = "Print Details", command = append_entry)
print_button.grid(column=3, row=1)
quit_button = Button(main_frame, text= "Quit", command=quit)
quit_button.grid(column=4, row=0)
delete_row_button = Button(main_frame, text = 'Delete Row', command = delete_row)
delete_row_button.grid(column=4, row=3)
def entry():
global name_entry
name_entry = Entry(main_frame)
name_entry.grid(column=1, row=0)
global receipt_entry
receipt_entry = Entry(main_frame)
receipt_entry.grid(column=1, row=1)
global hired_entry
hired_entry = Entry(main_frame)
hired_entry.grid(column=1, row=2)
global num_hired_entry
num_hired_entry = Entry(main_frame)
num_hired_entry.grid(column=1, row=3)
global delete_row_entry
delete_row_entry = Entry(main_frame)
delete_row_entry.grid(column=4, row=2)
def table_headers():
row_header = Label(main_frame, text='Row', font = 'Arial 10 bold')
row_header.grid(column=0, row=4)
customer_header = Label(main_frame, text='Customer Name', font = 'Arial 10 bold')
customer_header.grid(column=1, row=4)
receipt_header = Label(main_frame, text='Receipt Number', font = 'Arial 10 bold')
receipt_header.grid(column=3, row=4)
item_header = Label(main_frame, text='Item Hired', font = 'Arial 10 bold')
item_header.grid(column=2, row=4)
num_header = Label(main_frame, text='Number Hired', font = 'Arial 10 bold')
num_header.grid(column=4, row=4)
def append_entry():
global second_frame
second_frame = Frame(main_window)
second_frame.grid(column=0, row=6)
leader_error_var.set("")
location_error_var.set("")
numcamper_error_var.set("")
weather_error_var.set("")
global name_count
name_count = 0
global ROWS_ABOVE
ROWS_ABOVE = 6
try:
name_entry_str = str(name_entry.get())
hired_entry_str = str(hired_entry.get())
receipt_entry_int = str(receipt_entry.get())
num_hired_entry_int = str(num_hired_entry.get())
if len(name_entry.get()) != 0:
input_data_col1.append([name_entry_str])
input_data_col2.append([hired_entry_str])
input_data_col3.append([receipt_entry_int])
input_data_col4.append([num_hired_entry_int])
counters['total_entries'] += 1
print(input_data_col1)
print(input_data_col2)
print(input_data_col3)
print(input_data_col4)
while name_count < counters ['total_entries']:
global name
name = Label(second_frame, text=(input_data_col1[name_count][-1])) ##using -1 selects the latest entry in the list
name.grid(column=1, row=name_count + ROWS_ABOVE, padx=50)
item = Label(second_frame, text=(input_data_col2[name_count][-1]))
item.grid(column=2, row=name_count + ROWS_ABOVE, padx=50)
row = Label(second_frame, text=name_count)
row.grid(column=0, row=name_count + ROWS_ABOVE, padx=60)
receipt = Label(second_frame, text=(input_data_col3[name_count][-1]))
receipt.grid(column=3, row=name_count + ROWS_ABOVE, padx=50)
num = Label(second_frame, text=(input_data_col4[name_count][-1]))
num.grid(column=4, row= name_count + ROWS_ABOVE, padx=50)
name_count += 1
name_entry.delete(0,END)
receipt_entry.delete(0,END)
hired_entry.delete(0,END)
num_hired_entry.delete(0,END)
except:
leader_error_var.set("Check inputs")
#location_error_var.set("please enter a valid num")
#numcamper_error_var.set("numcamper error test")
weather_error_var.set("")
name_entry.delete(0,END)
receipt_entry.delete(0,END)
hired_entry.delete(0,END)
num_hired_entry.delete(0,END)
def delete_row():
user_del =int(delete_row_entry.get())
counters['total_entries'] -= 1
input_data_col1.pop(user_del)
input_data_col2.pop(user_del)
input_data_col3.pop(user_del)
input_data_col4.pop(user_del)
data = [input_data_col1,input_data_col2,input_data_col3,input_data_col4]
for widget in second_frame.winfo_children():
widget.destroy()
append_entry()
print(input_data_col1)
print(input_data_col2)
print(input_data_col3)
print(input_data_col4)
def error_prevention():
#leader_error_var.set("leader error test")
#location_error_var.set("location error test")
#numcamper_error_var.set("numcamper error test")
#weather_error_var.set("weather error test")
#weather_error_var.set("_______________")
leader_error = Label(main_frame, textvariable = leader_error_var, fg = 'red')
leader_error.grid(column=2, row=0)
location_error = Label(main_frame, textvariable = location_error_var, fg = 'red')
location_error.grid(column=2, row=1)
numcamper_error = Label(main_frame, textvariable = numcamper_error_var, fg = 'red', width = 13)
numcamper_error.grid(column=2, row=2)
weather_error = Label(main_frame, textvariable = weather_error_var, fg = 'red')
weather_error.grid(column=2, row=3)
def main():
global main_window
main_window = Tk()
global input_data_col1
input_data_col1 = []
global input_data_col2
input_data_col2 = []
global input_data_col3
input_data_col3 = []
global input_data_col4
input_data_col4 = []
global input_data
input_data = []
global main_frame
main_frame = Frame(main_window)
main_frame.grid(row=0,column=0)
global counters
counters = {'total_entries':0, 'name_count':0}
#global number
#number = {'total_entries':0}
def stringvars():
global location_error_var
location_error_var = StringVar()
location_error_var.set("")
global numcamper_error_var
numcamper_error_var = StringVar()
numcamper_error_var.set("")
global leader_error_var
leader_error_var = StringVar()
leader_error_var.set("")
global weather_error_var
weather_error_var = StringVar()
leader_error_var.set("")
stringvars()
entry_labels()
entry()
error_prevention()
button()
table_headers()
main()
main_window.mainloop()

Under the code
for widget in second_frame.winfo_children():
widget.destroy()
add this block of code
second_frame.pack()
it will be like this
for widget in second_frame.winfo_children():
widget.destroy()
second_frame.pack()
I hope this helps you

You can use
main_window.geometry("1200x800+100+100")
to change size and position of main_window. Here the window width will be 1200 and height will be 800, positioned 100px to the top left corner of screen. The +100+100 is optional.
You may add geometry() to delete_row callback to resize the frame. Though you might have to calculate the proper size after widget deletion.
As to "when I delete a row, the rows that weren't deleted start to get smaller and compress",
That's due to how grid layout works. If there are two widgets on the same grid row, the grid height will be equal the the higher widget height. When the higher widget is removed, the grid will resize to the smaller widget height and appear to 'shrink'.
To solve that, you can try add padding to the widget, the syntax is
widget.grid(column=1,row=1,padx=(10,10),pady=(10,10))
Alternatively, you may try other layout management: .place will give you absolute layout control. Or you can use pack and let tkinter decide the proper position.

Related

ValueError: <tkinter.OptionMenu object .!optionmenu> is not in list

I am trying to print a corresponding value to the index of a list from another list like so:
print(safeDis[chem.index(self.drop2)])
but when doing this i get the above error. I believe i had this working in a previous iteration but i cannot find the one that was.
import tkinter as tk
from tkinter import ttk
safeDis = [4,88,18,50,12,100]
chem = ["HTP 50%","HTP 84%","HTP 90%","Kerosene","Benzene"]
class Page4:
def __init__(self,root):
self.root = root
self.toplbl = ttk.Label(root, text="Select firing point meterials ",font=("arial",12)) #lable for select meterial 1
self.lbl1 = ttk.Label(root, text="Meterial 1: ",font=("arial",10)) #lable for select meterial 1
self.lbl2 = ttk.Label(root, text = "Meterial 2: " ,font = ("arial",10)) #lable for meterial 2
self.masslbl = ttk.Label(root, text="Mass in Kg:",font=("arial",10))
self.masslbl2 = ttk.Label(root, text="Mass in Kg:",font=("arial",10))
self.typelbl = ttk.Label(root, text="Type:",font=("arial",10))
self.typelbl2 = ttk.Label(root, text="Type:",font=("arial",10))
self.Apply = ttk.Button(root, text="Apply", command = self.new_window) #button to confirm the meterial choices
self.Back = ttk.Button(root, text="Back", command = print("DONG"))
self.mass1 = ttk.Entry(root, width=8)
self.mass2 = tk.Entry(root,width=8)
self.clicked = tk.StringVar() #set the variable to a string value allowing the meterial names to apeer in it
self.clicked.set(chem[3]) #set the default meterial from the chem list
self.clicked2 = tk.StringVar()
self.clicked2.set(chem[3])
self.drop2 = tk.OptionMenu(root, self.clicked2, *chem) #setup the dropdown menue with optionmenue function set to the chem list
self.drop = tk.OptionMenu(root, self.clicked, *chem)
self.toplbl.grid(column=0, row=0,columnspan=3,sticky="w",padx=10,pady=10) #place meterial label 1
self.lbl1.grid(column=0, row=1,padx=10) #place meterial label 1
self.lbl2.grid(column=0, row=3,padx=10) #place meterial label 2
self.mass1.grid(column=2, row=2)
self.mass2.grid(column=2, row=4)
self.masslbl.grid(column=1, row=2)
self.masslbl2.grid(column=1, row=4)
self.typelbl.grid(column=1, row=1,sticky="w")
self.typelbl2.grid(column=1, row=3,sticky="w")
self.drop.grid(column=2, row=1) #place the dropdown menue
self.drop2.grid(column=2, row=3)
self.Apply.grid(column=2,row=5,pady=10,padx=10)
self.Back.grid(column=1,row=5,pady=10,padx=10)
print(safeDis[chem.index(self.drop2)])
def new_window(self):
#print(dongalong)
for widget in self.root.winfo_children():
widget.destroy()
self.app = Page3(self.root)
#class Page5:
def main():
root = tk.Tk()
app = Page4(root)
root.mainloop()
if __name__ == '__main__':
main()
The problem was that self.drop2 is an object of OptionMenu, not the value of it. To get the value returned by it, use the get() method on its variable defined (self.clicked2.get())
So it should be:
print(safeDis[chem.index(self.clicked2.get())])
Hope it solved the error, do let me know if any more doubts
Cheers

Creating and Getting values from tkinter check boxes using for loop

This code is a part of my project in which I have to manage the attendance of 50 (or more) students.
The thing I want is that all the checkboxes should initially be 'checked' (showing the present state) and when I uncheck random checkboxes (to mark the absent) and click the Submit button (yet to be created at the bottom of the window), I should get a list with 'entered date' as first element and the roll numbers i.e. 2018-MC-XX as other elements.
For example: ['01/08/2020', '2018-MC-7', '2018-MC-11', '2018-MC-23', '2018-MC-44']
Actually my plan is when I will get a list I will easily write it to a text file. Also, if there is another way of creating multiple scrollable checkboxes without packing them inside a canvas then please do tell!
from tkinter import *
from tkcalendar import DateEntry
root = Tk()
root.geometry('920x600+270+50')
root.minsize(920,600)
Attendance_frame = Frame(root) ### Consider it a Main Frame
Attendance_frame.pack()
attendaceBox = LabelFrame(Attendance_frame, text = 'Take Attendance', bd = 4, relief = GROOVE, labelanchor = 'n',font = 'Arial 10 bold', fg = 'navy blue', width = 850, height = 525) # A Label Frame inside the main frame
attendaceBox.pack_propagate(0)
attendaceBox.pack(pady = 15)
dateFrame = Frame(attendaceBox) # A small frame to accommodate date entry label & entry box
dateFrame.pack(anchor = 'w')
font = 'TkDefaultFont 10 bold'
date_label = Label(dateFrame, text = 'Enter Date : ', font = font).grid(row = 0, column = 0, sticky = 'w', padx = 10, pady = 10)
date_entry = DateEntry(dateFrame, date_pattern = 'dd/mm/yyyy', showweeknumbers = FALSE, showothermonthdays = FALSE)
date_entry.grid(row = 0, column = 1, sticky = 'w')
noteLabel = Label(attendaceBox, text = 'Note: Uncheck the boxes for absentees').pack(anchor = 'w', padx = 10, pady = 5)
canvas = Canvas(attendaceBox, borderwidth=0, background="#ffffff")
checkFrame = Frame(canvas, width = 100, height = 50)
vsb = Scrollbar(canvas, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.pack_propagate(0)
canvas.create_window((4,4), window=checkFrame, anchor="nw")
def onFrameConfigure(canvas):
'''Reset the scroll region to encompass the inner frame'''
canvas.configure(scrollregion=canvas.bbox("all"))
checkFrame.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas))
for i in range(0,51): # A loop to create Labels of students roll numbers & names
c = Checkbutton(checkFrame, text = f"{'2018-MC-'+str(i+1)} Student {i+1}")
c.grid(row = i, column = 0, padx = 10, sticky = 'w')
mainloop()
First you need StringVar for each Checkbutton in order to get the state of the Checkbuttons later. Then you can use a list to hold the StringVars so that you can access them later. You can set the onvalue of the checkbuttons to the roll number associated to them.
Also you can use Text widget instead of Canvas+Frame. Below is an example:
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
text = tk.Text(frame, width=40, height=20)
text.pack(side=tk.LEFT, fill=tk.BOTH)
vars = []
for i in range(51):
rollnum = '2018-MC-'+str(i+1)
var = tk.StringVar(value=rollnum)
cb = tk.Checkbutton(text, text=rollnum, variable=var, onvalue=rollnum, offvalue='', bg='white')
text.window_create('end', window=cb)
text.insert('end', '\n')
vars.append(var)
vsb = tk.Scrollbar(frame, orient=tk.VERTICAL, command=text.yview)
vsb.pack(side=tk.RIGHT, fill=tk.Y)
text.config(yscrollcommand=vsb.set)
def submit():
# extract roll numbers for checked checkbuttons
result = [var.get() for var in vars if var.get()]
print(result)
tk.Button(root, text='Submit', command=submit).pack()
root.mainloop()

Tkinter dynamically change a widget position with grid geometry manager

I am currently working on a program where I need to change the position of a widget.
Here is my code:
root = Tk()
frame = Frame(root, bg="black")
label1 = Label(frame, text="not important")
label.grid(row=1, column=1)
#some stuff happens
x = aFunctionThatReturnsXCoordinate #not actual name
y = aFunctionThatReturnsYCoordinate
Now I want to change the position of label1 to the new coordinates.
I have tried:
label1.forget_grid()
label1.grid(rows=x, columns=y)
#Ive tried label1/frame.update after this code but it didnt do anything
I've also tried to do it without the grid_forget.
I didn't forget the frame.pack and root.mainloop.
Is there a function to change the position?
I have made a code which contains a label and a button. When the button is pressed , the row of the label changes from 0 to 1 and when pressed again row is changes from 1 to 0 and so on. To make difference between the frame and the root, I have set the color of the frame to sky blue and that of root to pink.
from tkinter import *
global rw;rw = 0
def changed():
global rw;rw += 1;
if rw == 2:rw=0
label.grid(row=rw,column=0)
root = Tk()
root.config(bg="pink")
frame = Frame(root, bg="sky blue")
frame.pack()
label = Label(frame,text="Hello")
label.grid(row=0,column=0)
b = Button(frame, text='Press me!', command=changed)
b.grid(row=0, column=1)
root.mainloop()
Well, the above code is a bit complicated so , if you want a simpler way to do the same:
from tkinter import *
def changed():
size = label.grid_info().get("row") #getting current row
if size == 0:size = 1
elif size == 1:size = 0
label.grid(row=size,column=0)
root = Tk()
root.config(bg="pink")
frame = Frame(root, bg="sky blue")
frame.pack()
label = Label(frame,text="Hello")
label.grid(row=0,column=0)
b = Button(frame, text='Press me!', command=changed)
b.grid(row=0, column=1)
root.mainloop()
Here is an animation:
Here is an example that toggles the position of a label from column 0 to column 1:
You have to keep in mind that unoccupied rows or columns are sized at zero pixels by the grid geometry manager.
pressing the button makes the label in row 1 swap places from column 0 to 1 and vice versa.
import tkinter as tk
def move_label(ndx=[0]):
# the 3 next toggle the column value
rowcol = [(1, 1), (1, 0)]
x, y = rowcol[ndx[0]]
ndx[0] = (ndx[0] + 1) % 2
label_at_1_0.grid(row=x, column=y)
root = tk.Tk()
frame = tk.Frame(root, bg="black")
frame.pack()
label_at_0_0 = tk.Label(frame, text="label_at_0_0")
label_at_0_0.grid(row=0, column=0)
label_at_0_1 = tk.Label(frame, text="not label_at_0_1")
label_at_0_1.grid(row=0, column=1)
label_at_1_0 = tk.Label(frame, text="label_at_1_0")
label_at_1_0.grid(row=1, column=0)
btn_to_move_label = tk.Button(frame, text='move label', command=move_label)
btn_to_move_label.grid(row=1, column=3)
root.mainloop()

How to store values from an Entry widget for loop in tkinter?

This should be a very very simple problem. I'm making a GUI in which I have multiple entry widgets... about 30 or so all in one column. Instead of making each box one by one it seems like a better idea to just generate the widgets with a loop. However, I'm finding it extremely difficult to .get() values from the entry widgets, and convert them into floats. This is what I have so far... any help would be greatly appreciated.
class Application(Frame):
def __init__(root,master):
Frame.__init__(root,master)
root.grid()
root.create_widgets()
def calcCR(root):
d1 = root.enter.get()
d1 = float(d1)
#root.answer.delete(0.0,END)
a = 'The C/R Alpha is! %lf \n' % (d1)
root.answer.insert(0.0, a)
def create_widgets(root):
### Generate Element List ###
for i in range(len(elem)):
Label(root, text=elem[i]).grid(row=i+1, column=0)
### Generate entry boxes for element wt% ###
for i in range(len(elem)):
enter = Entry(root, width = 8)
enter.grid(row = i+1,column=1)
enter.insert(0,'0.00')
root.button = Button(root, text = 'Calculate C/R', command = root.calcCR)
root.button.grid(row=11, column=2, sticky = W, padx = 10)
root.answer = Text(root, width = 50, height = 12.5, wrap = WORD)
root.answer.grid(row=1, column=2, rowspan = 10, sticky = W, padx = 10)
root = Tk()
root.title('C/R Calculator')
app = Application(root)
root.mainloop()
Put the Entry instances into a list.
from tkinter import Tk, Frame, Label, Entry, Button
class App(Frame):
def __init__(root, master):
Frame.__init__(root, master)
root.grid()
root.create_widgets()
def get_values(root):
return [float(entry.get()) for entry in root.entries]
def calc_CR(root):
answer = sum(root.get_values()) #Replace with your own calculations
root.answer.config(text=str(answer))
def create_widgets(root):
root.entries = []
for i in range(20):
label = Label(root, text=str(i))
label.grid(row=i, column=0)
entry = Entry(root, width=8)
entry.grid(row=i, column=1)
entry.insert(0, '0.00')
root.entries.append(entry)
root.calc_button = Button(root, text='Calculate C/R', command=root.calc_CR)
root.calc_button.grid(row=20, column=0)
root.answer = Label(root, text='0')
root.answer.grid(row=20, column=1)
def run(root):
root.mainloop()
root = Tk()
root.title('C/R Calculator')
app = App(root)
app.run()

Unable to achieve wanted entry textBox alignment

HI i am trying to place the alignment of my Entry(Text Box) according to the image below. I tried everything, TOP BOTTOM LEFT RIGHT.
This is the alignment i want. Obtained from paint program*
Problem
Coding of tkinter
master = Tk.Tk() # Open up GUI connection
master.title('Program Application')
print "VSM activated input range first (X2 must be larger than X1)"
#Declare button and respective method
button = Tk.Button(text='VSM', command=VSM, fg="red")
button.config( height = 10, width = 80 )
button.pack() #pack is needed to display the button
bluebutton = Tk.Button(text="AGM Folder",command= lambda: Folder(0), fg="blue").pack(side = LEFT)
bluebutton = Tk.Button(text="VSM Folder",command= lambda: Folder(1), fg="blue").pack(side = RIGHT)
Label(text='Correct Range for Gradient\nX2 X1').pack(side=TOP,padx=10,pady=10)
entryX2 = Entry(master, width=10)
entryX2.pack(side=LEFT,padx=10,pady=10)
entryX1 = Entry(master,width=10)
entryX1.pack(side=RIGHT,padx=10,pady=10)
buttonGradient = Tk.Button(text='Input Range OP',command= lambda: Folder(2), fg="red").pack()
entryX2IP = Entry(master, width=10)
entryX2IP.pack(side=LEFT,padx=10,pady=10)
entryX1IP = Entry(master,width=10)
entryX1IP.pack(side=RIGHT,padx=10,pady=10)
btnGradientIP = Tk.Button(text='Input Range IP',command= lambda: Folder(2), fg="red").pack(side = TOP)
master.mainloop() # Continue loop till user close tab
Use Frame:
master = Tk.Tk() # Open up GUI connection
master.title('Program Application')
print "VSM activated input range first (X2 must be larger than X1)"
#Declare button and respective method
button = Tk.Button(text='VSM', command=VSM, fg="red")
button.config( height = 10, width = 80 )
button.pack() #pack is needed to display the button
bluebutton = Tk.Button(text="AGM Folder",command= lambda: Folder(0), fg="blue").pack(side = LEFT)
bluebutton = Tk.Button(text="VSM Folder",command= lambda: Folder(1), fg="blue").pack(side = RIGHT)
Label(text='Correct Range for Gradient\nX2 X1').pack(side=TOP,padx=10,pady=10)
################### Use 3 frames to contains entries, buttons.
frameX2 = Frame(master)
frameX2.pack(side=LEFT, expand=1, anchor=E)
entryX2 = Entry(frameX2, width=10)
entryX2.pack(side=TOP,padx=10,pady=10)
entryX2IP = Entry(frameX2, width=10)
entryX2IP.pack(side=TOP,padx=10,pady=10)
frameButton = Frame(master)
frameButton.pack(side=LEFT)
Tk.Button(frameButton, text='Input Range OP',command= lambda: Folder(2), fg="red").pack(padx=10, pady=10)
Tk.Button(frameButton, text='Input Range IP',command= lambda: Folder(2), fg="red").pack(padx=10, pady=10)
frameX1 = Frame(master)
frameX1.pack(side=LEFT, expand=1, anchor=W)
entryX1 = Entry(frameX1,width=10)
entryX1.pack(side=TOP,padx=10,pady=10)
entryX1IP = Entry(frameX1,width=10)
entryX1IP.pack(side=TOP,padx=10,pady=10)
#####################
master.mainloop() # Continue loop till user close tab

Categories