How To Remove Old Widgets - python

Teaching myself python GUI building. Have a simple die roller program, which let's you pick the number and sides of dice. It uses a for loop to generate a series of widgets that show the individual rolls and the total of every roll.
If I increase the number of dice, I get more widgets.
If I decrease the number of dice, I get fewer widgets.
My problem is that while more dice overwrites the old widgets, fewer entries don't kill the older ones.
Where should I be putting something like widget.grid_remove() or how do I go about redrawing everything each time someone presses the button?
# Get the numbers off the spinboxen:
def rollDice():
size=int(spin_die.get())
dice=int(spin_number.get())
total=0
for i in range(0, dice):
roll=random.randint(1, size)
total+=roll
rollNumber=i+1
lbl_roll = tk.Label(app, text="Roll " + str(rollNumber) + ": " + str(roll), font=("Source Code Pro Med ium", 12))
roll_grid = i+3
lbl_roll.grid(column=0, row=roll_grid)
lbl_total = tk.Label(app, text="Total: " + str(total), font=("Source Code Pro Medium", 12))
lbl_total.grid(column=0, row=roll_grid+1)
# Create window
window = tk.Tk()
window.title("Die Roller")
app = tk.Frame(window)
app.grid()
# create text labels for spinboxen
lbl_die = tk.Label(app, text="Size of Die: ", font=("Source Code Pro Medium", 12))
lbl_die.grid(column=0, row=0)
lbl_number = tk.Label(app, text="Number to roll: ", font=("Source Code Pro Medium", 12))
lbl_number.grid(column=0, row=1)
# Adjacent to each label, create a 1-100 spinbox
defaultDie = tk.StringVar()
defaultDie.set(6)
spin_die = tk.Spinbox(app, from_=1, to=100, width=4, textvariable=defaultDie)
spin_die.grid(column=1, row=0)
defaultNumber = tk.StringVar()
defaultNumber.set(2)
spin_number = tk.Spinbox(app, from_=1, to=100, width=4, textvariable=defaultNumber)
spin_number.grid(column=1, row=1)
# Create a button to start the action!
btn_number = tk.Button(app, text="Roll Dice", command=rollDice)
btn_number.grid(column=0, row=2)
window.mainloop()

Keep all generated widgets on global list so you will have access to them when you will remove them before you create new onces.
import tkinter as tk
import random
# Get the numbers off the spinboxen:
def rollDice():
global all_labels # inform function to use external/global variable instead of local one when you do `= []`
# destroy old widgets
for item in all_labels:
item.destroy()
# create new empty list for new widgets
all_labels = []
size = int(spin_die.get())
dice = int(spin_number.get())
total = 0
for i in range(0, dice):
roll = random.randint(1, size)
total += roll
rollNumber = i+1
lbl_roll = tk.Label(app, text="Roll " + str(rollNumber) + ": " + str(roll), font=("Source Code Pro Med ium", 12))
roll_grid = i+3
lbl_roll.grid(column=0, row=roll_grid)
all_labels.append(lbl_roll) # append
lbl_total = tk.Label(app, text="Total: " + str(total), font=("Source Code Pro Medium", 12))
lbl_total.grid(column=0, row=roll_grid+1)
all_labels.append(lbl_total) # append
# --- main ---
# empty list at start
all_labels = []
# Create window
window = tk.Tk()
window.title("Die Roller")
app = tk.Frame(window)
app.grid()
# create text labels for spinboxen
lbl_die = tk.Label(app, text="Size of Die: ", font=("Source Code Pro Medium", 12))
lbl_die.grid(column=0, row=0)
lbl_number = tk.Label(app, text="Number to roll: ", font=("Source Code Pro Medium", 12))
lbl_number.grid(column=0, row=1)
# Adjacent to each label, create a 1-100 spinbox
defaultDie = tk.StringVar()
defaultDie.set(6)
spin_die = tk.Spinbox(app, from_=1, to=100, width=4, textvariable=defaultDie)
spin_die.grid(column=1, row=0)
defaultNumber = tk.StringVar()
defaultNumber.set(2)
spin_number = tk.Spinbox(app, from_=1, to=100, width=4, textvariable=defaultNumber)
spin_number.grid(column=1, row=1)
# Create a button to start the action!
btn_number = tk.Button(app, text="Roll Dice", command=rollDice)
btn_number.grid(column=0, row=2)
window.mainloop()
grid_remove() removes only from grid but not from memory so you could show them again but I don't want to use them again so I use destroy() to remove them from memory and automatically they are removed from grid.

