PanedWindow(面板窗口)是Tkinter中用於創建可調整大小的面板容器的組件,用户可以通過拖動分隔條來調整各面板的大小。

基本用法

1. 導入和基本創建

import tkinter as tk
from tkinter import ttk

# 創建主窗口
root = tk.Tk()
root.title("PanedWindow組件詳解")
root.geometry("600x400")

2. 創建基本PanedWindow

# 創建水平PanedWindow
paned = tk.PanedWindow(root, orient=tk.HORIZONTAL)
paned.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

# 創建左側面板
left_panel = tk.Frame(paned, bg="lightblue", relief=tk.RAISED, bd=2)
paned.add(left_panel)

# 創建右側面板  
right_panel = tk.Frame(paned, bg="lightgreen", relief=tk.RAISED, bd=2)
paned.add(right_panel)

# 在面板中添加內容
tk.Label(left_panel, text="左側面板", bg="lightblue", font=("Arial", 12)).pack(pady=20)
tk.Label(right_panel, text="右側面板", bg="lightgreen", font=("Arial", 12)).pack(pady=20)

root.mainloop()

PanedWindow的完整參數和功能

示例1:PanedWindow全面展示

import tkinter as tk
from tkinter import ttk

class PanedWindowComprehensive:
    def __init__(self, root):
        self.root = root
        self.setup_ui()
        
    def setup_ui(self):
        """設置用户界面"""
        self.root.title("PanedWindow全面展示")
        self.root.geometry("800x600")
        
        # 創建主Frame
        main_frame = tk.Frame(self.root, bg="#f0f0f0")
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 創建Notebook用於分頁顯示
        notebook = ttk.Notebook(main_frame)
        notebook.pack(fill=tk.BOTH, expand=True)
        
        # 1. 基本PanedWindow示例
        self.create_basic_panedwindow_tab(notebook)
        
        # 2. 不同方向的PanedWindow
        self.create_orientation_panedwindow_tab(notebook)
        
        # 3. 嵌套PanedWindow
        self.create_nested_panedwindow_tab(notebook)
        
        # 4. 動態操作PanedWindow
        self.create_dynamic_panedwindow_tab(notebook)
        
        # 5. 實際應用示例
        self.create_practical_example_tab(notebook)
        
    def create_basic_panedwindow_tab(self, notebook):
        """創建基本PanedWindow標籤頁"""
        frame = ttk.Frame(notebook)
        notebook.add(frame, text="基本示例")
        
        # 創建水平PanedWindow
        paned = tk.PanedWindow(frame, orient=tk.HORIZONTAL, sashrelief=tk.RAISED, sashwidth=8)
        paned.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 創建三個面板
        panels = []
        colors = ["#e8f4f8", "#f8f8e8", "#f8e8e8"]
        texts = ["面板 1", "面板 2", "面板 3"]
        
        for i, (color, text) in enumerate(zip(colors, texts)):
            panel = tk.Frame(paned, bg=color, relief=tk.RAISED, bd=1)
            
            # 添加標題
            title_frame = tk.Frame(panel, bg=color)
            title_frame.pack(fill=tk.X, padx=5, pady=5)
            tk.Label(title_frame, text=text, bg=color, font=("Arial", 10, "bold")).pack()
            
            # 添加內容
            content_text = tk.Text(panel, wrap=tk.WORD, bg=color, relief=tk.FLAT)
            content_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
            content_text.insert(tk.END, f"這是{text}的內容區域。\n\n你可以在這裏放置任何組件。")
            
            panels.append(panel)
            paned.add(panel)
            
        # 信息顯示
        info_frame = tk.Frame(frame, bg="white", relief=tk.GROOVE, bd=1)
        info_frame.pack(fill=tk.X, padx=10, pady=5)
        
        info_label = tk.Label(info_frame, text="拖動分隔條可以調整面板大小", 
                             bg="white", font=("Arial", 9))
        info_label.pack(pady=5)
        
    def create_orientation_panedwindow_tab(self, notebook):
        """創建不同方向的PanedWindow標籤頁"""
        frame = ttk.Frame(notebook)
        notebook.add(frame, text="方向控制")
        
        # 方向選擇
        orientation_frame = tk.Frame(frame, bg="white", relief=tk.GROOVE, bd=1)
        orientation_frame.pack(fill=tk.X, padx=10, pady=10)
        
        self.orient_var = tk.StringVar(value="horizontal")
        
        tk.Radiobutton(orientation_frame, text="水平方向 (HORIZONTAL)", 
                      variable=self.orient_var, value="horizontal",
                      command=self.update_orientation, bg="white").pack(anchor="w")
        tk.Radiobutton(orientation_frame, text="垂直方向 (VERTICAL)", 
                      variable=self.orient_var, value="vertical",
                      command=self.update_orientation, bg="white").pack(anchor="w")
        
        # PanedWindow容器
        self.orient_container = tk.Frame(frame)
        self.orient_container.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        self.create_orientation_panedwindow()
        
    def create_orientation_panedwindow(self):
        """創建方向PanedWindow"""
        # 清除現有內容
        for widget in self.orient_container.winfo_children():
            widget.destroy()
            
        orient = tk.HORIZONTAL if self.orient_var.get() == "horizontal" else tk.VERTICAL
        
        self.orient_paned = tk.PanedWindow(self.orient_container, orient=orient, 
                                          sashrelief=tk.SUNKEN, sashwidth=6)
        self.orient_paned.pack(fill=tk.BOTH, expand=True)
        
        # 創建面板
        panels_info = [
            ("面板 A", "#e8f4f8", "這是第一個面板的內容"),
            ("面板 B", "#f0f8e8", "這是第二個面板的內容"),
            ("面板 C", "#f8f0e8", "這是第三個面板的內容")
        ]
        
        for title, color, content in panels_info:
            panel = self.create_panel(self.orient_paned, title, color, content)
            self.orient_paned.add(panel)
            
    def create_nested_panedwindow_tab(self, notebook):
        """創建嵌套PanedWindow標籤頁"""
        frame = ttk.Frame(notebook)
        notebook.add(frame, text="嵌套佈局")
        
        # 主水平PanedWindow
        main_paned = tk.PanedWindow(frame, orient=tk.HORIZONTAL, sashwidth=8)
        main_paned.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 左側面板 - 垂直嵌套
        left_panel = tk.Frame(main_paned, bg="#f0f8ff")
        left_paned = tk.PanedWindow(left_panel, orient=tk.VERTICAL, sashwidth=6)
        left_paned.pack(fill=tk.BOTH, expand=True)
        
        # 左上角面板
        top_left = self.create_panel(left_paned, "導航欄", "#e8f4f8", 
                                   "項目1\n項目2\n項目3\n項目4")
        left_paned.add(top_left)
        
        # 左下角面板
        bottom_left = self.create_panel(left_paned, "文件列表", "#f0f8e8", 
                                      "文件1.txt\n文件2.py\n文件3.jpg")
        left_paned.add(bottom_left)
        
        main_paned.add(left_panel)
        
        # 右側面板 - 垂直嵌套
        right_panel = tk.Frame(main_paned, bg="#fff8f0")
        right_paned = tk.PanedWindow(right_panel, orient=tk.VERTICAL, sashwidth=6)
        right_paned.pack(fill=tk.BOTH, expand=True)
        
        # 右上角面板
        top_right = self.create_panel(right_paned, "編輯器", "#ffffff", 
                                    "def main():\n    print('Hello World')\n    return 0")
        right_paned.add(top_right)
        
        # 右下角面板
        bottom_right = self.create_panel(right_paned, "終端", "#2c3e50", 
                                       "$ python main.py\nHello World", 
                                       fg="white")
        right_paned.add(bottom_right)
        
        main_paned.add(right_panel)
        
    def create_dynamic_panedwindow_tab(self, notebook):
        """創建動態操作PanedWindow標籤頁"""
        frame = ttk.Frame(notebook)
        notebook.add(frame, text="動態操作")
        
        # 控制面板
        control_frame = tk.Frame(frame, bg="white", relief=tk.GROOVE, bd=1)
        control_frame.pack(fill=tk.X, padx=10, pady=10)
        
        # 添加面板按鈕
        add_frame = tk.Frame(control_frame, bg="white")
        add_frame.pack(fill=tk.X, pady=5)
        
        self.panel_counter = 3
        tk.Button(add_frame, text="添加面板", 
                 command=self.add_panel).pack(side=tk.LEFT, padx=5)
        tk.Button(add_frame, text="移除面板", 
                 command=self.remove_panel).pack(side=tk.LEFT, padx=5)
        
        # 面板配置
        config_frame = tk.Frame(control_frame, bg="white")
        config_frame.pack(fill=tk.X, pady=5)
        
        tk.Button(config_frame, text="顯示/隱藏分隔條", 
                 command=self.toggle_sash).pack(side=tk.LEFT, padx=5)
        tk.Button(config_frame, text="改變分隔條樣式", 
                 command=self.change_sash_style).pack(side=tk.LEFT, padx=5)
        
        # 動態PanedWindow
        self.dynamic_paned = tk.PanedWindow(frame, orient=tk.HORIZONTAL, 
                                           sashrelief=tk.RAISED, sashwidth=8)
        self.dynamic_paned.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 初始面板
        for i in range(3):
            panel = self.create_panel(self.dynamic_paned, f"面板 {i+1}", 
                                    f"#f{i%3+1}f{i%3+1}f{i%3+1}", 
                                    f"這是動態面板 {i+1} 的內容")
            self.dynamic_paned.add(panel)
            
    def create_practical_example_tab(self, notebook):
        """創建實際應用示例標籤頁"""
        frame = ttk.Frame(notebook)
        notebook.add(frame, text="實際應用")
        
        # 創建文件管理器佈局
        main_paned = tk.PanedWindow(frame, orient=tk.HORIZONTAL, sashwidth=6)
        main_paned.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 左側 - 目錄樹
        left_panel = tk.Frame(main_paned, bg="#f5f5f5")
        left_panel.pack_propagate(False)
        
        # 目錄樹標題
        tree_title = tk.Frame(left_panel, bg="#e0e0e0", height=30)
        tree_title.pack(fill=tk.X)
        tree_title.pack_propagate(False)
        tk.Label(tree_title, text="文件夾", bg="#e0e0e0", font=("Arial", 10, "bold")).pack(pady=5)
        
        # 目錄樹
        tree_frame = tk.Frame(left_panel, bg="#f5f5f5")
        tree_frame.pack(fill=tk.BOTH, expand=True)
        
        tree = ttk.Treeview(tree_frame)
        tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 添加示例數據
        folders = ["文檔", "圖片", "音樂", "視頻", "下載"]
        for folder in folders:
            tree.insert("", tk.END, text=folder)
            
        main_paned.add(left_panel, minsize=150)
        
        # 右側 - 文件列表和預覽
        right_panel = tk.Frame(main_paned, bg="#ffffff")
        right_paned = tk.PanedWindow(right_panel, orient=tk.VERTICAL, sashwidth=4)
        right_paned.pack(fill=tk.BOTH, expand=True)
        
        # 文件列表
        file_list_panel = tk.Frame(right_paned, bg="#ffffff")
        
        # 文件列表標題
        file_title = tk.Frame(file_list_panel, bg="#e0e0e0", height=30)
        file_title.pack(fill=tk.X)
        file_title.pack_propagate(False)
        tk.Label(file_title, text="文件", bg="#e0e0e0", font=("Arial", 10, "bold")).pack(pady=5)
        
        # 文件列表
        listbox = tk.Listbox(file_list_panel)
        listbox.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 添加示例文件
        files = ["報告.docx", "照片.jpg", "音樂.mp3", "視頻.mp4", "代碼.py"]
        for file in files:
            listbox.insert(tk.END, file)
            
        right_paned.add(file_list_panel)
        
        # 文件預覽
        preview_panel = tk.Frame(right_paned, bg="#f8f8f8")
        
        # 預覽標題
        preview_title = tk.Frame(preview_panel, bg="#e0e0e0", height=30)
        preview_title.pack(fill=tk.X)
        preview_title.pack_propagate(False)
        tk.Label(preview_title, text="預覽", bg="#e0e0e0", font=("Arial", 10, "bold")).pack(pady=5)
        
        # 預覽內容
        preview_text = tk.Text(preview_panel, wrap=tk.WORD, bg="#f8f8f8")
        preview_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        preview_text.insert(tk.END, "選擇文件查看預覽...")
        
        right_paned.add(preview_panel, minsize=100)
        
        main_paned.add(right_panel)
        
    def create_panel(self, parent, title, bg_color, content, fg="black"):
        """創建標準面板"""
        panel = tk.Frame(parent, bg=bg_color, relief=tk.RAISED, bd=1)
        
        # 標題欄
        title_frame = tk.Frame(panel, bg=bg_color)
        title_frame.pack(fill=tk.X, padx=5, pady=5)
        tk.Label(title_frame, text=title, bg=bg_color, fg=fg, 
                font=("Arial", 10, "bold")).pack()
        
        # 內容區域
        content_text = tk.Text(panel, wrap=tk.WORD, bg=bg_color, fg=fg, 
                              relief=tk.FLAT, height=6)
        content_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        content_text.insert(tk.END, content)
        
        return panel
        
    def update_orientation(self):
        """更新方向"""
        self.create_orientation_panedwindow()
        
    def add_panel(self):
        """添加面板"""
        self.panel_counter += 1
        color = f"#f{self.panel_counter%3+1}f{self.panel_counter%3+1}f{self.panel_counter%3+1}"
        panel = self.create_panel(self.dynamic_paned, f"面板 {self.panel_counter}", 
                                color, f"這是新添加的面板 {self.panel_counter}")
        self.dynamic_paned.add(panel)
        
    def remove_panel(self):
        """移除面板"""
        if self.panel_counter > 1:
            # 獲取最後一個面板並移除
            last_panel = self.dynamic_paned.panes()[-1]
            self.dynamic_paned.remove(last_panel)
            self.panel_counter -= 1
            
    def toggle_sash(self):
        """切換分隔條顯示"""
        current_state = self.dynamic_paned.cget('showhandle')
        new_state = not current_state
        self.dynamic_paned.config(showhandle=new_state)
        
    def change_sash_style(self):
        """改變分隔條樣式"""
        styles = [tk.FLAT, tk.RAISED, tk.SUNKEN, tk.GROOVE, tk.RIDGE]
        current_style = self.dynamic_paned.cget('sashrelief')
        current_index = styles.index(current_style)
        next_index = (current_index + 1) % len(styles)
        self.dynamic_paned.config(sashrelief=styles[next_index])

