I have a list of tkinter entry fields generated dynamically. I need to grab the contents of each field while knowing what it's referring to. I have tried adding something like "entry"+count = Entry(..) but as expected it wont work like that and is really quite a stupid design practice to attempt so I'm thinking it needs to be in the form of an array but can't seem to figure out how to do that with an Entry.
rowNum = 0
items = []
def tableGen(name):
global rowNum
entry = Entry(win).grid(row=rowNum, column=0)
label = Label(win, text=name).grid(row=rowNum, column=1)
self.items[rowNum] = [entry, label] # Problem Area 1
rowNum += 1
def generate(): # Print each items value
for item in items:
print (item.get()) # Problem Area 2
# Generates Entrys and labels from file
e = ET.parse('file.xml').getroot()
for atype in e.findall('things'):
tableGen(atype.get('name'))
generateBtn = Button(root, text="Generate", command=generate).pack()
generate()
root.mainloop()
I've simplifed the code to the minimal required to see what I'm trying to do. The problem seems to be either with where I put the items in a table or try and parse them (problem are 1 & 2). I'm very new to python and can't figure out how to do this. In it's current state the project fails with in tableGen self.items[rowNum] = [entry, label] Name Error: name 'self' is not defined.
Am I doing this wrong? Is there another way this can be done?
Solution
rowNum = 0
items = []
def tableGen(name):
global rowNum
entry = Entry(win)
entry.grid(row=rowNum, column=0)
label = Label(win, text=name)
label.grid(row=rowNum, column=1)
items.append([entry, label])
rowNum += 1
def generate(): # Print each items value
for item in items:
print('Entry:', item[0].get(), 'Label:', item[1].cget("text"))
# Generates Entrys and labels from file
e = ET.parse('file.xml').getroot()
for atype in e.findall('things'):
tableGen(atype.get('name'))
generateBtn = Button(root, text="Generate", command=generate).pack()
generate()
root.mainloop()
Use append() to add items to the end of a list:
def tableGen(name):
...
items.append([entry, label])
rowNum += 1
This will generate a list of lists, so you need to use an index to access the elements of each item:
def generate(): # Print each items value
for item in items:
print('Entry:', item[0].get(), 'Label:', item[1].get())
Related
I have an instance of two different "Listbox" objects
I save each of them in a different reference name
example:
l1 = tkinter.Listbox(exampleFrame)
l2 = tkinter.Listbox(exampleFrame)
l1.setvar("check", "1")
l2.setvar("check", "2")
print("var is - " + l1.getvar("check"))
the problem is whenever I call getvar of l1 the output will be the var of l2
output:
var is - 2
I can assume from this result that the Listbox setvar/getvar functions are staticmethod for all Listbox objects?
if so is there any option to store data for each Listbox object with built-in functions?
my goal is to be able to store an id for each listbox option
for example:
Listbox1:
Albert
Michel
Drake
will store the id of each user option inside a var.
so when I call the insert method
I store the user.id for each index in the setvar
l1.insert(index, user.name)
l1.setvar("var_"+index, user.id)
so when I call getvar("var_"+index)
I'll get the ID of the selected user
the current solution will work good only if I have one listbox in my entire project.
any good solution to store data for multi listbox objects?
-- Edit --
What I've tried to achieve is an option to store more values inside a Listbox option
Example:
l1 = Listbox(frame)
l1.insert(0, 'Drake')
l1.insert(1, 'Jacob')
so instead of having only the index and the string in the Listbox I'll have more stored data of each option.
#1966bc suggested to create a dict and use the option index as the key
so if I want to store more data in example for Drake I can do something like:
l1.anydictname = {}
l1.anydictname[0] = {'Name' : 'Drake', 'data' : 'moredata'}
so now I can check data of Drake I can use Drake list index and point for the data as l1.anydictname[index].data or name or whatever data I'm storing inside.
Thanks, works like magic. If someone have even a cleaner way of doing it you may suggest
If I understand correctly, to do what you want you have to associate the listbox index with a dictionary.
I attach a working example
When you select a line of listbox you get something as
on_listbox_select: index = 2, pk = 3 values = Drake
where index is the listbox index and pk is the id of the selected record.
In the example I've populate the listbox with an immaginary recordset composed of a primary key, a name and a field to enhance its status
rs = [(1,"Albert",1), (2,"Michel",1), (3,"Drake",0), (4,"Alan",1),(5,"George",0)]
#!/usr/bin/python3
import sys
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
class App(tk.Tk):
"""Application start here"""
def __init__(self):
super().__init__()
self.protocol("WM_DELETE_WINDOW", self.on_close)
self.title("Simple App")
self.init_ui()
self.set_values()
def init_ui(self):
w = ttk.Frame(self, padding=8)
r = 0
c = 1
self.lstItems = tk.Listbox(w)
self.lstItems.bind("<<ListboxSelect>>", self.on_listbox_select)
self.lstItems.grid(row=r, column=c, padx=5, pady=5)
w.grid(row=0, column=0, sticky=tk.N+tk.W+tk.S+tk.E)
def set_values(self):
self.lstItems.delete(0, tk.END)
index = 0
self.dict_items = {}
#simulate a recordset the fields are: pk, name, enable,
#if enable == 0 paint line gray
rs = [(1,"Albert",1),
(2,"Michel",1),
(3,"Drake",0),
(4,"Alan",1),
(5,"George",0)]
if rs:
for i in rs:
s = "{:}".format(i[1])
self.lstItems.insert(tk.END, s)
if i[2] != 1:
self.lstItems.itemconfig(index, {"bg":"light gray"})
self.dict_items[index] = i[0]
index += 1
def on_listbox_select(self, evt=None):
if self.lstItems.curselection():
index = self.lstItems.curselection()[0]
pk = self.dict_items.get(index)
s = self.lstItems.get(index)
print("on_listbox_select: index = {0}, pk = {1} values = {2}".format(index, pk, s))
def on_close(self,evt=None):
"""Close all"""
if messagebox.askokcancel(self.title(), "Do you want to quit?", parent=self):
self.destroy()
def main():
app = App()
app.mainloop()
if __name__ == '__main__':
main()
Trying to create a simple shopping list app, that will have user check the checkbox then enter the number of items. The button press should output a name with the number of item. for example, Apples 10, 1 banan etc. I would like to grow the fruits dictionary and later add other items to GUI, like meats/fish, general etc.
I thought the logical way would be to create a dictionary {key:value} where key would be the name of the checkbox, and a value will be the number inside entry text box.
Could you please show me how to do this? Thanks!
tried following some examples but could not understand how to extract a text="Apples" from checkbox and add this as a key into dictionary. Also played with lambda as output but nothing really materialized because all examples alreaddy had a dictionary created.
here is my example.
from tkinter import *
item_list = []
items_dict = {}
# mget gets output from checkboxes and prints results
def mget():
print(apples.get(), pears.get(), bananas.get(),apricots.get())
print(apples_qnty.get(),pears_qnty.get(), bananas_qnty.get(),
apricots_qnty.get())
#item_list.append(apples.get)
window = Tk()
apples = IntVar()
chk_apples = Checkbutton(window,text="Apples",
variable=apples).grid(row=1,sticky=W)
apples_qnty = StringVar()
apples_qnty = Entry(width=3)
apples_qnty.grid(column=1, row = 1)
item_list.append("Apples")
pears = IntVar()
chk_pears = Checkbutton(window,text="Pears",
variable=pears).grid(row=2,sticky=W)
pears_qnty = StringVar()
pears_qnty = Entry(width=3)
pears_qnty.grid(column=1, row = 2)
item_list.append("Pears")
bananas = IntVar()
Checkbutton(window,text="Bananas",
variable=bananas).grid(row=3,sticky=W)
bananas_qnty = StringVar()
bananas_qnty = Entry(width=3)
bananas_qnty.grid(column=1, row = 3)
item_list.append("Bananas")
apricots = IntVar()
Checkbutton(window,text="Apricots",
variable=apricots).grid(row=4,sticky=W)
apricots_qnty = StringVar()
apricots_qnty = Entry(width=3)
apricots_qnty.grid(column=1, row = 4)
item_list.append("Apricots")
print(item_list) # just to see list
Button(window,text="print check box states", command = mget).grid(row=5,sticky=W)
Button(window,text="exit", command =window.destroy).grid(row=5,sticky=E)
window.mainloop()
I would like the program to do the following:
user checks on checkbuttons and enters numbers of items.
pressing the button would output the items selected.
For example, Apples 4, Bananas 1 etc...
If the checkbutton is unchecked then don't output anything.
The variable parameter in each of your Checkbutton functions is currently IntVar(). This means that when it is checked it returns a value of 1 and when it is unchecked it returns the value of 0.
You can change the variable to a StringVar() and then assign it with a offvalue (unchecked) and onvalue (checked). This will give you scope to decide on what these values can return.
Using apples as an example:
#Your current code using IntVar()
apples = IntVar()
chk_apples = Checkbutton(window,text="Apples",
variable=apples).grid(row=1,sticky=W)
#Modification using StringVar()
apples = StringVar()
chk_apples = Checkbutton(window,text="Apples",
variable=apples, offvalue="", onvalue="Apples").grid(row=1,sticky=W)
By setting the onvalue as "Apples" the checkbutton will return the string variable "Apples".
I can then apply that logic for each of the fruits and then finally amend the mget function to the below:
def mget():
fruit_list = [apples.get(), pears.get(), bananas.get(), apricots.get()]
fruit_quantity =[apples_qnty.get(), pears_qnty.get() , bananas_qnty.get(),
apricots_qnty.get()]
fruit_dictionary = dict(zip(fruit_list,fruit_quantity)) #combines the two lists by their indices to create a dictionary
print(fruit_dictionary)
for keys, values in fruit_dictionary.items():
if keys == "" or values =="":
pass
else:
print(keys, values)
As a side note it would be good practice and improve readability if you assign each get.function to a variable i.e.
fruit_apple = apple.get()
I have a dynamically created Tkinter checkbutton widget, which takes in the contents of a list of usernames. I then displayed those names with a checkbox alongside.
What I need to do is obviously collect which usernames have been checked, so I can pass that off to another function to action.
How should I write the variable part of this so it creates a new list of chosen usernames?
What I have thus far:
def delprof_results(users_delprof):
for i in range(len(users_delprof)):
c = Checkbutton(resultsFrame, text=users_delprof[i], variable=users_delprof[i])
c.pack(anchor=W)
def delprof_del():
users_chosen = []
print str(users_delprof[i]).get() # Works up until this point. How to get individual variable with ID.
del_but = Button(resultsFrame, text="Delete", width=7, height=1, command=delprof_del)
del_but.pack(side=LEFT)
Thanks in advance,
Chris.
If you want to reach individual objects, simply keep a reference to the individual objects instead of creating objects while overwriting the same variable with each iteration of a loop like:
for i in range(30):
a = i
How to reach a's state where it was 13? Well, you can't as it's overwritten.
Instead, use collection types. In the example below I used dict:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
def upon_select(widget):
print("{}'s value is {}.".format(widget['text'], widget.var.get()))
if __name__ == '__main__':
root = tk.Tk()
names = {"Chester", "James", "Mike"}
username_cbs = dict()
for name in names:
username_cbs[name] = tk.Checkbutton(root, text=name,
onvalue=True, offvalue=False)
username_cbs[name].var = tk.BooleanVar()
username_cbs[name]['variable'] = username_cbs[name].var
username_cbs[name]['command'] = lambda w=username_cbs[name]: \
upon_select(w)
username_cbs[name].pack()
tk.mainloop()
You could make a list of values from the checkbuttons:
values = []
for i in range(len(users_delprof)):
v = IntVar()
c = Checkbutton(master, text="Don't show this again", variable=v)
c.var = v
values.append(v)
Now you can check the value by looking in the list values, and getting the value of a checkbutton with v.get().
I want a long list of checkbuttons and entries. I create them using a for loop, however that won't allow me to assign unique names to the objects (e.g. textbox1, textbox2, etc), because you can't do "foo" + char(i) = whatever. So I created two lists, one for the checkbuttons and one for the entries. But how yould you access the objects in the list?
slot1list_check = []
slot1list_text = []
for x in range (1,21):
label = "S1Ch. " + str(x)
chk = Checkbutton(app, text=label).grid(row=(x+1), column=0)
txt = Entry(app, text=label).grid(row=(x+1), column=1)
slot1list_check.append(chk)
slot1list_text.append(txt)
slot1list_text[x-1].insert(0,"whatever in this field")
I get the following error: AttributeError: 'NoneType' object has no attribute 'insert', referring to the last line in the code above.
How would I go about accessing the objects in the list? Is there a smarter/better way to create a large number of objects and assign them sequential names?
The .grid() method returns None as it alters the widget in-place. It does not return the CheckButton() or Entry() element.
Call .grid() separately instead:
slot1list_check = []
slot1list_text = []
for x in range (1,21):
label = "S1Ch. " + str(x)
chk = Checkbutton(app, text=label)
chk.grid(row=(x+1), column=0)
txt = Entry(app, text=label)
txt.grid(row=(x+1), column=1)
slot1list_check.append(chk)
slot1list_text.append(txt)
slot1list_text[x-1].insert(0,"whatever in this field")
Note that I moved the .grid() calls to a new line, using the chk and txt references.
You can reference the last element in a list with -1 as negative indices count from the end of the list backwards. In this case you already have the txt reference to the same object, so you could use that directly.
Personally, I'd just use range(20) and use + 1 where needed:
slot1list_check = []
slot1list_text = []
for x in range(20):
label = "S1Ch. {}".format(x + 1)
chk = Checkbutton(app, text=label)
chk.grid(row=x + 2, column=0)
slot1list_check.append(chk)
txt = Entry(app, text=label)
txt.grid(row=x + 2, column=1)
txt.insert(0,"whatever in this field")
slot1list_text.append(txt)
I am storing a matrix of Entries, each attached to a unique StringVar. On the callback for the StringVar, I want to be able to find which StringVar it was, with respect to the matrix. This is what I have:
def callback(sv, index):
print index
for i in range(ncols):
for j in range(self.nrows):
sv = StringVar()
uniqueId = str(i)+str(j)
sv.trace("w", lambda name, index, mode, sv=sv: callback(sv, uniqueId))
self.results[i][j] = Entry(gridtable,textvariable=sv,bg='grey',borderwidth=1,fg='black',width=self.widget_width).grid(row=j+2,column=i+1,sticky='ew')
However, it always prints the same index ('718' - which is '7'+'18': the values that i and j stop at). How can I get a unique identifier for these StringVars?
The sv variable is changing in your code, but uniqueID is getting stuck at the last value it's given. You can set it to remember its value on that iteration of the loop by including it in the lambda expression, like this:
from Tkinter import *
def callback(var, index):
print var, index
root = Tk()
for i in range(10):
var = StringVar()
uniqueID = i
var.trace('w', lambda name,index,mode, var=var, uniqueID=uniqueID: callback(var, uniqueID))
Entry(root, textvariable=var).pack()
mainloop()