Related

Tkinter displaying output in a entry box using when using nested classes

So the problem is that when using the second window (which you navigate to using the next page button). When I enter the 8-bit binary numbers in each box the premise is that it should add them together and display them in the labeled added binary box. but at the moment it does not seem to set the value correctly to do that and it just prints in the command line what I set it to which shows that the method is working.
Any insight into how to fix this would be greatly appreciated.
FYI yes it is deliberately done for 8-bit binary and yes it needs to use nested classes. as that is what the assignment requires the use of.
sorry if it is just a small error and I am being clumsy somewhere but I have been stumped for some time.
Here is the revised minimal reproducible example:
from tkinter import *
from tkinter.ttk import *
from tkinter import ttk
import tkinter
import math
import sys
def BinaryAddition(value1, value2):
if len(value1) != 8:
return "Binary number 1 is not 8 bits"
if len(value2) != 8:
return "Binary number 2 is not 8 bits"
res = bin(int(value1,2) + int(value2,2))
Ob = res[2:]#removing the ob prefix
return Ob
class MainWindow():
FONT = ("Consolas", 16)
TxtMaxLen = 32
class SecondWindow():
def __init__(self2):
self2._window = tkinter.Tk()
self2._window.title("Converter")
self2._window["bg"] = "#EFFFA3"
f = ("Times bold", 16)
TxtMaxLen = 32
self2._window.geometry("820x240")
label = tkinter.Label(self2._window, text="Binary Number 1: ", font= f)#label defined for number input box
label.grid(row=0, column=0, padx=10, pady=5)#positioning / dimensions of input box
label = tkinter.Label(self2._window, text="Binary Number 2: ", font= f)#label defined for number input box
label.grid(row=1, column=0, padx=10, pady=5)#positioning / dimensions of input box
#input box for binary number 1
self2.input1 = tkinter.Entry(self2._window, width= TxtMaxLen, font= f) #defined input box
self2.input1.grid(row=0, column=1, pady=5)#postioning / dimensions of input box
self2.input1.focus()
#input box for binary number 2
self2.input2 = tkinter.Entry(self2._window, width=TxtMaxLen, font= f) #defined input box
self2.input2.grid(row=1, column=1, pady=5)#postioning / dimensions of input box
self2.input2.focus()
separator = tkinter.ttk.Separator(self2._window,orient=tkinter.HORIZONTAL)
separator.grid(row=3, column=1, pady=15)
self2._bt_Add = tkinter.Button(self2._window, text="Add", font= f, command = self2.AdditionSelection)#button defined for bin
self2._bt_Add.grid(row=1, column=2, padx=5, pady=5)#postioning / dimensions of button box
#labels for output box of combined binary number
label = tkinter.Label(self2._window, text="Added Binary: ", font= f)#label defined for number input box
label.grid(row=4, column=0, padx=10, pady=5)#positioning / dimensions of input box
#label for output box for if there was or was not an overflow
label = tkinter.Label(self2._window, text="OverFlow: ", font= f)#label defined for number input box
label.grid(row=5, column=0, padx=10, pady=5)#positioning / dimensions of input box
#output box for the added binary number
self2.stringvar_Combined = tkinter.StringVar()
txt_output = tkinter.Entry(self2._window, textvariable=self2.stringvar_Combined, width=TxtMaxLen, state="readonly", font= f)#entry box set to readonly to act as a display box
txt_output.grid(row=4, column=1, pady=5)
#output box for if there was or was not an overflow
self2._stringvar_OverFlow = tkinter.StringVar()
txt_output = tkinter.Entry(self2._window, textvariable=self2._stringvar_OverFlow, width=TxtMaxLen, state="readonly", font= f)#entry box set to readonly to act as a display box
txt_output.grid(row=5, column=1, pady=5)
separator = tkinter.ttk.Separator(self2._window,orient=tkinter.VERTICAL)
separator.grid(row=3, column=2, pady=15)
PrevPageButton = tkinter.Button(self2._window, text="Previous Page", font=f, command=MainWindow)
PrevPageButton.grid(row=5, column = 2, padx = 5,pady = 5)
def set_values(self, BinAdd):
self.stringvar_Combined.set(BinAdd)
def AdditionSelection(self2):
try:
BinV1 = self2.input1.get().strip().replace(" ", "")
BinV2 = self2.input2.get().strip().replace(" ", "")
BinAdd = BinaryAddition(BinV1, BinV2)
self2.set_values(BinAdd)
print(BinAdd)
print("hi there")
except Exception as ex:
print("NOPE it aint workin")
def __init__(self):
self._window = tkinter.Tk()
self._window.title("Converter")
self._window["bg"] = "#20A3FF" #background colour
self._window.geometry("820x240")#setting window size
#setting up button for binary additon page
NextPageButton = tkinter.Button(self._window, text="Next Page", font=MainWindow.FONT, command=self.SecondWindow)
NextPageButton.grid(row=4, column = 3, padx = 5,pady = 5)
self._window.destroy
def mainloop(self):
self._window.mainloop()
if __name__ == "__main__":
win = MainWindow()
win.mainloop()
Main problem: to create second window you should use Toplevel() instead of Tk().
And when I use Toplevel then it show text in window.
And if you want to see only one window then you should first destroy() old window and later you can use Tk() to create new window (with new mainloop()).
But this can make other problem. It delete all values in window and when you will back some window then it will not have old value. It may be better to create two Frames and replace them in Tk() without destroying them - and they will keep old values.
Rest are only suggestions how to create more readable code:
don't put SecondWindow inside MainWindow
don't use self2
use lower_case_names for functions and variables
More in PEP 8 -- Style Guide for Python Code
Code with some changes.
It display value (I also added .zfill(8) to display 8 digits)
But this creates new window everytime when you change window - next or previous - so you may have two main windows, etc. It would need more changes.
import tkinter # PEP8: `import *` is not preferred
from tkinter import ttk
import math
import sys
def binary_addition(value1, value2):
if len(value1) != 8:
return "Binary number 1 is not 8 bits"
if len(value2) != 8:
return "Binary number 2 is not 8 bits"
res = bin(int(value1,2) + int(value2,2))
return res[2:].zfill(8) #removing the ob prefix
class MainWindow():
FONT = ("Consolas", 16)
TxtMaxLen = 32
def __init__(self):
self._window = tkinter.Tk()
self._window.title("Converter")
self._window["bg"] = "#20A3FF" #background colour
self._window.geometry("820x240")#setting window size
#setting up button for binary additon page
next_page_button = tkinter.Button(self._window, text="Next Page", font=MainWindow.FONT, command=SecondWindow)
next_page_button.grid(row=4, column = 3, padx = 5,pady = 5)
def mainloop(self):
self._window.mainloop()
class SecondWindow():
def __init__(self):
self._window = tkinter.Toplevel()
self._window.title("Converter")
self._window["bg"] = "#EFFFA3"
f = ("Times bold", 16)
TxtMaxLen = 32
self._window.geometry("820x240")
label = tkinter.Label(self._window, text="Binary Number 1: ", font= f)#label defined for number input box
label.grid(row=0, column=0, padx=10, pady=5)#positioning / dimensions of input box
label = tkinter.Label(self._window, text="Binary Number 2: ", font= f)#label defined for number input box
label.grid(row=1, column=0, padx=10, pady=5)#positioning / dimensions of input box
#input box for binary number 1
self.input1 = tkinter.Entry(self._window, width= TxtMaxLen, font= f) #defined input box
self.input1.grid(row=0, column=1, pady=5)#postioning / dimensions of input box
self.input1.focus()
#input box for binary number 2
self.input2 = tkinter.Entry(self._window, width=TxtMaxLen, font= f) #defined input box
self.input2.grid(row=1, column=1, pady=5)#postioning / dimensions of input box
self.input2.focus()
separator = tkinter.ttk.Separator(self._window,orient=tkinter.HORIZONTAL)
separator.grid(row=3, column=1, pady=15)
self._bt_add = tkinter.Button(self._window, text="Add", font= f, command = self.addition_selection)#button defined for bin
self._bt_add.grid(row=1, column=2, padx=5, pady=5)#postioning / dimensions of button box
#labels for output box of combined binary number
label = tkinter.Label(self._window, text="Added Binary: ", font= f)#label defined for number input box
label.grid(row=4, column=0, padx=10, pady=5)#positioning / dimensions of input box
#label for output box for if there was or was not an overflow
label = tkinter.Label(self._window, text="OverFlow: ", font= f)#label defined for number input box
label.grid(row=5, column=0, padx=10, pady=5)#positioning / dimensions of input box
#output box for the added binary number
self.stringvar_combined = tkinter.StringVar()
txt_output = tkinter.Entry(self._window, textvariable=self.stringvar_combined, width=TxtMaxLen, state="readonly", font= f)#entry box set to readonly to act as a display box
txt_output.grid(row=4, column=1, pady=5)
#output box for if there was or was not an overflow
self._stringvar_overflow = tkinter.StringVar()
txt_output = tkinter.Entry(self._window, textvariable=self._stringvar_overflow, width=TxtMaxLen, state="readonly", font= f)#entry box set to readonly to act as a display box
txt_output.grid(row=5, column=1, pady=5)
separator = tkinter.ttk.Separator(self._window,orient=tkinter.VERTICAL)
separator.grid(row=3, column=2, pady=15)
prev_page_button = tkinter.Button(self._window, text="Previous Page", font=f, command=MainWindow)
prev_page_button.grid(row=5, column = 2, padx = 5,pady = 5)
def set_values(self, value):
self.stringvar_combined.set(value)
def addition_selection(self):
try:
bin_1 = self.input1.get().strip().replace(" ", "")
bin_2 = self.input2.get().strip().replace(" ", "")
bin_add = binary_addition(bin_1, bin_2)
self.set_values(bin_add)
print("bin_add:", bin_add)
except Exception as ex:
print("NOPE it aint workin", ex)
if __name__ == "__main__":
win = MainWindow()
win.mainloop()

