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

如何让ttk.Treeview.bbox在实例化阶段正常工作?

问题:ttk.Treeview的.bbox方法在实例化阶段无法获取可见项IID

我编写了一个类,其中_show_visible_toplevel_iids方法用于获取ttk.Treeview组件的可见顶层项IID,但该方法在类实例化阶段调用时,.bbox方法无法正常工作;而后续手动点击触发该方法的ttk.Button组件时,功能却完全正常。

现象对比

实例化阶段打印输出

all_toplevel_iids=('G0', 'G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7', 'G8', 'G9')
visible_toplevel_iids=[]
children_iids=[]

all_toplevel_iids=('G0', 'G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7', 'G8', 'G9')
visible_toplevel_iids=[]
children_iids=[]

手动点击按钮时打印输出

all_toplevel_iids=('G0', 'G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7', 'G8', 'G9')
visible_toplevel_iids=['G0', 'G1', 'G2', 'G3', 'G4']
children_iids=[('F0', 'F1'), ('F2', 'F3'), ('F4', 'F5'), ('F6', 'F7'), ('F8', 'F9')]

测试脚本

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

import tkinter as tk
import tkinter.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')

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

        self._show_visible_toplevel_iids()  # .bbox doesn't work correctly
        self.showbutton.invoke()            # .bbox doesn't work correctly 
        

        
    def _show_visible_toplevel_iids(self):
        tree = self.tree
        tree.update_idletasks()
        all_toplevel_iids = tree.get_children()
        visible_toplevel_iids = [i for i in all_toplevel_iids
                                 if isinstance(tree.bbox(i), tuple)]
        children_iids = [tree.get_children([i]) for i in visible_toplevel_iids]
        print()
        print(f"{all_toplevel_iids=}")
        print(f"{visible_toplevel_iids=}")
        print(f"{children_iids=}")

               

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

原因与解决方法

这不是tkinter的bug,问题出在组件渲染时机上:

  • Treeview的.bbox方法依赖组件实际绘制到屏幕后的几何布局信息。在类实例化阶段,mainloop()还未启动,窗口和Treeview组件还未完成最终的布局计算,此时即使调用update_idletasks(),也无法获取到完整的渲染后尺寸数据,导致.bbox返回空值。
  • 手动点击按钮时,窗口已经完成渲染,布局信息全部确定,.bbox能正常返回可见项的边界框。

修复方案

将实例化阶段直接调用_show_visible_toplevel_iids的代码,替换为使用after_idle()延迟执行,它会等待tkinter处理完所有初始化和布局任务后再执行目标函数:

修改createWidgets方法末尾的代码:

# 替换原来的直接调用和invoke
self.parent.after_idle(self._show_visible_toplevel_iids)

修改后,程序会在窗口第一次渲染完成后自动调用方法,此时.bbox就能正确获取到可见项的IID。

内容的提问来源于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 10:54:58