TKinter中有一定数量的选择

时间:2018-04-04 16:29:13

标签: python user-interface checkbox tkinter items

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

3 个答案:

答案 0 :(得分:1)

复选栏需要所选按钮的最大和当前数量的属性。然后,您需要在每次单击一个复选按钮时调用的命令。 更改变量后,在没有参数的情况下调用该命令。

如果可能,应该发布代码而不依赖于外部文件。在这种情况下,只需传递一个字符串列表。 (我在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 [&#39;变量&#39;]。get不起作用。

答案 1 :(得分:0)

这可以通过在选中最大CheckButtons数时禁用所有未检查的CheckButtons来完成。禁用CheckButtons可以提高用户友好性,因为它为用户提供了一个视觉线索,可以不再检查按钮。如果我们没有禁用CheckButtons,用户可能会感到困惑,为什么当他试图单击CheckButton时没有任何反应。

为了实现此功能,我们必须将回调函数连接到每个CheckButton。回调函数计算检查的按钮数量,并相应地禁用或启用其他按钮。

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

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

答案 2 :(得分:0)

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

我可以将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()