if __name__ == "__main__":
    root = tk.Tk()
    app = PanedWindowComprehensive(root)
    root.mainloop()

實際應用示例

示例2:代碼編輯器佈局

import tkinter as tk
from tkinter import ttk, scrolledtext
import os

class CodeEditorLayout:
    def __init__(self, root):
        self.root = root
        self.setup_ui()
        
    def setup_ui(self):
        """設置用户界面"""
        self.root.title("代碼編輯器 - PanedWindow示例")
        self.root.geometry("1000x700")
        
        # 創建主菜單
        self.create_menu()
        
        # 創建主PanedWindow
        self.main_paned = tk.PanedWindow(self.root, orient=tk.HORIZONTAL, sashwidth=6)
        self.main_paned.pack(fill=tk.BOTH, expand=True)
        
        # 創建左側面板(文件瀏覽器)
        self.create_file_explorer()
        
        # 創建右側面板(編輯區域)
        self.create_editor_area()
        
        # 狀態欄
        self.create_status_bar()
        
    def create_menu(self):
        """創建菜單欄"""
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)
        
        # 文件菜單
        file_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="文件", menu=file_menu)
        file_menu.add_command(label="新建", command=self.new_file)
        file_menu.add_command(label="打開", command=self.open_file)
        file_menu.add_command(label="保存", command=self.save_file)
        file_menu.add_separator()
        file_menu.add_command(label="退出", command=self.root.quit)
        
        # 視圖菜單
        view_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="視圖", menu=view_menu)
        view_menu.add_command(label="切換文件瀏覽器", command=self.toggle_file_explorer)
        view_menu.add_command(label="切換終端", command=self.toggle_terminal)
        
    def create_file_explorer(self):
        """創建文件瀏覽器"""
        self.explorer_frame = tk.Frame(self.main_paned, bg="#f5f5f5")
        
        # 文件瀏覽器標題
        explorer_title = tk.Frame(self.explorer_frame, bg="#e0e0e0", height=30)
        explorer_title.pack(fill=tk.X)
        explorer_title.pack_propagate(False)
        
        title_label = tk.Label(explorer_title, text="文件瀏覽器", bg="#e0e0e0", 
                              font=("Arial", 10, "bold"))
        title_label.pack(side=tk.LEFT, padx=10, pady=5)
        
        # 關閉按鈕
        close_btn = tk.Button(explorer_title, text="×", font=("Arial", 12), 
                             command=self.toggle_file_explorer, bg="#e0e0e0", 
                             relief=tk.FLAT, bd=0)
        close_btn.pack(side=tk.RIGHT, padx=5)
        
        # 文件樹
        tree_frame = tk.Frame(self.explorer_frame, bg="#f5f5f5")
        tree_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 創建Treeview
        self.file_tree = ttk.Treeview(tree_frame)
        self.file_tree.pack(fill=tk.BOTH, expand=True)
        
        # 添加示例文件結構
        self.populate_file_tree()
        
        # 綁定選擇事件
        self.file_tree.bind('<<TreeviewSelect>>', self.on_file_select)
        
        self.main_paned.add(self.explorer_frame, minsize=200)
        
    def create_editor_area(self):
        """創建編輯區域"""
        self.editor_paned = tk.PanedWindow(self.main_paned, orient=tk.VERTICAL, sashwidth=4)
        
        # 創建編輯器面板
        self.create_editor_panel()
        
        # 創建終端面板
        self.create_terminal_panel()
        
        self.main_paned.add(self.editor_paned)
        
    def create_editor_panel(self):
        """創建編輯器面板"""
        self.editor_frame = tk.Frame(self.editor_paned, bg="#ffffff")
        
        # 標籤頁控件
        self.notebook = ttk.Notebook(self.editor_frame)
        self.notebook.pack(fill=tk.BOTH, expand=True)
        
        # 創建初始標籤頁
        self.create_editor_tab("main.py", "# 歡迎使用代碼編輯器\n\nprint('Hello, World!')")
        
        self.editor_paned.add(self.editor_frame)
        
    def create_terminal_panel(self):
        """創建終端面板"""
        self.terminal_frame = tk.Frame(self.editor_paned, bg="#2c3e50")
        
        # 終端標題
        terminal_title = tk.Frame(self.terminal_frame, bg="#34495e", height=30)
        terminal_title.pack(fill=tk.X)
        terminal_title.pack_propagate(False)
        
        title_label = tk.Label(terminal_title, text="終端", bg="#34495e", 
                              fg="white", font=("Arial", 10, "bold"))
        title_label.pack(side=tk.LEFT, padx=10, pady=5)
        
        # 關閉按鈕
        close_btn = tk.Button(terminal_title, text="×", font=("Arial", 12), 
                             command=self.toggle_terminal, bg="#34495e", 
                             fg="white", relief=tk.FLAT, bd=0)
        close_btn.pack(side=tk.RIGHT, padx=5)
        
        # 終端內容
        terminal_content = tk.Frame(self.terminal_frame, bg="#2c3e50")
        terminal_content.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        self.terminal_text = scrolledtext.ScrolledText(terminal_content, 
                                                     bg="#2c3e50", fg="#ecf0f1",
                                                     font=("Consolas", 10),
                                                     insertbackground="white")
        self.terminal_text.pack(fill=tk.BOTH, expand=True)
        self.terminal_text.insert(tk.END, "$ 終端就緒...\n\n")
        
        self.editor_paned.add(self.terminal_frame, minsize=150)
        
    def create_status_bar(self):
        """創建狀態欄"""
        status_frame = tk.Frame(self.root, bg="#e0e0e0", height=25)
        status_frame.pack(fill=tk.X, side=tk.BOTTOM)
        status_frame.pack_propagate(False)
        
        # 狀態信息
        self.status_label = tk.Label(status_frame, text="就緒", bg="#e0e0e0", 
                                    font=("Arial", 9))
        self.status_label.pack(side=tk.LEFT, padx=10)
        
        # 光標位置
        self.cursor_label = tk.Label(status_frame, text="行: 1, 列: 1", bg="#e0e0e0", 
                                    font=("Arial", 9))
        self.cursor_label.pack(side=tk.RIGHT, padx=10)
        
    def populate_file_tree(self):
        """填充文件樹"""
        # 添加示例項目結構
        project_root = self.file_tree.insert("", "end", text="我的項目", open=True)
        
        folders = [
            ("src", ["main.py", "utils.py", "config.py"]),
            ("docs", ["README.md", "API.md"]),
            ("tests", ["test_main.py", "test_utils.py"]),
            ("data", ["sample.csv", "config.json"])
        ]
        
        for folder, files in folders:
            folder_id = self.file_tree.insert(project_root, "end", text=folder, open=True)
            for file in files:
                self.file_tree.insert(folder_id, "end", text=file)
                
    def create_editor_tab(self, filename, content):
        """創建編輯器標籤頁"""
        tab_frame = ttk.Frame(self.notebook)
        
        # 創建文本編輯器
        text_widget = scrolledtext.ScrolledText(tab_frame, wrap=tk.WORD, 
                                               font=("Consolas", 11),
                                               undo=True)
        text_widget.pack(fill=tk.BOTH, expand=True)
        text_widget.insert(tk.END, content)
        
        # 綁定光標移動事件
        text_widget.bind('<KeyRelease>', self.update_cursor_position)
        text_widget.bind('<Button-1>', self.update_cursor_position)
        
        self.notebook.add(tab_frame, text=filename)
        
        return text_widget
        
    def on_file_select(self, event):
        """文件選擇事件"""
        selection = self.file_tree.selection()
        if selection:
            item = selection[0]
            item_text = self.file_tree.item(item, "text")
            
            # 如果是文件,在編輯器中打開
            if not self.file_tree.get_children(item):  # 沒有子項,説明是文件
                self.status_label.config(text=f"打開文件: {item_text}")
                self.create_editor_tab(item_text, f"# {item_text} 的內容\n\n文件內容在這裏...")
                
    def toggle_file_explorer(self):
        """切換文件瀏覽器顯示"""
        if self.explorer_frame in self.main_paned.panes():
            self.main_paned.remove(self.explorer_frame)
        else:
            self.main_paned.add(self.explorer_frame, minsize=200)
            
    def toggle_terminal(self):
        """切換終端顯示"""
        if self.terminal_frame in self.editor_paned.panes():
            self.editor_paned.remove(self.terminal_frame)
        else:
            self.editor_paned.add(self.terminal_frame, minsize=150)
            
    def update_cursor_position(self, event):
        """更新光標位置"""
        widget = event.widget
        if hasattr(widget, 'index'):  # 確保是Text組件
            cursor_pos = widget.index(tk.INSERT)
            line, col = cursor_pos.split('.')
            self.cursor_label.config(text=f"行: {int(line)+1}, 列: {int(col)+1}")
            
    def new_file(self):
        """新建文件"""
        self.create_editor_tab("新文件.py", "# 新文件\n\n")
        self.status_label.config(text="已創建新文件")
        
    def open_file(self):
        """打開文件"""
        self.status_label.config(text="打開文件功能")
        
    def save_file(self):
        """保存文件"""
        self.status_label.config(text="保存文件功能")

