Gather input from multiple tkinter checkboxes created by a for loop - python-3.x

I made an application with tkinter which creates a list of checkboxes for some data. The checkboxes are created dynamically depending on the size of the dataset. I want to know of a way to get the input of each specific checkbox.
Here is my code, which you should be able to run.
from tkinter import *
root = Tk()
height = 21
width = 5
for i in range(1, height):
placeholder_check_gen = Checkbutton(root)
placeholder_check_gen.grid(row=i, column=3, sticky="nsew", pady=1, padx=1)
for i in range(1, height):
placeholder_scope = Checkbutton(root)
placeholder_scope.grid(row=i, column=4, sticky="nsew", pady=1, padx=1)
root.mainloop()
I looked over other answers and some people got away by defining a variable inside the checkbox settings "variable=x" and then calling that variable with a "show():" function that would have "variable.get()" inside. If anyone could please point me in the right direction or how I could proceed here. Thank you and much appreciated.

Normally you need to create an instance of IntVar or StringVar for each checkbutton. You can store those in a list or dictionary and then retrieve the values in the usual way. If you don't create these variables, they will be automatically created for you. In that case you need to save a reference to each checkbutton.
Here's one way to save a reference:
self.general_checkbuttons = {}
for i in range(1, self.height):
cb = Checkbutton(self.new_window)
cb.grid(row=i, column=3, sticky="nsew", pady=1, padx=1)
self.general_checkbuttons[i] = cb
Then, you can iterate over the same range to get the values out. We do that by first asking the widget for the name of its associated variable, and then using tkinter's getvar method to get the value of that variable.
for i in range(1, self.height):
cb = self.general_checkbuttons[i]
varname = cb.cget("variable")
value = self.root.getvar(varname)
print(f"{i}: {value}")

Related

How do I return the input of a tkinter entry box generated by a loop to a list for later use

sorry for what I assume is a noob question. this seems like it should be so simple. I am trying to create a list from an entry box generated by a loop.
I have two lists, one list "myLabelList" that has info such as "job Name, Project name" etc. and one empty list "myEntryLists" to capture the info from the entry.
The problem is when i print(myEntryList) it seems to display info about the entry rather than the input itself. I have a workaround but that's exactly what it is.
sorry if i have formatted this badly, its my first post.
from tkinter import *
root = Tk()
root.title("Job Information Entry")
root.geometry("400x150")
topFrame = Frame(root)
bottomFrame = Frame(root)
topFrame.grid(row=0, column=0)
bottomFrame.grid(row=1, column=0)
myLabelList = ["Enter Job Number", "Enter Project Name", "Enter Job Name", "Enter Drawing Number"]
myEntryList = []
lst = []
# this is where i seem to be having problems
def ok_button_click():
for entry in myEntryList: # this is my workaround
lst.append(entry.get()) # this is my workaround
print(myEntryList) # this is what im getting
print(lst) # this is what i want to print()
x = 0
for i in myLabelList:
myLabel = Label(topFrame, text=i)
myEntry = Entry(topFrame, width=50)
myLabel.grid(row=x, sticky=E)
myEntry.grid(row=x, column=1)
x = x + 1
myEntryList.append(myEntry)
# bottomFrame
okButton = Button(bottomFrame, text="OK", command=ok_button_click)
cancelButton = Button(bottomFrame, text="Cancel")
okButton.grid(row=0, column=0)
cancelButton.grid(row=0, column=1)
root.mainloop()
In this line you insert an Entry widget to myEntryList:
myEntryList.append(myEntry)
When you're trying to print it, it gives you a printable representation of the the Entry widgets, saved in the list.
If you want to get the input itself, just print the lst list.
EDIT:
Entry is an object. In OOP (Object-Oriented Programming), you define classes, which represent an object. Each object has properties and methods. You can read more about OOP here .
Entry widget is an example of an object. It has constructor that creates an instance of that class (Entry()), propeties like 'bg', 'fg' and methods like get().
When you insert to myEntryList an Entry widget, you insert the whole object to the list, and not the input it holds. In order to get the input, you need to use the get() method on the Entry widget.
I hope everything is clear now :)

Is there a way to dynamically create Entry's and add their input to a list | Tkinter

