在 Tkinter 应用程序中,我希望所有小部件中的所有文本根据小部件的宽度进行换行,并将树视图中的列宽度设置为整个小部件宽度的某些预定义比例。我希望它随着窗口大小的调整而自动更新,但保持相同的比例。
我下面的代码重现了我正在尝试开发的应用程序的用户界面的最小部分。
我想出了一种根据窗口大小自动调整小部件属性大小的方法 - 但当应用程序首次启动时,它们不会以这种方式开始。我还觉得必须有一种更优雅的方式来设置比例并让 Tkinter 处理调整大小。
当您运行代码时,您可以看到树视图列不会立即调整大小到所需的比例,它们仅在主窗口调整大小后才调整大小。就我而言,第一列之后的所有列应为树视图宽度的 1/6,然后第一列应占用剩余空间。
如果为
window_size_multiplier
选择足够小的值,您可以看到文本不会立即开始换行,而是仅在您调整窗口大小时才开始正确换行。
我还努力在 init 中获取实际的 widget.winfo_width。
import tkinter as tk
from tkinter import ttk
# Main application class
class FuzzyMatcherApp(tk.Tk):
def __init__(self):
super().__init__()
# Initialize window
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
window_size_multiplier = 0.2
window_width = int(screen_width * window_size_multiplier)
window_height = int(screen_height * window_size_multiplier)
x_position = int((screen_width - window_width) / 2)
y_position = int((screen_height - window_height) / 2)
self.geometry(f"{window_width}x{window_height}+{x_position}+{y_position}")
# Configure grid
self.grid_columnconfigure(0, weight=1) # Column for match results display
self.grid_rowconfigure(0, weight=0) # Row for buttons and labels
# Create frame
self.frame = tk.Frame(self)
# Bind frames to grid
self.frame.grid(row=0, column=0, columnspan=3, sticky="ew", padx=10, pady=10)
# Create widgets
self.match_string_label = tk.Label(self.frame, text="Enter String to Match:")
self.match_button = tk.Button(self.frame, text="Match")
self.match_results_tree = ttk.Treeview(self.frame, columns=('Response', 'Score', 'Count'), show='headings')
self.match_results_tree.heading('Response', text='Response')
self.match_results_tree.heading('Score', text='Score')
self.match_results_tree.heading('Count', text='Count')
# Bind widgets to frame
self.match_string_label.grid(row=0, column=0, sticky="ew", padx=5)
self.match_button.grid(row=1, column=0, sticky="ew", padx=10, pady=10)
self.match_results_tree.grid(row=2, column=0, columnspan=2, sticky="nsew", padx=10, pady=10)
# Allow the treeview to expand vertically
self.frame.grid_rowconfigure(4, weight=1)
# Don't allow the scrollbar to expand horizontally
self.frame.grid_columnconfigure(2, weight=0)
# Allow all buttons and treviews to expand/contract horizontally together
self.frame.grid_columnconfigure(0, weight=1)
self.frame.grid_columnconfigure(1, weight=1)
# Bind UI resizing events
self.after(100, self.bind_resize_treeview_columns)
self.after(100, self.bind_resize_text_wraplength)
def bind_resize_text_wraplength(self):
for widget in self.frame.winfo_children():
if isinstance(widget, (tk.Label, tk.Button, tk.Radiobutton)):
widget.bind("<Configure>", self.resize_text_wraplength)
def resize_text_wraplength(self, event):
width = event.width + 10 # Widget width plus a little extra
event.widget.configure(wraplength=width)
def bind_resize_treeview_columns(self):
for widget in self.frame.winfo_children():
if isinstance(widget, ttk.Treeview):
widget.bind("<Configure>", self.resize_treeview_columns)
def resize_treeview_columns(self, event):
# All columns after the first one should be 1/6th the width of the treeview, and then the first should take the remaining space.
# Get the treeview associated with the event that called this function
treeview = event.widget
# Get the width of the treeview widget
treeview_width = treeview.winfo_width()
# Calculate the width for all columns that come after the first one
num_columns = len(treeview["columns"])
secondary_column_width = treeview_width // 6
first_column_width = treeview_width - (secondary_column_width * (num_columns - 1))
# Set the first column's width
treeview.column(treeview["columns"][0], width=first_column_width)
# Set the other columns' widths
for col in treeview["columns"][1:]:
treeview.column(col, minwidth=50, width=secondary_column_width)
# Running the application
if __name__ == "__main__":
app = FuzzyMatcherApp()
app.mainloop()
我发现这样做的一种方法是使用设置文本换行/列大小的函数,在 init 中调用它,然后将它们也绑定到 init 中的窗口调整大小事件。
像这样:
class App(tk.Tk):
def __init__(self):
super().__init__()
### Code to initialize UI ###
# Call resize functions immediately after setup
self.resize_treeview_columns()
self.resize_text_wraplength()
# Bind resizing to window size change
self.bind("<Configure>", self.on_window_resize)
def on_window_resize(self, event):
# Call resize functions when the window is resized
self.resize_treeview_columns()
self.resize_text_wraplength()
def resize_treeview_columns(self):
### code to set treeview column sizes ###
def resize_text_wraplength(self):
### code to set treeview wraplength ###
所以整个代码看起来像这样:
import tkinter as tk
from tkinter import ttk
# Main application class
class App(tk.Tk):
def __init__(self):
super().__init__()
# Initialize window
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
window_size_multiplier = 0.1
window_width = int(screen_width * window_size_multiplier)
window_height = int(screen_height * window_size_multiplier)
x_position = int((screen_width - window_width) / 2)
y_position = int((screen_height - window_height) / 2)
self.geometry(f"{window_width}x{window_height}+{x_position}+{y_position}")
# Configure grid
self.grid_columnconfigure(0, weight=1) # Allow column to expand
self.grid_rowconfigure(0, weight=0)
# Create frame
self.frame = tk.Frame(self)
# Bind frames to grid
self.frame.grid(row=0, column=0, columnspan=3, sticky="ew", padx=10, pady=10)
# Create widgets
self.label = tk.Label(self.frame, text="Big ol' label with lots of text")
self.button = tk.Button(self.frame, text="Big momma button with lots of text")
self.items_display = ttk.Treeview(self.frame, columns=('Col1', 'Col2', 'Col3'), show='headings')
self.items_display.heading('Col1', text='Col1')
self.items_display.heading('Col2', text='Col2')
self.items_display.heading('Col3', text='Col3')
# Bind widgets to frame
self.label.grid(row=0, column=0, sticky="ew", padx=5)
self.button.grid(row=1, column=0, sticky="ew", padx=10, pady=10)
self.items_display.grid(row=2, column=0, columnspan=2, sticky="nsew", padx=10, pady=10)
# Allow the treeview to expand vertically
self.frame.grid_rowconfigure(4, weight=1)
# Don't allow the scrollbar to expand horizontally
self.frame.grid_columnconfigure(2, weight=0)
# Allow all buttons and treviews to expand/contract horizontally together
self.frame.grid_columnconfigure(0, weight=1)
self.frame.grid_columnconfigure(1, weight=1)
# Call resize functions immediately after setup
self.resize_treeview_columns_initial()
self.resize_text_wraplength_initial()
# Bind resizing to window size change
self.bind("<Configure>", self.on_window_resize)
def on_window_resize(self, event):
# Call resize functions when the window is resized
self.resize_treeview_columns_initial()
self.resize_text_wraplength_initial()
def resize_treeview_columns_initial(self):
# Initial resize of treeview columns
treeview = self.items_display
treeview_width = treeview.winfo_width()
num_columns = len(treeview["columns"])
secondary_column_width = treeview_width // 6
first_column_width = treeview_width - (secondary_column_width * (num_columns - 1))
treeview.column(treeview["columns"][0], width=first_column_width)
for col in treeview["columns"][1:]:
treeview.column(col, minwidth=50, width=secondary_column_width)
def resize_text_wraplength_initial(self):
# Initial resize of text wraplength
for widget in self.frame.winfo_children():
if isinstance(widget, (tk.Label, tk.Button, tk.Radiobutton)):
width = widget.winfo_width() + 10
widget.configure(wraplength=width)
# Running the application
if __name__ == "__main__":
app = App()
app.mainloop()
大家觉得怎么样?