【问题标题】:Python Tkinter Grid CheckboxPython Tkinter 网格复选框
【发布时间】:2015-10-03 07:51:12
【问题描述】:

我想知道是否有一种简单的方法可以使用 Tkinter 创建一个复选框网格。我正在尝试制作一个 10 行和列的网格(所以 100 个复选框),以便每行只能选择两个复选框。

编辑:我正在使用带有 spyder 的 python 2.7

到目前为止我所拥有的:

from Tkinter import*

master = Tk()
master.title("Select Groups")

rows=10
columns=10


for x in range(rows):
    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)
        Checkbutton(master).grid(row=x+1, column=y+1)

mainloop()

一旦选中了两个复选框,我正在尝试使用 state='Disabled' 来灰显一行。

【问题讨论】:

  • 向我们展示你迄今为止的尝试! (同时告诉我们 Tkinter 的版本,以及您使用的 Python 版本)。当用户选择给定行中的第三个框时,您希望发生什么?是否希望新框拒绝被选中?或者您是否要取消选择当前选中的框之一,如果是,取消选择哪一个?
  • 我已经编辑了我的示例代码,以展示如何使用您已经提供的内容来完成它。
  • 感谢您解决问题,杰里米。添加的代码和信息使您的问题更适合想要回答的人,但更重要的是,它还使您的问题和我们的答案对未来的读者更有用很多

标签: python checkbox tkinter grid


【解决方案1】:

这是一个将所有内容都放入一个类的版本,因此我们不需要使用全局变量。它还避免了 import * 构造,这在 Python 中通常被认为是不好的样式。诚然,很多示例代码都使用了import *,但这不是一个好习惯,因为它会将全局命名空间与导入模块中的所有名称混为一谈。因此,这些名称可能会与您自己的变量名称发生冲突,也可能与您使用 import * 导入的其他模块的名称发生冲突。

当窗口关闭时,程序会为每个测试行打印所选组的列表。

#!/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 tk

class 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 data


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

【讨论】:

    【解决方案2】:

    这是一个使用您提供的 10x10 网格的示例。它应该为您提供有关如何实现此功能的基本概念。

    只需确保您保留对每个Checkbutton(示例中为boxes)以及每个IntVar(示例中为boxVars)的引用。

    原因如下:

    -Checkbuttons 需要调用config(state = DISABLED/NORMAL)

    -IntVars是需要确定每个Checkbutton的值。

    除了这些关键元素之外,它基本上只是一些二维数组处理。

    这是我的示例代码(现在基于您提供的代码)。

    from Tkinter import *
    
    master = Tk()
    master.title("Select Groups")
    
    rows=10
    columns=10
    
    boxes = []
    boxVars = []
    
    # Create all IntVars, set to 0
    
    for 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 selected
    
    
    for 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()
    

    【讨论】:

    • 谢谢!您说在您的示例中 IntVars 确定 Checkbutton 的值。有没有办法使用它们来输出所选按钮的列表。例如,如果在第一行选择了第 1 列和第 3 列,在第 2 行选择了第 1 列和第 9 列,并且在第 3 行仅选择了第 3 列,则列表看起来像 [(1,3),(1,9) ]。有什么建议吗?
    • 您只想要选择了 2 个元素的行?
    • 是的,只有 2 个元素。
    • 检查新代码。我使用最右侧的按钮进行检查,但您可以将其添加到 checkRow 方法的末尾(这样每次您选择新复选框时它都会检查)。我使用dict 来跟踪哪些行包含哪些元素,并且只显示具有 2 个元素的行。要访问它们,只需像列表一样访问它:selected[1] 在这种情况下会产生列表 [1, 3]。请注意,我必须使用列表,因为元组(如您在示例中所示)是不可变的。
    • dict 在示例中打印出来,以向您展示它的外观,但您可以将其存储在全局范围内或您决定的任何内容。
    猜你喜欢
    • 1970-01-01
    • 2021-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-24
    • 1970-01-01
    • 2014-05-11
    相关资源
    最近更新 更多