if __name__ == "__main__":
    root = tk.Tk()
    app = CodeEditorLayout(root)
    root.mainloop()

示例3:數據分析工作台

import tkinter as tk
from tkinter import ttk, scrolledtext
import pandas as pd
import numpy as np
from io import StringIO

class DataAnalysisWorkbench:
    def __init__(self, root):
        self.root = root
        self.current_data = None
        self.setup_ui()
        
    def setup_ui(self):
        """設置用户界面"""
        self.root.title("數據分析工作台 - PanedWindow示例")
        self.root.geometry("1200x800")
        
        # 創建主PanedWindow
        self.main_paned = tk.PanedWindow(self.root, orient=tk.HORIZONTAL, sashwidth=6)
        self.main_paned.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 創建左側控制面板
        self.create_control_panel()
        
        # 創建右側數據面板
        self.create_data_panel()
        
        # 加載示例數據
        self.load_sample_data()
        
    def create_control_panel(self):
        """創建控制面板"""
        control_frame = tk.Frame(self.main_paned, bg="#f8f9fa", width=300)
        control_frame.pack_propagate(False)
        
        # 控制面板標題
        title_frame = tk.Frame(control_frame, bg="#e9ecef", height=40)
        title_frame.pack(fill=tk.X)
        title_frame.pack_propagate(False)
        tk.Label(title_frame, text="數據分析工具", bg="#e9ecef", 
                font=("Arial", 12, "bold")).pack(pady=10)
        
        # 數據操作區域
        data_section = self.create_section(control_frame, "數據操作")
        
        tk.Button(data_section, text="加載CSV數據", 
                 command=self.load_csv_data, bg="#007bff", fg="white").pack(fill=tk.X, pady=2)
        tk.Button(data_section, text="生成示例數據", 
                 command=self.generate_sample_data, bg="#28a745", fg="white").pack(fill=tk.X, pady=2)
        tk.Button(data_section, text="清空數據", 
                 command=self.clear_data, bg="#dc3545", fg="white").pack(fill=tk.X, pady=2)
        
        # 數據分析區域
        analysis_section = self.create_section(control_frame, "數據分析")
        
        tk.Button(analysis_section, text="描述性統計", 
                 command=self.descriptive_stats).pack(fill=tk.X, pady=2)
        tk.Button(analysis_section, text="相關性分析", 
                 command=self.correlation_analysis).pack(fill=tk.X, pady=2)
        tk.Button(analysis_section, text="數據可視化", 
                 command=self.data_visualization).pack(fill=tk.X, pady=2)
        
        # 數據過濾區域
        filter_section = self.create_section(control_frame, "數據過濾")
        
        # 列選擇
        filter_frame = tk.Frame(filter_section, bg="#f8f9fa")
        filter_frame.pack(fill=tk.X, pady=5)
        
        tk.Label(filter_frame, text="選擇列:", bg="#f8f9fa").pack(side=tk.LEFT)
        self.column_var = tk.StringVar()
        self.column_combo = ttk.Combobox(filter_frame, textvariable=self.column_var, 
                                        state="readonly")
        self.column_combo.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
        
        # 過濾條件
        condition_frame = tk.Frame(filter_section, bg="#f8f9fa")
        condition_frame.pack(fill=tk.X, pady=5)
        
        tk.Label(condition_frame, text="條件:", bg="#f8f9fa").pack(side=tk.LEFT)
        self.condition_var = tk.StringVar()
        condition_combo = ttk.Combobox(condition_frame, textvariable=self.condition_var,
                                      values=[">", ">=", "==", "!=", "<", "<="])
        condition_combo.pack(side=tk.LEFT, padx=5)
        
        self.value_var = tk.StringVar()
        value_entry = tk.Entry(condition_frame, textvariable=self.value_var, width=10)
        value_entry.pack(side=tk.LEFT, padx=5)
        
        tk.Button(filter_section, text="應用過濾", 
                 command=self.apply_filter).pack(fill=tk.X, pady=2)
        
        self.main_paned.add(control_frame)
        
    def create_data_panel(self):
        """創建數據面板"""
        data_paned = tk.PanedWindow(self.main_paned, orient=tk.VERTICAL, sashwidth=4)
        
        # 數據表格面板
        self.create_data_table_panel(data_paned)
        
        # 分析結果面板
        self.create_analysis_panel(data_paned)
        
        self.main_paned.add(data_paned)
        
    def create_data_table_panel(self, parent):
        """創建數據表格面板"""
        table_frame = tk.Frame(parent, bg="#ffffff")
        
        # 表格標題
        table_title = tk.Frame(table_frame, bg="#e9ecef", height=30)
        table_title.pack(fill=tk.X)
        table_title.pack_propagate(False)
        
        tk.Label(table_title, text="數據表格", bg="#e9ecef", 
                font=("Arial", 10, "bold")).pack(side=tk.LEFT, padx=10, pady=5)
        
        # 數據信息
        self.data_info_label = tk.Label(table_title, text="無數據", bg="#e9ecef")
        self.data_info_label.pack(side=tk.RIGHT, padx=10, pady=5)
        
        # 表格容器
        table_container = tk.Frame(table_frame, bg="#ffffff")
        table_container.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 創建Treeview作為數據表格
        self.data_tree = ttk.Treeview(table_container)
        
        # 滾動條
        scrollbar = ttk.Scrollbar(table_container, orient=tk.VERTICAL, command=self.data_tree.yview)
        self.data_tree.configure(yscrollcommand=scrollbar.set)
        
        h_scrollbar = ttk.Scrollbar(table_container, orient=tk.HORIZONTAL, command=self.data_tree.xview)
        self.data_tree.configure(xscrollcommand=h_scrollbar.set)
        
        self.data_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
        
        parent.add(table_frame)
        
    def create_analysis_panel(self, parent):
        """創建分析結果面板"""
        analysis_frame = tk.Frame(parent, bg="#f8f9fa")
        
        # 分析標題
        analysis_title = tk.Frame(analysis_frame, bg="#e9ecef", height=30)
        analysis_title.pack(fill=tk.X)
        analysis_title.pack_propagate(False)
        
        tk.Label(analysis_title, text="分析結果", bg="#e9ecef", 
                font=("Arial", 10, "bold")).pack(side=tk.LEFT, padx=10, pady=5)
        
        # 分析結果文本
        self.analysis_text = scrolledtext.ScrolledText(analysis_frame, 
                                                      wrap=tk.WORD,
                                                      font=("Consolas", 10),
                                                      height=10)
        self.analysis_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        parent.add(analysis_frame, minsize=200)
        
    def create_section(self, parent, title):
        """創建分區"""
        section_frame = tk.Frame(parent, bg="#f8f9fa", relief=tk.GROOVE, bd=1)
        section_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 分區標題
        title_label = tk.Label(section_frame, text=title, bg="#e9ecef",
                              font=("Arial", 10, "bold"))
        title_label.pack(fill=tk.X, padx=5, pady=2)
        
        # 內容區域
        content_frame = tk.Frame(section_frame, bg="#f8f9fa")
        content_frame.pack(fill=tk.X, padx=5, pady=5)
        
        return content_frame
        
    def load_sample_data(self):
        """加載示例數據"""
        # 創建示例數據
        data = {
            '姓名': ['張三', '李四', '王五', '趙六', '錢七'],
            '年齡': [25, 30, 35, 28, 32],
            '城市': ['北京', '上海', '廣州', '深圳', '杭州'],
            '工資': [5000, 7000, 6000, 8000, 7500],
            '部門': ['技術部', '銷售部', '技術部', '市場部', '銷售部']
        }
        
        self.current_data = pd.DataFrame(data)
        self.display_data()
        
    def display_data(self):
        """顯示數據"""
        if self.current_data is not None:
            # 清空現有數據
            for item in self.data_tree.get_children():
                self.data_tree.delete(item)
                
            # 設置列
            columns = list(self.current_data.columns)
            self.data_tree['columns'] = columns
            self.data_tree['show'] = 'headings'
            
            # 設置列標題
            for col in columns:
                self.data_tree.heading(col, text=col)
                self.data_tree.column(col, width=100)
                
            # 添加數據
            for index, row in self.current_data.iterrows():
                self.data_tree.insert("", "end", values=list(row))
                
            # 更新數據信息
            info_text = f"行: {len(self.current_data)}, 列: {len(columns)}"
            self.data_info_label.config(text=info_text)
            
            # 更新列選擇
            self.column_combo['values'] = columns
            if columns:
                self.column_combo.set(columns[0])
                
    def load_csv_data(self):
        """加載CSV數據"""
        self.analysis_text.insert(tk.END, "CSV數據加載功能...\n\n")
        
    def generate_sample_data(self):
        """生成示例數據"""
        # 生成隨機數據
        np.random.seed(42)
        data = {
            'A': np.random.normal(0, 1, 100),
            'B': np.random.normal(5, 2, 100),
            'C': np.random.randint(1, 10, 100),
            'D': np.random.choice(['X', 'Y', 'Z'], 100)
        }
        
        self.current_data = pd.DataFrame(data)
        self.display_data()
        self.analysis_text.insert(tk.END, "已生成示例數據 (100行 × 4列)\n\n")
        
    def clear_data(self):
        """清空數據"""
        self.current_data = None
        for item in self.data_tree.get_children():
            self.data_tree.delete(item)
        self.data_info_label.config(text="無數據")
        self.analysis_text.delete(1.0, tk.END)
        
    def descriptive_stats(self):
        """描述性統計"""
        if self.current_data is not None:
            stats = self.current_data.describe()
            self.analysis_text.delete(1.0, tk.END)
            self.analysis_text.insert(tk.END, "描述性統計:\n\n")
            self.analysis_text.insert(tk.END, str(stats))
            
    def correlation_analysis(self):
        """相關性分析"""
        if self.current_data is not None:
            # 選擇數值列
            numeric_data = self.current_data.select_dtypes(include=[np.number])
            if not numeric_data.empty:
                correlation = numeric_data.corr()
                self.analysis_text.delete(1.0, tk.END)
                self.analysis_text.insert(tk.END, "相關性矩陣:\n\n")
                self.analysis_text.insert(tk.END, str(correlation))
            else:
                self.analysis_text.delete(1.0, tk.END)
                self.analysis_text.insert(tk.END, "沒有數值列可用於相關性分析\n")
                
    def data_visualization(self):
        """數據可視化"""
        self.analysis_text.insert(tk.END, "\n數據可視化功能...\n")
        
    def apply_filter(self):
        """應用過濾"""
        if self.current_data is not None and self.column_var.get():
            column = self.column_var.get()
            condition = self.condition_var.get()
            value = self.value_var.get()
            
            try:
                # 嘗試轉換為數值
                value = float(value)
                filter_expr = f"{column} {condition} {value}"
            except ValueError:
                # 作為字符串處理
                filter_expr = f"{column} {condition} '{value}'"
                
            try:
                filtered_data = self.current_data.query(filter_expr)
                self.analysis_text.insert(tk.END, f"\n應用過濾: {filter_expr}\n")
                self.analysis_text.insert(tk.END, f"結果: {len(filtered_data)} 行\n")
            except Exception as e:
                self.analysis_text.insert(tk.END, f"\n過濾錯誤: {str(e)}\n")