First time asking a question on here, but I was trying to create a program where Entry is dynamically created but I don't know how to take the text from the Entry and put it into a list.
I was looking at the question here: Getting values of dynamically generated Entry fields using Tkinter, but I couldn't figure out how to have the code work in my particular case and things have probably changed in the past five years.
Any feedback would be appreciated.
Example:
tk.Entry(TAB1, textvariable=username[i])
tk.Entry(TAB1, textvariable=username[i])
The first one would get a value like 'username1' and the second one would get 'username2' and so on for the rest of the dynamically added Entry's.
If there is a smarter way of doing this or you want me to show all my code, please let me know!
EDIT:
Here is my code for adding the buttons
nextRow = 4
usernames = []
def addInput():
global nextRow
entry = tk.Entry(TAB1)
entry.grid(row=nextRow, column=0, sticky='WE', padx=10, pady=2)
ent2 = tk.Entry(TAB1).grid(row=nextRow, column=1, sticky='WE', padx=10, pady=2)
ent3 = tk.Entry(TAB1).grid(row=nextRow, column=2, sticky='WE', padx=10, pady=2)
nextRow += 1
usernames.append(entry.get())
I also have a button that uses addInput() when the user would like to add a new row of three tk.Entry
tk.Button(TAB1, text='Add', command=addInput, font=("Consolas", 12), width = 8).grid(row=1, column=1, sticky='W')
At this point I am unsure how to add the entry and keep track of its value.
Generally speaking you don't need to use a textvariable at all. Just keep the entries in a list, and the refer to the list to get the values. The use of textvariable is ok, but it the way most people use it it just adds overhead without providing any real value.
entries = []
for i in range(10):
entry = tk.Entry(TAB1)
...
for i in range(10):
print(f"Entry {i} has value {entries[i].get()}")
If you really want to use a textvariable, the same technique still applies but you save the variable rather than the entry.
vars = []
for i in range(10):
var = tk.StringVar()
vars.append(var)
entry = tk.Entry(Tab1, textvariable=var)

When I enter text to individual ttk.entry every other ttk.entry in the array changes to the same value. What am I doing wrong?

When I enter text to individual ttk.entry every other ttk.entry in the array changes to the same value. What am I doing wrong?
Here is my simplified code:
from tkinter import *
from tkinter import ttk
root = Tk()
root.title("test")
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
text = [StringVar()]*3
textbox = [None]*3
for x in range(1, 3):
textbox[x] = ttk.Entry(mainframe, textvariable=text[x])
textbox[x].grid(column=1, row=x, sticky=(W, E))
text[1].set("1")
text[2].set("2")
for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5)
root.mainloop()
The root of the problem is that you aren't initializing text properly. If you look closely you'll see that your list contains three references to a single instance of StringVar.
One solution, then, is to properly initialize the list. For example:
text = [tk.StringVar() for i in range(3)]
Another solution is to simply not use StringVar and textvariable. The textvariable attribute is optional in most cases. The only time you need to use textvariable is when you want to link two or more widgets together, or when you want to put a trace on a variable. Since it looks like you are doing neither of those, I recommend simply not using the textvariable option.
When you create the list of StringVars you are creating a list containing 3 references to the same instance.
>>> vals = [tk.StringVar()] * 3
>>> vals
[<tkinter.StringVar object at 0x7f0623fcfa90>, <tkinter.StringVar object at 0x7f0623fcfa90>, <tkinter.StringVar object at 0x7f0623fcfa90>]
Note that the addresses are identical. You want 3 different instances. For instance:
>>> [tk.StringVar() for x in range(0,3)]
[<tkinter.StringVar object at 0x7f06213105f8>, <tkinter.StringVar object at 0x7f0621310668>, <tkinter.StringVar object at 0x7f0621310710>]
Would do. As you have a single instance, all the widgets reflect the value of that one instance. If you use separate StringVar instances you will get a different value for each widget.

StringVar.get() returns a blank value