How to insert values created by a loop function to tkinter text boxes which are also created automatically with a loop function

Hi I'm trying to write a code that calculates the net present values of the installments. The code runs perfect up to a point where I can't insert the calculated values in to the tkinter text boxes individually.
For example the code is asking the yearly interest rate and the input can be 15. The list price of an house can be 1000000 and the down payment can be 0.2 which is %20 of the list price. And we can input 12 as the number of installments.
Down payment will be calculated by multiplying 1000000 with 0.2 and gives 200000. And the execution will be inserted to the first tkinter text box. But when the calculation done for the net present values of the installments, all of them appear inside last tkinter text box rather than appearing individually.
Here is the code;
# This program is to create a payment schedule for the customers and check whether
# the payment plan is suitable for the seller in terms of net present value.
from tkinter import *
window=Tk()
# This part asks the number of installments and creates the desired amount of text boxes.
def create():
for y in range(number_of_installments.get()):
t11 = Label(window,text= '%s.Installment'%(y+1),foreground="green")
t11.grid(row = y+2,column=0)
for y in range(number_of_installments.get()):
ttt = Text(window, height=1, width=20, foreground="red")
ttt.grid(row = y+2,column=1)
#Net present values of the installments are calculated in this part.
def NPV_calculation():
interest_rate = [pow(1+0.0125,i) for i in range(1,int(number_of_installments.get())+1)]
installment_amount = (float(list_price.get())-(float(list_price.get())*float(downpayment_percentage.get())))/(float(number_of_installments.get()))
calculation = [installment_amount/i for i in interest_rate]
ttt.insert(END,calculation)
downpayment_calculation()
NPV_calculation()
#Downpayment calculated by entering a value between 0 to 1. For example if 0.2 is typed in the textbox it will multiply the list price by %20.
def downpayment_calculation():
Downpayment = float(list_price.get())*float(downpayment_percentage.get())
t0.insert(END,Downpayment)
#Entry boxes
number_of_installments = IntVar(value="")
installment = Entry(window, textvariable=number_of_installments)
installment.grid(row=3,column=3)
interest_value = StringVar()
interest = Entry(window, textvariable=interest_value)
interest.grid(row=0,column=3)
list_price = StringVar()
list = Entry(window, textvariable=list_price)
list.grid(row=1,column=3)
downpayment_percentage = StringVar()
downpayment = Entry(window, textvariable=downpayment_percentage)
downpayment.grid(row=2,column=3)
#Create button for creating number of grids based on the number of installments.
b1 = Button(window, text="Create", command=create)
b1.grid(row=0,column=4)
#Labels for the enrty boxes
n_installments = Label(window,text="Number Of Installments",foreground="blue")
n_installments.grid(row=3,column=2)
yearly_interest = Label(window,text="Yearly Interest",foreground="blue")
yearly_interest.grid(row=0,column=2)
price_list = Label(window,text="List Price",foreground="blue")
price_list.grid(row=1,column=2)
per_downpayment = Label(window,text="Downpayment Percentage",foreground="blue")
per_downpayment.grid(row=2,column=2)
#Label and text box for the downpayment
t0 = Label(window,text="Downpayment",foreground="green")
t0.grid(row=1,column=0)
t0 = Text(window, height=1, width=20, foreground="red")
t0.grid(row=1,column=1)
window.mainloop()
When you call the NPV_calculation function, ttt is the last text widget.
# This part asks the number of installments and creates the desired amount of text boxes.
def create():
for y in range(number_of_installments.get()):
t11 = Label(window,text= '%s.Installment'%(y+1),foreground="green")
t11.grid(row = y+2,column=0)
for y in range(number_of_installments.get()):
ttt = Text(window, height=1, width=20, foreground="red")
ttt.grid(row = y+2,column=1)
print("the object named ttt in this loop iteration is:", ttt)
#Net present values of the installments are calculated in this part.
def NPV_calculation():
interest_rate = [pow(1+0.0125,i) for i in range(1,int(number_of_installments.get())+1)]
installment_amount = (float(list_price.get())-(float(list_price.get())*float(downpayment_percentage.get())))/(float(number_of_installments.get()))
calculation = [installment_amount/i for i in interest_rate]
ttt.insert(END,calculation)
print("the object named ttt is:", ttt)
downpayment_calculation()
NPV_calculation() # <- when you call this function, ttt is the last Text widget
Output for number_of_installments = 3:
the object named ttt in this loop iteration is: .!text2
the object named ttt in this loop iteration is: .!text3
the object named ttt in this loop iteration is: .!text4
the object named ttt is: .!text4
You need to insert values when creating a text widget in a loop. It is better to move the NPV_calculation function out of the loop.
This is a simplified example for the part where the values are generated automatically.
import tkinter as tk
window = tk.Tk()
def do_n_calculation(n):
calculation = [x+1 for x in range(n)]
return calculation
def create():
# get new values
n = number_of_installments.get()
calculation_list = do_n_calculation(n)
# clear previous values
for i in installment_frame.winfo_children():
i.destroy()
# insert new values
for y in range(n):
t11 = tk.Label(installment_frame, text= '%s.Installment'%(y+1), foreground="green")
t11.grid(row=y, column=0)
for y in range(n):
ttt = tk.Text(installment_frame, height=1, width=20, foreground="red")
ttt.grid(row=y, column=1)
ttt.insert(tk.END, calculation_list[y])
label = tk.Label(window, text="Number Of Installments")
label.grid(row=0, column=0)
number_of_installments = tk.IntVar(value="")
entry = tk.Entry(window, textvariable=number_of_installments)
entry.grid(row=0, column=1)
button = tk.Button(window, text="Create", command=create)
button.grid(row=0, column=2)
installment_frame = tk.Frame(window)
installment_frame.grid(row=1, column=0)
window.mainloop()
Complete example using the class.
import tkinter as tk
class Application(tk.Frame):
"""
| column 0 | column 1 |
-----------------------------------------
row 0 | downpayment_frame | form_frame |
-----------------------------------------
row 1 | installment_frame | |
-----------------------------------------
"""
def __init__(self, master):
super().__init__(master)
# data attributes
self.downpayment_value = None
self.calculation_list = None
# left side
# Label and text box for the downpayment
self.downpayment_frame = tk.Frame(self)
self.downpayment_frame.grid(row=0, column=0, padx=5, pady=5)
self.downpayment_label = tk.Label(self.downpayment_frame, text="Downpayment", foreground="green")
self.downpayment_label.grid(row=0, column=0)
self.downpayment_text = tk.Text(self.downpayment_frame, height=1, width=20, foreground="red")
self.downpayment_text.grid(row=0, column=1)
self.installment_frame = tk.Frame(self)
self.installment_frame.grid(row=1, column=0, padx=5, pady=5)
# right side
self.form_frame = tk.Frame(self)
self.form_frame.grid(row=0, column=1, padx=5, pady=5)
# Labels for the enrty boxes
self.yearly_interest = tk.Label(self.form_frame, text="Yearly Interest", foreground="blue")
self.yearly_interest.grid(row=0, column=0)
self.price_list = tk.Label(self.form_frame, text="List Price", foreground="blue")
self.price_list.grid(row=1, column=0)
self.per_downpayment = tk.Label(self.form_frame, text="Downpayment Percentage", foreground="blue")
self.per_downpayment.grid(row=2, column=0)
self.n_installments = tk.Label(self.form_frame, text="Number Of Installments", foreground="blue")
self.n_installments.grid(row=3, column=0)
# Entry boxes
self.interest_value = tk.StringVar()
self.interest = tk.Entry(self.form_frame, textvariable=self.interest_value)
self.interest.grid(row=0, column=1)
self.list_price = tk.StringVar()
self.price = tk.Entry(self.form_frame, textvariable=self.list_price)
self.price.grid(row=1, column=1)
self.downpayment_percentage = tk.StringVar()
self.downpayment = tk.Entry(self.form_frame, textvariable=self.downpayment_percentage)
self.downpayment.grid(row=2, column=1)
self.number_of_installments = tk.IntVar(value="")
self.installment = tk.Entry(self.form_frame, textvariable=self.number_of_installments)
self.installment.grid(row=3, column=1)
# Create button for creating number of grids based on the number of installments.
self.create_button = tk.Button(self.form_frame, text="Create", command=self.create)
self.create_button.grid(row=0, column=2, rowspan=4, padx=[5, 0])
# Net present values of the installments are calculated in this part.
def NPV_calculation(self, number, list_price, percentage):
interest_rate = [pow(1+0.0125,i) for i in range(1,int(number)+1)]
installment_amount = (float(list_price)-(float(list_price)*float(percentage)))/(float(number))
calculation = [installment_amount/i for i in interest_rate]
return calculation
def downpayment_calculation(self, list_price, percentage):
downpayment = float(list_price)*float(percentage)
return downpayment
def create(self):
number = self.number_of_installments.get()
list_price = self.list_price.get()
percentage = self.downpayment_percentage.get()
# get new values and set data attributes
self.downpayment_value = self.downpayment_calculation(list_price, percentage)
self.calculation_list = self.NPV_calculation(number, list_price, percentage)
# clear previous values
self.downpayment_text.delete("1.0", tk.END)
for i in self.installment_frame.winfo_children():
i.destroy()
# insert new values
self.downpayment_text.insert(tk.END, self.downpayment_value)
for y in range(number):
label = tk.Label(self.installment_frame,text= '%s.Installment'%(y+1), foreground="green")
label.grid(row=y, column=0)
for y in range(number):
text = tk.Text(self.installment_frame, height=1, width=20, foreground="red")
text.grid(row=y, column=1)
text.insert(tk.END, self.calculation_list[y])
self.get_calculation()
def get_calculation(self):
print(self.downpayment_value)
print(self.calculation_list)
root = tk.Tk()
app = Application(master=root)
app.pack()
app.mainloop()

