latest.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import tkinter as tk
  2. class SquareWindow:
  3. def __init__(self, config, path_info=None):
  4. # Configuration: [(parent_number, count), ...]
  5. # (None, 11) means draw 11 squares inside square #0 (the main square)
  6. # (2, 7) means draw 7 squares inside square #2
  7. # path_info: Optional dict mapping square numbers to file/folder names
  8. self.config = config
  9. self.path_info = path_info if path_info is not None else {}
  10. # Set window size to 1000x1000 pixels (needed for calculate_squares)
  11. self.window_size = 1000
  12. # Calculate squares based on config
  13. self.squares = self.calculate_squares()
  14. # Create the main window
  15. self.root = tk.Tk()
  16. self.root.title("Square Window")
  17. self.root.geometry(f"{self.window_size}x{self.window_size}")
  18. # Create a canvas to draw on
  19. self.canvas = tk.Canvas(self.root, width=self.window_size, height=self.window_size, bg="white")
  20. self.canvas.pack(fill=tk.BOTH, expand=True)
  21. # Property to store square information: {number: (x, y, size, parent_number)}
  22. self.square_positions = {}
  23. # Draw all squares based on the calculated squares dictionary
  24. for number, (x, y, size, parent_number) in self.squares.items():
  25. self.draw_square_at_location(x, y, size, number, parent_number)
  26. # Print the square positions dictionary
  27. output_content = f"Square positions: {self.square_positions}"
  28. print(output_content)
  29. # Save the output to print.log file
  30. with open("print.log", "w", encoding="utf-8") as f:
  31. f.write(output_content)
  32. def calculate_squares(self):
  33. """
  34. Calculate all squares based on the config.
  35. Config format: [(parent_number, count), ...]
  36. (None, count) means draw 'count' squares inside the main square (number 0)
  37. (number, count) means draw 'count' squares inside square with 'number'
  38. """
  39. import math
  40. # Initialize the main square (number 0)
  41. main_square_size = int(self.window_size * 0.9) # 90% of 1000 = 900
  42. margin = (self.window_size - main_square_size) // 2 # (1000 - 900) / 2 = 50
  43. # Dictionary to store all squares: {number: (x, y, size, parent_number)}
  44. squares = {}
  45. # Add the main square (number 0)
  46. squares[0] = (margin, margin, main_square_size, None)
  47. # Track the next available number for new squares
  48. next_number = 1
  49. # Process each configuration entry
  50. for parent_number, count in self.config:
  51. if parent_number is None:
  52. # Draw 'count' squares inside the main square (number 0)
  53. parent_num = 0
  54. else:
  55. parent_num = parent_number
  56. # Get the parent square's info
  57. if parent_num in squares:
  58. parent_x, parent_y, parent_size, _ = squares[parent_num]
  59. # Handle case where count is 0
  60. if count <= 0:
  61. continue # Skip if there are no items to draw
  62. # Calculate grid size for the child squares
  63. n = math.ceil(math.sqrt(count)) # Number of rows/columns needed
  64. child_size = (parent_size // n) - 2 # Size of each child square minus padding
  65. # Calculate spacing to distribute squares evenly with gaps
  66. total_used_space = n * child_size # Total space occupied by squares
  67. remaining_space = parent_size - total_used_space # Remaining space for gaps
  68. gap = remaining_space // (n + 1) # Gap between squares and margins
  69. # Draw 'count' child squares inside the parent square
  70. squares_drawn = 0
  71. for i in range(n): # Rows
  72. for j in range(n): # Columns
  73. if squares_drawn >= count:
  74. break
  75. # Calculate position for each child square with proper spacing
  76. child_x = parent_x + gap + j * (child_size + gap)
  77. child_y = parent_y + gap + i * (child_size + gap)
  78. # Add the child square to the dictionary
  79. squares[next_number] = (child_x, child_y, child_size, parent_num)
  80. next_number += 1
  81. squares_drawn += 1
  82. if squares_drawn >= count:
  83. break
  84. return squares
  85. def draw_square_at_location(self, x, y, size, number, parent_number):
  86. """
  87. Draw a square at a specific location with a number in the center.
  88. Args:
  89. x: X coordinate of the top-left corner
  90. y: Y coordinate of the top-left corner
  91. size: Size of the square (width and height)
  92. number: Number to display in the center
  93. parent_number: The number of the parent square (None if no parent)
  94. """
  95. # Get the name associated with this square number if available
  96. name = self.path_info.get(number, str(number))
  97. # Calculate the level of the square (how deep it is in the hierarchy)
  98. level = self.get_square_level(number)
  99. # Define light colors for different levels (with transparency effect)
  100. colors = [
  101. "#E0E0E0", # Level 0 (light gray, main square)
  102. "#FFCCCC", # Level 1 (light red)
  103. "#CCE5FF", # Level 2 (light blue)
  104. "#CCFFCC", # Level 3 (light green)
  105. "#E5CCFF", # Level 4 (light purple)
  106. "#FFE5CC", # Level 5 (light orange)
  107. "#D9CCAA", # Level 6 (light brown)
  108. "#FFD9E5", # Level 7 (light pink)
  109. "#FFFFCC", # Level 8 (light yellow)
  110. "#CCFFFF" # Level 9+ (light cyan)
  111. ]
  112. # Select color based on level
  113. color = colors[level] if level < len(colors) else "gray"
  114. # Draw the square with light colored fill (simulating transparency) and darker outline
  115. outline_color = color.replace("#", "") # Get hex color without #
  116. # Darken the outline color by reducing RGB values
  117. if len(outline_color) == 6:
  118. # Convert hex to RGB, then darken
  119. r = max(0, int(outline_color[0:2], 16) - 60)
  120. g = max(0, int(outline_color[2:4], 16) - 60)
  121. b = max(0, int(outline_color[4:6], 16) - 60)
  122. dark_outline = f"#{r:02x}{g:02x}{b:02x}"
  123. else:
  124. dark_outline = "black" # fallback
  125. # Create a semi-transparent effect using stipple pattern
  126. # This creates a checkerboard-like pattern that simulates transparency
  127. self.canvas.create_rectangle(x, y, x + size, y + size, outline=dark_outline, fill=color, width=1, stipple='gray50')
  128. # Draw a line segment at the bottom edge of the square (inside the square) only if size >= 50
  129. if size >= 50:
  130. line_y = y + size - 20 # Just above the bottom edge of the square
  131. self.canvas.create_line(x, line_y, x + size, line_y, fill="black", width=1)
  132. # Draw the name between the square and the line
  133. name_y = y + size - 10 # Position the name between square and line
  134. self.canvas.create_text(x + size // 2, name_y, text=name, fill="black", font=("Arial", 8))
  135. # Record the square's position information
  136. self.square_positions[number] = (x, y, size, parent_number)
  137. def get_square_level(self, number):
  138. """
  139. Calculate the level of a square in the hierarchy.
  140. Level 0: Main square (number 0)
  141. Level 1: Direct children of main square
  142. Level 2: Children of level 1 squares
  143. etc.
  144. """
  145. if number == 0:
  146. return 0
  147. level = 1 # Start at level 1 since we're past the main square
  148. current_parent = self.squares[number][3] # Get parent number from (x, y, size, parent_number)
  149. while current_parent is not None:
  150. level += 1
  151. if current_parent == 0:
  152. break
  153. current_parent = self.squares[current_parent][3] # Get parent's parent
  154. return level
  155. def run(self):
  156. self.root.mainloop()
  157. def folder_to_config(folder_path, skip_folder=[]):
  158. """
  159. 根据文件夹路径生成配置列表,表示每个文件夹下的子项数量。
  160. Args:
  161. folder_path: 要分析的根文件夹路径
  162. skip_folder: 不需要遍历的文件夹名称列表
  163. Returns:
  164. tuple: (config_list, path_info_dict)
  165. config_list: 包含(父级编号, 子项数量)元组的列表
  166. (None, n) 表示根目录下有n个子项
  167. path_info_dict: 映射编号到文件/文件夹名称的字典
  168. """
  169. import os
  170. # 存储路径到编号的映射
  171. path_to_number = {}
  172. number_to_name = {}
  173. # 首先为根目录分配编号
  174. path_to_number[folder_path] = 0
  175. number_to_name[0] = os.path.basename(folder_path)
  176. # 使用栈进行迭代式深度优先遍历,确保正确处理父子关系
  177. stack = [folder_path]
  178. while stack:
  179. current_path = stack.pop()
  180. try:
  181. items = os.listdir(current_path)
  182. # 获取当前目录的直接子项(过滤掉需要跳过的目录)
  183. direct_children = []
  184. for item in items:
  185. item_path = os.path.join(current_path, item)
  186. # 检查是否是需要跳过的目录
  187. if os.path.isdir(item_path) and os.path.basename(item_path) in skip_folder:
  188. continue
  189. direct_children.append(item_path)
  190. # 为所有直接子项分配编号
  191. for item_path in direct_children:
  192. if item_path not in path_to_number:
  193. number = len(path_to_number)
  194. path_to_number[item_path] = number
  195. number_to_name[number] = os.path.basename(item_path)
  196. # 如果是目录且不在跳过列表中,加入栈中待处理
  197. if os.path.isdir(item_path):
  198. stack.append(item_path)
  199. except PermissionError:
  200. # 如果没有权限访问某个文件夹,则跳过
  201. continue
  202. # 构建结果 - 计算每个目录的直接子项数量
  203. result = []
  204. # 添加根目录信息
  205. root_items = []
  206. try:
  207. items = os.listdir(folder_path)
  208. for item in items:
  209. item_path = os.path.join(folder_path, item)
  210. if not (os.path.isdir(item_path) and os.path.basename(item_path) in skip_folder):
  211. root_items.append(item_path)
  212. except PermissionError:
  213. pass
  214. result.append((None, len(root_items))) # 根目录用None表示
  215. # 为每个目录添加信息(除了根目录)
  216. for path in path_to_number:
  217. if path != folder_path and os.path.isdir(path): # 排除根目录,只处理子目录
  218. try:
  219. items = os.listdir(path)
  220. # 计算当前目录的直接子项数量(过滤掉需要跳过的目录)
  221. child_count = 0
  222. for item in items:
  223. item_path = os.path.join(path, item)
  224. if not (os.path.isdir(item_path) and os.path.basename(item_path) in skip_folder):
  225. child_count += 1
  226. parent_number = path_to_number[path]
  227. result.append((parent_number, child_count))
  228. except PermissionError:
  229. # 如果无法访问目录,子项数量为0
  230. parent_number = path_to_number[path]
  231. result.append((parent_number, 0))
  232. return result, number_to_name
  233. if __name__ == "__main__":
  234. config, path_info = folder_to_config(r"E:\agricultural_research_platform", skip_folder=["automatedDeployment", "node_modules", ".vscode", ".git", "images"])
  235. print(config)
  236. app = SquareWindow(config, path_info)
  237. app.run()