在 tkinter 小部件中设置属性比例大小并使它们随窗口缩放的最简洁方法是什么?

问题描述 投票:0回答:1

在 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()
python user-interface tkinter widget
1个回答
0
投票

我发现这样做的一种方法是使用设置文本换行/列大小的函数,在 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()

大家觉得怎么样?

© www.soinside.com 2019 - 2024. All rights reserved.