TKinter 中的一定数量的选择

certain number of choices in TKinter

我在 Tkinter 中制作了一个带有很多项目的检查栏,但我希望用户只能选择等于或少于一定数量的项目。这是我当前的代码:

from tkinter import *

class Checkbar(Frame):
    def __init__(self, parent=None, picks=[], side=LEFT, anchor=W):
        Frame.__init__(self, parent)

        self.vars = []
        for pick in picks:
            var = IntVar()
            chk = Checkbutton(self, text=pick, variable=var)
            chk.pack(side=side, anchor=anchor, expand=YES)
            self.vars.append(var)

    def state(self):
        return [var.get() for var in self.vars]


root = Tk()

lng = Checkbar(root, range(10))
lng.pack(side=TOP,  fill=BOTH)

root.mainloop()

检查栏需要检查按钮的最大数量和当前数量的属性。然后,您需要一个每次单击复选按钮时都会调用的命令。在 变量更改后 调用不带参数的命令。

如果可能,应发布不依赖于外部文件的代码。在这种情况下,只需传递一个字符串列表。 (我在 Aran-Fey 的编辑之前复制了。)

from tkinter import *

def checkmax(bar, var):
    # called after the intvar is changed
    def _check():
        print(bar.numpicks, var.get())
        if var.get():  # checked
            if bar.numpicks < bar.maxpicks:
                bar.numpicks += 1
            else:
                var.set(0)
        else:             # unchecked
            bar.numpicks -= 1
    return _check


class Checkbar(Frame):
   def __init__(self, parent=None, picks=[], maxpicks=2, side=LEFT, anchor=W):
      Frame.__init__(self, parent)
      self.maxpicks = maxpicks
      self.numpicks = 0
      self.vars = []
      for pick in picks:
         var = IntVar()
         chk = Checkbutton(self, text=pick, variable=var,
                           command=checkmax(self, var))
         chk.pack(side=side, anchor=anchor, expand=YES)
         self.vars.append(var)


   def state(self):
        return map((lambda var: var.get()), self.vars)



if __name__ == '__main__':
   root = Tk()
   lng = Checkbar(root, ['a', 'b', 'c', 'd'], 2)

   lng.pack(side=TOP,  fill=BOTH)

   lng.config(relief=GROOVE, bd=2)

   def allstates():
      print( list(lng.state() ) )

   Button(root, text = 'Quit', command = root.quit).pack(side = RIGHT)
   Button(root, text = 'submit', command = allstates).pack(side = RIGHT)
   root.mainloop()

您应该添加一个标签,说明可以检查的最大数量。

添加注释:当 Python IntVar 作为 Checkbutton 变量传递时,它被没有 .get 和 .set 方法的 _tkinter.Tcl_Obj 替换。因此该命令需要引用 IntVar 实例,因为 chk['variable'].get 不起作用。

这可以通过在选中最大数量的 CheckButton 时禁用所有未选中的 CheckButton 来完成。禁用 CheckButtons 改进了 user-friendlyness,因为它为用户提供了一个视觉线索,即无法检查更多按钮。如果我们不禁用 CheckButton,用户可能会感到困惑,为什么当他尝试单击 CheckButton 时没有任何反应。


为实现此功能,我们必须为每个 CheckButton 连接一个回调函数。回调函数计算有多少按钮被选中,并相应地禁用或启用其他按钮。

我们要做的第一件事就是重写__init__方法。 Checkbar 需要一个新参数,可以同时检查多少个 Checkbutton。我们还必须将回调函数连接到每个 CheckButton。我使用 functools.partial 将复选按钮的变量绑定到回调函数。

def __init__(self, parent=None, picks=[], maxselect=1, side=tk.LEFT,
                   anchor=tk.W):
    super().__init__(parent)

    self.maxselect = maxselect

    # store all variables and checkbuttons; the callback function
    # needs access to them
    self._vars = []
    self._checkbuttons = []

    for pick in picks:
        var = tk.IntVar()
        # bind the callback function to each checkbutton
        chk = tk.Checkbutton(self, text=pick, variable=var,
                             command=partial(self._var_toggled, var))
        chk.pack(side=side, anchor=anchor, expand=tk.YES)

        self._vars.append(var)
        self._checkbuttons.append(chk)

现在剩下的就是实现回调函数了。它只需要计算有多少复选按钮被选中,并相应地启用或禁用它们:

def _var_toggled(self, var):
    # count how many checkbuttons are checked
    num = sum(v.get() for v in self._vars)

    if num == self.maxselect:
        # if we're at maxselect, disable all unchecked checkbuttons
        for var, chk in zip(self._vars, self._checkbuttons):
            if not var.get():
                chk['state'] = tk.DISABLED
    else:
        # otherwise, re-enable all checkbuttons
        for chk in self._checkbuttons:
            chk['state'] = tk.NORMAL

var参数来自我们之前使用的functools.partial。)


完整代码:

from functools import partial
import tkinter as tk

class Checkbar(tk.Frame):
    def __init__(self, parent=None, picks=[], maxselect=1, side=tk.LEFT,
                       anchor=tk.W):
        super().__init__(parent)

        self.maxselect = maxselect

        # store all variables and checkbuttons; the callback function
        # needs access to them
        self._vars = []
        self._checkbuttons = []

        for pick in picks:
            var = tk.IntVar()
            # bind the callback function to each checkbutton
            chk = tk.Checkbutton(self, text=pick, variable=var,
                                 command=partial(self._var_toggled, var))
            chk.pack(side=side, anchor=anchor, expand=tk.YES)

            self._vars.append(var)
            self._checkbuttons.append(chk)

    def _var_toggled(self, var):
        # count how many checkbuttons are checked
        num = sum(v.get() for v in self._vars)

        if num == self.maxselect:
            # if we're at maxselect, disable all unchecked checkbuttons
            for var, chk in zip(self._vars, self._checkbuttons):
                if not var.get():
                    chk['state'] = tk.DISABLED
        else:
            # otherwise, re-enable all checkbuttons
            for chk in self._checkbuttons:
                chk['state'] = tk.NORMAL

我个人会使用单个列表列表。

我可以将 Checkbutton() 和相应的 IntVar() 分配给列表中的列表。

这将允许我检查每个索引的 IntVar() 状态并相应地锁定或解锁检查按钮。

看看下面的代码,如果您有任何问题,请告诉我。

from tkinter import *

class Checkbar(Frame):
    def __init__(self, parent=None, picks=[], side=LEFT, anchor=W):
        Frame.__init__(self, parent)

        self.vars = []

        for pick in picks:
            var = IntVar()
            # appending self.vars with a list of 2 objects at each index.
            self.vars.append([Checkbutton(self, text=pick, command=self.check_count, variable = var), var])
            self.vars[pick][0].grid(row=0, column=pick)
            self.vars[pick][0].bind("<Configure>")

    # each time a Checkbutton is selected this method runs.
    def check_count(self):
        counter = 0
        # if the Checkbutton being looked at is check it will add a counter
        for cbtn in self.vars:
            if cbtn[1].get() == 1:
                counter += 1
        # if the counter reaches 3 then all Checkbuttons not selected will grey out.
        if counter == 3:
            for cbtn in self.vars:
                if cbtn[1].get() != 1:
                    cbtn[0].config(state="disabled")
        # if counter not 3 then all Checkbuttons are normal.
        else:
            for cbtn in self.vars:
                cbtn[0].config(state="normal")


if __name__ == "__main__":
    root = Tk()
    Checkbar(root, range(10)).pack(fill=BOTH)
    root.mainloop()