简体   繁体   中英

Python, Tkinter: Making a grid of Checkboxes using Lists

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM