如何使用Python中的复选框创建树视图

时间:2011-02-24 11:58:08

标签: python tkinter ttk tix

我一直在使用Tkinter和Tix编写一个小程序。 我正处于需要带有复选框(检查按钮)的树视图的位置,因此我可以从树视图中选择项目。 是否有捷径可寻? 我一直在看ttk.Treeview(),它看起来很容易得到树视图,但有没有办法在视图中插入一个检查按钮?

我们非常感谢一个简单的代码片段。

我不仅限于ttk。什么都行;只要我有一个示例或好的文档,我就可以使它工作

3 个答案:

答案 0 :(得分:18)

enter image description here

import Tix

class View(object):
    def __init__(self, root):
        self.root = root
        self.makeCheckList()

    def makeCheckList(self):
        self.cl = Tix.CheckList(self.root, browsecmd=self.selectItem)
        self.cl.pack()
        self.cl.hlist.add("CL1", text="checklist1")
        self.cl.hlist.add("CL1.Item1", text="subitem1")
        self.cl.hlist.add("CL2", text="checklist2")
        self.cl.hlist.add("CL2.Item1", text="subitem1")
        self.cl.setstatus("CL2", "on")
        self.cl.setstatus("CL2.Item1", "on")
        self.cl.setstatus("CL1", "off")
        self.cl.setstatus("CL1.Item1", "off")
        self.cl.autosetmode()

    def selectItem(self, item):
        print item, self.cl.getstatus(item)

def main():
    root = Tix.Tk()
    view = View(root)
    root.update()
    root.mainloop()

if __name__ == '__main__':
    main()

答案 1 :(得分:5)

我创建了一个treeview类,其复选框继承了ttk.Treeview,但复选框不是ttk.Checkbutton,而是已选中,未选中和三态复选框的图像。

import tkinter as tk
import tkinter.ttk as ttk

class CheckboxTreeview(ttk.Treeview):
    """
        Treeview widget with checkboxes left of each item.
        The checkboxes are done via the image attribute of the item, so to keep
        the checkbox, you cannot add an image to the item.
    """

    def __init__(self, master=None, **kw):
        ttk.Treeview.__init__(self, master, **kw)
        # checkboxes are implemented with pictures
        self.im_checked = tk.PhotoImage(file='checked.png')
        self.im_unchecked = tk.PhotoImage(file='unchecked.png')
        self.im_tristate = tk.PhotoImage(file='tristate.png')
        self.tag_configure("unchecked", image=self.im_unchecked)
        self.tag_configure("tristate", image=self.im_tristate)
        self.tag_configure("checked", image=self.im_checked)
        # check / uncheck boxes on click
        self.bind("<Button-1>", self.box_click, True)

    def insert(self, parent, index, iid=None, **kw):
        """ same method as for standard treeview but add the tag 'unchecked'
            automatically if no tag among ('checked', 'unchecked', 'tristate')
            is given """
        if not "tags" in kw:
            kw["tags"] = ("unchecked",)
        elif not ("unchecked" in kw["tags"] or "checked" in kw["tags"]
                  or "tristate" in kw["tags"]):
            kw["tags"] = ("unchecked",)
        ttk.Treeview.insert(self, parent, index, iid, **kw)

    def check_descendant(self, item):
        """ check the boxes of item's descendants """
        children = self.get_children(item)
        for iid in children:
            self.item(iid, tags=("checked",))
            self.check_descendant(iid)

    def check_ancestor(self, item):
        """ check the box of item and change the state of the boxes of item's
            ancestors accordingly """
        self.item(item, tags=("checked",))
        parent = self.parent(item)
        if parent:
            children = self.get_children(parent)
            b = ["checked" in self.item(c, "tags") for c in children]
            if False in b:
                # at least one box is not checked and item's box is checked
                self.tristate_parent(parent)
            else:
                # all boxes of the children are checked
                self.check_ancestor(parent)

    def tristate_parent(self, item):
        """ put the box of item in tristate and change the state of the boxes of
            item's ancestors accordingly """
        self.item(item, tags=("tristate",))
        parent = self.parent(item)
        if parent:
            self.tristate_parent(parent)

    def uncheck_descendant(self, item):
        """ uncheck the boxes of item's descendant """
        children = self.get_children(item)
        for iid in children:
            self.item(iid, tags=("unchecked",))
            self.uncheck_descendant(iid)

    def uncheck_ancestor(self, item):
        """ uncheck the box of item and change the state of the boxes of item's
            ancestors accordingly """
        self.item(item, tags=("unchecked",))
        parent = self.parent(item)
        if parent:
            children = self.get_children(parent)
            b = ["unchecked" in self.item(c, "tags") for c in children]
            if False in b:
                # at least one box is checked and item's box is unchecked
                self.tristate_parent(parent)
            else:
                # no box is checked
                self.uncheck_ancestor(parent)

    def box_click(self, event):
        """ check or uncheck box when clicked """
        x, y, widget = event.x, event.y, event.widget
        elem = widget.identify("element", x, y)
        if "image" in elem:
            # a box was clicked
            item = self.identify_row(y)
            tags = self.item(item, "tags")
            if ("unchecked" in tags) or ("tristate" in tags):
                self.check_ancestor(item)
                self.check_descendant(item)
            else:
                self.uncheck_descendant(item)
                self.uncheck_ancestor(item)



