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()