使用 CheckList 和 Hlist Python 自动检查 Tix 中的子项目

Autocheck children items in Tix with Python using CheckList and Hlist

我正在使用 Tix 在 Python 中编写一个小程序,该程序构建并显示具有复选框功能的特定文件夹(仅包含特定 xml 文件的文件夹)的树视图。每个项目都是一个可以选择(选中)的文件夹或子文件夹。为此我使用了 CheckList 和 Hlist

基于this answer:我设法用复选框显示我想要的文件夹结构。

问题是我需要在检查父项目时自动检查嵌套项目,这样我就不需要遍历同一父项目下的每个项目。 我在使用 Tix 的文档时遇到了很多麻烦。

网上关于对象和方法的信息似乎是相互矛盾的,而且往往因信息来源不同而有所不同。我几乎可以肯定 Hlist 中没有启用此 "autocheck" 功能的内置功能,所以如果我错了请纠正我,我将不得不自己开发它。

对此有什么提示或想法吗? 我将 post 涉及的代码片段和文件夹 treeview

首先我创建清单并找到我感兴趣的目录:

def startCheckList(self):

    self.cl = Tix.CheckList(self.testsFrame, browsecmd=self.selectItem, width=600, height=600)

    self.cl.hlist.configure(indent=20, pady=2, padx=2, bg='#eef4fa', relief=GROOVE, font=self.customFont)
    self.cl.pack()

    for root, dirs, files in os.walk(EA.TESTSFOLDER):
        for aFile in files:
            (fileName, extension) = os.path.splitext(aFile)
            if (fileName == EA.TESTNAME):
                self.testPaths.append(root)

获得文件夹列表后,我将关联的元素添加到要显示的 Hlist

def display_paths(self):

    for path in self.testPaths:
        L = []
        path2list(L, path)

        self.create_recursive(L)

    self.cl.autosetmode()

如果元素存在,我不会创建新条目

def create_recursive(self, list):
    path = '.'.join(list)

    if path:

        if self.cl.hlist.info_exists(path) == '1':
            pass

        elif self.cl.hlist.info_exists(path) == '0':

            if list:

                self.create_recursive(list[:-1])
                self.cl.hlist.add(path, text=list[-1])
                self.cl.setstatus(path, "off")

您将如何进行?

好的,因为我找不到任何内置方法,所以我编写了下一个代码:

def selectItem(self, item):

    if self.cl.getstatus(item) == 'on':
        self.autoCheckChildren(item, True)
    if self.cl.getstatus(item) == 'off':
        self.autoCheckChildren(item, False)


def autoCheckChildren(self, i_item, stat):
    item = i_item
    if stat:
        if self.cl.hlist.info_children(item):
            for child in self.cl.hlist.info_children(item):
                self.cl.setstatus(child, "on")
                self.autoCheckChildren(child, True)
    elif not stat:
        if self.cl.hlist.info_children(item):
            for child in self.cl.hlist.info_children(item):
                self.cl.setstatus(child, "off")
                self.autoCheckChildren(child, False)

其中 selectItem 是选中复选按钮时调用的函数。我再次使用了递归,但这可能不是最好的解决方案。

Another thing that I found is that when selecting a checkbox (any one from the checklist) the associated function specified by browsecmd=function is always called twice.我在某处读到这是由于按下和释放鼠标被视为两个事件。

这与 Tkinter 中的单个复选按钮的行为不同,其中关联的函数只被调用一次。我不知道为什么会有这样的差异。是否可以只调用一次此函数?

我知道这是一个旧的 post,但在为此苦苦挣扎了一段时间之后,我想出了一个解决方案。

根据文档,出现多个事件的原因是 browsecmd 被鼠标按钮按下、鼠标移动和鼠标按钮弹起触发。所以它至少会被触发两次——鼠标按下和鼠标弹起。

我已经通过将清单绑定到鼠标松开事件解决了这个问题,回调设置了一个变量来指示甚至已经收到鼠标松开。然后我的 browsecmd 函数可以检查这个变量的状态,如果它是 True,那么它会继续(然后再次将变量设置为 False)。如果是 False 它会忽略它,因为它不是鼠标松开事件。

下面是我的代码的相关部分。

def __init__(self):
    # Insert your own init code here
    self.clMouseUpEvent = False
def checkListClicked(self, event):
    self.clMmouseUpEvent = True
def browseEvent(self, itemID):
    if self.clMmouseUpEvent:
        if self.cl.getstatus(itemID) == 'off':
            status = 'on'
        else:
            status = 'off'
        self.setChildrenStatus(itemID, status)
        self.clMmouseUpEvent = False
def setChildrenStatus(self, itemID, status):
    self.cl.setstatus(itemID, status)
    for childID in self.cl.hlist.info_children(itemID):
        self.setChildrenStatus(childID, status)
def someFunction(self):
    # Insert your own Tix check list set up code here
    self.cl.hlist.config(bg='white', selectbackground='white', selectforeground='black', header=True, browsecmd=self.browseEvent)
    self.cl.hlist.bind("<ButtonRelease-1>", self.checkListClicked)

更新:

我随后发现点击披露指示器也会触发 checkListclicked 功能设置 clMouseUpEventTrue,然后导致下一次点击实际的复选框来处理这两个鼠标按下和鼠标弹起事件。

我现在修改了我的代码,将 clMouseUpEvent 变量设置为 0 而不是 Falsetime.time() 而不是 True。然后在我的 browseEvent 中检查 clMouseUpEvent == True 而不是检查 time.time() - self.clMouseUpEvent < 0.01。这似乎现在工作正常。

我使用了与@Son of a Beach 类似的方法,但我还更改了 CheckListhlist 中的 indicatorcmd。每次按下指示器(折叠按钮)时,此命令都会获得 运行,覆盖它使其不再触发 browsecmd

这是代码:

        def toggle_path(tree: tix.CheckList, path: str):
            # delay clicks to not trigger it twice
            if time.time() - self._event_toggle > App.TOGGLE_EVENT_DELAY:
                getattr(tree, tree.getmode(path))(path)
            self._event_toggle = time.time()

        self.checklist = tix.CheckList(self.master,
                                      browsecmd=lambda path: self.select(self.checklist, path),
                                      command=lambda path: self.select(self.checklist, path))
        self.checklist.hlist.configure(indicatorcmd=lambda path: toggle_path(self.to_tree, path))

希望对以后使用它的人有所帮助!