How to create checkboxes from a list - python

I'm trying to learn to code with Python. I'm trying to create checkbuttons from a list by using a for loop. After the user checks the appropriate boxes, I'd like to end up with a subset of the original list containing only the values corresponding to checked boxes. I've gotten the checkbuttons to show ok, but what do I need to do to get the values of the checkboxes into a list? I've looked at several examples but I may not have understood correctly since I'm still learning all of this. Thanks for the help.
category_data = ['Home', 'Auto', 'Groceries','Medical']
for category in category_data:
l=Checkbutton(root, text= category,onvalue = True, offvalue= False)
l.pack(anchor=W)

First you need to assign a BooleanVar to each Checkbox in order to get its status. Then you need to use a dict to store those BooleanVar using category as the key.
Later you can go through the dict to create the required list of checked categories.
from tkinter import *
root = Tk()
category_data = ['Home', 'Auto', 'Groceries', 'Medical']
cb_vars = {} # dict to store the BooleanVar
for category in category_data:
var = BooleanVar()
l = Checkbutton(root, text=category, variable=var, onvalue=True, offvalue=False)
l.pack(anchor=W)
cb_vars[category] = var # store the BooleanVar
def show_selected_categories():
selected_category = [c for c in cb_vars if cb_vars[c].get()]
print(selected_category)
Button(root, text="Check", command=show_selected_categories).pack()
root.mainloop()

you can create a list, category_values, and append tk.BooleanVar objects to it that are used for the checkbuttons.
category_data = ['Home', 'Auto', 'Groceries','Medical']
category_values = []
for category in category_data:
category_values.append(BooleanVar())
l=Checkbutton(root, text= category, variable=category_values[-1], onvalue = True, offvalue= False)
l.pack(anchor=W)
and to get the value:
category_values[<category number>].get()

Related

My code isn't getting the values I insert into an entry

I've got some code in python using tkinter which retrieves the name of a room and uses that to to insert into an SQL database the room name and which site it belongs to. However when I run the code its not retrieving the room name from the entry box.
Can anyone help?
def addroom():
global screen14
global roomsinsite
roomsinsite = StringVar()
screen14 = Tk()
screen14.geometry("300x250")
screen14.title("Insert rooms")
Label(screen14, text = "Insert room name:", bg = "LightSkyBlue1", width = "300", height = "2").pack()
Label(screen14, text = "").pack()
roomsinsite_entry = Entry(screen14, textvariable = roomsinsite)
roomsinsite_entry.pack()
Button(screen14, text = "Register room", width = "12", height = "1", command = insertroom).pack()
def insertroom():
sitename4_info = sitename2.get()
print(sitename4_info)
roomname1_info = roomsinsite.get()
print(roomname1_info)
cursor = cnn.cursor()
# SQL to select the siteID and insert rooms for that paticular site.
siteID_fetch3 = "SELECT siteID FROM Sites WHERE siteName = %s"
cursor.execute(siteID_fetch3, [sitename4_info])
siteID_fetch3 = cursor.fetchall()
# print out the values retrieved
print(siteID_fetch3[0][0])
insertRooms = "INSERT INTO `rooms`(`siteID_fk2`, `roomname`) VALUES (%s,%s)"
insertRooms_val = (siteID_fetch3[0][0], roomname1_info)
cursor.execute(insertRooms, insertRooms_val)
# print out the rows inserted.
print(cursor.rowcount)
cnn.commit()
You are probably having more than one Tk in your code, which means your StringVar does not know which Tk to belong to. So here there are three possible solutions:
Avoid using more than one Tk and replace all child windows with Toplevel, so:
screen14 = Toplevel()
roomsinsite = StringVar()
If you are adamant that you want to use more than one instance of Tk then you can specify master for each StringVar, like:
screen14 = Tk()
roomsinsite = StringVar(master=screen14)
To be honest, I wouldn't use StringVar with entry widgets except when I want to use trace, here if the only purpose of using StringVar is for getting the value of the entry widget, then remove it and use get() method of the entry widget, like:
roomname1_info = roomsinsite_entry.get()
The combination of first and third method seems like best practice, if you ask me. Also here, even if you are not using more than one Tk, one of the above methods would certainly solve the problem(as far as something is inputted inside the entry and then the insertroom() is called).