if __name__ == "__main__":
    root = tk.Tk()
    app = DataAnalysisWorkbench(root)
    root.mainloop()

PanedWindow的主要方法和屬性

# 創建PanedWindow
paned = tk.PanedWindow(parent, **options)

# 主要配置選項
paned.config(
    orient=tk.HORIZONTAL,      # 方向: HORIZONTAL 或 VERTICAL
    sashrelief=tk.RAISED,      # 分隔條樣式
    sashwidth=8,               # 分隔條寬度
    showhandle=True,           # 是否顯示手柄
    handlesize=8,              # 手柄大小
    opaqueresize=True,         # 是否實時調整
    background='color'         # 背景色
)

# 主要方法
paned.add(child, **options)    # 添加子組件
paned.remove(child)           # 移除子組件
paned.panes()                 # 獲取所有子組件
paned.sash_coord(index)       # 獲取分隔條座標
paned.sash_place(index, x, y) # 放置分隔條

# 添加選項
paned.add(child,
    after=other_child,        # 在哪個組件後添加
    before=other_child,       # 在哪個組件前添加  
    stretch='always',         # 拉伸方式: always, never, first, last
    minsize=100,              # 最小大小
    padx=5,                   # 水平間距
    pady=5                    # 垂直間距
)

使用場景總結

  1. 代碼編輯器:文件樹、編輯器、終端的可調整佈局
  2. 數據分析工具:控制面板、數據表格、分析結果的可調整視圖
  3. 文件管理器:目錄樹、文件列表、預覽面板
  4. 圖像處理軟件:工具欄、畫布、圖層面板
  5. IDE開發環境:多面板的專業開發界面
  6. 儀表板應用:多個數據可視化面板
  7. 文檔編輯器:導航、內容、屬性面板

PanedWindow是創建專業級可調整佈局應用程序的重要組件,通過合理使用可以創建出高度靈活和用户友好的界面。