I'm trying to make a tkinter program that has a 3x3 grid of checkboxes, the code I have created to do this is:
import tkinter as tk
class App(tk.Frame):
def __init__(self,* args,** qwargs):
tk.Frame.__init__(self)
self.strCategories=("Q","W","E","R","T","Y","U","I","O")
self.varCategories={}
for x in self.strCategories:
self.varCategories[x] = tk.IntVar()
self.chckbxCategories={}
for i in range(3):
for j in range(3):
var = self.varCategories[self.strCategories[i*3+j]]
x=10/40 + 11*i/40
y=7/20 + 4*j/20
print(str(x),str(y))
checkbox = tk.Checkbutton(self, text=self.strCategories[i*3+j], font=('Lucida Grande',15),
anchor='w', bg='#FFFFFF', variable=var, onvalue=1, offvalue=0)
checkbox.place(relx=x, rely=y, relwidth=10/40, relheight=5/20)
self.chckbxCategories[self.strCategories[i*3+j]] = checkbox
app = App()
app.mainloop()
However, the widgets don't appear on the screen! If I replace the .place
with some other geometry manager it works (I'd like to use .place
because its relx
and rely
handle resizing the screen for me) so I know the problem is not with making the widgets but with placing them.
What am I doing wrong?
Shell output:
0.25 0.35
0.25 0.55
0.25 0.75
0.525 0.35
0.525 0.55
0.525 0.75
0.8 0.35
0.8 0.55
0.8 0.75
The first reason that the widgets don't appear on screen is because they are in a frame, and you never added the frame to the main window. You need to call pack
, place
or grid
on the instance of App
.
Unrelated to the problem but related to good coding practices, you should explicitly create the root window. Change the last few lines of your code to look like this:
root = tk.Tk()
app = App(root)
app.pack(fill="both", expand=True)
app.mainloop()
When you do this, you will now notice that your window shrinks to just a few pixels. This is because place
does not cause the containing window to expand to fit its children. This is documented behavior, and is one of several reasons why place
is not usually a good choice for doing widget layout. If you are creating a grid of widgets, grid
is the natural choice, and it can easily be made to react properly when the window resizes.
That being said, to see your widgets when using place
you need to force the containing frame or the main window to be large enough to fit this. You'll have to calculate this size, which is another reason why place
isn't the optimal choice.
Once you've calculated a size, you can use geometry
on the root window to force it to be big enough:
root.geometry("200x200")
I use .grid(... , sticky='news')
with columnconfigure(..., weight=1)
to get the same result (I hope because I couldn't run your code to see expected result).
I keep categories as 2-dimensional tuple so I can easily use for
loops and item
- without all this i*3+j
and self.strCategories[i*3+j]
.
I create IntVar()
in the same loop. And I use temporary var
and cb
so it is more readable.
import tkinter as tk
root = tk.Tk()
str_categories = ( ("Q","W","E"), ("R","T","Y"), ("U","I","O") )
var_categories = {}
chckbx_categories = {}
for r, row in enumerate(str_categories):
root.columnconfigure(r, weight=1)
root.rowconfigure(r, weight=1)
for c, item in enumerate(row):
var = tk.IntVar()
cb = tk.Checkbutton(root, text=item, variable=var, onvalue=1, offvalue=0,
font=('Lucida Grande',15), anchor='w', bg='#FFFFFF')
cb.grid(row=r, column=c, sticky='news')
var_categories[item] = var
chckbx_categories[item] = cb
root.mainloop()
BTW: if you rather need categories as normal tuple then you can use range(3)
and item = str_categories[r*3+c]
but rest is the same.
import tkinter as tk
root = tk.Tk()
str_categories = ("Q", "W", "E", "R", "T", "Y", "U", "I", "O")
var_categories = {}
chckbx_categories = {}
for r in range(3):
root.columnconfigure(r, weight=1)
root.rowconfigure(r, weight=1)
for c in range(3):
item = str_categories[r*3+c]
var = tk.IntVar()
cb = tk.Checkbutton(root, text=item, variable=var, onvalue=1, offvalue=0,
font=('Lucida Grande',15), anchor='w', bg='#FFFFFF')
cb.grid(row=r, column=c, sticky='news')
var_categories[item] = var
chckbx_categories[item] = cb
root.mainloop()
Or you can create "iterator"
iterator = iter(str_categories)
and then you can get "next"
item with
item = next(iterator)
Full version:
import tkinter as tk
root = tk.Tk()
str_categories = ("Q", "W", "E", "R", "T", "Y", "U", "I", "O")
iterator = iter(str_categories)
var_categories = {}
chckbx_categories = {}
for r in range(3):
root.columnconfigure(r, weight=1)
root.rowconfigure(r, weight=1)
for c in range(3):
item = next(iterator)
var = tk.IntVar()
cb = tk.Checkbutton(root, text=item, variable=var, onvalue=1, offvalue=0,
font=('Lucida Grande',15), anchor='w', bg='#FFFFFF')
cb.grid(row=r, column=c, sticky='news')
var_categories[item] = var
chckbx_categories[item] = cb
root.mainloop()
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.