Changing entry text in tkinter, if the entry is created from list

I created 12 Entry boxes using a for loop with a default value of N/A. Any change in the text of entry is detected through .trace method.
I want to use reset button to make all the text on the Entry Boxes back to N/A
from tkinter import *
root = Tk()
t_diesel_price_EnF_variable = ["JanVar", "FebVar", "MarVar", "AprVar","MayVar","JuneVar","JulyVar","AugVar","SeptVar", "OctVar", "NovVar", "DecVar"]
t_diesel_price_EnF_values = ["N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A"]
def EnFChanging(*events):
for EnF in range(0,len(t_diesel_price_EnF_variable)):
t_diesel_price_EnF_values[EnF]=t_diesel_price_EnF_variable[EnF].get()
try:
t_diesel_price_EnF_values[EnF] = float(t_diesel_price_EnF_values[EnF])
except ValueError:
pass
print(t_diesel_price_EnF_values)
for EnF in range(0,len(t_diesel_price_EnF_values)):
t_diesel_price_EnF_variable[EnF] = StringVar(root , value = "N/A")
t_diesel_price = Entry(root , textvariable = t_diesel_price_EnF_variable[EnF], width = 10).pack()
t_diesel_price_EnF_variable[EnF].trace("w",EnFChanging)
def ChangeText():
for EnF in range(0, len(t_diesel_price_EnF_values)):
t_diesel_price[EnF].delete(0,END)
t_diesel_price[EnF].insert(0,"N/A")
return
b1 = Button(root, text = "Reset" , command = ChangeText).pack()
root.mainloop()
When I press the button it gives an error t_diesel_price[EnF].delete(0,END)
TypeError: 'NoneType' object is not subscriptable
What should I do now, Please ignore the basic errors of programming as I am a Mechanical Engineer with not a programming back ground. And I have to make a lot of other boxes too for my energy calculator.
You trying treat t_diesel_price as an Entry (and as a list) when your variable is None.
First of all I suggest you to install some IDE (e.g. PyCharm) and place break points to see whats wrong with variable!
Your problem occures because you create and pack your widget in one line! So t_diesel_price is None because pack() always returns None (link).
Just split your declaration and packing to:
t_diesel_price = Entry(root , textvariable = t_diesel_price_EnF_variable[EnF], width = 10)
t_diesel_price.pack()
After that it's works for me, except this fact that t_diesel_price is last created entry and the value changes only in it. So I assume that you need another list to iterate over entries:
...
# another fresh list
t_diesel_price_EnF_entries = list()
...
# declare entry
t_diesel_price = Entry(root, textvariable=t_diesel_price_EnF_variable[EnF], width=10)
# pack entry
t_diesel_price.pack()
# append entry to list
t_diesel_price_EnF_entries.append(t_diesel_price)
...
def ChangeText():
# iterate over entries
for diesel_price in t_diesel_price_EnF_entries:
diesel_price.delete(0,END)
diesel_price.insert(0,"N/A")
...
Alternatively you can iterate over StringVar's if you don't wanna store your entries at all:
def ChangeText():
# iterate over stringvars
for EnF in range(len(t_diesel_price_EnF_variable)):
t_diesel_price_EnF_variable[EnF].set('N/A')
And you can make it more readable as in example with entry iterating:
def ChangeText():
# iterate over stringvars
for string_var in t_diesel_price_EnF_variable:
string_var.set('N/A')
Cheers!

Multiple ComboBoxes in Tkinter Python

