如何让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
相关产品推荐
相关产品推荐

