|
@@ -0,0 +1,265 @@
|
|
|
|
|
+import tkinter as tk
|
|
|
|
|
+
|
|
|
|
|
+class SquareWindow:
|
|
|
|
|
+ def __init__(self, config):
|
|
|
|
|
+ # Configuration: [(parent_number, count), ...]
|
|
|
|
|
+ # (None, 11) means draw 11 squares inside square #0 (the main square)
|
|
|
|
|
+ # (2, 7) means draw 7 squares inside square #2
|
|
|
|
|
+ self.config = config
|
|
|
|
|
+
|
|
|
|
|
+ # Set window size to 1000x1000 pixels (needed for calculate_squares)
|
|
|
|
|
+ self.window_size = 1000
|
|
|
|
|
+
|
|
|
|
|
+ # Calculate squares based on config
|
|
|
|
|
+ self.squares = self.calculate_squares()
|
|
|
|
|
+
|
|
|
|
|
+ # Create the main window
|
|
|
|
|
+ self.root = tk.Tk()
|
|
|
|
|
+ self.root.title("Square Window")
|
|
|
|
|
+
|
|
|
|
|
+ self.root.geometry(f"{self.window_size}x{self.window_size}")
|
|
|
|
|
+
|
|
|
|
|
+ # Create a canvas to draw on
|
|
|
|
|
+ self.canvas = tk.Canvas(self.root, width=self.window_size, height=self.window_size, bg="white")
|
|
|
|
|
+ self.canvas.pack(fill=tk.BOTH, expand=True)
|
|
|
|
|
+
|
|
|
|
|
+ # Property to store square information: {number: (x, y, size, parent_number)}
|
|
|
|
|
+ self.square_positions = {}
|
|
|
|
|
+
|
|
|
|
|
+ # Draw all squares based on the calculated squares dictionary
|
|
|
|
|
+ for number, (x, y, size, parent_number) in self.squares.items():
|
|
|
|
|
+ self.draw_square_at_location(x, y, size, number, parent_number)
|
|
|
|
|
+
|
|
|
|
|
+ # Print the square positions dictionary
|
|
|
|
|
+ output_content = f"Square positions: {self.square_positions}"
|
|
|
|
|
+ print(output_content)
|
|
|
|
|
+
|
|
|
|
|
+ # Save the output to print.log file
|
|
|
|
|
+ with open("print.log", "w", encoding="utf-8") as f:
|
|
|
|
|
+ f.write(output_content)
|
|
|
|
|
+
|
|
|
|
|
+ def calculate_squares(self):
|
|
|
|
|
+ """
|
|
|
|
|
+ Calculate all squares based on the config.
|
|
|
|
|
+ Config format: [(parent_number, count), ...]
|
|
|
|
|
+ (None, count) means draw 'count' squares inside the main square (number 0)
|
|
|
|
|
+ (number, count) means draw 'count' squares inside square with 'number'
|
|
|
|
|
+ """
|
|
|
|
|
+ import math
|
|
|
|
|
+
|
|
|
|
|
+ # Initialize the main square (number 0)
|
|
|
|
|
+ main_square_size = int(self.window_size * 0.9) # 90% of 1000 = 900
|
|
|
|
|
+ margin = (self.window_size - main_square_size) // 2 # (1000 - 900) / 2 = 50
|
|
|
|
|
+
|
|
|
|
|
+ # Dictionary to store all squares: {number: (x, y, size, parent_number)}
|
|
|
|
|
+ squares = {}
|
|
|
|
|
+
|
|
|
|
|
+ # Add the main square (number 0)
|
|
|
|
|
+ squares[0] = (margin, margin, main_square_size, None)
|
|
|
|
|
+
|
|
|
|
|
+ # Track the next available number for new squares
|
|
|
|
|
+ next_number = 1
|
|
|
|
|
+
|
|
|
|
|
+ # Process each configuration entry
|
|
|
|
|
+ for parent_number, count in self.config:
|
|
|
|
|
+ if parent_number is None:
|
|
|
|
|
+ # Draw 'count' squares inside the main square (number 0)
|
|
|
|
|
+ parent_num = 0
|
|
|
|
|
+ else:
|
|
|
|
|
+ parent_num = parent_number
|
|
|
|
|
+
|
|
|
|
|
+ # Get the parent square's info
|
|
|
|
|
+ if parent_num in squares:
|
|
|
|
|
+ parent_x, parent_y, parent_size, _ = squares[parent_num]
|
|
|
|
|
+
|
|
|
|
|
+ # Handle case where count is 0
|
|
|
|
|
+ if count <= 0:
|
|
|
|
|
+ continue # Skip if there are no items to draw
|
|
|
|
|
+
|
|
|
|
|
+ # Calculate grid size for the child squares
|
|
|
|
|
+ n = math.ceil(math.sqrt(count)) # Number of rows/columns needed
|
|
|
|
|
+ child_size = (parent_size // n) - 2 # Size of each child square minus padding
|
|
|
|
|
+
|
|
|
|
|
+ # Calculate spacing to distribute squares evenly with gaps
|
|
|
|
|
+ total_used_space = n * child_size # Total space occupied by squares
|
|
|
|
|
+ remaining_space = parent_size - total_used_space # Remaining space for gaps
|
|
|
|
|
+ gap = remaining_space // (n + 1) # Gap between squares and margins
|
|
|
|
|
+
|
|
|
|
|
+ # Draw 'count' child squares inside the parent square
|
|
|
|
|
+ squares_drawn = 0
|
|
|
|
|
+ for i in range(n): # Rows
|
|
|
|
|
+ for j in range(n): # Columns
|
|
|
|
|
+ if squares_drawn >= count:
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ # Calculate position for each child square with proper spacing
|
|
|
|
|
+ child_x = parent_x + gap + j * (child_size + gap)
|
|
|
|
|
+ child_y = parent_y + gap + i * (child_size + gap)
|
|
|
|
|
+
|
|
|
|
|
+ # Add the child square to the dictionary
|
|
|
|
|
+ squares[next_number] = (child_x, child_y, child_size, parent_num)
|
|
|
|
|
+
|
|
|
|
|
+ next_number += 1
|
|
|
|
|
+ squares_drawn += 1
|
|
|
|
|
+ if squares_drawn >= count:
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ return squares
|
|
|
|
|
+
|
|
|
|
|
+ def draw_square_at_location(self, x, y, size, number, parent_number):
|
|
|
|
|
+ """
|
|
|
|
|
+ Draw a square at a specific location with a number in the center.
|
|
|
|
|
+
|
|
|
|
|
+ Args:
|
|
|
|
|
+ x: X coordinate of the top-left corner
|
|
|
|
|
+ y: Y coordinate of the top-left corner
|
|
|
|
|
+ size: Size of the square (width and height)
|
|
|
|
|
+ number: Number to display in the center
|
|
|
|
|
+ parent_number: The number of the parent square (None if no parent)
|
|
|
|
|
+ """
|
|
|
|
|
+ # Calculate the level of the square (how deep it is in the hierarchy)
|
|
|
|
|
+ level = self.get_square_level(number)
|
|
|
|
|
+
|
|
|
|
|
+ # Define light colors for different levels (with transparency effect)
|
|
|
|
|
+ colors = [
|
|
|
|
|
+ "#E0E0E0", # Level 0 (light gray, main square)
|
|
|
|
|
+ "#FFCCCC", # Level 1 (light red)
|
|
|
|
|
+ "#CCE5FF", # Level 2 (light blue)
|
|
|
|
|
+ "#CCFFCC", # Level 3 (light green)
|
|
|
|
|
+ "#E5CCFF", # Level 4 (light purple)
|
|
|
|
|
+ "#FFE5CC", # Level 5 (light orange)
|
|
|
|
|
+ "#D9CCAA", # Level 6 (light brown)
|
|
|
|
|
+ "#FFD9E5", # Level 7 (light pink)
|
|
|
|
|
+ "#FFFFCC", # Level 8 (light yellow)
|
|
|
|
|
+ "#CCFFFF" # Level 9+ (light cyan)
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ # Select color based on level
|
|
|
|
|
+ color = colors[level] if level < len(colors) else "gray"
|
|
|
|
|
+
|
|
|
|
|
+ # Draw the square with light colored fill (simulating transparency) and darker outline
|
|
|
|
|
+ outline_color = color.replace("#", "") # Get hex color without #
|
|
|
|
|
+ # Darken the outline color by reducing RGB values
|
|
|
|
|
+ if len(outline_color) == 6:
|
|
|
|
|
+ # Convert hex to RGB, then darken
|
|
|
|
|
+ r = max(0, int(outline_color[0:2], 16) - 60)
|
|
|
|
|
+ g = max(0, int(outline_color[2:4], 16) - 60)
|
|
|
|
|
+ b = max(0, int(outline_color[4:6], 16) - 60)
|
|
|
|
|
+ dark_outline = f"#{r:02x}{g:02x}{b:02x}"
|
|
|
|
|
+ else:
|
|
|
|
|
+ dark_outline = "black" # fallback
|
|
|
|
|
+
|
|
|
|
|
+ self.canvas.create_rectangle(x, y, x + size, y + size, outline=dark_outline, fill=color, width=1)
|
|
|
|
|
+
|
|
|
|
|
+ # Add number in the center of the square (ensure it's drawn last so it's not covered)
|
|
|
|
|
+ center_x = x + size // 2
|
|
|
|
|
+ center_y = y + size // 2
|
|
|
|
|
+ # Use black text for better contrast against any background
|
|
|
|
|
+ self.canvas.create_text(center_x, center_y, text=str(number), fill="black", font=("Arial", 10))
|
|
|
|
|
+
|
|
|
|
|
+ # Record the square's position information
|
|
|
|
|
+ self.square_positions[number] = (x, y, size, parent_number)
|
|
|
|
|
+
|
|
|
|
|
+ def get_square_level(self, number):
|
|
|
|
|
+ """
|
|
|
|
|
+ Calculate the level of a square in the hierarchy.
|
|
|
|
|
+ Level 0: Main square (number 0)
|
|
|
|
|
+ Level 1: Direct children of main square
|
|
|
|
|
+ Level 2: Children of level 1 squares
|
|
|
|
|
+ etc.
|
|
|
|
|
+ """
|
|
|
|
|
+ if number == 0:
|
|
|
|
|
+ return 0
|
|
|
|
|
+
|
|
|
|
|
+ level = 1 # Start at level 1 since we're past the main square
|
|
|
|
|
+ current_parent = self.squares[number][3] # Get parent number from (x, y, size, parent_number)
|
|
|
|
|
+
|
|
|
|
|
+ while current_parent is not None:
|
|
|
|
|
+ level += 1
|
|
|
|
|
+ if current_parent == 0:
|
|
|
|
|
+ break
|
|
|
|
|
+ current_parent = self.squares[current_parent][3] # Get parent's parent
|
|
|
|
|
+
|
|
|
|
|
+ return level
|
|
|
|
|
+
|
|
|
|
|
+ def run(self):
|
|
|
|
|
+ self.root.mainloop()
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def folder_to_config(folder_path, skip_folder=[]):
|
|
|
|
|
+ """
|
|
|
|
|
+ 根据文件夹路径生成配置列表,表示每个文件夹下的子项数量。
|
|
|
|
|
+
|
|
|
|
|
+ Args:
|
|
|
|
|
+ folder_path: 要分析的根文件夹路径
|
|
|
|
|
+ skip_folder: 不需要遍历的文件夹名称列表
|
|
|
|
|
+
|
|
|
|
|
+ Returns:
|
|
|
|
|
+ list: 包含(父级编号, 子项数量)元组的列表
|
|
|
|
|
+ (None, n) 表示根目录下有n个子项
|
|
|
|
|
+ """
|
|
|
|
|
+ import os
|
|
|
|
|
+
|
|
|
|
|
+ # 存储所有遇到的路径,按访问顺序编号
|
|
|
|
|
+ all_paths = [folder_path] # 根目录作为第一个元素
|
|
|
|
|
+ path_to_number = {} # 映射路径到编号
|
|
|
|
|
+
|
|
|
|
|
+ def dfs_collect_paths(path):
|
|
|
|
|
+ """深度优先收集所有路径"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ items = os.listdir(path)
|
|
|
|
|
+
|
|
|
|
|
+ for item in items:
|
|
|
|
|
+ item_path = os.path.join(path, item)
|
|
|
|
|
+ all_paths.append(item_path) # 将新路径添加到列表
|
|
|
|
|
+
|
|
|
|
|
+ if os.path.isdir(item_path):
|
|
|
|
|
+ # 检查是否在跳过列表中
|
|
|
|
|
+ if os.path.basename(item_path) not in skip_folder:
|
|
|
|
|
+ dfs_collect_paths(item_path) # 递归收集子目录
|
|
|
|
|
+ except PermissionError:
|
|
|
|
|
+ # 如果没有权限访问某个文件夹,则跳过
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ # 收集所有路径
|
|
|
|
|
+ dfs_collect_paths(folder_path)
|
|
|
|
|
+
|
|
|
|
|
+ # 为所有路径分配编号
|
|
|
|
|
+ for idx, path in enumerate(all_paths):
|
|
|
|
|
+ path_to_number[path] = idx + 1 # 编号从1开始
|
|
|
|
|
+
|
|
|
|
|
+ # 构建结果
|
|
|
|
|
+ result = []
|
|
|
|
|
+
|
|
|
|
|
+ # 添加根目录信息
|
|
|
|
|
+ try:
|
|
|
|
|
+ root_items = [item for item in os.listdir(folder_path)
|
|
|
|
|
+ if os.path.basename(os.path.join(folder_path, item)) not in skip_folder]
|
|
|
|
|
+ result.append((None, len(root_items))) # 根目录用None表示
|
|
|
|
|
+ except PermissionError:
|
|
|
|
|
+ result.append((None, 0))
|
|
|
|
|
+
|
|
|
|
|
+ # 为每个子目录添加信息
|
|
|
|
|
+ for path, number in path_to_number.items():
|
|
|
|
|
+ if path != folder_path and os.path.isdir(path): # 排除根目录,只处理子目录
|
|
|
|
|
+ try:
|
|
|
|
|
+ # 检查该目录是否在跳过列表中
|
|
|
|
|
+ dir_name = os.path.basename(path)
|
|
|
|
|
+ if dir_name in skip_folder:
|
|
|
|
|
+ # 如果在跳过列表中,将其视为空文件夹
|
|
|
|
|
+ result.append((number, 0))
|
|
|
|
|
+ else:
|
|
|
|
|
+ sub_items = [item for item in os.listdir(path)
|
|
|
|
|
+ if os.path.basename(os.path.join(path, item)) not in skip_folder]
|
|
|
|
|
+ result.append((number, len(sub_items))) # 使用该目录的编号
|
|
|
|
|
+ except PermissionError:
|
|
|
|
|
+ result.append((number, 0))
|
|
|
|
|
+
|
|
|
|
|
+ return result
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == "__main__":
|
|
|
|
|
+ config = folder_to_config(r"E:\agricultural_research_platform", skip_folder=["automatedDeployment", "node_modules", ".vscode"])
|
|
|
|
|
+ print(config)
|
|
|
|
|
+ app = SquareWindow(config)
|
|
|
|
|
+ app.run()
|