I'm trying to create a set of radiobuttons in Tkinter. One of the attributes I'd really like to have is the ability to tell which radiobutton was last entered. I tried to bind to each radiobutton. However when the event is triggered it returns the same value each time.
What am I doing wrong?
snippet:
i = 0
while i < 5 :
Frame = Tkinter.Frame(self.WS.SW.OptFrame, width=125, height=22, bd=1,
bg=self.WSbg)
Frame.grid(column=0, row=4 + i)
Frame.grid_propagate(0)
self.WS.SW.SearchFrame.append(Frame)
RB = Tkinter.Radiobutton(self.WS.SW.SearchFrame[i], value=i, #command=self.WSRB_UD,
variable=self.WS.RBvar, indicatoron=0, font=self.WSfo,
fg=self.WSfg, activeforeground=self.WSfg, bg=self.WSbg, activebackground=self.WSbg,
selectcolor=self.WSbg, bd=self.WSbw)
RB.grid()
RB.bind( "<Enter>", lambda event: self.WSRB_UD(event, i))
self.WS.SW.SearchRB.append(RB)
i = i + 1
self.QuickLinkList= []
self.WS_timer_count = 0
def WSRB_UD(self, event, opt):
m = self.WS.SW.SearchRB[opt-1].cget("value")
print m
Your lambda needs to be something like this:
lambda event, i=i: self.WSRB_UD(event, i))
This creates a local variable i inside the lambda that is bound to the value of i at the time the binding was created.
Related
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'm trying to make a GUI that display lots of checkbuttons, i create them from a list; make a dictionary form the list and assign each checkbutton a variable from the dictionary so i can check it's state later.
Problem is that all the checkbuttons are displayed in an 'alternate' state, even if i set the variable to either 0 or 1, i've also tried changing states, but nothing seems to help.
y = 0
for x in get_dir_names(r'D:\SKL\test\win10'):
drv_check[x] = Variable()
drv_check[x].set(0)
center_window(150, 500, top_child)
drv = ttk.Checkbutton(child_frame, text=x, variable=drv_check[x])
drv.grid(row=y, column=0, sticky=W)
y += 1
for reference
def get_dir_names(dir_path):
"""Get names only of all directories from a given path (none recursive)"""
drv_list = [x for x in os.walk(dir_path).__next__()[1]]
drv_name = dict({})
for y in drv_list:
tmp_ver = dir_path + r'\\' + y
drv_name[y] = (os.walk(tmp_ver).__next__()[1]).pop()
return drv_name
Figured it out, I've made a "toggle all" button and it seemed to fix it, but it's weird that it didn't work before.
here's the function i used:
def toggle_all(*args):
while True:
if toggle_all_var.get() == '1':
for name in drv_check:
drv_check[name].set('1')
elif toggle_all_var.get() == '0':
for name in drv_check:
drv_check[name].set('0')
ttk.Checkbutton(drv_frame, text='Toggle all', variable=toggle_all_var).grid(row=y, column=0, sticky=W)
Also i run the function in a new thread.
I'm writing a program for a calculator but i'm experiencing a small problem.
Whenever I press one of the buttons, it increases numbers by 9 every time, even though it should be i (from the for loop).
Please can someone tell me why its always 9 please?
Code -
import tkinter
plus = True
numbers = 0
def main():
def numButton(i):
global numbers
if plus == False:
numbers-=i
else:
numbers+=i
def quitHandler():
root.destroy()
def entryHandler():
global numbers
numbers+=int(text.get())
text.set("")
def printHandler():
text2.set(numbers)
def restartHandler():
global numbers
root.destroy()
plus = True
numbers = 0
main()
def plusHandler():
global plus
plus = True
def minusHandler():
global plus
plus = False
root = tkinter.Tk()
frame = tkinter.Frame(root).pack(side=tkinter.TOP)
text = tkinter.IntVar()
text2 = tkinter.IntVar()
text.set("")
text2.set("")
tkinter.Entry(frame,bd =8,textvariable=text).pack()
tkinter.Button(frame,padx=8,pady=8,bd=8,text="Enter",command=entryHandler).pack()
tkinter.Button(frame,padx=8,pady=8,bd=8,text="Quit",command=quitHandler).pack(side=tkinter.RIGHT)
tkinter.Button(frame,padx=8,pady=8,bd=8,text="Restart",command=restartHandler).pack(side=tkinter.RIGHT)
tkinter.Button(frame,padx=8,pady=8,bd=8,text="Print",command=printHandler).pack(side=tkinter.LEFT)
tkinter.Entry(frame,bd =8,textvariable=text2).pack(side=tkinter.LEFT)
_padx = 16
_pady = 16
_bd = 8
for i in range (1,10):
tkinter.Button(frame, padx = _padx, pady = _pady, bd = _bd, text = str(i), command = lambda: numButton(i)).pack(side = tkinter.LEFT)
tkinter.Button(frame,padx=8,pady=8,bd=8,text="+",command=plusHandler).pack(side=tkinter.LEFT)
tkinter.Button(frame,padx=8,pady=8,bd=8,text="-",command=minusHandler).pack(side=tkinter.LEFT)
main()
Could someone also tell me how to put all of it inside the code thing on this site, I cant figure it out and the ways the site's help shows me isn't working ( or admin fix please ).
OK thanks guys, someone's emailed me the solution:
tkinter.Button(frame, padx = _padx, pady = _pady, bd = _bd, text = str(i), command = lambda i=i: numButton(i)).pack(side = tkinter.LEFT)
Had to add the i=i after the lamda.
Can someone explain to me what the i=i thing does please?
Thanks
command = lambda: numButton(i)
This doesn't look up the value of i at the time of the lambda's creation and insert it into the function. When the lambda is called, then it looks up i. i is always 9 by that time.
There are several ways to get around the problem, all focused on ensuring that i is looked up at command's creation time instead of execution time. The one I'd use is functools.partial, a tool designed to associate a function with arguments:
from functools import partial
...
command=partial(numButton, i)
You can also use a default argument, which is kind of kludgy:
command=lambda i=i: numButton(i)
Or write a factory function:
def closure_maker(i):
def closure():
numButton(i)
return closure
...
command=closure_maker(i)
The lambda functions used for the commands reference the variable i:
for i in range (1,10):
tkinter.Button(frame, padx = _padx, pady = _pady, bd = _bd, text = str(i),
command = lambda: numButton(i)).pack(side = tkinter.LEFT)
When the command is executed and the lambda function runs, it selects button number i. But at that time, when the lambda function is executed, i contains the value 9 (That's the value i ends up with after the for loop that created the buttons was complete.
To solve this issue, make sure the lambda functions don't all share the same global variable. One way would be to use a function which creates a new local scope:
def numCommand(x):
return (lambda: numButton(x))
for i in range (1,10):
tkinter.Button(frame, padx = _padx, pady = _pady, bd = _bd, text = str(i),
command = numCommand(i)).pack(side = tkinter.LEFT)
Here each lambda function refers to its own x variable from its numCommand() call.
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()
I am writing a small Tkinter/Python program, that has a list of checkboxes with variable length (determined at run time).
I want to be able to read the state of all the checkboxes at any time, but I am not sure how I should go about that.
Here's the code snippet for generating the list (adopted from this post):
def relist(self):
self.text.delete(1.0,END)
p = subprocess.Popen (['ls', '/dev/'], stdout = subprocess.PIPE)
lst = p.communicate()[0].split('\n')
print lst
for item in lst:
v = tk.IntVar()
cb = tk.Checkbutton(text="/dev/%s" % item, variable=v, command=self.cb(index))
self.text.window_create("end", window=cb)
self.text.insert("end", "\n") # to force one checkbox per line
And my dummy handler:
def cb(self,idx):
print ("var is %s", str(idx))
lst[idx] = 1;
The problem is that my handler is getting called once (when the Checkbuttons are created), whereas I want it to get called everytime a Checkbutton is clicked (checked or unchecked), and when it is called, I want it to update lst.
Your CheckButton command is executing the callback because that's what you are telling it to do. The command is supposed to be a reference to a function that tkinter can execute when the checkbutton is clicked. Tkinter passes the event object to the callback function. See this Effbot tutorial, but it looks like you are trying to implement their pattern already. You can get a reference to the checkbutton from the event.widget attribute as explained here. Finally, you need to attach your variable to "self" if you want to refer to it in the callback.
def relist(self):
self.text.delete(1.0,END)
p = subprocess.Popen (['ls', '/dev/'], stdout = subprocess.PIPE)
lst = p.communicate()[0].split('\n')
print lst
self.var = tk.IntVar()
for item in lst:
cb = tk.Checkbutton(text="/dev/%s" % item, variable=self.var, command=self.myCallback)
self.text.window_create("end", window=cb)
self.text.insert("end", "\n") # to force one checkbox per line
def myCallback(self,event):
var = self.var.get()
print ("var is %s", str(var))
I think what you have asked for can be derived from here.
For each item in lst it must be previously created different IntVar() variable, just to indicate independent state of each checkbox.
I do not see other way than to create them manually (I assume you don't have hundred of checkboxes).
I will re-use the code from this answer and do the following:
def relist(self):
self.text.delete(1.0,END)
p = subprocess.Popen (['ls', '/dev/'], stdout = subprocess.PIPE)
lst = p.communicate()[0].split('\n')
print lst
self.var1 = tk.IntVar()
self.var2 = tk.IntVar()
self.var3 = tk.IntVar()
.
.
.
vars = [self.var1,self.var2,self.var3,...]
for item, var in zip(self.lst, vars):
cb = tk.Checkbutton(text="/dev/%s" % item, variable=var, command= lambda: self.myCallback(var))
self.text.window_create("end", window=cb)
self.text.insert("end", "\n") # to force one checkbox per line
def myCallback(self,event,var):
each_var = var.get()
print ("var is %s", str(each_var))
I had the same issue. Try this one:
cb = tk.Checkbutton(text="/dev/%s" % item, variable=v, command=lambda: self.cb(index))
If you pass method as lambda function it executes the method on every change of the variable.
Personally i don't use a tk.IntVar() / tk.StringVar() etc. but maybe i should.
It may not be the best way to do that but i think it's pretty much easy to understand. don't hesitate to criticize and tell me what's really bad and not pythonic so i can improve myself (i'm still a newbie).
i make an interator then i create my checkbuttons in a loop and in the callback I pass in parameter the value of the checkbutton and the iterator.
...
self.listeColonneFile1 = []
self.chbFile1 = []
indice = 0
for column in dfFile1.columns:
btn = ttk.Checkbutton(self.frameCheckButtonsFile1,
text=column,
command=lambda i=indice, col=column: self.callback_onCheck(col, i)
)
self.chbFile1.append(btn)
self.chbFile1[indice].grid(row = indice, column = 0, sticky="nw")
self.chbFile1[indice].state(['!alternate'])
indice += 1
In my callback, i have a list of all the checkButtons which are checked (well, not the ChB but its text or its value) :
def callback_onCheck(self, column, indice):
if self.chbFile1[indice].instate(['selected']) == True:
self.listeColonneFile1.append(column)
else:
self.listeColonneFile1.remove(column)
PS : dfFile1 is a pandas DataFrame, see the doc