import ttkbootstrap as ttk import tkinter from tkinter import ttk from pathlib import Path from ttkbootstrap import Style import sys import os def get_resource_path(relative_path): """ 获取资源文件的绝对路径(兼容开发环境和打包后) Args: relative_path: 相对路径,如 "assets/icons8_double_up_24px.png" Returns: str: 资源文件的绝对路径 """ try: # PyInstaller 打包后的临时文件夹路径 base_path = sys._MEIPASS except AttributeError: # 开发环境:使用项目根目录 # 当前文件: app/views/collapsing_frame.py # 项目根目录: app/views 的祖父目录 current_file = os.path.abspath(__file__) views_dir = os.path.dirname(current_file) app_dir = os.path.dirname(views_dir) base_path = os.path.dirname(app_dir) return os.path.join(base_path, relative_path) class CollapsingFrame(ttk.Frame): """ A collapsible frame widget that opens and closes with a button click. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.columnconfigure(0, weight=1) self.cumulative_rows = 0 p = Path(__file__).parent self.images = [ tkinter.PhotoImage( name="open", file=get_resource_path("assets/icons8_double_up_24px.png") ), tkinter.PhotoImage( name="closed", file=get_resource_path("assets/icons8_double_right_24px.png"), ), ] def add(self, child, title="", style="primary.TButton", **kwargs): """Add a child to the collapsible frame :param ttk.Frame child: the child frame to add to the widget :param str title: the title appearing on the collapsible section header :param str style: the ttk style to apply to the collapsible section header """ if child.winfo_class() != "TFrame": # must be a frame return style_color = style.split(".")[0] frm = ttk.Frame(self, style=f"{style_color}.TFrame") frm.grid(row=self.cumulative_rows, column=0, sticky="ew") # header title lbl = ttk.Label(frm, text=title, style=f"{style_color}.Inverse.TLabel") if kwargs.get("textvariable"): lbl.configure(textvariable=kwargs.get("textvariable")) lbl.pack(side="left", fill="both", padx=10) # header toggle button btn = ttk.Button( frm, image="open", style=style, command=lambda c=child: self._toggle_open_close(child), ) btn.pack(side="right") # assign toggle button to child so that it's accesible when toggling (need to change image) child.btn = btn child.grid(row=self.cumulative_rows + 1, column=0, sticky="news") # increment the row assignment self.cumulative_rows += 2 def _toggle_open_close(self, child): """ Open or close the section and change the toggle button image accordingly :param ttk.Frame child: the child element to add or remove from grid manager """ if child.winfo_viewable(): child.grid_remove() child.btn.configure(image="closed") else: child.grid() child.btn.configure(image="open") # class Application(tkinter.Tk): # def __init__(self): # super().__init__() # self.title('Collapsing Frame') # # self.style = Style() # cf = CollapsingFrame(self) # cf.pack(fill='both') # # option group 1 # group1 = ttk.Frame(cf, padding=10) # for x in range(5): # ttk.Checkbutton(group1, text=f'Option {x + 1}').pack(fill='x') # cf.add(group1, title='Option Group 1', style='primary.TButton') # # option group 2 # group2 = ttk.Frame(cf, padding=10) # for x in range(5): # ttk.Checkbutton(group2, text=f'Option {x + 1}').pack(fill='x') # cf.add(group2, title='Option Group 2', style='danger.TButton') # if __name__ == '__main__': # Application().mainloop()