I have a GUI, using tkinter, that has multiple groups of checkbuttons. Each group of checkbuttons is comprised of a list of checkbuttons that corresponds with a list of tk.IntVar() for each checkbutton. That is, for checkbutton group "Alpha", cbAlpha[3] is one example of a checkbutton and cbAlphaVar[3] is its corresponding tk.IntVar().
Each individual group of checkbuttons has its own (Un)Check-All checkbutton that selects/deselects all of that group's checkbuttons at once. Each of these (Un)Check-All checkbuttons has its own individual function (called by its checkbutton command).
My desire is to have a single function (that can be called via a checkbutton command), or at least a simplified lambda, that can be used to check/uncheck ALL checkbuttons in a given group.
Generically, I would have a function that takes a list (of tk.IntVar()s) as an argument and just turns on/off each element of that list. But I cannot specify an argument in a checkbutton command, and so each one has its own function... Which results in redundant code. I haven't been able to nail down a structure that allows me to just use a single function, or at least a self-contained lambda in the command.
So for a simplified example, let's say I have two groups of checkbuttons: Alpha and Bravo. I have list of tk.Checkbutton()s in alphaCbs[], and a corresponding list of tk.IntVar()s in alphaCbVars[]. The same for bravoCbs[] and bravoCbVars[]. The Alpha group has a (Un)Check-All checkbutton as follows:
allAlphaCbVar = tk.IntVar()
allAlphaCb = tk.Checkbutton(root, text='Alpha', variable=allAlphaCbVar, command=allAlpha)
with the corresponding function:
def allAlpha():
for eachVar in alphaCbVars:
eachVar.set(allAlphaVar.get())
And I have a similar function for Bravo... and Charlie... and Delta... etc. So needlessly redundant. Any suggestions as to combining into a single function or lambda within the command?
Thanks!
As I understand it, the problem is that you have to write a bunch of functions like this:
def allAlpha():
for eachVar in alphaCbVars:
eachVar.set(allAlphaVar.get())
def allBravo():
for eachVar in bravoCbVars:
eachVar.set(allBravoVar.get())
… and so on.
What you want is a function that takes the list of vars and the all-var as parameters:
def allGroup(listOfVars, groupVar):
for eachVar in listOfVars:
eachVar.set(groupVar.get())
And now, you can use partial to create all of the individual partial functions:
from functools import partial
allAlphaCb = tk.Checkbutton(root, text='Alpha', variable=allAlphaCbVar,
command=partial(allGroup, alphaCbVars, allAlphaVar)
allBetaCb = tk.Checkbutton(root, text='Beta', variable=allBetaCbVar,
command=partial(allGroup, betaCbVars, allBetaVar)
However, you can reduce the duplication even further by writing a function that does all the stuff for building a group together in one place. It's hard to show that without more of your code to use, but it would look something like this:
def makeGroup(name): # maybe some more params needed for the individual cbs
allCbVars = []
allCbs = []
# some loop that generates the individual variables and checkboxes
# and puts them in those lists
cbVar = tk.IntVar()
cb = tk.Checkbutton(root, text=name, variable=cbVar,
command=partial(allGroup, allCbVars)
return allCbVars, allCbs, cbVar, cb
alphaCbVars, alphaCbs, allAlphaVar, allAlphaCb = makeGroup('Alpha')
Or, alternatively, maybe just a function to group a bunch of cbs together:
def groupify(name, cbVars):
cbVar = tk.IntVar()
cb = tk.Checkbutton(root, text=name, variable=cbVar,
command=partial(allGroup, cbVars)
return cbVar, cb
# now build all your individual alpha cbs the same as in your current code
alphaCbVar, alphaCb = groupify('Alpha', alphaCbVars)
And maybe you don't even want to store alphaCbVars, betaCbVars, etc. as separate variables. Maybe it makes more sense to have a list of groups, or a dict mapping names to groups, or some kind of object with attributes, or… whatever.
Related
Recently i have made a code which adds checkbuttons based on the items i put in the list.Items in the list consist of String connected to integer, so one window with checkboxes would look like this: London(3.40) New York(2.20) California(1.2) etc.What i would like to do now is make a button that when clicked sums the integers from the checkboxes.
Note: I have two lists, one list is list of integers Example = [1.2,3.4,2.45,2.4] and other list is list of items Items = ["London","New York","California","Toronto"], after they have been connected with a loop in another list which consists of Items(Value) i made a loop that generates checkbuttons based on amount of Items from that connected list.
I do not know how to implement get() function in a loop so that when i click few checkboxes it sums them and prints them through the button Sum.
In which ways can this be done?
Example of a code:
Values = [1.2,3.4,5.4]
Items = ["London","Lisbon","Athens"]
Connect = ['%.2f', % i for i in Values]
while i != len(Connect):
Items[i] = Items[i]+"(" + Connect[i] + ")"
i = i+ 1
for j in range(len(Values)):
items = tkinter.Checkbutton(window, text=Items[j], onvalue=1, offvalue=0)
From now on i need to make a function that will sum checked checkboxes and print the sum once button Sum is clicked.
Thank you for your help in forward!
Simply store the checkbuttons or the associated variables in a loop. You can then iterate over the loop to get the values.
Since you are wanting to created checkbuttons that represent numbers other than 1 or 0, I recommend you set the value of the checkbutton to the actual value rather than 1.
For example, you can create the checkbuttons like so:
vars = []
for label, value in zip(Items, Values):
var = tk.DoubleVar(value=0)
vars.append(var)
cb = tk.Checkbutton(root, text=label, onvalue=value, offvalue=0, variable=var)
cb.pack(side="top", anchor="w")
With the variables in vars, we can sum them by getting the value of each variable and then adding them together:
def do_sum():
values = [var.get() for var in vars]
result = sum(values)
print("sum:", result)
I am trying to create dynamically a certain amount of widgets, and then be able to modify them.
For example, I need some Checkboxes :
# Class level lists to store the CheckButtons and their values
self.checkbutton_list = []
self.checkbutton_values_list = []
# With self.amount_of_widgets an integer
for i in range(0, self.amount_of_widgets):
# We create the value of the current CheckButton, default is 0
self.checkbutton_values_list.append(IntVar())
# We create the CheckButton and append it to our list
self.checkbutton_list.append(Checkbutton(self.canvasR,
text="rtest : " + str(i),
variable=self.checkbutton_values_list[i],
onvalue=1,
offvalue=0,
height=1,
width=10))
# As there is a lot of CheckButtons, they have to be browsable inside the canvas
# named canvasR, which has a scrollbar. To browse the created CheckButtons in the
# canva, we need to use the create_window function :
self.canvasR.create_window(x, y, window=self.comp_checkbutton_list[i])
y = y + 100
This generation works well, and I am able, to create all the desired widgets at their desired positions, and have them stored into the dedicated lists. For example I do something similar to create blank images ( I want to update those images later ) :
for i in range(0, self.amount_of_widgets):
# With default photo a PhotoImage object stored at class level
self.photo_list.append(self.default_photo)
self.photo_area_list.append(self.canvasR.create_image(x, y,
image=self.photo_list[i],
anchor=NW))
y = y + 100
The issue is I fail to update the created widgets, if I try to call .itemconfig() like in the following code, I am getting a _tkinter.TclError: invalid boolean operator in tag search expression :
for i in range(0, self.max_image_displayed):
self.canvasR.itemconfig(self.checkbutton_list[i], fill='black', text='')
I understand that it may not work because each widget does not specifically exist for the main class, because they have not been explicitly created inside the class. Only the list storing them does exist in this scope.
But I am not going to declare thousands of CheckBoxes or Images fields, one by one, by hand in my code like :
self.area1 = self.canvasR.create_image(0, 0, image=self.photoimage1, anchor=NW)
self.area_list.append(self.area1)
self.area2 = self.canvasR.create_image(0, 100, image=self.photoimage2, anchor=NW)
self.area_list.append(self.area2)
# ...
self.area9999 = self.canvasR.create_image(0, 0, image=self.photoimage999, anchor=NW)
self.area_list.append(self.area9999)
What could I do?
The issue is I fail to update the created widgets, if I try to call .itemconfig() like in the following code, i am getting a _tkinter.TclError: invalid boolean operator in tag search expression
When you call self.canvasR.itemconfig, the first argument needs to be identifier of an individual item on the canvas, or a tag. However, you're passing in self.defaultPhoto which is definitely not an item identifier or a tag.
In other words, what you pass to itemconfig should be the same as what is returned by self.canvasR.create_image(...) rather than the image itself, if your goal is to modify the canvas item created by create_image.
I have a window in Tkinter that looks like this:
When i click on a button in the first row, it stays. However, when i click on a button in the second row, it unselects the one i chose above.
I want it to be able to only select one option per row. Is there something I'm missing? When it's done, I want to be able to iterate over the rows and get the value of the boxes, but I'm not sure how to do that either.
The code for that section is:
for i in studentList:
Label(left,text=i[0][::]+' ' + i[1][::],fg='black',bg='#dbdbdb',font=('Arial',11,'bold')).grid(row=counter,column=0,pady=13,sticky='news')
P = Radiobutton(right,text='Present',bg='#56ab32',fg='black',value='P'+str(counter),indicatoron = 0,font=('Arial',12,'bold'))
P.grid(row=counter,column=0,pady=10,padx=20,sticky='news')
L = Radiobutton(right,text='Leave',bg='#e6a800',fg='white',indicatoron = 0,value='L'+str(counter),font=('Arial',12,'bold'))
L.grid(row=counter,column=1,pady=10,padx=20,sticky='news')
Radiobutton(right,text='Absent',bg='#bd2900',fg='white',indicatoron = 0,value='A'+str(counter),font=('Arial',12,'bold')).grid(row=counter,column=2,pady=10,padx=20,sticky='news')
counter+=1
Radiobuttons work by assigning two or more radiobuttons the same instance of one of tkinter's special variable objects -- usuallyStringVar or IntVar. This sharing of a variable is what makes a group of radiobuttons work as a set, since the variable can only hold a single value.
Because you aren't assigning a variable, tkinter is using a default variable which is the same for every button. Thus, all buttons are acting as a single set.
To make your code work, each row needs to use it's own instance of StringVar. It would look something like this:
vars = []
for i in studentList:
var = StringVar()
vars.append(var)
...
Radiobutton(right, variable=var, ...)
Radiobutton(right, variable=var, ...)
Radiobutton(right, variable=var, ...)
...
With the above, you can get the choice of each row by getting the value of the variable for that row. For example, the first row would be vars[0].get(), the second row would be vars[1].get() and so on.
I have 20 entries in my Tkinter GUI created using for-loop (there might be more of them in the future and I really don't want to have 50 lines of code just for deifining the entries). I need to collect entries values to create a numpy array out of them. As a shot in the dark I have tried this:
master = Tk()
R=StringVar()
namR = []
for ii in range(0,20):
namR.append(Entry(master), textvariable=R[ii])
namR[ii].grid(row=2+ii, column=3)
which obviously does not work (StringVar instance has no attribute '__getitem__'), but I think the goal is clear.
Any suggestions to make this work, please?
You should include your textvariable within the Entry() call, not after it (append(Entry(master, textvariable=xyz)) rather than append(Entry(master), textvariable=xyz)). append() won't know what to do with the second argument. Next, you can create a list for the StringVar objects and refer to them with something like Entry(master, stringvariable=svars[ii]). However, this is only necessary if you want to do things like variable tracing. If you just want to retrieve the text in an entry object, you can do it with my_entry.get().
master = Tk()
namR = []
for ii in range(0,20):
namR.append(Entry(master))
namR[ii].grid(row=2+ii, column=3)
[e.get() for e in namR] will then be a list of all the entry contents.
So I know how to retrieve the text from a single entry widget using the get function but now I am trying to create multiple entry widgets at the same time using a for loop. How can I go back and retrieve the text from anyone of the entry widgets once the user types in them?
rows=11
for line in range(10):
rows=rows+1
widget = Tk.Entry(self.frame)
widget.grid(row=rows, column=5)
The problem is that all of your widget objects are being assigned to a reference, and with each next iteration of the loop are being unreferenced. A way to fix this is to create a list and add these widgets to the list:
entries = []
for line in range(10):
rows = rows + 1
widget = Tk.Entry(self.name)
widget.grid(row = rows, column = 5)
entries.append(widget) # Add this line to your code
Now, to access a specific entrybox, you just find it in the array. So, for example, the second box will be found at entries[1] (because it is 0-based).
The fundamental problem you're having is that you don't have any sort of data structure. Are you familiar with the list type?
rows=11
entries = [Tk.Entry(self.frame) for item in range(10)]
for item in entries:
rows=rows+1
item.grid(row=row, column=5)
This creates a list of Entry widgets, then goes through that list and grids each one into a row (starting with 12). You can access list items by index, e.g. entries[0] for the first Entry.
widgets = []
for i in range(11, 23):
widgets.append(Tk.Entry(self.frame))
widget[i-11].grid(row = i, column = 5)
To answer your question directly, you need the get() method, which is available to the Entry class. To get the value of your widget object, the code would look something like this:
myValue = widget.get()
Please note that, as others have mentioned, your "for" loop does not actually create 10 Entry objects. Since you keep reassigning your new Entry objects to the variable "widget", the old Entry object you created gets de-referenced. Instead of assigning each new Entry object to the variable "widget", append them to a list instead.