Only using grid Tkinter but still getting TclError

So I just started using Google Colab and I keep getting this error:
TclError: cannot use geometry manager grid inside . which already has slaves managed by pack
I'm trying to make a GUI window that takes in the information from the user and saves it.
Everything I've read online says that the issue is that I'm using pack() and grid(), but I'm only using grid(). The error starts when I first try to place my labels (sourceLabel).
I'm so confused, any help would be great.
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
from tkinter import *
except ImportError:
import Tkinter as tk
from Tkinter import *
#creates window
window = tk.Tk()
window.title("File Information")
window.rowconfigure([0,1], minsize=30)
window.columnconfigure([0, 1, 2, 3], minsize=30)
#this program opens the file with information and adds the new information to it
def saveInfo():
value = path.get()
loc = source.get()
recode = recoding.get()
#change name of file and will this make them see everything
#f = open("./info.txt", "a+")
#f.write("Source Data File Location: " + loc + ", Complete File Path: " + value + ", Is recoding of column names and/or values desired?: " + recode)
#f.flush()
#f.seek(0)
#content = f.read()
#print (content)
finalList = [value,loc,recode]
#f.close()
window.withdraw()
print (finalList)
return finalList
#creates a text label, fg=foreground and bg=background, theyre the locations of colors, width and height are measured by text units which are separate horizonatal and vertical
sourceLabel = tk.Label(
text="Source Data File Location:",
width = 21,
height=2)
#adds text to window
sourceLabel.grid(row=0,column=0)
#creates second label
pathLabel = tk.Label(
text="Complete File Path:",
width = 20,
height=2)
#adds text to window
pathLabel.grid(row=1,column=0)
#creates third label
sourceLabel = tk.Label(
text="Is recoding of column \n names and/or values \n desired:",
width = 20,
height=4)
#adds text to window
sourceLabel.grid(row=2,column=0)
#create dropdown for sources
source = StringVar(window)
source.set("Local") # default value
sourceOption = OptionMenu(window, source, "Local", "Google Drive", "One Drive")
sourceOption.grid(row=0,column=1,sticky="ew")
#adds path entry
path = tk.Entry(fg="black", bg="white", width=35)
#adds path to window
path.grid(row=1,column=1,sticky="ew")
#create dropdown for recoding
recoding = StringVar(window)
recoding.set("Yes") # default value
recodingOption = OptionMenu(window, recoding, "Yes", "No")
recodingOption.grid(row=2,column=1,sticky="new")
#creates the click to save button
button = tk.Button(
text="Click to Save",
width=10,
height=1,
bg="white",
fg="black", command=saveInfo
)
#adds Button to window
button.grid(row=4,column=1,sticky="w")
#runs window
window.mainloop()
window.destroy()
This is a very weird error you had here, I just re-wrote your code using the canvas and pack method, instead of a grid, rows, and columns. Just make sure you are using Python3x Just so none of these weird bugs resurface, hope this helps, you can play around with the x and y values at the bottom, and you can mess with the height and width values at the top when we set the canvas. Happy coding!
from tkinter import *
global path, source, recoding # Global Values so that save_info can get the values
# Creates window
root = Tk()
root.title("File Information")
# Canvas Creates the base layer for the window, so instead of l = Label(text="Test").grid(row=2, column=3)
# We would now do l = Label(text="Test")
# canvas.create_window(20, 30, anchor="nw", window=l)
canvas = Canvas(width=400, height=300)
canvas.pack(fill="both", expand=True)
# Canvas.pack() just finishes creating the canvas.
# This program opens the file with information and adds the new information to it.
def save_info():
global path, source, recoding
value = path.get()
loc = source.get()
recode = recoding.get()
# change name of file and will this make them see everything
# f = open("./info.txt", "a+")
# f.write("Source Data File Location: " + loc + ", Complete File Path: " + value + ", Is recoding of column names and/or values desired?: " + recode)
# f.flush()
# f.seek(0)
# content = f.read()
# print (content)
finalList = [value, loc, recode]
# f.close()
root.withdraw()
print(finalList)
return finalList
sourceLabel = Label(
text="Source Data File Location:",
width=21,
height=2)
pathLabel = Label(
text="Complete File Path:",
width=20,
height=2)
recoding_label = Label(
text="Is recoding of column \n names and/or values \n desired:",
width=20,
height=4)
source = StringVar(root)
source.set("Local") # default value
sourceOption = OptionMenu(root, source, "Local", "Google Drive", "One Drive")
path = Entry(fg="black", bg="white", width=35)
recoding = StringVar(root)
recoding.set("Yes") # default value
recodingOption = OptionMenu(root, recoding, "Yes", "No")
button = Button(
text="Click to Save",
width=10,
height=1,
bg="white",
fg="black", command=save_info
)
# Since we are now using canvas, we must add all the elements using canvas.create_window, the first int is the x value, 2nd is the y
# Just leave anchor always as nw, and windows need to equal the variable of the widget they need to add
canvas.create_window(0, 50, anchor="nw", window=sourceLabel)
canvas.create_window(0, 90, anchor="nw", window=pathLabel)
canvas.create_window(0, 140, anchor="nw", window=recoding_label)
canvas.create_window(150, 50, anchor="nw", window=sourceOption)
canvas.create_window(150, 90, anchor="nw", window=path)
canvas.create_window(150, 140, anchor="nw", window=recodingOption)
canvas.create_window(150, 225, anchor="nw", window=button)
root.mainloop()
# I refactored some of the variables so they would be unique