I've asked this before - but I think I phrased it incorrectly.
What I essentially want is to have a user fill out a tkinter entry box, and have the value saved - somewhere.
I the want to click on a button, and have the user's entered text to manipulate.
I want essentially what is below - although that doesn't work at all - as I can't enter a StringVar there, apparently. I'm really at my wits end here :(
How can I rewrite this, so it will work?
class Driver():
firstname = StringVar()
def __init__(self):
root = Tk()
root.wm_title("Driver")
firstname_label = ttk.Label(root, text="First Name *").grid(row=0, column=0)
firstname_field = ttk.Entry(root, textvariable=self.firstname).grid(row=0, column=1)
ttk.Button(root, text="Send", command=self.submit).grid()
root.mainloop()
def submit(self):
print(self.firstname.get())
I've having lots, and lots of trouble with this. It seems to printing blank values and references to the variable - rather than the value inside it
You cannot use StringVar in this way -- you can't create a StringVar until after you've created the root window. Because you are creating the root window inside the constructor the code will throw an error.
The solution is to move the creation of the StringVar inside your constructor:
class Driver():
def __init__(self):
root = Tk()
root.wm_title("Driver")
self.firstname = StringVar()
firstname_label = ttk.Label(root, text="First Name *").grid(row=0, column=0)
Note that the way you've written the code, firstname_label and firstname_field will always be None, because that is what grid returns. It's always best to separate widget creation from layout.
Also, you don't really need the StringVar under most circumstances (assuming you correctly store a reference to the widget). Just omit it, and when you want the value of the entry widget you can just get it straight from the entry widget:
...
self.firstname_field = Entry(...)
...
print(self.firstname_field.get())
The use of a StringVar is only necessary if you want to share the value between widgets, or you want to put a trace on the variable.

Tkinter: creating an arbitrary number of buttons/widgets

So, I've got a list with entries that look like this:
Option1 Placeholder1 2 Placeholder2 0
Option2 Placeholder1 4
Option3 Placeholder1 2 Placeholder2 -2 Placeholder3 6
I have a listbox of the Options and a button that creates a new window with the values for the selected Option. What I want to do is to create n number of buttons when this new window is created, where n is the number of values of the selected Options (i.e. 2, 1 and 3 for Options 1 through 3, respectively). I want it to look something like this:
Option1
Placeholder1 [button1 containing value=2]
Placeholder2 [button2 containing value=0]
... which is of course quite simple if I just assign a button for the maximum number of n that I know will be present, but I'm wondering if there's a way to do it more arbitrarily. Obviously the same problem applies to the arbitrary number of Labels I would need to use for the value names (the 'PlaceholderX's) as well.
I've been trying to do some reading on this type of thing, variable variables, etc., and it seems it's a very big NO-NO most (if not all) of the time. Some advocate the use of dictionaries, but I don't really get how that's supposed to work (i.e. naming variables from entries/values in a dict).
Is this something that can (and should) be done, or am I better off just creating all the buttons manually?
[EDIT: added code]
from tkinter import *
import csv
root = Tk()
root.wm_title("RP")
listFrame = Frame(root, bd=5)
listFrame.grid(row=1, column=2)
listbox1 = Listbox(listFrame)
listbox1.insert(1, "Option1")
listbox1.insert(2, "Option2")
listbox1.insert(3, "Option3")
listbox1.pack()
infoFrame = Frame(root, bd=5)
infoFrame.grid(row=1, column=3)
info_message = Message(infoFrame, width=300)
info_message.pack()
# Read stats from file
stat_file = open('DiceTest.csv', 'rU')
all_stats = list(csv.reader(stat_file, delimiter=';'))
def list_selection(event):
# gets selection and info/stats for info_message
index = int(listbox1.curselection()[0])
stats = all_stats[index]
infotext = str(stats[0]) # just the name
for n in range(int((len(stats)-2)/2)): # rest of the stats
infotext += ('\n' + str(stats[n*2 + 2]) + '\t' + str(stats[n*2 + 3]))
info_message.config(text=infotext)
listbox1.bind('<ButtonRelease-1>', list_selection)
def load():
top = Toplevel()
top.geometry('300x100')
index = int(listbox1.curselection()[0])
stats = all_stats[index]
# some way to create arbitrary buttons/labels here (?)
load_button = Button(root, text='Load', command=load)
load_button.grid(row=2, column=2)
root.mainloop()
Oh, and every button should have the same command/function, which reduces whatever value currently is in the button by 2.
Figured it out! Creating the widgets dynamically with a dictionary worked just fine, but calling the correct widget on the various button presses was more difficult. This is what I had:
buttons = dict()
for k in range(len(info)):
buttons[k] = Button(top, text=info[k], command=lambda: my_function(buttons[k]))
... which would work, but all button presses would call the function with the last created button as the target. All that was needed was a few extra characters in the command part of the buttons:
buttons = dict()
for k in range(len(info)):
buttons[k] = Button(top, text=info[k], command=lambda a=k: my_function(buttons[a]))
... which I assume works because it somehow stores the value of k inside a rather than taking the last known value of k, i.e. equivalent to the last created button. Is this correct?
You can store Buttons in a list:
from tkinter import *
master = Tk()
buttons = []
n = 10
for i in range(n):
button = Button(master, text = str(i))
button.pack()
buttons.append(button)
master.mainloop()

Resources