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 # 垂直間距
)
使用場景總結
- 代碼編輯器:文件樹、編輯器、終端的可調整佈局
- 數據分析工具:控制面板、數據表格、分析結果的可調整視圖
- 文件管理器:目錄樹、文件列表、預覽面板
- 圖像處理軟件:工具欄、畫布、圖層面板
- IDE開發環境:多面板的專業開發界面
- 儀表板應用:多個數據可視化面板
- 文檔編輯器:導航、內容、屬性面板
PanedWindow是創建專業級可調整佈局應用程序的重要組件,通過合理使用可以創建出高度靈活和用户友好的界面。