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

如何让CustomTkinter图片框在Y轴方向填充空隙?

解决CustomTkinter图片预览框Y轴无法拉伸填充空隙的问题

要让图片预览框在Y轴方向填充空隙,需从布局跨越、标签占满框架、图片尺寸适配三个方面修改:

1. 调整图片预览框架的布局跨越

原代码中图片预览框架仅占据窗口第0行,而窗口第0、1、2行都设置了weight=1,需让框架跨越这三行以利用垂直空间,同时移除固定宽度限制:

# create image preview
self.image_field_frame = customtkinter.CTkFrame(self)  # 移除固定width=250,允许水平拉伸
self.image_field_frame.grid(row=0, column=1, padx=(20, 0), pady=(20, 0), sticky="nsew", rowspan=3)  # 增加rowspan=3,跨越row0-2
self.image_field_frame.grid_rowconfigure(0, weight=1)
self.image_field_frame.grid_columnconfigure(0, weight=1)

2. 让图片标签占满预览框架

使用grid布局将图片标签设置为占满整个框架:

self.image_field = tkinter.Label(self.image_field_frame, bg=self.image_field_frame.cget("fg_color"))
self.image_field.grid(row=0, column=0, sticky="nsew")  # 用sticky="nsew"让标签填充框架

3. 修复图片尺寸适配逻辑

添加窗口大小变化的绑定事件,确保图片在框架尺寸改变时自动重新缩放,同时修正初始加载时的尺寸获取问题:

  • 在__init__方法末尾添加绑定:
self.image_field_frame.bind("<Configure>", self.on_image_frame_resize)  # 绑定框架尺寸变化事件
  • 修改图片加载和缩放逻辑:
def load_image(self):
    file_path = customtkinter.filedialog.askopenfilename()
    if file_path:
        self.current_image = Image.open(file_path)  # 保存当前图片对象
        self.on_image_frame_resize()  # 初始加载时调整图片大小

def on_image_frame_resize(self, event=None):
    if hasattr(self, 'current_image'):
        image = self.current_image.copy()
        segment_width = self.image_field_frame.winfo_width()
        segment_height = self.image_field_frame.winfo_height()
        
        # 避免无效尺寸导致的错误
        if segment_width <= 0 or segment_height <= 0:
            return
            
        width_ratio = segment_width / image.width
        height_ratio = segment_height / image.height
        scale_factor = min(width_ratio, height_ratio)
        
        new_width = int(image.width * scale_factor)
        new_height = int(image.height * scale_factor)
        image = image.resize((new_width, new_height))
        
        # 计算居中偏移量
        x_offset = (segment_width - new_width) // 2
        y_offset = (segment_height - new_height) // 2
        self.image_field.place(x=x_offset, y=y_offset)
        
        photo = ImageTk.PhotoImage(image)
        self.image_field.configure(image=photo)
        self.image_field.image = photo

完整修改后的代码

import tkinter
import tkinter.messagebox
import customtkinter
from PIL import Image, ImageTk

customtkinter.set_appearance_mode("System")  # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue")  


