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
Related
Still pretty new to Python, so my apologies in advance...
I'm trying to use a button to move thru a List one item at a time.
It works the first time the button is clicked and moves to the second item in the List, but subsequent clicks keep returning the same values
from tkinter import *
window=Tk()
window.title('nextArrItem')
window.geometry("300x200+10+10")
options = ["H9Iu49E6Mxs", "YuWZNV4BkkY", "mBf6kJIbXLg", "Hz-xbM6jaRY"]
print("options[0] = " + options[0])
curItemText = options[0]
nextItemText = options[1]
curItem = 0
print('curItemText = '+curItemText)
print('nextItemText = '+nextItemText)
def nextArrItem(curItem=curItem+1):
print("str(curItem) = "+str(curItem))
try:
curItemText = options[curItem]
nextItemText = options[curItem+1]
print('curItemText = '+curItemText)
print('nextItemText = '+nextItemText)
curItem = curItem + 1
except:
print("End of Array Reached")
nextButton = Button(window, text="Next Item", command=nextArrItem)
nextButton.place(x=130, y=110)
window.mainloop()
When the Window opens initially, these values are returned:
options[0] = H9Iu49E6Mxs
curItemText = H9Iu49E6Mxs
nextItemText = YuWZNV4BkkY
The first click returns the following:
str(curItem) = 1
curItemText = YuWZNV4BkkY
nextItemText = mBf6kJIbXLg
Subsequent clicks keep returning the same values, so it only advances the first time and I'm not sure how to fix it. Although it probably doesn't look like it, this is the culmination of a lot of work just to get it to this point but I'm not sure where to go from here. I have the feeling the solution is going to be a true Homer Simpson "D'oh!" moment but I've steered this boat into shallow waters and need someone to help me from running aground...
Thanks in advance!
Paul
You need to increment the parameter each time to the next highest value. Currently your code just feeds the nextArrItem function the same value each time.
You could also try something to put the curItem inside a mutable data type so that it can be updated from within the scope of the function call like this:
...
options = ["H9Iu49E6Mxs", "YuWZNV4BkkY", "mBf6kJIbXLg", "Hz-xbM6jaRY"]
curItem = [0]
...
def nextArrItem(label=label):
try:
option = options[curItem[0]]
print(option)
label["text"] = option # updates label on each call
curItem[0] += 1 # increments index for option text
except IndexError:
print("End of Array Reached")
...
nextButton = Button(window, text="Next Item", command=nextArrItem)
...
Another way of doing it would be to bind the curItem variable to the window itself like this:
from tkinter import *
window=Tk()
window.curItem = 0
window.title('nextArrItem')
window.geometry("300x200+10+10")
options = ["H9Iu49E6Mxs", "YuWZNV4BkkY", "mBf6kJIbXLg", "Hz-xbM6jaRY"]
label = Label(window, text=options[window.curItem])
label.place(x=130, y=50)
def nextArrItem(label=label):
try:
option = options[window.curItem]
print(option)
label["text"] = option # updates label on each call
window.curItem += 1 # increments index for option text
except IndexError:
print("End of Array Reached")
nextButton = Button(window, text="Next Item", command=nextArrItem)
nextButton.place(x=130, y=110)
window.mainloop()
The issue you have is that curItem is both a global variable and a local variable in your callback function. You only ever update the local variable, so the change doesn't persist.
Here's how you're currently setting up the local variable, as an argument with a default value:
def nextArrItem(curItem=curItem+1):
The default value comes from the global variable, but it is only evaluated once, at the time the function is defined. It does not keep checking the global value, nor do changes to the local variable in the function change the global value either.
There's a better way. You can use a global statement to make it so that your function's code can directly read and write the global variable, so that there's only one curItem that everything is accessing the same way.
def nextArrItem():
global curItem
# the rest can be the same
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.
Is it possible to remove/deactivate variables from a line of code once it has been executed? If not, what are my other options? I wrote a code here to demonstrate what I mean:
from Tkinter import *
root = Tk()
ent_var1 = StringVar()
ent_var2 = StringVar()
ent_var3 = StringVar()
cbtn_var1 = BooleanVar()
cbtn_var2 = BooleanVar()
cbtn_var3 = BooleanVar()
ent1 = Entry(textvariable=ent_var1).pack()
ent2 = Entry(textvariable=ent_var2).pack()
ent3 = Entry(textvariable=ent_var3).pack()
cbtn1 = Checkbutton(text=1, variable=cbtn_var1).pack(side = LEFT)
cbtn2 = Checkbutton(text=2, variable=cbtn_var2).pack(side = LEFT)
cbtn3 = Checkbutton(text=3, variable=cbtn_var3).pack(side = LEFT)
# prints what was written in entires
def set_variables():
lbl1 = ent_var1.get()
lbl2 = ent_var2.get()
lbl3 = ent_var3.get()
print lbl1, lbl2, lbl3
return
# calls set_variables
btn1 = Button(root, text="Done!", command=set_variables).pack()
root.mainloop()
When you fill the entries and press "Done!", what was written is printed. But how do I make it so that when I press the checkboxes, the entry linked to it will not be printed the next the I press "Done!"? The checkbox with the text "1" should be linked with the first entry, and so on.
I came up with this:
def should_print():
global lbl_print
if cbtn1:
lbl_print += lbl1
if cbtn2:
lbl_print += lbl2
if cbtn3:
lbl_print += lbl3
But it would only print the values of my variables at that very moment, not the variables themselves (meaning I'd have to run this code every time a variable changes).
Thank you!
Your question is very hard to understand. I think what you want is for set_variables to only print the variables associated with a checked checkbox. If so, does the following do what you want?
def set_variables():
to_print = []
if cbtn_var1.get():
to_print.append(ent_var1.get())
if cbtn_var2.get():
to_print.append(ent_var2.get())
if cbtn_var3.get():
to_print.append(ent_var3.get())
print " ".join(to_print)
return
There are other ways to accomplish the same thing, but I'm guessing your main goal is to decide what to print based on which checkbuttons are checked. This does that, albeit in a rather ham-fisted manner.
Why don't you simply check in your set_variables function if each button is pressed? For example:
def set_variables():
if not cbtn_var1.get():
print ent_var1.get(),
if not cbtn_var2.get():
print ent_var2.get(),
if not cbtn_var3.get():
print ent_var3.get(),
print
The commas at the end of each print statement will cause it to not print a newline, which is taken care of by the print at the end. Also, this will make so that if the box is checked, the value they entered won't print. If you want it to print only if the box is checked, then remove the nots.
If you refactor your code a little bit, you can do the same thing with one line. First, add this line:
cbtn_var1 = BooleanVar()
cbtn_var2 = BooleanVar()
cbtn_var3 = BooleanVar()
buttonsAndValues = [(cbtn_var1,ent_var1), (cbtn_var2,ent_var2), (cbtn_var3,ent_var3)]
With the variables in a list, you can use a list comprehension in some Python magic:
def set_variables():
print ' '.join(value.get() for checked, value in buttonsAndValues if checked.get())
If you haven't seen list comprehensions before, I'd suggest you read up about them - they can be very handy.
I'm working on a GUI for a project in school. All the buttons that I have in my GUI are bound with functions that I have created. These functions call for already predefined functions. For some of the predefined functions, I need one or two arguments and I have solved that with entries. I type in the arguments in the right entries that are connected to the specific button and when I press the button, the function will run with the corresponding arguments.
The thing I want to do is to in some way when I press a button, the function should be saved to a list instead of being executed right away. And when I push the "run" button(a new button that I will create) everything in my list will be executed. I have been thinking about using a list box but I don't know exactly how they work or if its even possible to run a list box that contains a number of functions. Does someone have any ideas or solutions for me? Can I use the list box for this or is there something else that is better to use?
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.entry1 = IntVar()
self.entry2 = IntVar()
def do_something():
value1 = self.entry1.get()
value2 = self.entry2.get()
self.listbox.insert(END, "predefined_function(value1, value2)")
def run_listbox_contents():
pass
self.button = Button(frame, text="Move", command=lambda: do_something())
self.button.pack(side=TOP)
self.entry1.set("value1")
self.entry = Entry(frame, textvariable=self.entry1)
self.entry.pack(side=TOP)
self.entry2.set("value2")
self.entry = Entry(frame, textvariable=self.entry2)
self.entry.pack(side=TOP)
self.listbox = Listbox(master)
self.listbox.pack(side=TOP)
root = Tk()
app = App(root)
root.title("Mindstorms GUI")
root.geometry("800x1200")
root.mainloop()
root.destroy()
Just use a standard list.
something like this
def hest(txt):
print "hest: " +txt
def horse(txt):
print "horse: " + txt
funcList = []
funcList.append(hest)
funcList.append(horse)
for x in funcList:
x("Wow")
This outputs
hest: Wow
horse: Wow
Was this what you wanted?
If I were you, I wouldn't want to save functions to a list. I would suggest another solution for you.
I suppose you have heard of the principle of MVC (Model-View-Controller). In your case, the list box is a part of view, and the process that saves functions and then calls them at once is a part of controller. Separate them.
You might want to save and display any string in the list box to let the users know that the corresponding functions have been enlisted and ready to run. For example, save a string "Function1 aug1 aug2 aug3" or "Funtion2 aug1 aug2" or whatever you like as a handle of the corresponding function.
And for the controller part, write a function (let's say conductor()). It reads the handle strings from the list, parses them and calls the corresponding functions. Where you want to run the enlisted functions, there you just call conductor().
Update:
Due to your comment I understand that you are pretty new to program. Let me show you how to write a simplest parser with your given variable names.
def run_listbox():
to_do_list = #get the list of strings
for handle_string in to_do_list:
#Let's say you got
#handle_string = "Predfined_function1 value1 value2"
#by here
handle = handle_string.split(" ")
#Split the string by space, so you got
#handle = ["Predfined_function1", "value1", "value2"]
#by here
if handle[0] == "Predfined_function1":
Predfined_function1(handle[1], handle[2]) #Call Predfined_function1(value1, value2)
elif handle[0] == "Predfined_function2":
Predfined_function2(handle[1], handle[2])
#elif ...
#...
#elif ...
#...
#elif ...
#...
This is not a perfect parser, but I hope it could let you know what does a parser look like.