ttk.Treeview 的宽度在击中 ttk.Radiobutton 后增加

ttk.Treeview's width increases after hitting ttk.Radiobutton

我一直在 python 使用 tkinter 和 tkinter.ttk 制作这个下载器应用程序,并在应用程序中添加了一个最新功能供用户查看程序以前执行的操作的日志。它工作正常,但是,我最近发现了一个错误。

我有一个创建另一个 Toplevel 的设置按钮,然后您可以管理默认目录并使用三个 ttk.Radiobuttons 但是每当我点击前两个单选按钮(前两个负责将主题从浅色模式切换到深色模式,反之亦然)时,我的 ttk.treeview 的宽度会加一个原因。奇怪的是,它不会发生在第三个单选按钮上,负责制作自定义模式。

我尝试为日志的顶层设置 maxsize,但它影响了 ttk.treeview 本身。为什么会这样?我该如何防止这种情况?

模拟我的应用程序(这只是一个演示,所以我没有编写整个主题代码):

from tkinter import *
from tkinter import colorchooser
from tkinter import ttk

columns = ("Operation", "URL", "File Path", "Status", "Start Date", "End Date")
log_data = [("Download (File)", "https://www.youtube.com", "C:/Users/Mypc/Downloads/youtube.html", "Finished", "2021-03-30 13:15:30", "2021-03-30 13:15:33"),
            ("Format Fetch", "https://www.youtube.com/watch?v=xxNxqveseyI", "------", "Finished", "2021-03-30 13:15:30", "2021-03-30 13:15:33")]
font_color, bg_color = "black", "white"
root = Tk()
root.configure(bg=bg_color)
root.resizable(False, False)
night_on = IntVar(value=1)
style = ttk.Style()
style.configure("Treeview", rowheight=25, font=('Arial', 10))
style.configure("Treeview.Heading", font=('Arial', 10))
style.configure("TLabel", foreground=font_color, background=bg_color)
style.configure('my.TButton', font=('Helvetica', 20, 'italic'))
style.configure("TRadiobutton", foreground=font_color, background=bg_color, font=('Arial', 10))
font_color_var = StringVar(value=f"Current font color:  \t  {font_color}")
bg_color_var = StringVar(value=f"Current background color:  \t  {bg_color}")
log_top = Toplevel(root)
log_top.withdraw()
log_top.resizable(False, False)
log_fr = Frame(log_top)
log_scroll = ttk.Scrollbar(log_fr, orient=VERTICAL)
log_tree = ttk.Treeview(log_fr, selectmode="browse", yscrollcommand=log_scroll.set, height=12, columns=columns)
log_scroll.config(command=log_tree.yview)


def clear_records():
    for child in log_tree.get_children():
        log_tree.delete(child)


clr_log_btn = ttk.Button(log_top, text="Clear Log", takefocus=False, style="my.TButton", command=clear_records)
log_tree.column("#0", width=0, stretch=NO)
log_tree.column("Operation", width=100, anchor=CENTER)
log_tree.column("URL", width=100, anchor=CENTER)
log_tree.column("File Path", width=100, anchor=CENTER)
log_tree.column("Status", width=80, anchor=CENTER)
log_tree.column("Start Date", width=126, anchor=CENTER)
log_tree.column("End Date", width=126, anchor=CENTER)

for head in columns:
    log_tree.heading(head, text=head, anchor=CENTER)

for item_indices, element in enumerate(log_data):
    log_tree.insert(parent='', index=0, iid=item_indices, values=element)


log_tree.pack(side=LEFT)
log_scroll.pack(side=RIGHT, fill=Y)
clr_log_btn.pack(side=BOTTOM, fill=X)
log_fr.pack()
log_top.protocol("WM_DELETE_WINDOW", log_top.withdraw)
log_lbl = Label(root, text="Show Log", fg="blue", bg=bg_color, cursor="hand2")


def show_log(event):
    if log_top.state() == "withdrawn":
        log_top.deiconify()
    elif log_top.state() == "normal":
        log_top.focus_set()