make tkinter window shrink, as widgets are deleted

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.

Add/delete items from tkinter Listbox permanently

I am trying to create a program that will allow the user to edit the Listbox widget below. I had a (getactive) delete configuration that would visually delete an item from the list. But I had no luck permanently adding or deleting items from the Listbox widget.
Can anybody help me understand how I would configure the Listbox widget to do the above features?
from tkinter import *
modules_list = [
'CLD4002: Introduction to Operating Systems Virtualisation',
'CLD4003: Linux Essentials',
'SEC4001: Introduction to Networking',
'SEC4002: Routing Fundamentals',
'SEC4003: Security Fundamentals',
'SWE4001: Introduction to Software Development',
'CLD5005: Advanced Linux',
'SEC5001: Computing Security',
'SEC5002: Network Architecture',
'SEC5003: Wide Area Networks',
'SEC5005: Enterprise Infrastructure',
'HE5: CHOSEN OPTIONAL MODULE',
'CLD6000: Contemporary Problems Analysis',
'CDL6001: Undergraduate Research Project',
'SEC6003: Operations Management',
'SEC6004: Cloud and Network Security',
'HE6: CHOSEN OPTIONAL MODULE'
]
entries=[]
AVERAGE_TOT = 0 # global variable
CLASSIFICATION = "not Classified" # global variable
def print_Listbox():
z = listbox.get(0, END)
print (z)
# YEAR ONE LABELS
y1 = Label (right_frame, text="Enter Grade")
y1.grid(row=1, column=4)
row_offset = 0+2
for module in modules_list:
#Create labesl from modules_list
lbl = Label(right_frame, text=module)
lbl.grid(row=row_offset, column=3)
mod_code = module[:7] # splitting the string at the 7th character from the beginint
# create entry fields based on number of modules in modules_list
ent= Entry(right_frame, textvariable=mod_code)
ent.grid(row=row_offset, column=4)
entries.append(ent)
row_offset+=1
classification = Label (right_frame, text="Your degree classification is :" + CLASSIFICATION)
average_result = Label (right_frame, text="Your average is " + str(AVERAGE_TOT))
# FINAL AWARD CONFIGURATIONS
classification.grid(row=len(modules_list)+2, column=4)
average_result.grid(row=len(modules_list)+3, column=4)
b1 = Button (right_frame, text="press", command=lambda: setAverage(classification,average_result))
b1.grid(row=len(modules_list)+4, column=4)
def setAverage(classification, average_result):
total = 0
for entry in entries:
thisent = entry.get()
total += int(thisent)
average = total / len(entries)
if average <=39:
degreeclass = "fail"
if average >=40 and average <=49:
degreeclass = "3rd"
if average >=50 and average <=59:
degreeclass = "2:2"
if average >=60 and average <=69:
degreeclass = "2:2"
if average >=70:
degreeclass = "1st"
average_result.config(text="Your percentage is :" + str(average))
classification.config(text="Your degree classification is :" + degreeclass)
main = Tk()
var = StringVar
left_frame = Frame(main)
left_frame.grid(row=0, column=0)
middle_frame = Frame(main)
middle_frame.grid(row=0, column=1)
right_frame = Frame(main)
right_frame.grid(row=0, column=2)
l1 = Label(left_frame, text="Search")
l1.grid(row=0, column=0)
listbox = Listbox(left_frame, font = ("Purisa", 10, "bold"), height=20, width=55)
for i in modules_list:
listbox.insert(END, i)
listbox.grid(rowspan=10)
all_items = listbox.get(0, END)
b1 = Button(middle_frame, text="Add", font = ("Purisa", 10, "bold"))
b1.grid(row=3, column=1, columnspan=1)
b2 = Button(middle_frame, text="Print", font = ("Purisa", 10, "bold"), command=print_Listbox)
b2.grid(row=4, column=1, columnspan=1)
b3 = Button(middle_frame, text="Delete", font = ("Purisa", 10, "bold"))
b3.grid(row=5, column=1, columnspan=1)
main.mainloop()
You can simply use index = listbox.curselection() to get the selected item in the listbox and then use listbox.delete(index) to remove the item from the listbox.
To add new item to listbox, you need to get the item using any input method, for example tkinter.simpledialog, and then use listbox.insert('end', item) to append the item to the listbox.
Therefore, define two new functions for adding and deleting item from listbox:
from tkinter import simpledialog
def add_item():
item = simpledialog.askstring("Input", "Enter item name:")
if item is not None:
listbox.insert('end', item)
def delete_item():
index = listbox.curselection()
listbox.delete(index)
Then modify Add and Delete buttons to call the corresponding function.
BTW, there are issues in your print_Listbox function:
for module in modules_list: should be for module in z:
mod_code = module[:7] # splitting the string at the 7th character from the beginint should be mod_code = module.split(':')[0] because the mod_code in your list are not all 7 characters long.

Categories