if __name__ == '__main__':
    root = tk.Tk()
    t = CheckboxTreeview(root, show="tree")
    t.pack()
    t.insert("", 0, "1", text="1")
    t.insert("1", "end", "11", text="1")
    t.insert("1", "end", "12", text="2")
    t.insert("12", "end", "121", text="1")
    t.insert("12", "end", "122", text="2")
    t.insert("122", "end", "1221", text="1")
    t.insert("1", "end", "13", text="3")
    t.insert("13", "end", "131", text="1")
    root.mainloop()

ttkwidgets模块中提供了CheckboxTreeview的改进版本。

答案 2 :(得分:1)

如果您可以使用 Tix,请使用 @Brandon 的解决方案。如果您坚持使用 Ttk (就像我一样),这里有一个基于 @j_4231 想法的解决方案。我们可以使用 Unicode 提供的两个字符,而不是使用图像来表示复选框:

  • “投票箱”(U+2610):
  • '带有 X 的投票箱 (U+2612)':

这些字符位于项目名称之后,用于检查当前状态:treeview.item(iid, "text")[-1]。我们可以在点击文本时更新项目名称。

TtkCheckList 继承了 ttk.Treeview,因此可以使用 Treeview 的常用参数/方法。

import tkinter as tk
from tkinter import ttk

BALLOT_BOX = "\u2610"
BALLOT_BOX_WITH_X = "\u2612"


class TtkCheckList(ttk.Treeview):
    def __init__(self, master=None, width=200, clicked=None, separator='.',
                 unchecked=BALLOT_BOX, checked=BALLOT_BOX_WITH_X, **kwargs):
        """
        :param width: the width of the check list
        :param clicked: the optional function if a checkbox is clicked. Takes a
                        `iid` parameter.
        :param separator: the item separator (default is `'.'`)
        :param unchecked: the character for an unchecked box (default is
                          "\u2610")
        :param unchecked: the character for a checked box (default is "\u2612")

        Other parameters are passed to the `TreeView`.
        """
        if "selectmode" not in kwargs:
            kwargs["selectmode"] = "none"
        if "show" not in kwargs:
            kwargs["show"] = "tree"
        ttk.Treeview.__init__(self, master, **kwargs)
        self._separator = separator
        self._unchecked = unchecked
        self._checked = checked
        self._clicked = self.toggle if clicked is None else clicked

        self.column('#0', width=width, stretch=tk.YES)
        self.bind("<Button-1>", self._item_click, True)

    def _item_click(self, event):
        assert event.widget == self
        x, y = event.x, event.y
        element = self.identify("element", x, y)
        if element == "text":
            iid = self.identify_row(y)
            self._clicked(iid)

    def add_item(self, item):
        """
        Add an item to the checklist. The item is the list of nodes separated
        by dots: `Item.SubItem.SubSubItem`. **This item is used as `iid`  at
        the underlying `Treeview` level.**
        """
        try:
            parent_iid, text = item.rsplit(self._separator, maxsplit=1)
        except ValueError:
            parent_iid, text = "", item
        self.insert(parent_iid, index='end', iid=item,
                    text=text+" "+self._unchecked, open=True)

    def toggle(self, iid):
        """
        Toggle the checkbox `iid`
        """
        text = self.item(iid, "text")
        checked = text[-1] == self._checked
        status = self._unchecked if checked else self._checked
        self.item(iid, text=text[:-1] + status)

    def checked(self, iid):
        """
        Return True if checkbox `iid` is checked
        """
        text = self.item(iid, "text")
        return text[-1] == self._checked

    def check(self, iid):
        """
        Check the checkbox `iid`
        """
        text = self.item(iid, "text")
        if text[-1] == self._unchecked:
            self.item(iid, text=text[:-1] + self._checked)

    def uncheck(self, iid):
        """
        Uncheck the checkbox `iid`
        """
        text = self.item(iid, "text")
        if text[-1] == self._checked:
            self.item(iid, text=text[:-1] + self._unchecked)

这是一个例子:

items = [
    'Item',
    'Item.SubItem1',
    'Item.SubItem2',
    'Item.SubItem2.SubSubItem1',
    'Item.SubItem2.SubSubItem2',
    'Item.SubItem2.SubSubItem3',
    'Item.SubItem3',
    'Item.SubItem3.SubSubItem1',
    'Item.SubItem4'
]

root = tk.Tk()
root.title('Test')
root.geometry('400x300')

check_list = TtkCheckList(root, height=len(items))

for item in items:
    check_list.add_item(item)
check_list.pack()

root.mainloop()

您可以使用 clicked 参数来定义当项目被删除时的新行为 点击。例如:

def obey_ancestor(iid):
    """
    If the status of an item is toggled, the status of all its descendants
    is also set to the new status.
    """
    set_status = check_list.uncheck if check_list.checked(iid) else check_list.check
    stack = [iid]
    while stack:
        iid = stack.pop()
        set_status(iid)
        stack.extend(check_list.get_children(iid))

还有:

check_list = TtkCheckList(root, height=len(items),
                      clicked=obey_ancestor)
相关问题