| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- import tkinter as tk
- class SquareWindow:
- def __init__(self, config, path_info=None):
- # 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
- # path_info: Optional dict mapping square numbers to file/folder names
- self.config = config
- self.path_info = path_info if path_info is not None else {}
-
- # 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)
- """
- # Get the name associated with this square number if available
- name = self.path_info.get(number, str(number))
- # 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
-
- # Create a semi-transparent effect using stipple pattern
- # This creates a checkerboard-like pattern that simulates transparency
- self.canvas.create_rectangle(x, y, x + size, y + size, outline=dark_outline, fill=color, width=1, stipple='gray50')
-
- # Draw a line segment at the bottom edge of the square (inside the square) only if size >= 50
- if size >= 50:
- line_y = y + size - 20 # Just above the bottom edge of the square
- self.canvas.create_line(x, line_y, x + size, line_y, fill="black", width=1)
-
- # Draw the name between the square and the line
- name_y = y + size - 10 # Position the name between square and line
- self.canvas.create_text(x + size // 2, name_y, text=name, fill="black", font=("Arial", 8))
-
- # 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:
- tuple: (config_list, path_info_dict)
- config_list: 包含(父级编号, 子项数量)元组的列表
- (None, n) 表示根目录下有n个子项
- path_info_dict: 映射编号到文件/文件夹名称的字典
- """
- import os
-
- # 存储路径到编号的映射
- path_to_number = {}
- number_to_name = {}
-
- # 首先为根目录分配编号
- path_to_number[folder_path] = 0
- number_to_name[0] = os.path.basename(folder_path)
-
- # 使用栈进行迭代式深度优先遍历,确保正确处理父子关系
- stack = [folder_path]
-
- while stack:
- current_path = stack.pop()
-
- try:
- items = os.listdir(current_path)
-
- # 获取当前目录的直接子项(过滤掉需要跳过的目录)
- direct_children = []
- for item in items:
- item_path = os.path.join(current_path, item)
-
- # 检查是否是需要跳过的目录
- if os.path.isdir(item_path) and os.path.basename(item_path) in skip_folder:
- continue
-
- direct_children.append(item_path)
-
- # 为所有直接子项分配编号
- for item_path in direct_children:
- if item_path not in path_to_number:
- number = len(path_to_number)
- path_to_number[item_path] = number
- number_to_name[number] = os.path.basename(item_path)
-
- # 如果是目录且不在跳过列表中,加入栈中待处理
- if os.path.isdir(item_path):
- stack.append(item_path)
-
- except PermissionError:
- # 如果没有权限访问某个文件夹,则跳过
- continue
-
- # 构建结果 - 计算每个目录的直接子项数量
- result = []
-
- # 添加根目录信息
- root_items = []
- try:
- items = os.listdir(folder_path)
- for item in items:
- item_path = os.path.join(folder_path, item)
- if not (os.path.isdir(item_path) and os.path.basename(item_path) in skip_folder):
- root_items.append(item_path)
- except PermissionError:
- pass
-
- result.append((None, len(root_items))) # 根目录用None表示
-
- # 为每个目录添加信息(除了根目录)
- for path in path_to_number:
- if path != folder_path and os.path.isdir(path): # 排除根目录,只处理子目录
- try:
- items = os.listdir(path)
- # 计算当前目录的直接子项数量(过滤掉需要跳过的目录)
- child_count = 0
- for item in items:
- item_path = os.path.join(path, item)
- if not (os.path.isdir(item_path) and os.path.basename(item_path) in skip_folder):
- child_count += 1
-
- parent_number = path_to_number[path]
- result.append((parent_number, child_count))
- except PermissionError:
- # 如果无法访问目录,子项数量为0
- parent_number = path_to_number[path]
- result.append((parent_number, 0))
-
- return result, number_to_name
- if __name__ == "__main__":
- config, path_info = folder_to_config(r"E:\agricultural_research_platform", skip_folder=["automatedDeployment", "node_modules", ".vscode", ".git", "images"])
- print(config)
- app = SquareWindow(config, path_info)
- app.run()
|