Why isn't the grid_forget method working? - python

Trying to make an tkinter atm gui, in the gui I made it so you can create new accounts with a set balance. I also wanted to make it so you can delete the last previous account you created but ran into an error. I stored all created accounts in a list and to delete them I tried to use the grid_forget method to delete the account in the -1 index but the program says it doesn't have an attribute called grid_forget, please help and thank you.
code:
import tkinter as tk
from tkinter import messagebox as mb
class BankAccount(tk.Tk):
def __init__(self, frame_title):
super().__init__()
self.title(frame_title)
self.geometry("350x370")
self.acc_list = []
#main window buttons
self.addAcc_button = tk.Button(self, text="Add Account", width=50, height=3, command=self.addAcc_window)
self.addAcc_button.grid()
self.removeAcc_button = tk.Button(self, text="Clear accounts", width=50, height=3, command=self.removeAcc)
self.removeAcc_button.grid()
self.deposit_button = tk.Button(self, text="Deposit", width=50, height=3)
self.deposit_button.grid()
self.withdrawal_button = tk.Button(self, text="Withdraw", width=50, height=3)
self.withdrawal_button.grid()
#main window label
self.accounts_label = tk.Label(self, text="Accounts: ", justify="center")
self.accounts_label.grid(pady=10)
#create second window when add acc* button is pressed + done button function/command
def addAcc_window(self):
self.AAW = tk.Toplevel(self)
self.accName_label = tk.Label(self.AAW, text="Enter Account Name:")
self.accName_label.grid()
self.accName_entry = tk.Entry(self.AAW)
self.accName_entry.grid()
self.AAW_done_button = tk.Button(self.AAW, text="Done", command=self.AAW_Done_button)
self.AAW_done_button.grid(pady=20)
def AAW_Done_button(self):
if len(self.accName_entry.get()) == 0:
mb.showerror("Blank entry", "Field cannot be blank")
else:
self.add_accLabel = tk.Label(self, text="Account " + self.accName_entry.get() + ": Balance($0)")
self.add_accLabel.grid()
self.acc_list.append(self.accName_entry.get())
self.AAW.destroy()
#create third window when remove acc* button is pressed
def removeAcc(self):
self.acc_list[-1].grid_forget()
root = BankAccount("ATM")
root.mainloop()

In your code, self.acc_list contains the name of the accounts, so they are all strings which does not have grid_forget() function.
For your case, you need to add two things into self.acc_list for each account:
the account name
the label showing account name and balance
def AAW_Done_button(self):
...
self.acc_list.append([self.accName_entry.get(), self.add_accLabel])
...
Then you can delete the last account inside removeAcc() as below:
def removeAcc(self):
if self.acc_list:
name, label = self.acc_list.pop()
label.grid_forget() # or better call label.destroy()

Related

tkinter window doesn't load correctly after first file run in Spyder

