You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何准确提取ttk.Treeview中所有可见顶层项的iid?

提取ttk.Treeview中可见顶层项iid的正确方法

我在根窗口中创建了带有垂直和水平ttk.Scrollbar的ttk.Treeview组件,希望实现点击「Show Visible Top-Level Items iid」按钮时,提取所有可见顶层项的iid(即Restaurant 0、1、2这类项的iid)。

之前的尝试与问题

使用@JRiggles的解决方案后,返回结果不准确。例如调整窗口仅显示特定内容后,self._show()方法返回:

total_items=10 treeview_top_level_iids=('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
y_top=0.3333333333333333 y_bottom=0.6
first_visible=3 last_visible=6
visible_tl_idds=['G3', 'G4', 'G5', 'G6']

但正确的visible_tl_idds应为['G4', 'G5']。

我尝试修改self._show()的计算逻辑:

if y_top == 0.0:
    first_visible = 0
else:
    first_visible = int(y_top * total_items) + 1
if y_bottom != 1.0:
    last_visible = int(y_bottom * total_items)
else:
    last_visible = total_items - 1

替代原有代码:

first_visible = int(y_top * total_items)
last_visible = int(y_bottom * total_items)

结果更接近实际,但仍不准确,G6不应出现在结果中:

total_items=10 treeview_top_level_iids=('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
y_top=0.3333333333333333 y_bottom=0.6
first_visible=4 last_visible=6
visible_tl_idds=['G4', 'G5', 'G6']

正确解决方案

问题根源在于直接用yview()返回的比例计算可见项,没有考虑顶层项展开后包含子项,实际占据高度不均。正确做法是通过bbox()方法获取每个顶层项的边界框,判断其是否在Treeview的可视区域内。

修改后的_show()方法:

def _show(self):
    self.update_idletasks()
    # 获取Treeview组件的整体边界框(可视区域坐标)
    tree_bbox = self.tree.bbox("")
    if not tree_bbox:
        print("visible_tl_idds=[]")
        return
    tree_x, tree_y, tree_width, tree_height = tree_bbox
    visible_tl_idds = []
    
    # 遍历所有顶层项
    for iid in self.tree.get_children():
        # 获取当前顶层项的边界框
        item_bbox = self.tree.bbox(iid)
        if not item_bbox:
            continue  # 项完全不可见时bbox返回空
        
        item_x, item_y, item_w, item_h = item_bbox
        # 判断项是否与可视区域有重叠:项的底部大于可视区域顶部,且项的顶部小于可视区域底部
        if (item_y + item_h > tree_y) and (item_y < tree_y + tree_height):
            visible_tl_idds.append(iid)
    
    print(f"{visible_tl_idds=}")

完整测试脚本

#!/usr/bin/python3
# -*- coding: utf-8 -*-

try:
    import tkinter as tk
    import tkinter.ttk as ttk
except:
    import Tkinter as tk
    import ttk as ttk

class App(ttk.Frame):

    def __init__(self, parent, *args, **kwargs):
        BG0 = '#aabfe0' #Blue scheme
        BG1 = '#4e88e5' #Blue scheme

        ttk.Frame.__init__(self, parent=None, style='App.TFrame', borderwidth=0,
                           relief='raised', width=390, height=390)
        self.parent = parent
        self.parent.title('Treeview')
        self.parent.geometry('470x350')
        
        self.setStyle()
        self.createWidgets(BG0, BG1)
        
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)


    def setStyle(self):
        style = ttk.Style()
        style.configure('App.TFrame', background='pink')


    def createWidgets(self, BG0, BG1):
        # Treeview with scroll bars
        columns = [f'Column {i}' for i in range(10)]
        self.tree = ttk.Treeview(
            self, height=20, selectmode='extended', takefocus=True,
            columns=("type", "property A", "property B", "selected"),
            displaycolumns=["property A", "property B", "selected"])
        self.ysb = ttk.Scrollbar(self, orient=tk.VERTICAL)
        self.xsb = ttk.Scrollbar(self, orient=tk.HORIZONTAL)
        self.tree.configure(yscrollcommand=self.ysb.set,
                            xscrollcommand=self.xsb.set)
        self.tree.grid(row=0, column=0, columnspan=4, sticky='nsew')
        self.ysb.grid(row=0, column=5, sticky='ns')
        self.xsb.grid(row=1, column=0, columnspan=4, sticky='ew')

        self.tree.column('#0', stretch=True, anchor='w', width=100)
        self.tree.column('property A', stretch=True, anchor='n', width=100)
        self.tree.column('property B', stretch=True, anchor='n', width=100)
        self.tree.column('selected', stretch=True, anchor='n', width=100)

        self.tree.heading('#0', text="Type", anchor='w')
        self.tree.heading('property A', text='Property A', anchor='center')
        self.tree.heading('property B', text='Property B', anchor='center')
        self.tree.heading('selected', text='Selected', anchor='center')

        for i in range(10):
            self.tree.tag_configure(i)
            self.tree.insert("", "end", iid=f"G{i}", open=True,
                         tags=[i, 'Parent'], text=f"Restaurant {i}")
            self.tree.insert(f"G{i}", "end", iid=f"C-{i}", text=f"Cookie", 
                         tags=[f"{i}-a", 'Child', "Not Selected"],
                         values=(f"C-{i}-a", f"{i}-Ca", f"{i}-Cb", False))
            self.tree.insert(f"G{i}", "end", iid=f"P-{i}", text=f"Pudding", 
                         tags=[f"P-{i}", 'Child', "Not Selected"],
                         values=(f"P-{i}", f"{i}-Pa", f"{i}-Pb", False))
            
        # Button
        self.showbutton = ttk.Button(self, text="Show Visible Top-Level Items iid", command=self._show)
        self.showbutton.grid(row=2, column=0, sticky='nsew')


    def _show(self):
        self.update_idletasks()
        # 获取Treeview组件的整体边界框(可视区域坐标)
        tree_bbox = self.tree.bbox("")
        if not tree_bbox:
            print("visible_tl_idds=[]")
            return
        tree_x, tree_y, tree_width, tree_height = tree_bbox
        visible_tl_idds = []
        
        # 遍历所有顶层项
        for iid in self.tree.get_children():
            # 获取当前顶层项的边界框
            item_bbox = self.tree.bbox(iid)
            if not item_bbox:
                continue  # 项完全不可见时bbox返回空
            
            item_x, item_y, item_w, item_h = item_bbox
            # 判断项是否与可视区域有重叠:项的底部大于可视区域顶部,且项的顶部小于可视区域底部
            if (item_y + item_h > tree_y) and (item_y < tree_y + tree_height):
                visible_tl_idds.append(iid)
        
        print(f"{visible_tl_idds=}")

               

if __name__ == '__main__':
    root = tk.Tk()
    app = App(root)
    app.grid(row=0, column=0, sticky='nsew')
    root.rowconfigure(0, weight=1)
    root.columnconfigure(0, weight=1)

    root.mainloop()

内容的提问来源于stack exchange,提问作者Sun Bear

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 23:24:58