class App(customtkinter.CTk):
    def __init__(self):
        super().__init__()

        # configure window
        self.title("CustomTkinter complex_example.py")
        self.geometry(f"{1100}x{580}")

        # configure grid layout (4x4)
        self.grid_columnconfigure(1, weight=1)
        self.grid_columnconfigure((2, 3), weight=0)
        self.grid_rowconfigure((0, 1, 2), weight=1)

        # create sidebar frame with widgets
        self.sidebar_frame = customtkinter.CTkFrame(self, width=140, corner_radius=0)
        self.sidebar_frame.grid(row=0, column=0, rowspan=4, sticky="nsew")
        self.sidebar_frame.grid_rowconfigure(4, weight=1)
        self.logo_label = customtkinter.CTkLabel(self.sidebar_frame, text="CustomTkinter", font=customtkinter.CTkFont(size=20, weight="bold"))
        self.logo_label.grid(row=0, column=0, padx=20, pady=(20, 10))
        self.sidebar_button_1 = customtkinter.CTkButton(self.sidebar_frame, text="Upload", command=self.load_image)
        self.sidebar_button_1.grid(row=1, column=0, padx=20, pady=10)
        self.sidebar_button_2 = customtkinter.CTkButton(self.sidebar_frame, command=self.sidebar_button_event)
        self.sidebar_button_2.grid(row=2, column=0, padx=20, pady=10)
        self.sidebar_button_3 = customtkinter.CTkButton(self.sidebar_frame, command=self.sidebar_button_event)
        self.sidebar_button_3.grid(row=3, column=0, padx=20, pady=10)
        self.appearance_mode_label = customtkinter.CTkLabel(self.sidebar_frame, text="Appearance Mode:", anchor="w")
        self.appearance_mode_label.grid(row=5, column=0, padx=20, pady=(10, 0))
        self.appearance_mode_optionemenu = customtkinter.CTkOptionMenu(self.sidebar_frame, values=["Light", "Dark", "System"],
                                                                       command=self.change_appearance_mode_event)
        self.appearance_mode_optionemenu.grid(row=6, column=0, padx=20, pady=(10, 10))
        self.scaling_label = customtkinter.CTkLabel(self.sidebar_frame, text="UI Scaling:", anchor="w")
        self.scaling_label.grid(row=7, column=0, padx=20, pady=(10, 0))
        self.scaling_optionemenu = customtkinter.CTkOptionMenu(self.sidebar_frame, values=["80%", "90%", "100%", "110%", "120%"],
                                                               command=self.change_scaling_event)
        self.scaling_optionemenu.grid(row=8, column=0, padx=20, pady=(10, 20))

        # create main entry and button
        self.entry = customtkinter.CTkEntry(self, placeholder_text="CTkEntry")
        self.entry.grid(row=3, column=1, columnspan=2, padx=(20, 0), pady=(20, 20), sticky="nsew")

        self.main_button_1 = customtkinter.CTkButton(master=self, fg_color="transparent", border_width=2, text_color=("gray10", "#DCE4EE"))
        self.main_button_1.grid(row=3, column=3, padx=(20, 20), pady=(20, 20), sticky="nsew")

        # create image preview
        self.image_field_frame = customtkinter.CTkFrame(self)
        self.image_field_frame.grid(row=0, column=1, padx=(20, 0), pady=(20, 0), sticky="nsew", rowspan=3)
        self.image_field_frame.grid_rowconfigure(0, weight=1)
        self.image_field_frame.grid_columnconfigure(0, weight=1)
        self.image_field = tkinter.Label(self.image_field_frame, bg=self.image_field_frame.cget("fg_color"))
        self.image_field.grid(row=0, column=0, sticky="nsew")
        # 绑定框架尺寸变化事件
        self.image_field_frame.bind("<Configure>", self.on_image_frame_resize)
    
        # create tabview
        self.tabview = customtkinter.CTkTabview(self, width=250)
        self.tabview.grid(row=0, column=3, padx=(20, 20), pady=(20, 0), sticky="nsew")
        self.tabview.add("CTkTabview")
        self.tabview.add("Tab 2")
        self.tabview.add("Tab 3")
        self.tabview.tab("CTkTabview").grid_columnconfigure(0, weight=1)  # configure grid of individual tabs
        self.tabview.tab("Tab 2").grid_columnconfigure(0, weight=1)

        self.optionmenu_1 = customtkinter.CTkOptionMenu(self.tabview.tab("CTkTabview"), dynamic_resizing=False,
                                                        values=["Value 1", "Value 2", "Value Long Long Long"])
        self.optionmenu_1.grid(row=0, column=0, padx=20, pady=(20, 10))
        self.combobox_1 = customtkinter.CTkComboBox(self.tabview.tab("CTkTabview"),
                                                    values=["Value 1", "Value 2", "Value Long....."])
        self.combobox_1.grid(row=1, column=0, padx=20, pady=(10, 10))
        self.string_input_button = customtkinter.CTkButton(self.tabview.tab("CTkTabview"), text="Open CTkInputDialog",
                                                           command=self.open_input_dialog_event)
        self.string_input_button.grid(row=2, column=0, padx=20, pady=(10, 10))
        self.label_tab_2 = customtkinter.CTkLabel(self.tabview.tab("Tab 2"), text="CTkLabel on Tab 2")
        self.label_tab_2.grid(row=0, column=0, padx=20, pady=20)

        # create scrollable frame
        self.scrollable_frame = customtkinter.CTkScrollableFrame(self, label_text="CTkScrollableFrame")
        self.scrollable_frame.grid(row=1, column=3, padx=(20, 20), pady=(20, 0), sticky="nsew")
        self.scrollable_frame.grid_columnconfigure(0, weight=1)
        self.scrollable_frame_switches = []
        for i in range(100):
            switch = customtkinter.CTkSwitch(master=self.scrollable_frame, text=f"CTkSwitch {i}")
            switch.grid(row=i, column=0, padx=10, pady=(0, 20))
            self.scrollable_frame_switches.append(switch)

        # set default values
        self.sidebar_button_3.configure(state="disabled", text="Disabled CTkButton")
        self.scrollable_frame_switches[0].select()
        self.scrollable_frame_switches[4].select()
        self.appearance_mode_optionemenu.set("Dark")
        self.scaling_optionemenu.set("100%")
        self.optionmenu_1.set("CTkOptionmenu")
        self.combobox_1.set("CTkComboBox")

    def open_input_dialog_event(self):
        dialog = customtkinter.CTkInputDialog(text="Type in a number:", title="CTkInputDialog")
        print("CTkInputDialog:", dialog.get_input())

    def change_appearance_mode_event(self, new_appearance_mode: str):
        customtkinter.set_appearance_mode(new_appearance_mode)

    def change_scaling_event(self, new_scaling: str):
        new_scaling_float = int(new_scaling.replace("%", "")) / 100
        customtkinter.set_widget_scaling(new_scaling_float)

    def sidebar_button_event(self):
        print("sidebar_button click")

    def load_image(self):
        file_path = customtkinter.filedialog.askopenfilename()
        if file_path:
            self.current_image = Image.open(file_path)
            self.on_image_frame_resize()

    def on_image_frame_resize(self, event=None):
        if hasattr(self, 'current_image'):
            image = self.current_image.copy()
            segment_width = self.image_field_frame.winfo_width()
            segment_height = self.image_field_frame.winfo_height()
            
            if segment_width <= 0 or segment_height <= 0:
                return
                
            width_ratio = segment_width / image.width
            height_ratio = segment_height / image.height
            scale_factor = min(width_ratio, height_ratio)
            
            new_width = int(image.width * scale_factor)
            new_height = int(image.height * scale_factor)
            image = image.resize((new_width, new_height))
            
            x_offset = (segment_width - new_width) // 2
            y_offset = (segment_height - new_height) // 2
            self.image_field.place(x=x_offset, y=y_offset)
            
            photo = ImageTk.PhotoImage(image)
            self.image_field.configure(image=photo)
            self.image_field.image = photo


if __name__ == "__main__":
    app = App()
    app.mainloop()

内容的提问来源于stack exchange,提问作者user22614632

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 08:52:32