So far I have tried the advice from u/OA998
"""https://www.reddit.com/r/learnpython/comments/45h05k/solved_kernel_crashing_when_closing_gui_spyder/
My code is supposed to just open up a window with two labels and an entry field. The code works fine if I restart the Kernel but just running it after closing the window will result in an window popping up that has no text (from the StringVar). The third time it doesn't even open anymore. I'm not quite sure what causes this."""
Code:
""" create a GUI inside a class (this allows variables to be added and changed by several parts in the GUI). By using the “self” keyword we can access the attributes and methods of the class in python. It binds the attributes with the given arguments.
from tkinter import *
""" create a GUI inside a class (this allows variables to be added and changed by several parts in the GUI)"""
""" By using the “self” keyword we can access the attributes and methods of the class in python. It binds the attributes with the given arguments."""
class TESTGUI:
def __init__(self, window):
"""We will use an entry widget, and StringVar to keep track of the current text in the
box, and a label to display a message."""
"""labelpositioning"""
margin = 2
self.spacer = Label(window, width=margin, height=margin)
self.spacer.grid(column=0,row=0)
"""for toogle button create StringVar in tkinter class to hold the text"""
"""labeltexts"""
self.labelText_1 = StringVar()
self.labelText_1.set("begin experiment")
self.labelText_2 = StringVar()
self.labelText_2.set("calibrate")
"""labelformat"""
self.label = Label(window, textvariable=self.labelText_1, width=12, height=3, borderwidth=3, relief=SOLID)
self.label.grid(column=1, row=1)
self.label = Label(window, textvariable=self.labelText_2, width=12, height=3, borderwidth=3, relief=SOLID)
self.label.grid(column=2, row=1)
"""labelbutton"""
self.button = Button(window, text="press", command=self.pressed_button_1)
self.button.grid(column=1,row=2)
self.button = Button(window, text="press", command=self.pressed_button_2)
self.button.grid(column=2,row=2)
"""Entry(password)Label"""
self.entryLabel_1 = Label(window, text = "enter your password")
self.entryLabel_1.grid(column=0,row=3)
""" Next add a StringVar to hold the password and Entry box to type the password."""
""" The trace function will call a checkStrength() function when the StringVar is changed."""
self.password = StringVar()
self.password.trace("w", lambda name, index, mode, password=self.password:self.checkStrenght())
self.entry = Entry(window, textvariable=self.password)
self.entry.grid(column=1, row=3)
""" then create the StringVar to hold the strength string, and the label to display it. """
self.strenghtText = StringVar()
self.strenghtText.set("")
self.strenghtLabel = Label(window, textvariable=self.strenghtText, width=10)
self.strenghtLabel.grid(column=3, row=3)
"""CheckStrenghtFunctionOfEntry"""
def checkStrenght(self):
lenght = len(self.password.get())
if lenght == 0:
self.strenghtText.set("")
self.strenghtLabel.config(bg="SystemWindowBody")
elif lenght >= 1:
self.strenghtText.set("strong")
self.strenghtText.config(bg = "green3")
"""ButtonFunction"""
def pressed_button_1(self):
if self.labelText_1.get() == "begin experiment":
self.labelText_1.set("abort experiment")
else:
self.labelText_1.set("begin experiment")
def pressed_button_2(self):
if self.labelText_2.get() == "calibrate":
self.labelText_2.set("recalibrate")
else:
self.labelText_2.set("calibrate")
""" define Variables for window size """
width=300
height=300
""" define tk for used variable to use tkinter class """
if __name__ == "__main__":
window = Tk()
window.minsize(width, height)
window.title("Photonics Lab")
gui = TESTGUI(window)
window.mainloop()

Tkinter - Retrieve Values from Dynamically Generated Widgets - Callback

I'm trying to make a GUI through Tkinter that will calculate production based on some user input. Based on the number of systems the user selects, I have that number of option menus pop up for the inverter type and that number of entry widgets pop up for modules per string, strings per inverter, and inverters per system. See the picture for an example if the user selects 2 systems.
I'm using a callback function to grab the user selected number of systems real time to dynamically generate the inverter/module widgets discussed above.
My issue is that I'm unable to retrieve the values from these widgets. My attempt is shown in the weather calculation function.
I'm assuming the issue is because I generate the widgets/variables within the callback function. However, I haven't been able to figure out a way to dynamically generate the number of widgets based on user input outside of the callback function.
Any assistance with this would be greatly appreciated!
class Window:
# Define User Inputs:
def __init__(self, master):
master.title('Production Analysis Tool')
# EQUIPMENT PARAMETERS
# callback function to create entry boxes based on number of systems
def callback(*args):
self.system_size = int(self.system_size_raw.get())
# Modules per String
self.L3 = Label(root, text = "Number of Modules Per String").grid(row=20, column=1, sticky=E)
self.modules_string_raw = IntVar(root)
modules_per_string =[]
for i in range(self.system_size):
self.label = Label(root, text = "System {}".format(i+1)).grid(row=21+i, column=1, sticky=E)
self.widget = Entry(root).grid(row=21+i, column=2, sticky=W)
modules_per_string.append(self.widget)
# Number of Systems
self.L1 = Label(root, text = "Number of Systems").grid(row=1, column=1, sticky=E)
self.system_size_raw = IntVar(root)
choices = [1,2,3,4,5,6,7,8,9,10]
self.popupMenu2 = OptionMenu(root, self.system_size_raw, *choices).grid(row=1, column=2, sticky=W)
self.system_size_raw.trace("w", callback)
#Calculation Function
def weather_calculation(self):
# Get Values from User Input
self.mod_strings = np.float(self.modules_string_raw.get())
root = Tk()
root.configure()
window = Window(root)
root.mainloop()
All you need to do is save a reference to your Entry widgets in a list. You can then iterate over that list to get the value of each widget.
It appears that you're already saving the widgets to the list variable modules_per_string. All you need to do is make that global or an object attribute rather than a local variable so other functions can reference it.
As Bryan Oakley said, make list for widgets to store each objects of entries and label in two list.
For Example:
import tkinter as tk
class Demo:
def __init__(self):
self.root = tk.Tk()
self.root.geometry("600x600")
systems_label = tk.Label(self.root, text="No Of Systems:")
systems_label.place(x=100, y=20)
no_Of_System_Ent = tk.Entry(self.root, width=15)
no_Of_System_Ent.place(x=200, y=20)
submit_Button = tk.Button(self.root, text="Submit", command=lambda: self.process(no_Of_System_Ent.get()))
submit_Button.place(x=350,y=20)
def display(self,sys_len):
for i in range(sys_len):
buffer = self.obj_of_entries[i].get()
print(buffer)
def delete(self,sys_len):
for i in range(sys_len):
self.obj_of_entries[i].destroy()
self.obj_of_labels[i].destroy()
def process(self,length_sys):
self.obj_of_entries = []
self.obj_of_labels = []
y_pos = 80
for i in range(int(length_sys)):
#Adding objects of label in list 'obj_of_labels'
self.obj_of_labels.append(tk.Label(self.root,text="System "+str(i)))
self.obj_of_labels[len(self.obj_of_labels)-1].place(x=100,y=y_pos)
#Adding objects of entry in list 'obj_of_entries'
self.obj_of_entries.append(tk.Entry(self.root,width=15))
self.obj_of_entries[len(self.obj_of_entries)-1].place(x=200,y=y_pos)
#Increments Y by 50
y_pos = y_pos + 50
self.delete_Button = tk.Button(self.root, text="Delete All", command=lambda: self.delete(int(length_sys)))
self.delete_Button.place(x=200,y=400)
self.print_Button = tk.Button(self.root, text="Print All", command=lambda: self.display(int(length_sys)))
self.print_Button.place(x=350,y=400)
ob=Demo()
In this example:
I created a entry and button in the init function to take no of systems from user.
def __init__(self):
self.root = tk.Tk()
self.root.geometry("600x600")
systems_label = tk.Label(self.root, text="No Of Systems:")
systems_label.place(x=100, y=20)
no_Of_System_Ent = tk.Entry(self.root, width=15)
no_Of_System_Ent.place(x=200, y=20)
submit_Button = tk.Button(self.root, text="Submit", command=lambda: self.process(no_Of_System_Ent.get()))
submit_Button.place(x=350,y=20)
After clicking submit button,it will go to process function.
Ps: length_sys is the no of systems.
def process(self,length_sys):
self.obj_of_entries = []
self.obj_of_labels = []
y_pos = 80
for i in range(int(length_sys)):
#Adding objects of label in list 'obj_of_labels'
self.obj_of_labels.append(tk.Label(self.root,text="System "+str(i)))
self.obj_of_labels[len(self.obj_of_labels)-1].place(x=100,y=y_pos)
#Adding objects of entry in list 'obj_of_entries'
self.obj_of_entries.append(tk.Entry(self.root,width=15))
self.obj_of_entries[len(self.obj_of_entries)-1].place(x=200,y=y_pos)
#Increments Y by 50
y_pos = y_pos + 50
self.delete_Button = tk.Button(self.root, text="Delete All", command=lambda: self.delete(int(length_sys)))
self.delete_Button.place(x=200,y=400)
It will append the entry and label obj in its respective list and place the current obj in GUI window.
At Last,It will increment y axis by 80 so that next label and entry comes down to the previous one.
If user clicks delete all button,then it will go to delete all list obj of both entries and labels.
Ps: sys_len is the no of systems.
def delete(self,sys_len):
for i in range(sys_len):
self.obj_of_entries[i].destroy()
self.obj_of_labels[i].destroy()
To see the content,use this code:
(It will print in the Python shell so you can see if data is correct or not.)
def display(self,sys_len):
for i in range(sys_len):
buffer = self.obj_of_entries[i].get()
print(buffer)
I think I solved the doubt.
Ciao!

GUI - hiding buttons

from tkinter import *
import tkinter as tk
class dashboard(Frame):
def __init__(self, master):
super(dashboard, self).__init__(master)
self.grid()
self.buttons()
def buttons(self):
#student dashboard button
bttn1 = Button(self, text = "Student",
command=self.student, height = 2, width= 15)
bttn1.grid()
#teacher dashboard button
bttn2 = Button(self, text = "Teacher", height = 2, width= 15)
bttn2.grid()
#exit button
bttn3 = Button(self, text = "Exit",
command=root.destroy, height = 2, width= 15)
bttn3.grid()
def student(self):
#view highscores button
bttn1 = Button(self, text = "Highscores", height = 2, width= 15)
bttn1.grid()
#print score button
bttn2 = Button(self, text = "Print Score", height = 2, width= 15)
bttn2.grid()
#exit button
bttn3 = Button(self, text = "Main Menu",
command=root.destroy, height = 2, width= 15)
bttn3.grid()
#main
root = Tk()
root.title("Dashboard")
root.geometry("300x170")
app = dashboard(root)
root.mainloop()
Wondered if someone could help me basically, with this GUI I am creating I want to be able to access a new page on the same frame but the buttons from the main menu stay once I go to another page, does anyone know how I can hide/forget the buttons and go back to them at a later stage? Thanks.
Updated to use sub-Frames
You could do it using the universal grid_remove() method (here's some documentation). One way to use it would be to keep references to each of the Button widgets created so you can call this method on them as needed.
However that can be simplified slightly—even though it takes about the same amount of code—by putting all the Buttonss for each page into a separate sub-Frame and just showing or hiding it which will automatically propagate do to all the widgets it contains. This approach also provides a better foundation for the rest of your program.
I've implemented this by adding a main_button_frame attribute to your class, as well as one named student_button_frame to hold those you have on the student page (since you'll probably need it to hide them too).
One nice thing about grid_remove() is that if you later call grid() on the same widget, it will remember all the settings it (and its sub-widgets) had before it was removed, so you don't need to create and maintain a list of every single one of them yourself.
Also note I also made some general modifications to your code so it conforms to the PEP 8 - Style Guide for Python Code recommendations better. I highly recommend you read and start following it.
from tkinter import *
import tkinter as tk
class Dashboard(Frame):
def __init__(self, master):
super().__init__(master)
self.grid()
self.main_button_frame = None
self.student_button_frame = None
self.create_main_buttons()
def create_main_buttons(self):
if self.student_button_frame: # Exists?
self.student_button_frame.grid_remove() # Make it invisible.
if self.main_button_frame: # Exists?
self.main_button_frame.grid() # Just make it visible.
else: # Otherwise create it.
button_frame = self.main_button_frame = Frame(self)
button_frame.grid()
# Student Dashboard button
bttn1 = Button(button_frame, text="Student",
command=self.create_student_buttons, height=2,
width=15)
bttn1.grid()
# Teacher Dashboard button
bttn2 = Button(button_frame, text="Teacher", height=2, width=15)
bttn2.grid()
# Dashboard Exit button
bttn3 = Button(button_frame, text="Exit", command=root.destroy,
height=2, width=15)
bttn3.grid()
def create_student_buttons(self):
if self.main_button_frame: # Exists?
self.main_button_frame.grid_remove() # Make it invisible.
if self.student_button_frame: # Exists?
student_button_frame.grid() # Just make it visible.
else: # Otherwise create it.
button_frame = self.student_button_frame = Frame(self)
button_frame.grid()
# Highscores button
bttn1 = Button(button_frame, text="Highscores", height=2, width=15)
bttn1.grid()
# Print Score button
bttn2 = Button(button_frame, text="Print Score", height=2, width=15)
bttn2.grid()
# Student Exit button
bttn3 = Button(button_frame, text="Exit", command=root.destroy,
height=2, width=15)
bttn3.grid()
# main
root = Tk()
root.title("Dashboard")
root.geometry("300x170")
app = Dashboard(root)
root.mainloop()

Tkinter store entry box value as value for other file

This is my first python personal project. I am wanting to use Tkinter to create a window (GUARDIAN LOCATOR) that asks the user to input a value (enter sailor guardian) into the entry box. The rest of the program is dependent on what the user types in the entry box as I will be having if/else statements reacting to the sailor guardian entered.
The issue I am having is storing what is entered in the entry box as a variable to use in my main file for the if/else statements. I can get the value to print to the prompt window, but I haven't been able to store it successfully to a global variable.
My Tkinter window is in it's own class.
I have tried many different ways of doing this based on similar issues from stackoverflow, but I am getting an error every time. This is my base code that still produces the error.
Class file with the Tkinter window
class GuardianLocator:
def __init__(self, master):
frame = Frame(master)
frame.grid()
master.title("GUARDIAN LOCATOR")
self.locator_label = Label(frame, text="Which Sailor Guardian are you looking for?", width=40, height=2)
self.locator_label.grid()
self.entry = Entry(frame)
self.entry.grid()
self.button1 = Button(frame, text="Search", command=self.guardian_name, pady=2)
self.button1.grid()
def guardian_name(self):
print(self.entry.get())
and in my main working file
root = Tk()
locator = guardian_locator.GuardianLocator(root)
root.mainloop()
This is my test loop to see if it's working.
if locator.guardian_input() is "Sailor Moon":
print("hi")
else:
print("no")
Not sure exactly how your code is organized and where is your "test loop" located, but I assume it is after root.mainloop(). Thus the script can be as follows:
from tkinter import *
class GuardianLocator:
def __init__(self, master):
self._name = ""
frame = Frame(master)
frame.grid()
master.title("GUARDIAN LOCATOR")
self.locator_label = Label(frame, text="Which Sailor Guardian are you looking for?", width=40, height=2)
self.locator_label.grid()
self.entry = Entry(frame)
self.entry.grid()
self.button1 = Button(frame, text="Search", command=self.guardian_name, pady=2)
self.button1.grid()
def guardian_name(self):
self._name = self.entry.get()
print(self.entry.get())
root = Tk()
locator = GuardianLocator(root)
root.mainloop()
# this will be executed after the root window is closed.
print("Name is", locator._name)
Please note self._name = "" in the constructor. This instance variable can be used to store the name provided in your GuardianLocator window.

Get the same input multiple times in python tkinter?

I am making a program to get multiple user names and store them in a XML file. The XML part works fine, but I am having trouble with the GUI. I want to be able to ask the user how many users he wants to input, then repeat an Entry that number of times. How can I do that, while maintaining a quit function? I tried:
def quitter():
exit()
quit()
quitterButton = Button(master, text="Quit", command=quitter)
mainCanvas.create_window(50, 330, window=quitterButton, tag="quitter")
num = int(raw_input("How many users are you going to put in?"))
for x in range(0,num):
#Create User entry Variable
userEnter = StringVar()
usrVar = ""
#Create enter box:
userEntryBox = Entry(master, textvariable = userEnter)
mainCanvas.create_window(250, 300, window=userEntryBox, tag="UserEnterA")
def gotInput(self):
usrVar = userEnter.get();
mainCanvas.create_text(250, 330, text="User Inputted: " + usrVar, tags="0")
mainCanvas.delete("UserEnterA")
#Bind entry box
userEntryBox.bind('<Key-Return>', gotInput)
userEntryBox.wait_window(userEntryBox)
#Create a new user element
newUsr= ET.Element('Member')
#Add element to the Root
root.append(newUsr)
#Make a sub element Name, set name
usrName = ET.SubElement(newUsr, 'Name')
usrName.text = usrVar;
...
tree.write('./output.xml')
What is the best way to go around it? I won't know the number of inputs, and I want the quit button to work at all times .
Behavior of your program is a bit unclear for me, but I try to help.
First solution: show tkinter askstring dialog num times. You can break for loop if user press Cancel button. It's not exactly what you want, but it's very easy to implement:
from tkinter import *
import tkinter.simpledialog as simpledialog
def add_users():
n = simpledialog.askinteger('', 'How many users are you going to put in?', initialvalue=1, minvalue=1, maxvalue=10)
if not n: # 'Cancel'
return
for i in range(n):
user = simpledialog.askstring('', 'User #%s from #%s' % (i+1, n))
if user is None: # 'Cancel'
return
# Do something useful
print(user)
root = Tk()
Button(root, text='Add users', command=add_users).pack(padx=50, pady=50)
Button(root, text='Quit', command=root.destroy).pack(pady=30)
root.mainloop()
Second (if you want to put entry and all new names to window with quit button):
from tkinter import *
import tkinter.simpledialog as simpledialog
class YourApp():
def __init__(self):
self.root = Tk()
Button(self.root, text='Quit', command=self.root.destroy).pack(pady=20)
self.ask_button = Button(self.root, text='Add users', command=self.add_users)
self.ask_button.pack(padx=50, pady=50)
self.root.mainloop()
def add_users(self):
self.users_count = 0
self.user_name = StringVar()
self.frame = Frame(self.root)
self.frame.pack()
self.users_count = simpledialog.askinteger('', 'How many users are you going to put in?', initialvalue=1, minvalue=1, maxvalue=10)
self.user_entry = Entry(self.frame, textvariable=self.user_name)
self.user_entry.pack(pady=10)
self.user_entry.bind('<Key-Return>', self.on_new_user)
self.user_entry.focus_set()
def on_new_user(self, event):
# your code
print(self.user_name.get())
Label(self.frame, text='User Inputted: %s' % self.user_name.get()).pack()
self.users_count -= 1
if not self.users_count:
self.user_entry.destroy()
self.user_name.set('')
YourApp()
There are three geometry managers in Tkinter: place (it's very similar to canvas in your case), grid and pack.
Canvas usually used for drawing pictures or graphs.

Categories

Resources