I have to implement 5 to 6 checkbuttons next to each other at run time. The checkboxes are generating but when i deselect any one of them, the rest get deselected too. Also, another thing if i want is , if the text attribute of a checkbutton is repeated, it should line up at the same place as where it was and not create a new checkbutton.
the only code i have for the checkbutton is this
option2 = Checkbutton(self.controls,text = name,
variable = self.variable,command lambda:display_temperature(self.variable,name))
option2.pack(side = 'left', anchor = 'sw', pady = 10, padx = 10)
self.controls.pack()
If all the checkbuttons have the same name and are attached to the same variable, python won't be able to distinguish between them.
Related
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.
In a recipe book about Python/tkinter, I found this snippet on how to create three Radiobutton widgets within one loop:
colors = ["Blue", "Gold", "Red"]
radVar = tk.IntVar()
radVar.set(99)
for col in range(3):
curRad = 'rad' + str(col)
curRad = tk.Radiobutton(win, text=colors[col], variable=radVar,
value=col,
command=radCall)
curRad.grid(column=col, row=5, sticky=tk.W)
I get the point of using a loop, but I would expect the variable curRad to be used for instance as a list, ending up containing references to all those Radiobuttons. What is hapening here? Is Python somehow creating custom-named variables at each iteration? To me it looks like we're assigning a string to a variable and then assigning a reference to a widget to the same variable, and doing the same at each iteration.
What am I missing here?
The line curRad = 'rad' + str(col) accomplishes absolutely nothing, since the variable gets reassigned on the next line.
The code does not give you any lasting reference to the individual radio buttons - but you don't normally need one: determining which one is selected, or programmatically selecting one, is done via the variable (radVar) that they all share.
If you really wanted to keep a reference to each button, you could put:
allRads = []
above the loop, and:
allRads.append(curRad)
inside the loop.
I really like your solution, jason. You can reference later the buttons through the indexes of the list generated.
You can also create the buttons as follow
for col in range(3):
globals()["curRad" + str(col)] = tk.Radiobutton(win, text=colors[col], variable=radVar, value=col, command=radCall)
So the name of the variables associated to each button will be different, in this case:
curRad0
curRad1
curRad2
At the moment each option menu box replaces the previous one so if write ent1.get() i dont get the value within any box, how can i distinguish betweeen each optionmenu and therefor retrieve each value distinctly? Ideally calling them all by a different name decided by their position in the grid.
for x in range (xval):#creating the matrix
for y in range (yval):
variable = StringVar(root)
ent1 = OptionMenu(root, variable, *inputvalues)#creating the dropdown menus
ent1.config(width=3)
ent1.grid(row=(y+1), column=x)
Not sure if I understand correctly but if you are trying to create different StringVar's in a single loop, you could use the variable.format() method to make sure each StringVar is saved as a unique variable.
Thus the following loop will create StringVar's that are saved into variables ent1, ent2, ent3, etc. This will allow you to call them separately later on..
for x in range (xval):#creating the matrix
for y in range (yval):
variable = StringVar(root)
ent["ent{0}"].format(y+1) = OptionMenu(root, variable, *inputvalues)#creating the dropdown menus
ent["ent{0}"].format(y+1).config(width=3)
ent["ent{0}"].format(y+1).grid(row=(y+1), column=x)
Use a dictionary to keep track of the widgets and variables.
entries = {}
vars = {}
for x in range (xval):#creating the matrix
for y in range (yval):
variable = StringVar(root)
entry = OptionMenu(root, variable, *inputvalues)
entry.config(width=3)
entry.grid(row=(y+1), column=x)
entries[(x,y)] = entry
vars[(x,y)] = variable
Is there a way to only allow a user to check one Checkbutton widget at a time out of a set of Checkbutton widgets? I can imagine some brute-force solutions, but I'm looking for something elegant.
You can tie all the checkbutton to single variable with different onvalue.
import tkinter
root = tk.Tk() #Creating the root window
var = tk.IntVar() #Creating a variable which will track the selected checkbutton
cb = [] #Empty list which is going to hold all the checkbutton
for i in range(5):
cb.append(tk.Checkbutton(root, onvalue = i, variable = var))
#Creating and adding checkbutton to list
cb[i].pack() #packing the checkbutton
root.mainloop() #running the main loop
I have created them in a loop for demonstration purpose. Even when you create them sequentially you can use the same variable name with different onvalue.