I'm trying to generate multiple ComboBoxes with values from a "config.ini" file, the config.ini file data is:
priority1 = Normal:farty-blobble-fx.wav:2
priority8 = Reclamacao:buzzy-blop.wav:3
priority3 = Critico:farty-blobble-fx.wav:5
priority2 = Urgente:echo-blip-thing.wav:4
and the goal is turning the sound files names to the select values in the comboboxes.
My code to generate the comboboxes is:
content_data = []
for name, value in parser.items(section_name):
if name=="name":
self.note.add(self.tab2, text = value)
else:
data_prior = value.split(":")
self.PRIOR_LABEL = Label(self.tab2, text=data_prior[0])
self.PRIOR_LABEL.grid(row=data_prior[2],column=0,pady=(10, 2),padx=(40,0))
self.PRIOR_SOUNDS = None
self.PRIOR_SOUNDS = None
self.box_value = StringVar()
self.PRIOR_SOUNDS = Combobox(self.tab2, textvariable=self.box_value,state='readonly',width=35)
self.PRIOR_SOUNDS['values'] = getSoundsName()
self.PRIOR_SOUNDS.current(int(getSoundsName().index(data_prior[1])))
self.PRIOR_SOUNDS.grid(row=data_prior[2],column=1,pady=(10, 2),padx=(30,0))
self.PLAY = Button(self.tab2)
self.PLAY["width"] = 5
self.PLAY["text"] = "Play"
self.PLAY["command"] = lambda:playSound(self.PRIOR_SOUNDS.get())
self.PLAY.grid(row=data_prior[2], column=3,pady=(10,2),padx=(5,0))
And i was unable to show the current values of the "config.ini" file in the comboboxes.
Thank you in advance.
The problem is that you're creating more than one combobox, yet you keep overwriting the variables in each iteration of the loop. At the end of the loop, self.PRIOR_SOUNDS will always point to the last combobox that you created. The same is true for self.box_value, self.PLAY, etc.
The simplest solution is to use an array or dictionary to store all of your variables. A dictionary lets you reference each widget or variable by name; using a list lets you reference them by their ordinal position.
A solution using a dictionary would look something like this:
self.combo_var = {}
self.combo = {}
for name, value in parser.items(section_name):
...
self.combo_var[name] = StringVar()
self.combo[name] = Combobox(..., textvariable = self.combo_var[name])
...

tkinter Treeview: get selected item values

I'm just starting with a small tkinter tree program in python 3.4.
I'm stuck with returning the first value of the row selected.
I have multiple rows with 4 columns and I am calling a function on left-click on a item:
tree.bind('<Button-1>', selectItem)
The function:
def selectItem(a):
curItem = tree.focus()
print(curItem, a)
This gives me something like this:
I003 <tkinter.Event object at 0x0179D130>
It looks like the selected item gets identified correctly.
All I need now is how to get the first value in the row.
tree-creation:
from tkinter import *
from tkinter import ttk
def selectItem():
pass
root = Tk()
tree = ttk.Treeview(root, columns=("size", "modified"))
tree["columns"] = ("date", "time", "loc")
tree.column("date", width=65)
tree.column("time", width=40)
tree.column("loc", width=100)
tree.heading("date", text="Date")
tree.heading("time", text="Time")
tree.heading("loc", text="Loc")
tree.bind('<Button-1>', selectItem)
tree.insert("","end",text = "Name",values = ("Date","Time","Loc"))
tree.grid()
root.mainloop()
To get the selected item and all its attributes and values, you can use the item method:
def selectItem(a):
curItem = tree.focus()
print tree.item(curItem)
This will output a dictionary, from which you can then easily retrieve individual values:
{'text': 'Name', 'image': '', 'values': [u'Date', u'Time', u'Loc'], 'open': 0, 'tags': ''}
Also note that the callback will be executed before the focus in the tree changed, i.e. you will get the item that was selected before you clicked the new item. One way to solve this is to use the event type ButtonRelease instead.
tree.bind('<ButtonRelease-1>', selectItem)
This is a good example of getting information of a row selected in a python tkinter treeview. Allow me to represent a final neat coding discussed here. I use python 3.8
from tkinter import *
from tkinter import ttk
def selectItem(a):
curItem = tree.focus()
print(tree.item(curItem))
root = Tk()
tree = ttk.Treeview(root, columns=("size", "modified"))
tree["columns"] = ("date", "time", "loc")
tree.column("date", width=65)
tree.column("time", width=40)
tree.column("loc", width=100)
tree.heading("date", text="Date")
tree.heading("time", text="Time")
tree.heading("loc", text="Loc")
tree.bind('<ButtonRelease-1>', selectItem)
tree.insert("","end",text = "Name",values = ("Date","Time","Loc"))
tree.grid()
root.mainloop()
Ther result is
{'text': 'Name', 'image': '', 'values': ['Date', 'Time', 'Loc'], 'open': 0, 'tags': ''}
You can copy, paste and try it. It's good.
And if you want to get row as {column name:value pair}:
def selectItem(a):
curRow = tree.set(a)
loc_value = curRow["loc"]
Or you want to get cell value by column name
def selectItem(a):
loc_value = tree.set(a, column="loc")

