我在框架中有一个树视图。该树由网格几何管理器管理。 按下按钮我想:
树应该粘在网格的两端,柱子应该延伸到树的两侧。 我试图通过使用新的 columnspan 重新映射树、删除树的子项并添加新列来实现这一点。参见 toggle_span()。
import tkinter as tk
from tkinter import ttk
def setup_tree():
columns = ["col1"]
tree['columns'] = columns
tree.column('#0', width=20, minwidth=20, stretch=tk.YES)
tree.heading('#0', text='id')
for i, column in enumerate(columns):
tree.column(i, width=20, minwidth=20, stretch=tk.YES)
tree.heading(i, text=column)
tree.insert('', 'end', iid='1', text='1', values=("item1",))
tree.insert('', 'end', iid='2', text='2', values=("item2",))
# Toggle treeview
def toggle_span():
global span
# Extend tree
if span == 1:
span = 2
columns = ["col1", "col2"]
row1 = ("item1", "item2")
row2 = ("item3", "item4")
# Retract tree
else:
span = 1
columns = ["col1"]
row1 = ("item1",)
row2 = ("item2",)
# Refresh columnspan of old tree
tree.grid_forget()
tree.grid(column=1, row=0, sticky="nsew", columnspan=span)
# Clear tree
for item in tree.get_children():
tree.delete(item)
# Rebuild tree
tree['columns'] = columns
tree.column('#0', width=20, minwidth=20, stretch=tk.YES)
tree.heading('#0', text='id')
for i, column in enumerate(columns):
tree.column(i, width=20, minwidth=20, stretch=tk.YES)
tree.heading(i, text=column)
tree.insert('', 'end', iid='1', text='1', values=row1)
tree.insert('', 'end', iid='2', text='2', values=row2)
span = 1
# Root
root = tk.Tk()
root.state("zoomed")
# Frame
frame = tk.Frame(root, bg="cyan")
frame.pack(fill='both', expand=True)
frame.grid_rowconfigure(0, weight=1)
for i in range(3):
frame.grid_columnconfigure(i, weight=1)
# Button
button = tk.Button(frame, text="Toggle Span", command=toggle_span)
button.grid(column=0, row=0, sticky="nsew")
# Tree
tree = ttk.Treeview(frame)
setup_tree()
tree.grid(column=1, row=0, sticky="nsew", columnspan=span)
# Manual toggle works as expected!
toggle_span()
toggle_span()
root.mainloop()
当手动调用 toggle_span() 时,列会按预期拉伸。然而,当通过按下按钮调用 toggle_span() 时,只有初始缩回状态和紧随其后的扩展状态才能正确显示。之后,列最初不会拉伸。但是,当单击列的边缘时,它们确实会拉伸。
如何确保列始终处于拉伸状态?
在修改第一个树视图列后调用 grid_forget(但在其余列之前)。
...
tree.column('#0', width=20, minwidth=20, stretch=tk.YES)
tree.heading('#0', text='id')
tree.grid_forget()
tree.grid(column=1, row=0, sticky="nsew", columnspan=span)
for i, column in enumerate(columns):
tree.column(i, width=20, minwidth=20, stretch=tk.YES)
tree.heading(i, text=column)
...
在此之后,列将按预期伸展。我不知道为什么会这样。
这几列几乎不可见,但是按钮和树视图的宽度并不总是相同的,它们有点晃动。这可以通过将框架列配置为 uniform.
来解决frame.grid_rowconfigure(0, weight=1)
for i in range(3):
frame.grid_columnconfigure(i, weight=1, uniform="colgroup")
为树视图创建一个独特的框架以适应。可以通过调整此框架的大小来调整树的大小。有了这个,列按预期伸展。
内部框架的 columnspan 可以直接使用 grid_configure(columnspan=...) 代替 grid_forget() 和 grid().
然而,现在内部框架根据树视图变化,导致更狂野的“摇摆”。为了阻止这种情况,我们可以在内部框架上调用 grid_propagate(False)。
问题示例代码:
import tkinter as tk
from tkinter import ttk
def setup_tree():
columns = ["col1"]
tree['columns'] = columns
tree.column('#0', width=20, minwidth=20, stretch=tk.YES)
tree.heading('#0', text='id')
for i, column in enumerate(columns):
tree.column(i, width=20, minwidth=20, stretch=tk.YES)
tree.heading(i, text=column)
tree.insert('', 'end', iid='1', text='1', values=("item1",))
tree.insert('', 'end', iid='2', text='2', values=("item2",))
# Toggle treeview
def toggle_span():
global span
# Extend tree
if span == 1:
span = 2
columns = ["col1", "col2"]
row1 = ("item1", "item2")
row2 = ("item3", "item4")
# Retract tree
else:
span = 1
columns = ["col1"]
row1 = ("item1",)
row2 = ("item2",)
# Clear tree
for item in tree.get_children():
tree.delete(item)
# Rebuild tree
tree['columns'] = columns
tree.column('#0', width=20, minwidth=20, stretch=tk.YES)
tree.heading('#0', text='id')
for i, column in enumerate(columns):
tree.column(i, width=20, minwidth=20, stretch=tk.YES)
tree.heading(i, text=column)
tree.insert('', 'end', iid='1', text='1', values=row1)
tree.insert('', 'end', iid='2', text='2', values=row2)
tree_frame.grid_configure(columnspan=span)
span = 1
# Root
root = tk.Tk()
root.state("zoomed")
# Frame
frame = tk.Frame(root, bg="cyan")
frame.pack(fill='both', expand=True)
frame.grid_rowconfigure(0, weight=1)
for i in range(3):
frame.grid_columnconfigure(i, weight=1)
# Tree frame
tree_frame = tk.Frame(frame)
tree_frame.grid(row=0, column=1, sticky="nsew", columnspan=span)
tree_frame.grid_propagate(False)
tree_frame.grid_rowconfigure(0, weight=1)
tree_frame.grid_columnconfigure(0, weight=1)
# Button
button = tk.Button(frame, text="Toggle Span", command=toggle_span)
button.grid(column=0, row=0, sticky="nsew")
# Tree
tree = ttk.Treeview(tree_frame)
setup_tree()
tree.grid(row=0, column=0, sticky="nsew")
root.mainloop()
我不太明白发生了什么,但这是我所看到的。
列很好地伸展,因为树视图在主循环之前切换。 它们在主循环期间仍然会收缩,即使没有通过按下按钮调用切换:
# Produces unexpected state
def toggle():
print("toggling")
toggle_span()
root.after(2000, toggle)
root.after(2000, toggle)
root.mainloop()
这似乎是 Tcl/Tk 的一个错误,因为
Treeview
小部件不会根据当前小部件宽度调整列的大小。
您可以通过在重新创建列后调用
grid_forget()
和 grid()
来解决这个问题,如下例所示。
...
def toggle_span():
...
# Clear tree
for item in tree.get_children():
tree.delete(item)
# Rebuild tree
tree['columns'] = columns
...
tree.grid_forget()
tree.grid(column=1, row=0, sticky="nsew", columnspan=span)
...