如何在 Python 中创建带有复选框的树视图

发布于 2024-10-19 01:19:31 字数 216 浏览 4 评论 0原文

我一直在使用 Tkinter 和 Tix 编写一个小程序。 我现在需要一个带有复选框(复选框)的树视图,以便我可以从树视图中选择项目。 有没有简单的方法可以做到这一点? 我一直在研究 ttk.Treeview() ,看起来很容易获得树视图,但是有没有办法在视图中插入复选按钮?

一个简单的代码片段将非常感激。

我不限于ttk。任何事都可以;只要我有一个例子或好的文档我就能让它工作

I've been using Tkinter and Tix to write a small program.
I'm at a point where I need a tree view with checkboxes (checkbuttons) so I can select items from the tree view.
Is there an easy way to do this?
I've been looking at ttk.Treeview () and it looks easy to get the tree view but is there a way to insert a checkbutton to the view?

A simple code snippet would be really appreciated.

I'm not limited to ttk. Anything will do; as long as I have an example or good docs I can make it work

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

荆棘i 2024-10-26 01:19:31

在此处输入图像描述

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

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()
谜兔 2024-10-26 01:19:31

我制作了一个带有继承 ttk.Treeview 的复选框的 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 的改进版本。

I made a treeview class with checkboxes inheriting ttk.Treeview, but the checkboxes are not ttk.Checkbutton but images of checked, unchecked and tristate checkboxes.

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

An improved version of CheckboxTreeview is available in the ttkwidgets module.

旧伤还要旧人安 2024-10-26 01:19:31

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

  • 'BALLOT BOX' (U+2610) :
  • 'BALLOT BOX WITH 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)

If you can use Tix, go with @Brandon's solution. If you are stuck with Ttk (as I am), here is an solution based on @j_4231's idea. Rather than using an image to represent the checkbox, we can use two characters provided by Unicode:

  • 'BALLOT BOX' (U+2610) :
  • 'BALLOT BOX WITH X (U+2612)' : .

Those character are located after the item name and are used to check the current state: treeview.item(iid, "text")[-1] is either or . We can update the item name when the text is clicked.

The class TtkCheckList inherits ttk.Treeview, hence the usual parameters/methods of Treeview can be used.

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)

Here is an example:

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

You can use the clicked parameter to define a new behavior when an item is
clicked. For instance:

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

And:

check_list = TtkCheckList(root, height=len(items),
                      clicked=obey_ancestor)
谁对谁错谁最难过 2024-10-26 01:19:31

我想补充一下 jferard 的精彩答案,如果您想要一个值表而不是树结构,请更改以下内容:

在 init 中添加:

        self.column('#1', width=width, stretch=tk.YES)

对于您想要的每一列。

add_item 应该是:

    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)
        self.insert('', index='end', iid=item, values = item, text=self._unchecked, open=True)

将示例更改为:

cols = ['One', 'Two']
items = [('A', '1',),('B','2')]
check_list = TtkCheckList(root, columns = cols, height=len(items))

I would add to jferard's great answer that if you want to have a table of values rather than a tree structure change the following:

In the init add:

        self.column('#1', width=width, stretch=tk.YES)

for each column you want.

add_item should be:

    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)
        self.insert('', index='end', iid=item, values = item, text=self._unchecked, open=True)

Change the example as such:

cols = ['One', 'Two']
items = [('A', '1',),('B','2')]
check_list = TtkCheckList(root, columns = cols, height=len(items))
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文