def settings_win():
    settings_top = Toplevel(root, bg=bg_color)
    settings_top.resizable(False, False)

    def set_night():
        global bg_color, font_color
        bg_color = "black"
        font_color = "white"
        settings_top.config(bg=bg_color)
        style.configure("TLabel", foreground=font_color, background=bg_color)
        style.configure("TRadiobutton", foreground=font_color, background=bg_color)

    def set_light():
        global bg_color, font_color
        bg_color = "white"
        font_color = "black"
        settings_top.config(bg=bg_color)
        style.configure("TLabel", foreground=font_color, background=bg_color)
        style.configure("TRadiobutton", foreground=font_color, background=bg_color)

    def set_custom():
        global font_color, bg_color
        color_fr = Toplevel(root, bg=bg_color)
        color_fr.resizable(False, False)
        current_font_color = ttk.Label(color_fr, textvariable=font_color_var)
        current_bg_color = ttk.Label(color_fr, textvariable=bg_color_var)

        def change_color(name):
            global font_color, bg_color
            new_color = colorchooser.askcolor()[1]
            if new_color is not None:
                if name == "font":
                    font_color = new_color
                    font_color_var.set(f"Current font color:  \t  {font_color}")
                    style.configure("TLabel", foreground=font_color)
                    style.configure("TRadiobutton", foreground=font_color)
                elif name == "bg":
                    bg_color = new_color
                    bg_color_var.set(f"Current background color:  \t  {bg_color}")
                    color_fr.config(bg=bg_color)
                    style.configure("TLabel", background=bg_color)
                    style.configure("TRadiobutton", background=bg_color)

        change_font = ttk.Button(color_fr, text="Change! ", command=lambda: change_color("font"),
                                 takefocus=False)
        change_bg = ttk.Button(color_fr, text="Change! ", command=lambda: change_color("bg"), takefocus=False)
        current_font_color.grid(row=0, column=0, pady=(10, 0), padx=5)
        change_font.grid(row=0, column=1, padx=(0, 7))
        current_bg_color.grid(row=1, column=0, pady=(10, 0), padx=5)
        change_bg.grid(row=1, column=1, padx=(0, 7))

    night_mode = ttk.Radiobutton(settings_top, text="Night Mode", variable=night_on, value=0,
                                 command=set_night, takefocus=False)
    light_mode = ttk.Radiobutton(settings_top, text="Light Mode", variable=night_on, value=1,
                                 command=set_light, takefocus=False)
    custom_mode = ttk.Radiobutton(settings_top, text="Custom Mode", variable=night_on, value=2,
                                  command=set_custom, takefocus=False)

    night_mode.grid(row=0, column=0)
    light_mode.grid(row=1, column=0)
    custom_mode.grid(row=2, column=0)


log_lbl.bind("<Button-1>", show_log)
log_lbl.pack(side=LEFT, pady=30, padx=30)
settings_btn = ttk.Button(root, text="Open Settings", takefocus=False, command=settings_win)
settings_btn.pack(side=RIGHT, padx=30, pady=30)
root.focus_set()
root.mainloop()

我想我找到了解决您问题的方法。首先,我缩小了您的代码以使其更易于调试:

import tkinter as tk
from tkinter import ttk

columns = ("1", "2", "3")
font_color, bg_color = "black", "white"

def change_theme():
    global bg_color, font_color
    # Switches the values of `bg_color` and `font_color`
    bg_color, font_color = font_color, bg_color
    root.config(bg=bg_color)
    style.configure("TLabel", fg=font_color, bg=bg_color)


root = tk.Tk()

style = ttk.Style()

tree = ttk.Treeview(root, columns=columns)
tree.pack()

button = tk.Button(root, text="Click me", command=change_theme)
button.pack()

root.mainloop()

再次查看您的代码后,我注意到您的 stretch=NO#0 标题的 stretch=False 相同。我决定将它应用于所有这样的标题:

tree.column("#0", stretch=False)
for column_name in columns:
    tree.column(column_name, stretch=False)

它解决了你的问题。在查看 ttk.TreeViewthis 文档后,我注意到它对拉伸参数的描述:“如果此选项为 True,则在调整小部件大小时将调整列的宽度。该默认设置为 1。 从中我得出结论,由于某种原因 style.configure("TLabel", ...) 更改了触发所有列调整大小的树视图的宽度。

我对 TreeViewStyles 的了解还不够,无法告诉您为什么会发生这种情况,但如果有人知道,他们可以编辑我的答案。