Python Tkinter Grid Checkbox Python Tkinter Grid Checkbox tkinter tkinter

Python Tkinter Grid Checkbox


Here's a version that puts everything into a class so we don't need to use global variables. It also avoids the import * construction which is generally considered bad style in Python. True, lots of example code uses import * but it's not a good practice because it clutters up the global namespace with all the names from the imported module. So those names can clash with the names of your own variables, and they can also clash with the names of other modules you import using import *.

The program prints lists of the selected Groups for each Test row when the window closes.

#!/usr/bin/env python''' Create a grid of Tkinter Checkbuttons    Each row permits a maximum of two selected buttons    From http://stackoverflow.com/q/31410640/4014959    Written by PM 2Ring 2015.07.15'''import Tkinter as tkclass CheckGrid(object):    ''' A grid of Checkbuttons '''    def __init__(self, rows=10, columns=10):        master = tk.Tk()        master.title("Select Groups")        rowrange = range(rows)        colrange = range(columns)        #Create the grid labels        for x in colrange:            w = tk.Label(master, text="Group %s" % (x + 1))            w.grid(row=0, column=x+1)        for y in rowrange:            w = tk.Label(master, text="Test %s" % (y + 1))            w.grid(row=y+1, column=0)        #Create the Checkbuttons & save them for future reference        self.grid = []        for y in rowrange:            row = []            for x in colrange:                b = tk.Checkbutton(master)                #Store the button's position and value as attributes                b.pos = (y, x)                b.var = tk.IntVar()                #Create a callback bound to this button                func = lambda w=b: self.check_cb(w)                b.config(variable=b.var, command=func)                b.grid(row=y+1, column=x+1)                row.append(b)            self.grid.append(row)        #Track the number of on buttons in each row        self.rowstate = rows * [0]        master.mainloop()    def check_cb(self, button):        ''' Checkbutton callback '''        state = button.var.get()        y, x = button.pos        #Get the row containing this button        row = self.grid[y]        if state == 1:           self.rowstate[y] += 1            if self.rowstate[y] == 2:               #Disable all currently off buttons in this row               for b in row:                   if b.var.get() == 0:                        b.config(state=tk.DISABLED)        else:           self.rowstate[y] -= 1            if self.rowstate[y] == 1:               #Enable all currently off buttons in this row               for b in row:                   if b.var.get() == 0:                        b.config(state=tk.NORMAL)        #print y, x, state, self.rowstate[y]     def get_checked(self):        ''' Make a list of the selected Groups in each row'''        data = []        for row in self.grid:            data.append([x + 1 for x, b in enumerate(row) if b.var.get()])        return datadef main():    g = CheckGrid(rows=10, columns=10)    #Print selected Groups in each Test row when the window closes    data = g.get_checked()    for y, row in enumerate(data):        print "Test %2d: %s" % (y + 1, row)if __name__ == '__main__':    main()


Here's an example using your provided 10x10 grid. It should give you the basic idea of how to implement this.

Just make sure you keep a reference to every Checkbutton (boxes in the example) as well as every IntVar (boxVars in the example).

Here's why:

-Checkbuttons are needed to call config(state = DISABLED/NORMAL).

-IntVars are needed to determine the value of each Checkbutton.

Aside from those crucial elements its basically just some 2D array processing.

Here's my example code (now based off of your provided code).

from Tkinter import *master = Tk()master.title("Select Groups")rows=10columns=10boxes = []boxVars = []# Create all IntVars, set to 0for i in range(rows):    boxVars.append([])    for j in range(columns):        boxVars[i].append(IntVar())        boxVars[i][j].set(0)def checkRow(i):    global boxVars, boxes    row = boxVars[i]    deselected = []    # Loop through row that was changed, check which items were not selected     # (so that we know which indeces to disable in the event that 2 have been selected)    for j in range(len(row)):        if row[j].get() == 0:            deselected.append(j)    # Check if enough buttons have been selected. If so, disable the deselected indeces,    # Otherwise set all of them to active (in case we have previously disabled them).    if len(deselected) == (len(row) - 2):        for j in deselected:            boxes[i][j].config(state = DISABLED)    else:        for item in boxes[i]:            item.config(state = NORMAL)def getSelected():    selected = {}    for i in range(len(boxVars)):        temp = []        for j in range(len(boxVars[i])):            if boxVars[i][j].get() == 1:                temp.append(j + 1)        if len(temp) > 1:            selected[i + 1] = temp    print selectedfor x in range(rows):    boxes.append([])    for y in range(columns):        Label(master, text= "Group %s"%(y+1)).grid(row=0,column=y+1)        Label(master, text= "Test %s"%(x+1)).grid(row=x+1,column=0)        boxes[x].append(Checkbutton(master, variable = boxVars[x][y], command = lambda x = x: checkRow(x)))        boxes[x][y].grid(row=x+1, column=y+1)b = Button(master, text = "Get", command = getSelected, width = 10)b.grid(row = 12, column = 11)mainloop()