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!
Related
I'm trying to create a basic counting app so that I can go in the field and count multiple items at once. I created the majority of the widgets with a for loop and I am trying to get the value of the label self.count and add to it.
Is there a way to retrieve the values with the references I have stored?
Since the values are in different memory locations and I don't know how to tell which button is pressed based on the below code.
(Note: the buttons should only interact with the values directly above them.)
import tkinter as tk
from tkinter import ttk
from dataclasses import dataclass
#dataclass
class fwidgets:
frame: str
framenum: int
widnum: int
widgets: list
ref: list
class App:
def addcount(self):
pass
def resetcount(self):
pass
def main(self):
win = tk.Tk()
largedoublefrm = ttk.Frame(win)
largesinglefrm = ttk.Frame(win)
smalldoublefrm = ttk.Frame(win)
smallsinglefrm = ttk.Frame(win)
combofrm = ttk.Frame(win)
largedoublefrm.grid(column=2, row=0)
largesinglefrm.grid(column=0, row=0)
smalldoublefrm.grid(column=3, row=0)
smallsinglefrm.grid(column=1, row=0)
combofrm.grid(column=4, row=0)
mainframe = fwidgets('win', 200, 5, ('largesinglefrm', 'smallsinglefrm',
'largedoublefrm', 'smalldoublefrm', 'combofrm'),
(largesinglefrm, smallsinglefrm, largedoublefrm,
smalldoublefrm, combofrm))
self.refframe = []
x=0
for wid in mainframe.ref:
ttk.Label(wid, text=mainframe.widgets[0]).pack()
self.count = ttk.Label(wid, text='0', font=("Arial", 20))
add = tk.Button(wid, text='ADD', height=5, width=15,
command=self.addcount)
reset = tk.Button(wid, text='RESET', height=5, width=15,
command=self.resetcount)
self.count.pack()
add.pack()
reset.pack()
frame = fwidgets(mainframe.widgets[x], x, 3, ('count', 'add', 'reset'),
(self.count, add, reset))
self.refframe.append(frame)
x += 1
print(self.refframe)
win.mainloop()
if __name__ == '__main__':
app = App()
app.main()
I'm not sure where to go from here. Any help would be appreciated.
To find the relevant objects first remove win.mainloop(), since only one
mainloop is permitted per tk.tk instance.
Then modify your code like this.
def addcount(self, i):
print(i.winfo_children())
def resetcount(self, i):
print(i.winfo_children())
add = tk.Button(wid, text='ADD', height=5, width=15,
command = lambda w = wid: self.addcount(w))
reset = tk.Button(wid, text='RESET', height=5, width=15,
command = lambda w = wid: self.resetcount(w))
# win.mainloop()
Now when you click a button a list of relevant objects will be displayed.
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()
i am developing an application to calculate some taxes and show the result in the graphical interface. The code itself works perfectly, but if i use numbers with bigger squares, the result overlaps over the previous one. My question is, is it possible to clear the previous result and calculate the new one?
Follow the complete code below:
from tkinter import *
root = Tk()
l_vlrRec = Label(root, text='Receita')
l_vlrRec.place(x=10, y=10)
e_vlrRec = Entry(root)
e_vlrRec.place(x=75, y=10, width=75)
def calcular():
receita = float(e_vlrRec.get())
l_result = Label(root, text='{:.2f}'.format(receita))
l_result.place(x=10, y=150)
e_vlrRec.delete(0, END)
bt = Button(root, text='Calcular', command=calcular)
bt.place(x=10, y=50)
root.mainloop()
You can use the label's textvariable and also you don't have to instantiate a new Label every time the button is pressed:
v_result = DoubleVar()
l_result = Label(root, textvariable=v_result)
l_result.place(x=10, y=150)
def calcular():
v_result.set(round(float(e_vlrRec.get()),2))
You can do the same for your Entry object e_vlrRec so you don't have to cast the string you get by calling e_vlrRec.get() but use the variable's get() instead
Without using textvariable you can also reconfigure the label's text parameter:
l_result.configure(text='{:.2f}'.format(receita))
or
l_result['text'] = '{:.2f}'.format(receita)
I've hunted through some of the previous answers and got a step closer, but my problem is still that I can not get the value from multiple entry boxes.
import tkinter as tk
from tkinter import ttk
window = tk.Tk()
my_list = []
def get_info():
for each_player in my_list:
tk.Label(window, text=temp_entry.get()).grid()
#number of players is determined by the user.
#In this example, lets say there are 3 players
tk.Label(window, text="Number of Players: ").grid()
num_of_players = ttk.Combobox(window, values=[1, 2, 3])
num_of_players.current(2)
num_of_players.grid(row=0, column=1)
#The code above is only the recreate the user selecting the amount of players from a combobox
#create number of entry boxes for user-determined number of players
for each_player in range(1, int(num_of_players.get()) + 1):
temp_label = tk.Label(window, text="Player {}: ".format(each_player))
temp_entry = tk.Entry(window)
my_list.append(temp_entry)
temp_label.grid(row=each_player, column=0, pady=10)
temp_entry.grid(row=each_player, column=1, pady=10)
button = tk.Button(window, text="Save", command=get_info)
button.grid()
window.mainloop()
It's at the end of the code where I struggle to find out how I could possibly get information from the entry boxes. How can I use the get() method but only after the user has input text?
Your list contains entry widgets, so in your loop you need to reference the loop variable rather than temp_entry:
def get_info():
for each_player in my_list:
print(each_player.get())
Use a button that triggers a method that gets the text for u like:
play = Button(window, text="get_button", command=that_getter_method)
.....
.....
def that_getter_method():
var = field.get()
Part of my program prompts the user to enter a sample number into an entry field, and then click a button which generates that number of entry widgets beneath it (to enter information about each sample). The entry widgets appear exactly as expected, one beneath the other for the entered number of samples.
I can't figure out how to separate the variables for each of those new entry widgets now. I had hoped that each one would allow me to enter a different value, and then call them back via .get(). That's not the case, they ALL change together to whatever I type into one. Below is the section of code that I believe is the issue:
normval_f= IntVar()
x= samp.get()
f=1
while f<=x:
f_entry = ttk.Entry(mainframe, width=7, textvariable=normval_f)
f_entry.grid(column=1, row=f+12)
f_label = ttk.Label(mainframe, text="Sample "+str(f)+ " value").grid(column=2, row=f+12, sticky=W)
f=f+1
List and Dictionaries can be very useful when dealing with large amounts or potently large amounts of widgets.
Below is a bit of code to accomplish what you described in your question.
I use a list called self.my_entries to store all the entries that will be created based on the value of what the user types in. I also added a little error handling just in case the user tries to type in something other than a number or nothing is enter when the button is pressed.
The first entry and button are placed on the root window and for all of the entry fields that are going to be created we add a frame below the first button. This will allow us to manage the entry fields a bit easier if we want to reset the field later.
from tkinter import *
class Example(Frame):
def __init__(self):
Frame.__init__(self)
self.pack()
self.my_entries = []
self.entry1 = Entry(self)
self.entry1.grid(row = 0, column = 0)
Button(self, text="Set Entry Fields", command=self.create_entry_fields).grid(row = 1, column = 0)
self.frame2 = Frame(self)
self.frame2.grid(row = 2, column = 0)
def create_entry_fields(self):
x = 0
try:
x = int(self.entry1.get())
if x != 0:
for i in range(x):
self.my_entries.append(Entry(self.frame2))
self.my_entries[i].grid(row=i, column=1)
f_label = Label(self.frame2, text="Label {}: ".format(i+1))
f_label.grid(row=i, column=0)
Button(self.frame2, text="Print to console", command=self.print_all_entries).grid(row=x, column=0, sticky = "nsew")
Button(self.frame2, text="Reset", command=self.clear_frame2).grid(row=x, column=1, sticky = "nsew")
except:
print("Invalid entry. Only numbers are allowed.")
def print_all_entries(self):
for i in self.my_entries:
print(i.get())
def clear_frame2(self):
self.my_entries = []
self.frame2.destroy()
self.frame2 = Frame(self)
self.frame2.grid(row = 2, column = 0)
if __name__ == '__main__':
root = Tk()
test_app = Example()
root.mainloop()
Let me know if you have any questions about the above code.
A few suggestions; don't use a while loop, use a for loop instead. Secondly, I'd suggest storing each of your variables inside a list, or as I have done, in a list. At the moment, you are overwriting your variables each time you go through the loop so you need to create a new one each time and store it somewhere.
Here is an example of storing a number of fields in a dictionary.
from tkinter import *
class App(Frame):
def __init__(self,master):
Frame.__init__(self, master)
fields = ['name','age','gender']
self.field_variables = {}
for idx,field in enumerate(fields):
self.field_variables[field] = StringVar()
f_entry = Entry(self,textvariable=self.field_variables[field])
f_entry.grid(column=2,row=idx)
f_label = Label(self,text=field)
f_label.grid(column=1,row=idx)
go_btn = Button(self,text="Go",command=self.get_all)
go_btn.grid()
def get_all(self):
print(self.field_variables)
if __name__ == '__main__':
root = Tk()
app = App(root)
app.grid()