Using list defined in one function in another function. Python 2.7

I have a problem with the following code. Now I'm very new to programming in general, and most of the code is copied off the internet, and I adjusted it so it would work the way I want it to. So if there is no easy way of solving it, that's ok. Maybe you can just point out some topics of programming or python, that I should read about.
I try to explain it anyways. I have defined the function query(), that makes some changes to sqlite databases. The input is a list. That function works just fine if I use it just by itself.
Now I'm trying to have an Interface, where I can define, what should be inside that list, depending on what checkboxes are checked. Then I want to execute the function with that specific list, when I press a button. The checkboxes are generated just fine, the button, too. Also, when I check or uncheck the buttons, it updates the list just fine, and it shows the new updated list in the interpreter.
The problem is, that the button does not work:
1. It doesn't use the new updated list, instead it uses an empty list ()
2. When I input a predefined list, that is not empty, it automatically runs query() without me even clicking the button.
I may not have explained this well, but I hope you understand what my problem is.
Thanks for the help
`
def chkbox_checked():
for ix, item in enumerate(cb):
opt[ix]=(cb_v[ix].get())
print opt
def query(opt):
import sqlite3
connection = sqlite3.connect("gather.sqlite")
cursor1 = connection.cursor()
cursor1.execute('Drop table IF EXISTS matches')
cursor1.execute('CREATE TABLE matches(date TEXT, team1 TEXT, team2 TEXT, league TEXT)')
cursor1.execute('DELETE FROM "main"."matches"')
for i in range(0, len(opt)):
a=opt[i]
cursor1.execute('INSERT INTO matches (date, team1, team2, league) SELECT * FROM gather WHERE team1=? or team2=? or league=?', (a,a,a,))
cursor1.execute('Drop table IF EXISTS matchessorted')
cursor1.execute('CREATE TABLE matchessorted(date TEXT, team1 TEXT, team2 TEXT, league TEXT)')
cursor1.execute('DELETE FROM "main"."matchessorted"')
cursor1.execute('INSERT INTO matchessorted (date, team1, team2, league) SELECT * FROM matches ORDER BY date')
connection.commit()
import Tkinter as tk
from Tkinter import *
opt = []
root = tk.Tk()
mylist = [
'name1',
'name2',
'name3'
]
cb = []
cb_v = []
for ix, text in enumerate(mylist):
cb_v.append(tk.StringVar())
off_value=0
cb.append(tk.Checkbutton(root, text=text, onvalue=text,offvalue=off_value,
variable=cb_v[ix],
command=chkbox_checked))
cb[ix].grid(row=ix, column=0, sticky='w')
opt.append(off_value)
cb[-1].deselect()
label = tk.Label(root, width=20)
label.grid(row=ix+1, column=0, sticky='w')
button1 = Button(root, text = "Calculate", command = query(opt))
button1.grid(column=1, row=0, sticky=W)
root.mainloop()
`
A couple of points about how to structure your code: You need to write a function that populates the list based on your selection. It can return a list call 'options' and when you want to execute the code inside the query, you call the function that constructs the options list. The query function will have a statement like this:
options = get_options() #assuming the function that populates the options is called get_options
and then you execute the query function's code.
button1 = Button(root, text = "Calculate", command = query(opt))
This calls query(opt) immediately, before you create your Button, and passes the result of that call (None) to the Button constructor as the command argument. What you really want is a function that, when called, executes query(opt). Something like this:
def calculate_clicked():
query(opt)
button1 = Button(root, text = "Calculate", command = calculate_clicked)
or this:
button1 = Button(root, text = "Calculate", command = lambda : query(opt))

Categories

Resources